Merge "Increase PerformBackupTask unit coverage 2"
diff --git a/Android.bp b/Android.bp
index 4d2e5e1..8c81534 100644
--- a/Android.bp
+++ b/Android.bp
@@ -27,6 +27,7 @@
java_library {
name: "framework",
+ installable: true,
srcs: [
// From build/make/core/pathmap.mk FRAMEWORK_BASE_SUBDIRS
@@ -104,6 +105,7 @@
"core/java/android/app/timedetector/ITimeDetectorService.aidl",
"core/java/android/app/timezone/ICallback.aidl",
"core/java/android/app/timezone/IRulesManager.aidl",
+ "core/java/android/app/timezonedetector/ITimeZoneDetectorService.aidl",
"core/java/android/app/usage/ICacheQuotaService.aidl",
"core/java/android/app/usage/IStorageStatsManager.aidl",
"core/java/android/app/usage/IUsageStatsManager.aidl",
@@ -148,12 +150,14 @@
"core/java/android/hardware/IConsumerIrService.aidl",
"core/java/android/hardware/ISerialManager.aidl",
"core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl",
+ "core/java/android/hardware/biometrics/IBiometricServiceLockoutResetCallback.aidl",
"core/java/android/hardware/display/IDisplayManager.aidl",
"core/java/android/hardware/display/IDisplayManagerCallback.aidl",
"core/java/android/hardware/display/IVirtualDisplayCallback.aidl",
"core/java/android/hardware/fingerprint/IFingerprintClientActiveCallback.aidl",
+ "core/java/android/hardware/face/IFaceService.aidl",
+ "core/java/android/hardware/face/IFaceServiceReceiver.aidl",
"core/java/android/hardware/fingerprint/IFingerprintService.aidl",
- "core/java/android/hardware/fingerprint/IFingerprintServiceLockoutResetCallback.aidl",
"core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl",
"core/java/android/hardware/hdmi/IHdmiControlCallback.aidl",
"core/java/android/hardware/hdmi/IHdmiControlService.aidl",
@@ -703,6 +707,7 @@
// specified on the build command line.
java_library {
name: "framework-oahl-backward-compatibility",
+ installable: true,
srcs: [
"core/java/android/content/pm/OrgApacheHttpLegacyUpdater.java",
],
@@ -758,6 +763,7 @@
// ============================================================
java_library {
name: "ext",
+ installable: true,
no_framework_libs: true,
static_libs: [
"libphonenumber-platform",
@@ -1179,6 +1185,7 @@
"core/java/overview.html",
":current-support-api",
],
+ dex_api_filename: "public-dex.txt",
private_dex_api_filename: "private-dex.txt",
removed_dex_api_filename: "removed-dex.txt",
args: framework_docs_args +
@@ -1189,6 +1196,24 @@
" -showAnnotation android.annotation.TestApi",
}
+droiddoc {
+ name: "hiddenapi-mappings",
+ defaults: ["framework-docs-default"],
+ arg_files: [
+ "core/res/AndroidManifest.xml",
+ ":api-version-xml",
+ "core/java/overview.html",
+ ":current-support-api",
+ ],
+ dex_mapping_filename: "dex-mapping.txt",
+ args: framework_docs_args +
+ " -referenceonly" +
+ " -nodocs" +
+ " -showUnannotated" +
+ " -showAnnotation android.annotation.SystemApi" +
+ " -showAnnotation android.annotation.TestApi",
+}
+
filegroup {
name: "apache-http-stubs-sources",
srcs: [
diff --git a/Android.mk b/Android.mk
index 031809c..7890983 100644
--- a/Android.mk
+++ b/Android.mk
@@ -712,6 +712,7 @@
LOCAL_SRC_GREYLIST := frameworks/base/config/hiddenapi-light-greylist.txt
LOCAL_SRC_VENDOR_LIST := frameworks/base/config/hiddenapi-vendor-list.txt
LOCAL_SRC_FORCE_BLACKLIST := frameworks/base/config/hiddenapi-force-blacklist.txt
+LOCAL_SRC_PUBLIC_API := $(INTERNAL_PLATFORM_DEX_API_FILE)
LOCAL_SRC_PRIVATE_API := $(INTERNAL_PLATFORM_PRIVATE_DEX_API_FILE)
LOCAL_SRC_REMOVED_API := $(INTERNAL_PLATFORM_REMOVED_DEX_API_FILE)
@@ -719,6 +720,7 @@
$(LOCAL_SRC_GREYLIST) \
$(LOCAL_SRC_VENDOR_LIST) \
$(LOCAL_SRC_FORCE_BLACKLIST) \
+ $(LOCAL_SRC_PUBLIC_API) \
$(LOCAL_SRC_PRIVATE_API) \
$(LOCAL_SRC_REMOVED_API)
@@ -791,7 +793,8 @@
# (4) subtract entries shared with LOCAL_LIGHT_GREYLIST
$(LOCAL_DARK_GREYLIST): $(LOCAL_SRC_ALL) $(LOCAL_LIGHT_GREYLIST)
comm -13 <(sort $(LOCAL_LIGHT_GREYLIST) $(LOCAL_SRC_FORCE_BLACKLIST)) \
- <(sed 's/\->.*//' $(LOCAL_LIGHT_GREYLIST) | sed 's/\(.*\/\).*/\1/' | sort | uniq | \
+ <(cat $(LOCAL_SRC_PUBLIC_API) $(LOCAL_LIGHT_GREYLIST) | \
+ sed 's/\->.*//' | sed 's/\(.*\/\).*/\1/' | sort | uniq | \
while read PKG_NAME; do \
grep -E "^$${PKG_NAME}[^/;]*;" $(LOCAL_SRC_PRIVATE_API); \
done | sort | uniq) \
diff --git a/CleanSpec.mk b/CleanSpec.mk
index 3f19b8f..2247e43 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -246,6 +246,7 @@
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/statsdprotolite_intermediates)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/framework/com.android.mediadrm.signer.jar)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/framework/com.android.location.provider.jar)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/framework/com.android.future.usb.accessory.jar)
# ******************************************************************
# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST ABOVE THIS BANNER
# ******************************************************************
diff --git a/apct-tests/perftests/core/src/android/graphics/perftests/RenderNodePerfTest.java b/apct-tests/perftests/core/src/android/graphics/perftests/RenderNodePerfTest.java
index dfbabeb..a283e06 100644
--- a/apct-tests/perftests/core/src/android/graphics/perftests/RenderNodePerfTest.java
+++ b/apct-tests/perftests/core/src/android/graphics/perftests/RenderNodePerfTest.java
@@ -47,8 +47,7 @@
public void testCreateRenderNodeNoName() {
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
while (state.keepRunning()) {
- RenderNode node = RenderNode.create(null, null);
- node.destroy();
+ RenderNode.create(null, null);
}
}
@@ -56,8 +55,7 @@
public void testCreateRenderNode() {
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
while (state.keepRunning()) {
- RenderNode node = RenderNode.create("LinearLayout", null);
- node.destroy();
+ RenderNode.create("LinearLayout", null);
}
}
diff --git a/apct-tests/perftests/core/src/android/os/BinderCallsStatsPerfTest.java b/apct-tests/perftests/core/src/android/os/BinderCallsStatsPerfTest.java
index e4a8503..e126fb8 100644
--- a/apct-tests/perftests/core/src/android/os/BinderCallsStatsPerfTest.java
+++ b/apct-tests/perftests/core/src/android/os/BinderCallsStatsPerfTest.java
@@ -45,7 +45,7 @@
@Before
public void setUp() {
- mBinderCallsStats = new BinderCallsStats(true);
+ mBinderCallsStats = new BinderCallsStats();
}
@After
@@ -54,6 +54,7 @@
@Test
public void timeCallSession() {
+ mBinderCallsStats.setDetailedTracking(true);
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
Binder b = new Binder();
int i = 0;
@@ -66,9 +67,9 @@
@Test
public void timeCallSessionTrackingDisabled() {
+ mBinderCallsStats.setDetailedTracking(false);
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
Binder b = new Binder();
- mBinderCallsStats = new BinderCallsStats(false);
while (state.keepRunning()) {
BinderCallsStats.CallSession s = mBinderCallsStats.callStarted(b, 0);
mBinderCallsStats.callEnded(s, 0, 0);
diff --git a/apct-tests/perftests/core/src/android/os/TracePerfTest.java b/apct-tests/perftests/core/src/android/os/TracePerfTest.java
new file mode 100644
index 0000000..8e5cfaa
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/os/TracePerfTest.java
@@ -0,0 +1,86 @@
+/*
+ * 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.perftests.utils.ShellHelper;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class TracePerfTest {
+ @Rule
+ public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ @BeforeClass
+ public static void startTracing() {
+ ShellHelper.runShellCommandRaw("atrace -c --async_start -a *");
+ }
+
+ @AfterClass
+ public static void endTracing() {
+ ShellHelper.runShellCommandRaw("atrace --async_stop");
+ }
+
+ @Before
+ public void verifyTracingEnabled() {
+ Assert.assertTrue(Trace.isEnabled());
+ }
+
+ @Test
+ public void testEnabled() {
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ Trace.isEnabled();
+ }
+ }
+
+ @Test
+ public void testBeginEndSection() {
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ Trace.beginSection("testBeginEndSection");
+ Trace.endSection();
+ }
+ }
+
+ @Test
+ public void testAsyncBeginEnd() {
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ Trace.beginAsyncSection("testAsyncBeginEnd", 42);
+ Trace.endAsyncSection("testAsyncBeginEnd", 42);
+ }
+ }
+
+ @Test
+ public void testCounter() {
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ Trace.setCounter("testCounter", 123);
+ }
+ }
+}
diff --git a/apct-tests/perftests/core/src/android/text/BoringLayoutCreateDrawPerfTest.java b/apct-tests/perftests/core/src/android/text/BoringLayoutCreateDrawPerfTest.java
index 586c385..64f2800 100644
--- a/apct-tests/perftests/core/src/android/text/BoringLayoutCreateDrawPerfTest.java
+++ b/apct-tests/perftests/core/src/android/text/BoringLayoutCreateDrawPerfTest.java
@@ -46,7 +46,7 @@
private static final float SPACING_ADD = 10f;
private static final float SPACING_MULT = 1.5f;
- @Parameterized.Parameters(name = "cached={3},{1}chars,{0}")
+ @Parameterized.Parameters(name = "cached {3} {1}chars {0}")
public static Collection cases() {
final List<Object[]> params = new ArrayList<>();
for (int length : new int[]{128}) {
diff --git a/apct-tests/perftests/core/src/android/text/BoringLayoutIsBoringPerfTest.java b/apct-tests/perftests/core/src/android/text/BoringLayoutIsBoringPerfTest.java
index 9d11f29..194a88c 100644
--- a/apct-tests/perftests/core/src/android/text/BoringLayoutIsBoringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/text/BoringLayoutIsBoringPerfTest.java
@@ -40,7 +40,7 @@
private static final boolean[] BOOLEANS = new boolean[]{false, true};
- @Parameterized.Parameters(name = "cached={4},{1}chars,{0}")
+ @Parameterized.Parameters(name = "cached {4} {1}chars {0}")
public static Collection cases() {
final List<Object[]> params = new ArrayList<>();
for (int length : new int[]{128}) {
diff --git a/apct-tests/perftests/core/src/android/text/PaintMeasureDrawPerfTest.java b/apct-tests/perftests/core/src/android/text/PaintMeasureDrawPerfTest.java
index 6768798..ad5a34e 100644
--- a/apct-tests/perftests/core/src/android/text/PaintMeasureDrawPerfTest.java
+++ b/apct-tests/perftests/core/src/android/text/PaintMeasureDrawPerfTest.java
@@ -42,7 +42,7 @@
private static final boolean[] BOOLEANS = new boolean[]{false, true};
- @Parameterized.Parameters(name = "cached={1},{0}chars")
+ @Parameterized.Parameters(name = "cached {1} {0}chars")
public static Collection cases() {
final List<Object[]> params = new ArrayList<>();
for (int length : new int[]{128}) {
diff --git a/apct-tests/perftests/core/src/android/text/StaticLayoutCreateDrawPerfTest.java b/apct-tests/perftests/core/src/android/text/StaticLayoutCreateDrawPerfTest.java
index bfdb758..deb2b0a 100644
--- a/apct-tests/perftests/core/src/android/text/StaticLayoutCreateDrawPerfTest.java
+++ b/apct-tests/perftests/core/src/android/text/StaticLayoutCreateDrawPerfTest.java
@@ -50,7 +50,7 @@
@Rule
public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
- @Parameterized.Parameters(name = "cached={3},{1}chars,{0}")
+ @Parameterized.Parameters(name = "cached {3} {1}chars {0}")
public static Collection cases() {
final List<Object[]> params = new ArrayList<>();
for (int length : new int[]{128}) {
diff --git a/apct-tests/perftests/core/src/android/text/TextViewSetTextMeasurePerfTest.java b/apct-tests/perftests/core/src/android/text/TextViewSetTextMeasurePerfTest.java
index ff2d57e..c2898fa 100644
--- a/apct-tests/perftests/core/src/android/text/TextViewSetTextMeasurePerfTest.java
+++ b/apct-tests/perftests/core/src/android/text/TextViewSetTextMeasurePerfTest.java
@@ -51,7 +51,7 @@
@Rule
public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
- @Parameterized.Parameters(name = "cached={3},{1}chars,{0}")
+ @Parameterized.Parameters(name = "cached {3} {1}chars {0}")
public static Collection cases() {
final List<Object[]> params = new ArrayList<>();
for (int length : new int[]{128}) {
diff --git a/apct-tests/perftests/utils/src/android/perftests/utils/ShellHelper.java b/apct-tests/perftests/utils/src/android/perftests/utils/ShellHelper.java
index cae87fb..895547d 100644
--- a/apct-tests/perftests/utils/src/android/perftests/utils/ShellHelper.java
+++ b/apct-tests/perftests/utils/src/android/perftests/utils/ShellHelper.java
@@ -37,6 +37,14 @@
@NonNull
public static String runShellCommand(@NonNull String template, Object...args) {
String command = String.format(template, args);
+ return runShellCommandRaw(command);
+ }
+
+ /**
+ * Runs a Shell command, returning a trimmed response.
+ */
+ @NonNull
+ public static String runShellCommandRaw(@NonNull String command) {
UiAutomation automan = InstrumentationRegistry.getInstrumentation()
.getUiAutomation();
ParcelFileDescriptor pfd = automan.executeShellCommand(command);
diff --git a/api/current.txt b/api/current.txt
index 41211f6..e3db256 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -6120,8 +6120,10 @@
}
public final class UiAutomation {
+ method public void adoptShellPermissionIdentity();
method public void clearWindowAnimationFrameStats();
method public boolean clearWindowContentFrameStats(int);
+ method public void dropShellPermissionIdentity();
method public android.view.accessibility.AccessibilityEvent executeAndWaitForEvent(java.lang.Runnable, android.app.UiAutomation.AccessibilityEventFilter, long) throws java.util.concurrent.TimeoutException;
method public android.os.ParcelFileDescriptor executeShellCommand(java.lang.String);
method public android.view.accessibility.AccessibilityNodeInfo findFocus(int);
@@ -9135,6 +9137,7 @@
method public android.os.Bundle call(java.lang.String, java.lang.String, android.os.Bundle) throws android.os.RemoteException;
method public final android.net.Uri canonicalize(android.net.Uri) throws android.os.RemoteException;
method public void close();
+ method public static void closeQuietly(android.content.ContentProviderClient);
method public int delete(android.net.Uri, java.lang.String, java.lang.String[]) throws android.os.RemoteException;
method public android.content.ContentProvider getLocalContentProvider();
method public java.lang.String[] getStreamTypes(android.net.Uri, java.lang.String) throws android.os.RemoteException;
@@ -10137,6 +10140,7 @@
field public static final int FLAG_ACTIVITY_SINGLE_TOP = 536870912; // 0x20000000
field public static final int FLAG_ACTIVITY_TASK_ON_HOME = 16384; // 0x4000
field public static final int FLAG_DEBUG_LOG_RESOLUTION = 8; // 0x8
+ field public static final int FLAG_DIRECT_BOOT_AUTO = 256; // 0x100
field public static final int FLAG_EXCLUDE_STOPPED_PACKAGES = 16; // 0x10
field public static final int FLAG_FROM_BACKGROUND = 4; // 0x4
field public static final int FLAG_GRANT_PERSISTABLE_URI_PERMISSION = 64; // 0x40
@@ -11388,6 +11392,7 @@
field public static final int INSTALL_REASON_USER = 4; // 0x4
field public static final int MATCH_ALL = 131072; // 0x20000
field public static final int MATCH_DEFAULT_ONLY = 65536; // 0x10000
+ field public static final int MATCH_DIRECT_BOOT_AUTO = 268435456; // 0x10000000
field public static final int MATCH_DIRECT_BOOT_AWARE = 524288; // 0x80000
field public static final int MATCH_DIRECT_BOOT_UNAWARE = 262144; // 0x40000
field public static final int MATCH_DISABLED_COMPONENTS = 512; // 0x200
@@ -17123,6 +17128,8 @@
field public static final int GAMAL = 12; // 0xc
field public static final int HAH = 13; // 0xd
field public static final int HAMZA_ON_HEH_GOAL = 14; // 0xe
+ field public static final int HANIFI_ROHINGYA_KINNA_YA = 100; // 0x64
+ field public static final int HANIFI_ROHINGYA_PA = 101; // 0x65
field public static final int HE = 15; // 0xf
field public static final int HEH = 16; // 0x10
field public static final int HEH_GOAL = 17; // 0x11
@@ -17374,6 +17381,8 @@
field public static final int CHEROKEE_ID = 32; // 0x20
field public static final android.icu.lang.UCharacter.UnicodeBlock CHEROKEE_SUPPLEMENT;
field public static final int CHEROKEE_SUPPLEMENT_ID = 255; // 0xff
+ field public static final android.icu.lang.UCharacter.UnicodeBlock CHESS_SYMBOLS;
+ field public static final int CHESS_SYMBOLS_ID = 281; // 0x119
field public static final android.icu.lang.UCharacter.UnicodeBlock CJK_COMPATIBILITY;
field public static final android.icu.lang.UCharacter.UnicodeBlock CJK_COMPATIBILITY_FORMS;
field public static final int CJK_COMPATIBILITY_FORMS_ID = 83; // 0x53
@@ -17450,6 +17459,8 @@
field public static final int DEVANAGARI_ID = 15; // 0xf
field public static final android.icu.lang.UCharacter.UnicodeBlock DINGBATS;
field public static final int DINGBATS_ID = 56; // 0x38
+ field public static final android.icu.lang.UCharacter.UnicodeBlock DOGRA;
+ field public static final int DOGRA_ID = 282; // 0x11a
field public static final android.icu.lang.UCharacter.UnicodeBlock DOMINO_TILES;
field public static final int DOMINO_TILES_ID = 171; // 0xab
field public static final android.icu.lang.UCharacter.UnicodeBlock DUPLOYAN;
@@ -17485,6 +17496,8 @@
field public static final int GEOMETRIC_SHAPES_EXTENDED_ID = 227; // 0xe3
field public static final int GEOMETRIC_SHAPES_ID = 54; // 0x36
field public static final android.icu.lang.UCharacter.UnicodeBlock GEORGIAN;
+ field public static final android.icu.lang.UCharacter.UnicodeBlock GEORGIAN_EXTENDED;
+ field public static final int GEORGIAN_EXTENDED_ID = 283; // 0x11b
field public static final int GEORGIAN_ID = 29; // 0x1d
field public static final android.icu.lang.UCharacter.UnicodeBlock GEORGIAN_SUPPLEMENT;
field public static final int GEORGIAN_SUPPLEMENT_ID = 135; // 0x87
@@ -17502,6 +17515,8 @@
field public static final int GREEK_ID = 8; // 0x8
field public static final android.icu.lang.UCharacter.UnicodeBlock GUJARATI;
field public static final int GUJARATI_ID = 18; // 0x12
+ field public static final android.icu.lang.UCharacter.UnicodeBlock GUNJALA_GONDI;
+ field public static final int GUNJALA_GONDI_ID = 284; // 0x11c
field public static final android.icu.lang.UCharacter.UnicodeBlock GURMUKHI;
field public static final int GURMUKHI_ID = 17; // 0x11
field public static final android.icu.lang.UCharacter.UnicodeBlock HALFWIDTH_AND_FULLWIDTH_FORMS;
@@ -17516,6 +17531,8 @@
field public static final int HANGUL_JAMO_ID = 30; // 0x1e
field public static final android.icu.lang.UCharacter.UnicodeBlock HANGUL_SYLLABLES;
field public static final int HANGUL_SYLLABLES_ID = 74; // 0x4a
+ field public static final android.icu.lang.UCharacter.UnicodeBlock HANIFI_ROHINGYA;
+ field public static final int HANIFI_ROHINGYA_ID = 285; // 0x11d
field public static final android.icu.lang.UCharacter.UnicodeBlock HANUNOO;
field public static final int HANUNOO_ID = 99; // 0x63
field public static final android.icu.lang.UCharacter.UnicodeBlock HATRAN;
@@ -17534,6 +17551,8 @@
field public static final int IDEOGRAPHIC_SYMBOLS_AND_PUNCTUATION_ID = 267; // 0x10b
field public static final android.icu.lang.UCharacter.UnicodeBlock IMPERIAL_ARAMAIC;
field public static final int IMPERIAL_ARAMAIC_ID = 186; // 0xba
+ field public static final android.icu.lang.UCharacter.UnicodeBlock INDIC_SIYAQ_NUMBERS;
+ field public static final int INDIC_SIYAQ_NUMBERS_ID = 286; // 0x11e
field public static final android.icu.lang.UCharacter.UnicodeBlock INSCRIPTIONAL_PAHLAVI;
field public static final int INSCRIPTIONAL_PAHLAVI_ID = 190; // 0xbe
field public static final android.icu.lang.UCharacter.UnicodeBlock INSCRIPTIONAL_PARTHIAN;
@@ -17612,6 +17631,8 @@
field public static final int MAHAJANI_ID = 233; // 0xe9
field public static final android.icu.lang.UCharacter.UnicodeBlock MAHJONG_TILES;
field public static final int MAHJONG_TILES_ID = 170; // 0xaa
+ field public static final android.icu.lang.UCharacter.UnicodeBlock MAKASAR;
+ field public static final int MAKASAR_ID = 287; // 0x11f
field public static final android.icu.lang.UCharacter.UnicodeBlock MALAYALAM;
field public static final int MALAYALAM_ID = 23; // 0x17
field public static final android.icu.lang.UCharacter.UnicodeBlock MANDAIC;
@@ -17626,6 +17647,10 @@
field public static final int MATHEMATICAL_ALPHANUMERIC_SYMBOLS_ID = 93; // 0x5d
field public static final android.icu.lang.UCharacter.UnicodeBlock MATHEMATICAL_OPERATORS;
field public static final int MATHEMATICAL_OPERATORS_ID = 47; // 0x2f
+ field public static final android.icu.lang.UCharacter.UnicodeBlock MAYAN_NUMERALS;
+ field public static final int MAYAN_NUMERALS_ID = 288; // 0x120
+ field public static final android.icu.lang.UCharacter.UnicodeBlock MEDEFAIDRIN;
+ field public static final int MEDEFAIDRIN_ID = 289; // 0x121
field public static final android.icu.lang.UCharacter.UnicodeBlock MEETEI_MAYEK;
field public static final android.icu.lang.UCharacter.UnicodeBlock MEETEI_MAYEK_EXTENSIONS;
field public static final int MEETEI_MAYEK_EXTENSIONS_ID = 213; // 0xd5
@@ -17695,6 +17720,8 @@
field public static final int OLD_PERMIC_ID = 241; // 0xf1
field public static final android.icu.lang.UCharacter.UnicodeBlock OLD_PERSIAN;
field public static final int OLD_PERSIAN_ID = 140; // 0x8c
+ field public static final android.icu.lang.UCharacter.UnicodeBlock OLD_SOGDIAN;
+ field public static final int OLD_SOGDIAN_ID = 290; // 0x122
field public static final android.icu.lang.UCharacter.UnicodeBlock OLD_SOUTH_ARABIAN;
field public static final int OLD_SOUTH_ARABIAN_ID = 187; // 0xbb
field public static final android.icu.lang.UCharacter.UnicodeBlock OLD_TURKIC;
@@ -17759,6 +17786,8 @@
field public static final int SINHALA_ID = 24; // 0x18
field public static final android.icu.lang.UCharacter.UnicodeBlock SMALL_FORM_VARIANTS;
field public static final int SMALL_FORM_VARIANTS_ID = 84; // 0x54
+ field public static final android.icu.lang.UCharacter.UnicodeBlock SOGDIAN;
+ field public static final int SOGDIAN_ID = 291; // 0x123
field public static final android.icu.lang.UCharacter.UnicodeBlock SORA_SOMPENG;
field public static final int SORA_SOMPENG_ID = 218; // 0xda
field public static final android.icu.lang.UCharacter.UnicodeBlock SOYOMBO;
@@ -17883,6 +17912,7 @@
field public static final int OTHER = 0; // 0x0
field public static final int REGIONAL_INDICATOR = 13; // 0xd
field public static final int SINGLE_QUOTE = 15; // 0xf
+ field public static final int WSEGSPACE = 22; // 0x16
field public static final int ZWJ = 21; // 0x15
}
@@ -18014,6 +18044,7 @@
field public static final int EMOJI_MODIFIER = 59; // 0x3b
field public static final int EMOJI_MODIFIER_BASE = 60; // 0x3c
field public static final int EMOJI_PRESENTATION = 58; // 0x3a
+ field public static final int EXTENDED_PICTOGRAPHIC = 64; // 0x40
field public static final int EXTENDER = 8; // 0x8
field public static final int FULL_COMPOSITION_EXCLUSION = 9; // 0x9
field public static final int GENERAL_CATEGORY = 4101; // 0x1005
@@ -18144,6 +18175,7 @@
field public static final int DEMOTIC_EGYPTIAN = 69; // 0x45
field public static final int DESERET = 9; // 0x9
field public static final int DEVANAGARI = 10; // 0xa
+ field public static final int DOGRA = 178; // 0xb2
field public static final int DUPLOYAN = 135; // 0x87
field public static final int EASTERN_SYRIAC = 97; // 0x61
field public static final int EGYPTIAN_HIEROGLYPHS = 71; // 0x47
@@ -18156,9 +18188,11 @@
field public static final int GRANTHA = 137; // 0x89
field public static final int GREEK = 14; // 0xe
field public static final int GUJARATI = 15; // 0xf
+ field public static final int GUNJALA_GONDI = 179; // 0xb3
field public static final int GURMUKHI = 16; // 0x10
field public static final int HAN = 17; // 0x11
field public static final int HANGUL = 18; // 0x12
+ field public static final int HANIFI_ROHINGYA = 182; // 0xb6
field public static final int HANUNOO = 43; // 0x2b
field public static final int HAN_WITH_BOPOMOFO = 172; // 0xac
field public static final int HARAPPAN_INDUS = 77; // 0x4d
@@ -18201,6 +18235,7 @@
field public static final int LYCIAN = 107; // 0x6b
field public static final int LYDIAN = 108; // 0x6c
field public static final int MAHAJANI = 160; // 0xa0
+ field public static final int MAKASAR = 180; // 0xb4
field public static final int MALAYALAM = 26; // 0x1a
field public static final int MANDAEAN = 84; // 0x54
field public static final int MANDAIC = 84; // 0x54
@@ -18209,6 +18244,7 @@
field public static final int MASARAM_GONDI = 175; // 0xaf
field public static final int MATHEMATICAL_NOTATION = 128; // 0x80
field public static final int MAYAN_HIEROGLYPHS = 85; // 0x55
+ field public static final int MEDEFAIDRIN = 181; // 0xb5
field public static final int MEITEI_MAYEK = 115; // 0x73
field public static final int MENDE = 140; // 0x8c
field public static final int MEROITIC = 86; // 0x56
@@ -18234,6 +18270,7 @@
field public static final int OLD_NORTH_ARABIAN = 142; // 0x8e
field public static final int OLD_PERMIC = 89; // 0x59
field public static final int OLD_PERSIAN = 61; // 0x3d
+ field public static final int OLD_SOGDIAN = 184; // 0xb8
field public static final int OLD_SOUTH_ARABIAN = 133; // 0x85
field public static final int OL_CHIKI = 109; // 0x6d
field public static final int ORIYA = 31; // 0x1f
@@ -18260,6 +18297,7 @@
field public static final int SIMPLIFIED_HAN = 73; // 0x49
field public static final int SINDHI = 145; // 0x91
field public static final int SINHALA = 33; // 0x21
+ field public static final int SOGDIAN = 183; // 0xb7
field public static final int SORA_SOMPENG = 152; // 0x98
field public static final int SOYOMBO = 176; // 0xb0
field public static final int SUNDANESE = 113; // 0x71
@@ -21201,6 +21239,7 @@
field public static final android.icu.util.VersionInfo UCOL_BUILDER_VERSION;
field public static final android.icu.util.VersionInfo UCOL_RUNTIME_VERSION;
field public static final android.icu.util.VersionInfo UNICODE_10_0;
+ field public static final android.icu.util.VersionInfo UNICODE_11_0;
field public static final android.icu.util.VersionInfo UNICODE_1_0;
field public static final android.icu.util.VersionInfo UNICODE_1_0_1;
field public static final android.icu.util.VersionInfo UNICODE_1_1_0;
@@ -32543,6 +32582,21 @@
ctor public FileUriExposedException(java.lang.String);
}
+ public class FileUtils {
+ method public static void closeQuietly(java.lang.AutoCloseable);
+ method public static void closeQuietly(java.io.FileDescriptor);
+ method public static long copy(java.io.File, java.io.File) throws java.io.IOException;
+ method public static long copy(java.io.File, java.io.File, android.os.CancellationSignal, java.util.concurrent.Executor, android.os.FileUtils.ProgressListener) throws java.io.IOException;
+ method public static long copy(java.io.InputStream, java.io.OutputStream) throws java.io.IOException;
+ method public static long copy(java.io.InputStream, java.io.OutputStream, android.os.CancellationSignal, java.util.concurrent.Executor, android.os.FileUtils.ProgressListener) throws java.io.IOException;
+ method public static long copy(java.io.FileDescriptor, java.io.FileDescriptor) throws java.io.IOException;
+ method public static long copy(java.io.FileDescriptor, java.io.FileDescriptor, android.os.CancellationSignal, java.util.concurrent.Executor, android.os.FileUtils.ProgressListener) throws java.io.IOException;
+ }
+
+ public static abstract interface FileUtils.ProgressListener {
+ method public abstract void onProgress(long);
+ }
+
public class Handler {
ctor public Handler();
ctor public Handler(android.os.Handler.Callback);
@@ -32809,6 +32863,7 @@
method public void readMap(java.util.Map, java.lang.ClassLoader);
method public <T extends android.os.Parcelable> T readParcelable(java.lang.ClassLoader);
method public android.os.Parcelable[] readParcelableArray(java.lang.ClassLoader);
+ method public <T extends android.os.Parcelable> java.util.List<T> readParcelableList(java.util.List<T>, java.lang.ClassLoader);
method public android.os.PersistableBundle readPersistableBundle();
method public android.os.PersistableBundle readPersistableBundle(java.lang.ClassLoader);
method public java.io.Serializable readSerializable();
@@ -32855,6 +32910,7 @@
method public void writeNoException();
method public void writeParcelable(android.os.Parcelable, int);
method public <T extends android.os.Parcelable> void writeParcelableArray(T[], int);
+ method public <T extends android.os.Parcelable> void writeParcelableList(java.util.List<T>, int);
method public void writePersistableBundle(android.os.PersistableBundle);
method public void writeSerializable(java.io.Serializable);
method public void writeSize(android.util.Size);
@@ -33206,7 +33262,9 @@
method public android.os.StrictMode.VmPolicy.Builder detectAll();
method public android.os.StrictMode.VmPolicy.Builder detectCleartextNetwork();
method public android.os.StrictMode.VmPolicy.Builder detectContentUriWithoutPermission();
+ method public android.os.StrictMode.VmPolicy.Builder detectCredentialProtectedWhileLocked();
method public android.os.StrictMode.VmPolicy.Builder detectFileUriExposure();
+ method public android.os.StrictMode.VmPolicy.Builder detectImplicitDirectBoot();
method public android.os.StrictMode.VmPolicy.Builder detectLeakedClosableObjects();
method public android.os.StrictMode.VmPolicy.Builder detectLeakedRegistrationObjects();
method public android.os.StrictMode.VmPolicy.Builder detectLeakedSqlLiteObjects();
@@ -33254,8 +33312,12 @@
}
public final class Trace {
+ method public static void beginAsyncSection(java.lang.String, int);
method public static void beginSection(java.lang.String);
+ method public static void endAsyncSection(java.lang.String, int);
method public static void endSection();
+ method public static boolean isEnabled();
+ method public static void setCounter(java.lang.String, int);
}
public class TransactionTooLargeException extends android.os.RemoteException {
@@ -33608,6 +33670,9 @@
public final class ContentUriWithoutPermissionViolation extends android.os.strictmode.Violation {
}
+ public final class CredentialProtectedWhileLockedViolation extends android.os.strictmode.Violation {
+ }
+
public final class CustomViolation extends android.os.strictmode.Violation {
}
@@ -33620,6 +33685,9 @@
public final class FileUriExposedViolation extends android.os.strictmode.Violation {
}
+ public final class ImplicitDirectBootViolation extends android.os.strictmode.Violation {
+ }
+
public class InstanceCountViolation extends android.os.strictmode.Violation {
method public long getNumberOfInstances();
}
@@ -39654,6 +39722,7 @@
field public static final int SHOW_SOURCE_ACTIVITY = 16; // 0x10
field public static final int SHOW_SOURCE_APPLICATION = 8; // 0x8
field public static final int SHOW_SOURCE_ASSIST_GESTURE = 4; // 0x4
+ field public static final int SHOW_SOURCE_PUSH_TO_TALK = 32; // 0x20
field public static final int SHOW_WITH_ASSIST = 1; // 0x1
field public static final int SHOW_WITH_SCREENSHOT = 2; // 0x2
}
@@ -41006,6 +41075,7 @@
method public void onReject(java.lang.String);
method public void onSeparate();
method public void onShowIncomingCallUi();
+ method public void onSilence();
method public void onStartRtt(android.telecom.Connection.RttTextStream);
method public void onStateChanged(int);
method public void onStopDtmfTone();
@@ -41799,6 +41869,7 @@
field public static final java.lang.String KEY_SMS_REQUIRES_DESTINATION_NUMBER_CONVERSION_BOOL = "sms_requires_destination_number_conversion_bool";
field public static final java.lang.String KEY_SUPPORT_3GPP_CALL_FORWARDING_WHILE_ROAMING_BOOL = "support_3gpp_call_forwarding_while_roaming_bool";
field public static final java.lang.String KEY_SUPPORT_CONFERENCE_CALL_BOOL = "support_conference_call_bool";
+ field public static final java.lang.String KEY_SUPPORT_EMERGENCY_SMS_OVER_IMS_BOOL = "support_emergency_sms_over_ims_bool";
field public static final java.lang.String KEY_SUPPORT_PAUSE_IMS_VIDEO_CALLS_BOOL = "support_pause_ims_video_calls_bool";
field public static final java.lang.String KEY_SUPPORT_SWAP_AFTER_MERGE_BOOL = "support_swap_after_merge_bool";
field public static final java.lang.String KEY_TREAT_DOWNGRADED_VIDEO_CALLS_AS_VIDEO_CALLS_BOOL = "treat_downgraded_video_calls_as_video_calls_bool";
@@ -42398,8 +42469,10 @@
method public java.lang.CharSequence getDisplayName();
method public java.lang.String getIccId();
method public int getIconTint();
- method public int getMcc();
- method public int getMnc();
+ method public deprecated int getMcc();
+ method public java.lang.String getMccString();
+ method public deprecated int getMnc();
+ method public java.lang.String getMncString();
method public java.lang.String getNumber();
method public int getSimSlotIndex();
method public int getSubscriptionId();
@@ -42493,6 +42566,8 @@
method public java.lang.String getImei();
method public java.lang.String getImei(int);
method public java.lang.String getLine1Number();
+ method public java.lang.String getManufacturerCode();
+ method public java.lang.String getManufacturerCode(int);
method public java.lang.String getMeid();
method public java.lang.String getMeid(int);
method public java.lang.String getMmsUAProfUrl();
@@ -42517,6 +42592,8 @@
method public int getSimState();
method public int getSimState(int);
method public java.lang.String getSubscriberId();
+ method public java.lang.String getTypeAllocationCode();
+ method public java.lang.String getTypeAllocationCode(int);
method public java.lang.String getVisualVoicemailPackageName();
method public java.lang.String getVoiceMailAlphaTag();
method public java.lang.String getVoiceMailNumber();
@@ -44729,6 +44806,7 @@
public class Linkify {
ctor public Linkify();
method public static final boolean addLinks(android.text.Spannable, int);
+ method public static final boolean addLinks(android.text.Spannable, int, android.text.util.Linkify.UrlSpanFactory);
method public static final boolean addLinks(android.widget.TextView, int);
method public static final void addLinks(android.widget.TextView, java.util.regex.Pattern, java.lang.String);
method public static final void addLinks(android.widget.TextView, java.util.regex.Pattern, java.lang.String, android.text.util.Linkify.MatchFilter, android.text.util.Linkify.TransformFilter);
@@ -44736,6 +44814,7 @@
method public static final boolean addLinks(android.text.Spannable, java.util.regex.Pattern, java.lang.String);
method public static final boolean addLinks(android.text.Spannable, java.util.regex.Pattern, java.lang.String, android.text.util.Linkify.MatchFilter, android.text.util.Linkify.TransformFilter);
method public static final boolean addLinks(android.text.Spannable, java.util.regex.Pattern, java.lang.String, java.lang.String[], android.text.util.Linkify.MatchFilter, android.text.util.Linkify.TransformFilter);
+ method public static final boolean addLinks(android.text.Spannable, java.util.regex.Pattern, java.lang.String, java.lang.String[], android.text.util.Linkify.MatchFilter, android.text.util.Linkify.TransformFilter, android.text.util.Linkify.UrlSpanFactory);
field public static final int ALL = 15; // 0xf
field public static final int EMAIL_ADDRESSES = 2; // 0x2
field public static final deprecated int MAP_ADDRESSES = 8; // 0x8
@@ -44754,6 +44833,11 @@
method public abstract java.lang.String transformUrl(java.util.regex.Matcher, java.lang.String);
}
+ public static class Linkify.UrlSpanFactory {
+ ctor public Linkify.UrlSpanFactory();
+ method public android.text.style.URLSpan create(java.lang.String);
+ }
+
public class Rfc822Token {
ctor public Rfc822Token(java.lang.String, java.lang.String, java.lang.String);
method public java.lang.String getAddress();
@@ -49375,18 +49459,18 @@
method public void setCheckable(boolean);
method public void setChecked(boolean);
method public void setClassName(java.lang.CharSequence);
- method public void setClickable(boolean);
+ method public deprecated void setClickable(boolean);
method public void setCollectionInfo(android.view.accessibility.AccessibilityNodeInfo.CollectionInfo);
method public void setCollectionItemInfo(android.view.accessibility.AccessibilityNodeInfo.CollectionItemInfo);
method public void setContentDescription(java.lang.CharSequence);
method public void setContentInvalid(boolean);
- method public void setContextClickable(boolean);
- method public void setDismissable(boolean);
+ method public deprecated void setContextClickable(boolean);
+ method public deprecated void setDismissable(boolean);
method public void setDrawingOrder(int);
method public void setEditable(boolean);
method public void setEnabled(boolean);
method public void setError(java.lang.CharSequence);
- method public void setFocusable(boolean);
+ method public deprecated void setFocusable(boolean);
method public void setFocused(boolean);
method public void setHeading(boolean);
method public void setHintText(java.lang.CharSequence);
@@ -49397,7 +49481,7 @@
method public void setLabeledBy(android.view.View);
method public void setLabeledBy(android.view.View, int);
method public void setLiveRegion(int);
- method public void setLongClickable(boolean);
+ method public deprecated void setLongClickable(boolean);
method public void setMaxTextLength(int);
method public void setMovementGranularities(int);
method public void setMultiLine(boolean);
@@ -49408,7 +49492,7 @@
method public void setPassword(boolean);
method public void setRangeInfo(android.view.accessibility.AccessibilityNodeInfo.RangeInfo);
method public void setScreenReaderFocusable(boolean);
- method public void setScrollable(boolean);
+ method public deprecated void setScrollable(boolean);
method public void setSelected(boolean);
method public void setShowingHintText(boolean);
method public void setSource(android.view.View);
@@ -70166,7 +70250,7 @@
public final class Pattern implements java.io.Serializable {
method public java.util.function.Predicate<java.lang.String> asPredicate();
method public static java.util.regex.Pattern compile(java.lang.String);
- method public static java.util.regex.Pattern compile(java.lang.String, int) throws java.util.regex.PatternSyntaxException;
+ method public static java.util.regex.Pattern compile(java.lang.String, int);
method public int flags();
method public java.util.regex.Matcher matcher(java.lang.CharSequence);
method public static boolean matches(java.lang.String, java.lang.CharSequence);
diff --git a/api/system-current.txt b/api/system-current.txt
index 143b46d4..924d3af 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -1202,6 +1202,9 @@
}
public class PermissionGroupInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable {
+ field public int backgroundRequestDetailResourceId;
+ field public int backgroundRequestResourceId;
+ field public int requestDetailResourceId;
field public int requestRes;
}
@@ -1209,6 +1212,7 @@
field public static final int FLAG_REMOVED = 2; // 0x2
field public static final int PROTECTION_FLAG_OEM = 16384; // 0x4000
field public static final int PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER = 65536; // 0x10000
+ field public java.lang.String backgroundPermission;
field public int requestRes;
}
@@ -3923,6 +3927,7 @@
method public void readFromParcel(android.os.Parcel);
method public int sectionCount();
method public void setAll(boolean);
+ method public void setPrivacyPolicy(int);
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.os.IncidentReportArgs> CREATOR;
}
@@ -4659,6 +4664,7 @@
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.service.notification.Adjustment> CREATOR;
field public static final java.lang.String KEY_PEOPLE = "key_people";
+ field public static final java.lang.String KEY_SMART_ACTIONS = "key_smart_actions";
field public static final java.lang.String KEY_SNOOZE_CRITERIA = "key_snooze_criteria";
field public static final java.lang.String KEY_USER_SENTIMENT = "key_user_sentiment";
}
@@ -5174,8 +5180,8 @@
method public int[] getAvailableServices();
method public android.telephony.CellIdentity getCellIdentity();
method public int getDomain();
- method public int getReasonForDenial();
method public int getRegState();
+ method public int getRejectCause();
method public int getTransportType();
method public boolean isEmergencyEnabled();
method public void writeToParcel(android.os.Parcel, int);
diff --git a/api/test-current.txt b/api/test-current.txt
index 6e3c768..b8acfdb 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -130,6 +130,12 @@
method public android.content.ComponentName getEffectsSuppressor();
}
+ public final class PictureInPictureParams implements android.os.Parcelable {
+ method public java.util.List<android.app.RemoteAction> getActions();
+ method public float getAspectRatio();
+ method public android.graphics.Rect getSourceRectHint();
+ }
+
public class TimePickerDialog extends android.app.AlertDialog implements android.content.DialogInterface.OnClickListener android.widget.TimePicker.OnTimeChangedListener {
method public android.widget.TimePicker getTimePicker();
}
@@ -251,6 +257,8 @@
public class ApplicationInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable {
method public boolean isPrivilegedApp();
method public boolean isSystemApp();
+ field public static final int PRIVATE_FLAG_PRIVILEGED = 8; // 0x8
+ field public int privateFlags;
}
public class LauncherApps {
@@ -266,16 +274,21 @@
method public abstract java.lang.String getPermissionControllerPackageName();
method public abstract java.lang.String getServicesSystemSharedLibraryPackageName();
method public abstract java.lang.String getSharedSystemSharedLibraryPackageName();
+ method public abstract void grantRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
method public abstract boolean isPermissionReviewModeEnabled();
+ method public abstract void revokeRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
field public static final java.lang.String FEATURE_ADOPTABLE_STORAGE = "android.software.adoptable_storage";
field public static final java.lang.String FEATURE_FILE_BASED_ENCRYPTION = "android.software.file_based_encryption";
field public static final int MATCH_FACTORY_ONLY = 2097152; // 0x200000
field public static final int MATCH_KNOWN_PACKAGES = 4202496; // 0x402000
+ field public static final java.lang.String SYSTEM_SHARED_LIBRARY_SERVICES = "android.ext.services";
+ field public static final java.lang.String SYSTEM_SHARED_LIBRARY_SHARED = "android.ext.shared";
}
public class PermissionInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable {
field public static final int PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER = 65536; // 0x10000
field public static final int PROTECTION_FLAG_VENDOR_PRIVILEGED = 32768; // 0x8000
+ field public java.lang.String backgroundPermission;
}
public final class ShortcutInfo implements android.os.Parcelable {
@@ -625,6 +638,10 @@
package android.os {
+ public class Build {
+ method public static boolean is64BitAbi(java.lang.String);
+ }
+
public static class Build.VERSION {
field public static final int FIRST_SDK_INT;
field public static final int RESOURCES_SDK_INT;
@@ -637,6 +654,7 @@
public class Environment {
method public static java.io.File buildPath(java.io.File, java.lang.String...);
+ method public static java.io.File getStorageDirectory();
}
public class IncidentManager {
@@ -654,12 +672,14 @@
method public void readFromParcel(android.os.Parcel);
method public int sectionCount();
method public void setAll(boolean);
+ method public void setPrivacyPolicy(int);
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.os.IncidentReportArgs> CREATOR;
}
- public final class PowerManager {
- method public void nap(long);
+ public final class MessageQueue {
+ method public int postSyncBarrier();
+ method public void removeSyncBarrier(int);
}
public class Process {
@@ -680,23 +700,12 @@
}
public final class StrictMode {
+ method public static void conditionallyCheckInstanceCounts();
method public static void setViolationLogger(android.os.StrictMode.ViolationLogger);
- field public static final int DETECT_CUSTOM = 8; // 0x8
- field public static final int DETECT_DISK_READ = 2; // 0x2
- field public static final int DETECT_DISK_WRITE = 1; // 0x1
- field public static final int DETECT_NETWORK = 4; // 0x4
- field public static final int DETECT_RESOURCE_MISMATCH = 16; // 0x10
- field public static final int DETECT_UNBUFFERED_IO = 32; // 0x20
- field public static final int DETECT_VM_ACTIVITY_LEAKS = 1024; // 0x400
- field public static final int DETECT_VM_CLEARTEXT_NETWORK = 16384; // 0x4000
- field public static final int DETECT_VM_CLOSABLE_LEAKS = 512; // 0x200
- field public static final int DETECT_VM_CONTENT_URI_WITHOUT_PERMISSION = 32768; // 0x8000
- field public static final int DETECT_VM_CURSOR_LEAKS = 256; // 0x100
- field public static final int DETECT_VM_FILE_URI_EXPOSURE = 8192; // 0x2000
- field public static final int DETECT_VM_INSTANCE_LEAKS = 2048; // 0x800
- field public static final int DETECT_VM_NON_SDK_API_USAGE = 1073741824; // 0x40000000
- field public static final int DETECT_VM_REGISTRATION_LEAKS = 4096; // 0x1000
- field public static final int DETECT_VM_UNTAGGED_SOCKET = -2147483648; // 0x80000000
+ }
+
+ public static final class StrictMode.ThreadPolicy.Builder {
+ method public android.os.StrictMode.ThreadPolicy.Builder detectExplicitGc();
}
public static final class StrictMode.ViolationInfo implements android.os.Parcelable {
@@ -704,9 +713,8 @@
ctor public StrictMode.ViolationInfo(android.os.Parcel, boolean);
method public int describeContents();
method public void dump(android.util.Printer, java.lang.String);
- method public int getPolicyMask();
method public java.lang.String getStackTrace();
- method public int getViolationBit();
+ method public java.lang.Class<? extends android.os.strictmode.Violation> getViolationClass();
method public java.lang.String getViolationDetails();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.os.StrictMode.ViolationInfo> CREATOR;
@@ -724,6 +732,7 @@
}
public class SystemProperties {
+ method public static java.lang.String get(java.lang.String);
method public static java.lang.String get(java.lang.String, java.lang.String);
}
@@ -743,7 +752,17 @@
method public static android.os.VibrationEffect get(int);
method public static android.os.VibrationEffect get(int, boolean);
method public static android.os.VibrationEffect get(android.net.Uri, android.content.Context);
+ method public abstract long getDuration();
method protected static int scale(int, float, int);
+ field public static final int EFFECT_CLICK = 0; // 0x0
+ field public static final int EFFECT_DOUBLE_CLICK = 1; // 0x1
+ field public static final int EFFECT_HEAVY_CLICK = 5; // 0x5
+ field public static final int EFFECT_POP = 4; // 0x4
+ field public static final int EFFECT_STRENGTH_LIGHT = 0; // 0x0
+ field public static final int EFFECT_STRENGTH_MEDIUM = 1; // 0x1
+ field public static final int EFFECT_STRENGTH_STRONG = 2; // 0x2
+ field public static final int EFFECT_THUD = 3; // 0x3
+ field public static final int EFFECT_TICK = 2; // 0x2
field public static final int[] RINGTONES;
}
@@ -784,6 +803,86 @@
field public static final android.os.Parcelable.Creator<android.os.VibrationEffect.Waveform> CREATOR;
}
+ public class WorkSource implements android.os.Parcelable {
+ ctor public WorkSource(int);
+ method public boolean add(int);
+ method public boolean add(int, java.lang.String);
+ method public deprecated android.os.WorkSource addReturningNewbs(android.os.WorkSource);
+ method public int get(int);
+ method public java.lang.String getName(int);
+ method public deprecated android.os.WorkSource[] setReturningDiffs(android.os.WorkSource);
+ method public int size();
+ }
+
+}
+
+package android.os.health {
+
+ public class HealthKeys {
+ ctor public HealthKeys();
+ field public static final int BASE_PACKAGE = 40000; // 0x9c40
+ field public static final int BASE_PID = 20000; // 0x4e20
+ field public static final int BASE_PROCESS = 30000; // 0x7530
+ field public static final int BASE_SERVICE = 50000; // 0xc350
+ field public static final int BASE_UID = 10000; // 0x2710
+ field public static final int TYPE_COUNT = 5; // 0x5
+ field public static final int TYPE_MEASUREMENT = 1; // 0x1
+ field public static final int TYPE_MEASUREMENTS = 4; // 0x4
+ field public static final int TYPE_STATS = 2; // 0x2
+ field public static final int TYPE_TIMER = 0; // 0x0
+ field public static final int TYPE_TIMERS = 3; // 0x3
+ field public static final int UNKNOWN_KEY = 0; // 0x0
+ }
+
+ public static abstract class HealthKeys.Constant implements java.lang.annotation.Annotation {
+ }
+
+ public static class HealthKeys.Constants {
+ ctor public HealthKeys.Constants(java.lang.Class);
+ method public java.lang.String getDataType();
+ method public int getIndex(int, int);
+ method public int[] getKeys(int);
+ method public int getSize(int);
+ }
+
+ public class HealthStats {
+ ctor public HealthStats(android.os.Parcel);
+ }
+
+ public class HealthStatsParceler implements android.os.Parcelable {
+ ctor public HealthStatsParceler(android.os.health.HealthStatsWriter);
+ ctor public HealthStatsParceler(android.os.Parcel);
+ method public int describeContents();
+ method public android.os.health.HealthStats getHealthStats();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.os.health.HealthStatsParceler> CREATOR;
+ }
+
+ public class HealthStatsWriter {
+ ctor public HealthStatsWriter(android.os.health.HealthKeys.Constants);
+ method public void addMeasurement(int, long);
+ method public void addMeasurements(int, java.lang.String, long);
+ method public void addStats(int, java.lang.String, android.os.health.HealthStatsWriter);
+ method public void addTimer(int, int, long);
+ method public void addTimers(int, java.lang.String, android.os.health.TimerStat);
+ method public void flattenToParcel(android.os.Parcel);
+ }
+
+}
+
+package android.os.storage {
+
+ public final class StorageVolume implements android.os.Parcelable {
+ method public java.lang.String getPath();
+ }
+
+}
+
+package android.os.strictmode {
+
+ public final class ExplicitGcViolation extends android.os.strictmode.Violation {
+ }
+
}
package android.print {
@@ -944,6 +1043,7 @@
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.service.notification.Adjustment> CREATOR;
field public static final java.lang.String KEY_PEOPLE = "key_people";
+ field public static final java.lang.String KEY_SMART_ACTIONS = "key_smart_actions";
field public static final java.lang.String KEY_SNOOZE_CRITERIA = "key_snooze_criteria";
field public static final java.lang.String KEY_USER_SENTIMENT = "key_user_sentiment";
}
diff --git a/cmds/bootanimation/Android.mk b/cmds/bootanimation/Android.mk
index 453fb72..6943dab 100644
--- a/cmds/bootanimation/Android.mk
+++ b/cmds/bootanimation/Android.mk
@@ -27,7 +27,9 @@
LOCAL_SHARED_LIBRARIES += \
libandroidthings \
+ libandroidthings_protos \
libchrome \
+ libprotobuf-cpp-lite \
LOCAL_STATIC_LIBRARIES += \
libjsoncpp
diff --git a/cmds/bootanimation/iot/Android.mk b/cmds/bootanimation/iot/Android.mk
index 8b475d3..3d288e4 100644
--- a/cmds/bootanimation/iot/Android.mk
+++ b/cmds/bootanimation/iot/Android.mk
@@ -25,9 +25,11 @@
LOCAL_SHARED_LIBRARIES := \
libandroidthings \
+ libandroidthings_protos \
libbase \
libchrome \
liblog \
+ libprotobuf-cpp-lite \
LOCAL_STATIC_LIBRARIES += \
libjsoncpp
diff --git a/cmds/bootanimation/iot/BootParameters.cpp b/cmds/bootanimation/iot/BootParameters.cpp
index 2cf1c19..30a9b28 100644
--- a/cmds/bootanimation/iot/BootParameters.cpp
+++ b/cmds/bootanimation/iot/BootParameters.cpp
@@ -22,10 +22,13 @@
#include <fcntl.h>
#include <android-base/file.h>
+#include <json/json.h>
#include <utils/Log.h>
using android::base::ReadFileToString;
using android::base::RemoveFileIfExists;
+using android::base::WriteStringToFile;
+using Json::ArrayIndex;
using Json::Reader;
using Json::Value;
@@ -33,23 +36,34 @@
namespace {
-// Keys for supporting a silent boot and user-defined BootAction parameters.
-constexpr const char *kKeySilentBoot = "silent_boot";
-constexpr const char* kKeyParams = "params";
+// Keys for deprecated parameters. Devices that OTA from N to O and that used
+// the hidden BootParameters API will store these in the JSON blob. To support
+// the transition from N to O, these keys are mapped to the new parameters.
+constexpr const char *kKeyLegacyVolume = "volume";
+constexpr const char *kKeyLegacyAnimationsDisabled = "boot_animation_disabled";
+constexpr const char *kKeyLegacyParamNames = "param_names";
+constexpr const char *kKeyLegacyParamValues = "param_values";
-constexpr const char* kNextBootFile = "/data/misc/bootanimation/next_boot.json";
-constexpr const char* kLastBootFile = "/data/misc/bootanimation/last_boot.json";
+constexpr const char *kNextBootFile = "/data/misc/bootanimation/next_boot.proto";
+constexpr const char *kLastBootFile = "/data/misc/bootanimation/last_boot.proto";
-void swapBootConfigs() {
- // rename() will fail if next_boot.json doesn't exist, so delete
- // last_boot.json manually first.
+constexpr const char *kLegacyNextBootFile = "/data/misc/bootanimation/next_boot.json";
+constexpr const char *kLegacyLastBootFile = "/data/misc/bootanimation/last_boot.json";
+
+void removeLegacyFiles() {
std::string err;
- if (!RemoveFileIfExists(kLastBootFile, &err))
- ALOGE("Unable to delete last boot file: %s", err.c_str());
+ if (!RemoveFileIfExists(kLegacyLastBootFile, &err)) {
+ ALOGW("Unable to delete %s: %s", kLegacyLastBootFile, err.c_str());
+ }
- if (rename(kNextBootFile, kLastBootFile) && errno != ENOENT)
- ALOGE("Unable to swap boot files: %s", strerror(errno));
+ err.clear();
+ if (!RemoveFileIfExists(kLegacyNextBootFile, &err)) {
+ ALOGW("Unable to delete %s: %s", kLegacyNextBootFile, err.c_str());
+ }
+}
+void createNextBootFile() {
+ errno = 0;
int fd = open(kNextBootFile, O_CREAT, DEFFILEMODE);
if (fd == -1) {
ALOGE("Unable to create next boot file: %s", strerror(errno));
@@ -64,54 +78,120 @@
} // namespace
+// Renames the 'next' boot file to the 'last' file and reads its contents.
+bool BootParameters::swapAndLoadBootConfigContents(const char *lastBootFile,
+ const char *nextBootFile,
+ std::string *contents) {
+ if (!ReadFileToString(nextBootFile, contents)) {
+ RemoveFileIfExists(lastBootFile);
+ return false;
+ }
+
+ errno = 0;
+ if (rename(nextBootFile, lastBootFile) && errno != ENOENT)
+ ALOGE("Unable to swap boot files: %s", strerror(errno));
+
+ return true;
+}
+
BootParameters::BootParameters() {
- swapBootConfigs();
loadParameters();
}
-void BootParameters::loadParameters() {
- std::string contents;
- if (!ReadFileToString(kLastBootFile, &contents)) {
- if (errno != ENOENT)
- ALOGE("Unable to read from %s: %s", kLastBootFile, strerror(errno));
+// Saves the boot parameters state to disk so the framework can read it.
+void BootParameters::storeParameters() {
+ errno = 0;
+ if (!WriteStringToFile(mProto.SerializeAsString(), kLastBootFile)) {
+ ALOGE("Failed to write boot parameters to %s: %s", kLastBootFile, strerror(errno));
+ }
+ // WriteStringToFile sets the file permissions to 0666, but these are not
+ // honored by the system.
+ errno = 0;
+ if (chmod(kLastBootFile, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) {
+ ALOGE("Failed to set permissions for %s: %s", kLastBootFile, strerror(errno));
+ }
+}
+
+// Load the boot parameters from disk, try the old location and format if the
+// file does not exist. Note:
+// - Parse errors result in defaults being used (a normal boot).
+// - Legacy boot parameters default to a silent boot.
+void BootParameters::loadParameters() {
+ // Precedence is given to the new file format (.proto).
+ std::string contents;
+ if (swapAndLoadBootConfigContents(kLastBootFile, kNextBootFile, &contents)) {
+ parseBootParameters(contents);
+ } else if (swapAndLoadBootConfigContents(kLegacyLastBootFile, kLegacyNextBootFile, &contents)) {
+ parseLegacyBootParameters(contents);
+ storeParameters();
+ removeLegacyFiles();
+ }
+
+ createNextBootFile();
+}
+
+void BootParameters::parseBootParameters(const std::string &contents) {
+ if (!mProto.ParseFromString(contents)) {
+ ALOGW("Failed to parse parameters from %s", kLastBootFile);
return;
}
- loadParameters(contents);
+ loadStateFromProto();
}
-// If the boot parameters -
-// - File is missing, we assume a normal, non-silent boot.
-// - Are well-formed, initially assume a normal, non-silent boot and parse.
-void BootParameters::loadParameters(const std::string& raw_json) {
- if (!Reader().parse(raw_json, mJson)) {
- return;
- }
-
- parseBootParameters();
-}
-
-void BootParameters::parseBootParameters() {
- // A missing key returns a safe, missing value.
- // Ignore invalid or missing JSON parameters.
- Value &jsonValue = mJson[kKeySilentBoot];
- if (jsonValue.isBool()) {
- mIsSilentBoot = jsonValue.asBool();
+// Parses the JSON in the proto.
+void BootParameters::parseLegacyBootParameters(const std::string &contents) {
+ Value json;
+ if (!Reader().parse(contents, json)) {
+ ALOGW("Failed to parse parameters from %s", kLegacyLastBootFile);
+ return;
}
- jsonValue = mJson[kKeyParams];
- if (jsonValue.isObject()) {
- // getMemberNames returns a copy of the keys which must be stored.
- mKeys = jsonValue.getMemberNames();
- for (auto &key : mKeys) {
- Value &value = jsonValue[key];
- if (value.isString()) {
- mParameters.push_back(
- {.key = key.c_str(), .value = value.asCString()});
+ int volume = 0;
+ bool bootAnimationDisabled = true;
+
+ Value &jsonValue = json[kKeyLegacyVolume];
+ if (jsonValue.isIntegral()) {
+ volume = jsonValue.asInt();
+ }
+
+ jsonValue = json[kKeyLegacyAnimationsDisabled];
+ if (jsonValue.isIntegral()) {
+ bootAnimationDisabled = jsonValue.asInt() == 1;
+ }
+
+ // Assume a silent boot unless all of the following are true -
+ // 1. The volume is neither 0 nor -1000 (the legacy default value).
+ // 2. The boot animations are explicitly enabled.
+ // Note: brightness was never used.
+ mProto.set_silent_boot((volume == 0) || (volume == -1000) || bootAnimationDisabled);
+
+ Value &keys = json[kKeyLegacyParamNames];
+ Value &values = json[kKeyLegacyParamValues];
+ if (keys.isArray() && values.isArray() && (keys.size() == values.size())) {
+ for (ArrayIndex i = 0; i < keys.size(); ++i) {
+ auto &key = keys[i];
+ auto &value = values[i];
+ if (key.isString() && value.isString()) {
+ auto userParameter = mProto.add_user_parameter();
+ userParameter->set_key(key.asString());
+ userParameter->set_value(value.asString());
}
}
}
+
+ loadStateFromProto();
+}
+
+void BootParameters::loadStateFromProto() {
+ // A missing key returns a safe, default value.
+ // Ignore invalid or missing parameters.
+ mIsSilentBoot = mProto.silent_boot();
+
+ for (const auto ¶m : mProto.user_parameter()) {
+ mParameters.push_back({.key = param.key().c_str(), .value = param.value().c_str()});
+ }
}
} // namespace android
diff --git a/cmds/bootanimation/iot/BootParameters.h b/cmds/bootanimation/iot/BootParameters.h
index 49c34dd..cbd1ca6 100644
--- a/cmds/bootanimation/iot/BootParameters.h
+++ b/cmds/bootanimation/iot/BootParameters.h
@@ -22,7 +22,7 @@
#include <vector>
#include <boot_action/boot_action.h> // libandroidthings native API.
-#include <json/json.h>
+#include <boot_parameters.pb.h>
namespace android {
@@ -39,22 +39,33 @@
// Returns the additional boot parameters that were set on reboot.
const std::vector<ABootActionParameter>& getParameters() const { return mParameters; }
- // Exposed for testing. Updates the parameters with new JSON values.
- void loadParameters(const std::string& raw_json);
-private:
+ // Exposed for testing. Sets the parameters to the serialized proto.
+ void parseBootParameters(const std::string &contents);
+
+ // For devices that OTA from N to O.
+ // Exposed for testing. Sets the parameters to the raw JSON.
+ void parseLegacyBootParameters(const std::string &contents);
+
+ // Exposed for testing. Loads the contents from |nextBootFile| and replaces
+ // |lastBootFile| with |nextBootFile|.
+ static bool swapAndLoadBootConfigContents(const char *lastBootFile, const char *nextBootFile,
+ std::string *contents);
+
+ private:
void loadParameters();
- void parseBootParameters();
+ // Replaces the legacy JSON blob with the updated version, allowing the
+ // framework to read it.
+ void storeParameters();
+
+ void loadStateFromProto();
bool mIsSilentBoot = false;
std::vector<ABootActionParameter> mParameters;
- // Store parsed JSON because mParameters makes a shallow copy.
- Json::Value mJson;
-
- // Store parameter keys because mParameters makes a shallow copy.
- Json::Value::Members mKeys;
+ // Store the proto because mParameters makes a shallow copy.
+ android::things::proto::BootParameters mProto;
};
} // namespace android
diff --git a/cmds/bootanimation/iot/BootParameters_test.cpp b/cmds/bootanimation/iot/BootParameters_test.cpp
index 27d7d6f..d55bce6 100644
--- a/cmds/bootanimation/iot/BootParameters_test.cpp
+++ b/cmds/bootanimation/iot/BootParameters_test.cpp
@@ -16,6 +16,13 @@
#include "BootParameters.h"
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <android-base/file.h>
+#include <android-base/test_utils.h>
+#include <boot_parameters.pb.h>
#include <gtest/gtest.h>
namespace android {
@@ -23,50 +30,51 @@
namespace {
TEST(BootParametersTest, TestNoBootParametersIsNotSilent) {
- BootParameters boot_parameters = BootParameters();
- boot_parameters.loadParameters("");
+ android::things::proto::BootParameters proto;
- ASSERT_FALSE(boot_parameters.isSilentBoot());
- ASSERT_EQ(0u, boot_parameters.getParameters().size());
+ BootParameters bootParameters = BootParameters();
+ bootParameters.parseBootParameters(proto.SerializeAsString());
+
+ ASSERT_FALSE(bootParameters.isSilentBoot());
+ ASSERT_EQ(0u, bootParameters.getParameters().size());
}
TEST(BootParametersTest, TestParseIsSilent) {
- BootParameters boot_parameters = BootParameters();
- boot_parameters.loadParameters(R"(
- {
- "silent_boot":true,
- "params":{}
- }
- )");
+ android::things::proto::BootParameters proto;
+ proto.set_silent_boot(true);
- ASSERT_TRUE(boot_parameters.isSilentBoot());
+ BootParameters bootParameters = BootParameters();
+ bootParameters.parseBootParameters(proto.SerializeAsString());
+
+ ASSERT_TRUE(bootParameters.isSilentBoot());
}
TEST(BootParametersTest, TestParseIsNotSilent) {
- BootParameters boot_parameters = BootParameters();
- boot_parameters.loadParameters(R"(
- {
- "silent_boot":false,
- "params":{}
- }
- )");
+ android::things::proto::BootParameters proto;
+ proto.set_silent_boot(false);
- ASSERT_FALSE(boot_parameters.isSilentBoot());
+ BootParameters bootParameters = BootParameters();
+ bootParameters.parseBootParameters(proto.SerializeAsString());
+
+ ASSERT_FALSE(bootParameters.isSilentBoot());
}
TEST(BootParametersTest, TestParseBootParameters) {
- BootParameters boot_parameters = BootParameters();
- boot_parameters.loadParameters(R"(
- {
- "silent_boot":false,
- "params":{
- "key1":"value1",
- "key2":"value2"
- }
- }
- )");
+ android::things::proto::BootParameters proto;
+ proto.set_silent_boot(false);
- auto ¶meters = boot_parameters.getParameters();
+ auto userParameter = proto.add_user_parameter();
+ userParameter->set_key("key1");
+ userParameter->set_value("value1");
+
+ userParameter = proto.add_user_parameter();
+ userParameter->set_key("key2");
+ userParameter->set_value("value2");
+
+ BootParameters bootParameters = BootParameters();
+ bootParameters.parseBootParameters(proto.SerializeAsString());
+
+ auto ¶meters = bootParameters.getParameters();
ASSERT_EQ(2u, parameters.size());
ASSERT_STREQ(parameters[0].key, "key1");
ASSERT_STREQ(parameters[0].value, "value1");
@@ -74,35 +82,182 @@
ASSERT_STREQ(parameters[1].value, "value2");
}
-TEST(BootParametersTest, TestParseMissingParametersIsNotSilent) {
- BootParameters boot_parameters = BootParameters();
- boot_parameters.loadParameters(R"(
+TEST(BootParametersTest, TestParseLegacyDisableBootAnimationIsSilent) {
+ BootParameters bootParameters = BootParameters();
+ bootParameters.parseLegacyBootParameters(R"(
{
- "params":{}
+ "brightness":200,
+ "volume":100,
+ "boot_animation_disabled":1,
+ "param_names":[],
+ "param_values":[]
}
)");
- ASSERT_FALSE(boot_parameters.isSilentBoot());
+ ASSERT_TRUE(bootParameters.isSilentBoot());
}
-TEST(BootParametersTest, TestParseMalformedParametersAreSkipped) {
- BootParameters boot_parameters = BootParameters();
- boot_parameters.loadParameters(R"(
+TEST(BootParametersTest, TestParseLegacyZeroVolumeIsSilent) {
+ BootParameters bootParameters = BootParameters();
+ bootParameters.parseLegacyBootParameters(R"(
{
- "silent_boot":false,
- "params":{
- "key1":123,
- "key2":"value2"
- }
+ "brightness":200,
+ "volume":0,
+ "boot_animation_disabled":0,
+ "param_names":[],
+ "param_values":[]
}
)");
- auto ¶meters = boot_parameters.getParameters();
+ ASSERT_TRUE(bootParameters.isSilentBoot());
+}
+
+TEST(BootParametersTest, TestParseLegacyDefaultVolumeIsSilent) {
+ BootParameters bootParameters = BootParameters();
+ bootParameters.parseLegacyBootParameters(R"(
+ {
+ "brightness":200,
+ "volume":-1000,
+ "boot_animation_disabled":0,
+ "param_names":[],
+ "param_values":[]
+ }
+ )");
+
+ ASSERT_TRUE(bootParameters.isSilentBoot());
+}
+
+TEST(BootParametersTest, TestParseLegacyNotSilent) {
+ BootParameters bootParameters = BootParameters();
+ bootParameters.parseLegacyBootParameters(R"(
+ {
+ "brightness":200,
+ "volume":500,
+ "boot_animation_disabled":0,
+ "param_names":[],
+ "param_values":[]
+ }
+ )");
+
+ ASSERT_FALSE(bootParameters.isSilentBoot());
+}
+
+TEST(BootParametersTest, TestParseLegacyParameters) {
+ BootParameters bootParameters = BootParameters();
+ bootParameters.parseLegacyBootParameters(R"(
+ {
+ "brightness":200,
+ "volume":100,
+ "boot_animation_disabled":1,
+ "param_names":["key1", "key2"],
+ "param_values":["value1", "value2"]
+ }
+ )");
+
+ auto parameters = bootParameters.getParameters();
+ ASSERT_EQ(2u, parameters.size());
+ ASSERT_STREQ(parameters[0].key, "key1");
+ ASSERT_STREQ(parameters[0].value, "value1");
+ ASSERT_STREQ(parameters[1].key, "key2");
+ ASSERT_STREQ(parameters[1].value, "value2");
+}
+
+TEST(BootParametersTest, TestParseLegacyZeroParameters) {
+ BootParameters bootParameters = BootParameters();
+ bootParameters.parseLegacyBootParameters(R"(
+ {
+ "brightness":200,
+ "volume":100,
+ "boot_animation_disabled":1,
+ "param_names":[],
+ "param_values":[]
+ }
+ )");
+
+ ASSERT_EQ(0u, bootParameters.getParameters().size());
+}
+
+TEST(BootParametersTest, TestMalformedLegacyParametersAreSkipped) {
+ BootParameters bootParameters = BootParameters();
+ bootParameters.parseLegacyBootParameters(R"(
+ {
+ "brightness":500,
+ "volume":500,
+ "boot_animation_disabled":0,
+ "param_names":["key1", "key2"],
+ "param_values":[1, "value2"]
+ }
+ )");
+
+ auto parameters = bootParameters.getParameters();
ASSERT_EQ(1u, parameters.size());
ASSERT_STREQ(parameters[0].key, "key2");
ASSERT_STREQ(parameters[0].value, "value2");
}
+TEST(BootParametersTest, TestLegacyUnequalParameterSizesAreSkipped) {
+ BootParameters bootParameters = BootParameters();
+ bootParameters.parseLegacyBootParameters(R"(
+ {
+ "brightness":500,
+ "volume":500,
+ "boot_animation_disabled":0,
+ "param_names":["key1", "key2"],
+ "param_values":["value1"]
+ }
+ )");
+
+ ASSERT_EQ(0u, bootParameters.getParameters().size());
+}
+
+TEST(BootParametersTest, TestMissingLegacyBootParametersIsSilent) {
+ BootParameters bootParameters = BootParameters();
+ bootParameters.parseLegacyBootParameters(R"(
+ {
+ "brightness":500
+ }
+ )");
+
+ EXPECT_TRUE(bootParameters.isSilentBoot());
+ ASSERT_EQ(0u, bootParameters.getParameters().size());
+}
+
+TEST(BootParametersTest, TestLastFileIsRemovedOnError) {
+ TemporaryFile lastFile;
+ TemporaryDir tempDir;
+ std::string nonExistentFilePath(std::string(tempDir.path) + "/nonexistent");
+ std::string contents;
+
+ BootParameters::swapAndLoadBootConfigContents(lastFile.path, nonExistentFilePath.c_str(),
+ &contents);
+
+ struct stat buf;
+ ASSERT_EQ(-1, lstat(lastFile.path, &buf));
+ ASSERT_TRUE(contents.empty());
+}
+
+TEST(BootParametersTest, TestNextFileIsRemovedLastFileExistsOnSuccess) {
+ TemporaryFile lastFile;
+ TemporaryFile nextFile;
+
+ base::WriteStringToFile("foo", nextFile.path);
+
+ std::string contents;
+ // Expected side effects:
+ // - |next_file| is moved to |last_file|
+ // - |contents| is the contents of |next_file| before being moved.
+ BootParameters::swapAndLoadBootConfigContents(lastFile.path, nextFile.path, &contents);
+
+ struct stat buf;
+ ASSERT_EQ(0, lstat(lastFile.path, &buf));
+ ASSERT_EQ(-1, lstat(nextFile.path, &buf));
+ ASSERT_EQ(contents, "foo");
+
+ contents.clear();
+ ASSERT_TRUE(base::ReadFileToString(lastFile.path, &contents));
+ ASSERT_EQ(contents, "foo");
+}
+
} // namespace
} // namespace android
diff --git a/cmds/content/src/com/android/commands/content/Content.java b/cmds/content/src/com/android/commands/content/Content.java
index 6e0bd3a..36e51b9 100644
--- a/cmds/content/src/com/android/commands/content/Content.java
+++ b/cmds/content/src/com/android/commands/content/Content.java
@@ -462,7 +462,7 @@
IBinder token = new Binder();
try {
ContentProviderHolder holder = activityManager.getContentProviderExternal(
- providerName, mUserId, token);
+ providerName, mUserId, token, "*cmd*");
if (holder == null) {
throw new IllegalStateException("Could not find provider: " + providerName);
}
diff --git a/cmds/dpm/src/com/android/commands/dpm/Dpm.java b/cmds/dpm/src/com/android/commands/dpm/Dpm.java
index 7c1a555..376b13c 100644
--- a/cmds/dpm/src/com/android/commands/dpm/Dpm.java
+++ b/cmds/dpm/src/com/android/commands/dpm/Dpm.java
@@ -46,6 +46,7 @@
private static final String COMMAND_SET_PROFILE_OWNER = "set-profile-owner";
private static final String COMMAND_REMOVE_ACTIVE_ADMIN = "remove-active-admin";
private static final String COMMAND_CLEAR_FREEZE_PERIOD_RECORD = "clear-freeze-period-record";
+ private static final String COMMAND_FORCE_NETWORK_LOGS = "force-network-logs";
private static final String COMMAND_FORCE_SECURITY_LOGS = "force-security-logs";
private IDevicePolicyManager mDevicePolicyManager;
@@ -84,6 +85,9 @@
"feature development to prevent triggering restriction on setting freeze " +
"periods.\n" +
"\n" +
+ "dpm " + COMMAND_FORCE_NETWORK_LOGS + ": makes all network logs available to " +
+ "the DPC and triggers DeviceAdminReceiver.onNetworkLogsAvailable() if needed.\n" +
+ "\n" +
"dpm " + COMMAND_FORCE_SECURITY_LOGS + ": makes all security logs available to " +
"the DPC and triggers DeviceAdminReceiver.onSecurityLogsAvailable() if needed.");
}
@@ -114,6 +118,9 @@
case COMMAND_CLEAR_FREEZE_PERIOD_RECORD:
runClearFreezePeriodRecord();
break;
+ case COMMAND_FORCE_NETWORK_LOGS:
+ runForceNetworkLogs();
+ break;
case COMMAND_FORCE_SECURITY_LOGS:
runForceSecurityLogs();
break;
@@ -122,6 +129,18 @@
}
}
+ private void runForceNetworkLogs() throws RemoteException, InterruptedException {
+ while (true) {
+ final long toWait = mDevicePolicyManager.forceNetworkLogs();
+ if (toWait == 0) {
+ break;
+ }
+ System.out.println("We have to wait for " + toWait + " milliseconds...");
+ Thread.sleep(toWait);
+ }
+ System.out.println("Success");
+ }
+
private void runForceSecurityLogs() throws RemoteException, InterruptedException {
while (true) {
final long toWait = mDevicePolicyManager.forceSecurityLogs();
diff --git a/cmds/incident_helper/src/main.cpp b/cmds/incident_helper/src/main.cpp
index 418dc3f..5b6ac7a 100644
--- a/cmds/incident_helper/src/main.cpp
+++ b/cmds/incident_helper/src/main.cpp
@@ -73,7 +73,8 @@
case 2006:
return new BatteryTypeParser();
default:
- return NULL;
+ // Return no op parser when no specific ones are implemented.
+ return new NoopParser();
}
}
diff --git a/cmds/incidentd/src/IncidentService.cpp b/cmds/incidentd/src/IncidentService.cpp
index e305b54..e92cf94 100644
--- a/cmds/incidentd/src/IncidentService.cpp
+++ b/cmds/incidentd/src/IncidentService.cpp
@@ -314,6 +314,19 @@
mThrottler->dump(out);
return NO_ERROR;
}
+ if (!args[0].compare(String8("section"))) {
+ int id = atoi(args[1]);
+ int idx = 0;
+ while (SECTION_LIST[idx] != NULL) {
+ const Section* section = SECTION_LIST[idx];
+ if (section->id == id) {
+ fprintf(out, "Section[%d] %s\n", id, section->name.string());
+ break;
+ }
+ idx++;
+ }
+ return NO_ERROR;
+ }
}
return cmd_help(out);
}
@@ -321,8 +334,9 @@
status_t IncidentService::cmd_help(FILE* out) {
fprintf(out, "usage: adb shell cmd incident privacy print <section_id>\n");
fprintf(out, "usage: adb shell cmd incident privacy parse <section_id> < proto.txt\n");
- fprintf(out, " Prints/parses for the section id.\n");
- fprintf(out, "\n");
+ fprintf(out, " Prints/parses for the section id.\n\n");
+ fprintf(out, "usage: adb shell cmd incident section <section_id>\n");
+ fprintf(out, " Prints section id and its name.\n\n");
fprintf(out, "usage: adb shell cmd incident throttler\n");
fprintf(out, " Prints the current throttler state\n");
return NO_ERROR;
diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp
index b566099..14af5b9 100644
--- a/cmds/statsd/Android.bp
+++ b/cmds/statsd/Android.bp
@@ -25,6 +25,7 @@
],
shared_libs: [
+ "libmetricprotos",
"libplatformprotos",
],
@@ -37,6 +38,7 @@
},
export_shared_lib_headers: [
+ "libmetricprotos",
"libplatformprotos",
]
diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk
index 0b5e358..49ea6d5 100644
--- a/cmds/statsd/Android.mk
+++ b/cmds/statsd/Android.mk
@@ -60,7 +60,6 @@
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 \
@@ -71,10 +70,9 @@
src/guardrail/StatsdStats.cpp \
src/socket/StatsSocketListener.cpp
-# TODO: Once statsd is using a blueprint file, migrate to the proper filegroups.
+# TODO(b/110563449): Once statsd is using a blueprint file, migrate to the proper filegroups.
statsd_common_src += \
- ../../../../system/extras/perfprofd/binder_interface/aidl/android/os/IPerfProfd.aidl \
- src/perfprofd/perfprofd_config.proto
+ ../../../../system/extras/perfprofd/binder_interface/aidl/android/os/IPerfProfd.aidl
statsd_common_c_includes := \
$(LOCAL_PATH)/src \
@@ -84,8 +82,7 @@
$(LOCAL_PATH)/../../core/java
statsd_common_static_libraries := \
- libhealthhalutils \
- libplatformprotos \
+ libhealthhalutils
statsd_common_shared_libraries := \
libbase \
@@ -234,7 +231,9 @@
LOCAL_STATIC_LIBRARIES := \
$(statsd_common_static_libraries) \
- libgmock
+ libgmock \
+ libmetricprotos \
+ libplatformprotos
LOCAL_PROTOC_OPTIMIZE_TYPE := full
@@ -254,10 +253,9 @@
LOCAL_MODULE := statsdprotolite
LOCAL_SRC_FILES := \
+ src/metrics_constants/metrics_constants.proto \
src/stats_log.proto \
src/statsd_config.proto \
- src/perfetto/perfetto_config.proto \
- src/perfprofd/perfprofd_config.proto \
src/atoms.proto
LOCAL_PROTOC_OPTIMIZE_TYPE := lite
@@ -301,7 +299,6 @@
LOCAL_SHARED_LIBRARIES := $(statsd_common_shared_libraries) \
libprotobuf-cpp-full
-
LOCAL_STATIC_JAVA_LIBRARIES := \
platformprotoslite
@@ -319,7 +316,9 @@
LOCAL_AIDL_INCLUDES := $(statsd_common_aidl_includes)
LOCAL_STATIC_LIBRARIES := \
- $(statsd_common_static_libraries)
+ $(statsd_common_static_libraries) \
+ libmetricprotos \
+ libplatformprotos
LOCAL_SHARED_LIBRARIES := $(statsd_common_shared_libraries) \
libgtest_prod \
diff --git a/cmds/statsd/src/FieldValue.cpp b/cmds/statsd/src/FieldValue.cpp
index f150f07..7b6d29b 100644
--- a/cmds/statsd/src/FieldValue.cpp
+++ b/cmds/statsd/src/FieldValue.cpp
@@ -141,6 +141,9 @@
case FLOAT:
float_value = from.float_value;
break;
+ case DOUBLE:
+ double_value = from.double_value;
+ break;
case STRING:
str_value = from.str_value;
break;
@@ -157,6 +160,8 @@
return std::to_string(long_value) + "[L]";
case FLOAT:
return std::to_string(float_value) + "[F]";
+ case DOUBLE:
+ return std::to_string(double_value) + "[D]";
case STRING:
return str_value + "[S]";
default:
@@ -174,6 +179,8 @@
return long_value == that.long_value;
case FLOAT:
return float_value == that.float_value;
+ case DOUBLE:
+ return double_value == that.double_value;
case STRING:
return str_value == that.str_value;
default:
@@ -190,6 +197,8 @@
return long_value != that.long_value;
case FLOAT:
return float_value != that.float_value;
+ case DOUBLE:
+ return double_value != that.double_value;
case STRING:
return str_value != that.str_value;
default:
@@ -207,6 +216,8 @@
return long_value < that.long_value;
case FLOAT:
return float_value < that.float_value;
+ case DOUBLE:
+ return double_value < that.double_value;
case STRING:
return str_value < that.str_value;
default:
@@ -214,6 +225,142 @@
}
}
+bool Value::operator>(const Value& that) const {
+ if (type != that.getType()) return type > that.getType();
+
+ switch (type) {
+ case INT:
+ return int_value > that.int_value;
+ case LONG:
+ return long_value > that.long_value;
+ case FLOAT:
+ return float_value > that.float_value;
+ case DOUBLE:
+ return double_value > that.double_value;
+ case STRING:
+ return str_value > that.str_value;
+ default:
+ return false;
+ }
+}
+
+bool Value::operator>=(const Value& that) const {
+ if (type != that.getType()) return type >= that.getType();
+
+ switch (type) {
+ case INT:
+ return int_value >= that.int_value;
+ case LONG:
+ return long_value >= that.long_value;
+ case FLOAT:
+ return float_value >= that.float_value;
+ case DOUBLE:
+ return double_value >= that.double_value;
+ case STRING:
+ return str_value >= that.str_value;
+ default:
+ return false;
+ }
+}
+
+Value Value::operator-(const Value& that) const {
+ Value v;
+ if (type != that.type) {
+ ALOGE("Can't operate on different value types, %d, %d", type, that.type);
+ return v;
+ }
+ if (type == STRING) {
+ ALOGE("Can't operate on string value type");
+ return v;
+ }
+
+ switch (type) {
+ case INT:
+ v.setInt(int_value - that.int_value);
+ break;
+ case LONG:
+ v.setLong(long_value - that.long_value);
+ break;
+ case FLOAT:
+ v.setFloat(float_value - that.float_value);
+ break;
+ case DOUBLE:
+ v.setDouble(double_value - that.double_value);
+ break;
+ default:
+ break;
+ }
+ return v;
+}
+
+Value& Value::operator=(const Value& that) {
+ type = that.type;
+ switch (type) {
+ case INT:
+ int_value = that.int_value;
+ break;
+ case LONG:
+ long_value = that.long_value;
+ break;
+ case FLOAT:
+ float_value = that.float_value;
+ break;
+ case DOUBLE:
+ double_value = that.double_value;
+ break;
+ case STRING:
+ str_value = that.str_value;
+ break;
+ default:
+ break;
+ }
+ return *this;
+}
+
+Value& Value::operator+=(const Value& that) {
+ if (type != that.type) {
+ ALOGE("Can't operate on different value types, %d, %d", type, that.type);
+ return *this;
+ }
+ if (type == STRING) {
+ ALOGE("Can't operate on string value type");
+ return *this;
+ }
+
+ switch (type) {
+ case INT:
+ int_value += that.int_value;
+ break;
+ case LONG:
+ long_value += that.long_value;
+ break;
+ case FLOAT:
+ float_value += that.float_value;
+ break;
+ case DOUBLE:
+ double_value += that.double_value;
+ break;
+ default:
+ break;
+ }
+ return *this;
+}
+
+double Value::getDouble() const {
+ switch (type) {
+ case INT:
+ return int_value;
+ case LONG:
+ return long_value;
+ case FLOAT:
+ return float_value;
+ case DOUBLE:
+ return double_value;
+ default:
+ return 0;
+ }
+}
+
bool equalDimensions(const std::vector<Matcher>& dimension_a,
const std::vector<Matcher>& dimension_b) {
bool eq = dimension_a.size() == dimension_b.size();
diff --git a/cmds/statsd/src/FieldValue.h b/cmds/statsd/src/FieldValue.h
index 02c49b9..b1b885e 100644
--- a/cmds/statsd/src/FieldValue.h
+++ b/cmds/statsd/src/FieldValue.h
@@ -32,7 +32,7 @@
const int32_t kClearLastBitDeco = 0x7f;
const int32_t kClearAllPositionMatcherMask = 0xffff00ff;
-enum Type { UNKNOWN, INT, LONG, FLOAT, STRING };
+enum Type { UNKNOWN, INT, LONG, FLOAT, DOUBLE, STRING };
int32_t getEncodedField(int32_t pos[], int32_t depth, bool includeDepth);
@@ -212,7 +212,7 @@
* the result is equal to the Matcher Field. That's a bit wise AND operation + check if 2 ints are
* equal. Nothing can beat the performance of this matching algorithm.
*
- * TODO: ADD EXAMPLE HERE.
+ * TODO(b/110561213): ADD EXAMPLE HERE.
*/
struct Matcher {
Matcher(const Field& matcher, int32_t mask) : mMatcher(matcher), mMask(mask){};
@@ -283,6 +283,11 @@
type = FLOAT;
}
+ Value(double v) {
+ double_value = v;
+ type = DOUBLE;
+ }
+
Value(const std::string& v) {
str_value = v;
type = STRING;
@@ -298,10 +303,21 @@
type = LONG;
}
+ void setFloat(float v) {
+ float_value = v;
+ type = FLOAT;
+ }
+
+ void setDouble(double v) {
+ double_value = v;
+ type = DOUBLE;
+ }
+
union {
int32_t int_value;
int64_t long_value;
float float_value;
+ double double_value;
};
std::string str_value;
@@ -313,12 +329,19 @@
return type;
}
+ double getDouble() const;
+
Value(const Value& from);
bool operator==(const Value& that) const;
bool operator!=(const Value& that) const;
bool operator<(const Value& that) const;
+ bool operator>(const Value& that) const;
+ bool operator>=(const Value& that) const;
+ Value operator-(const Value& that) const;
+ Value& operator+=(const Value& that);
+ Value& operator=(const Value& that);
};
/**
diff --git a/cmds/statsd/src/HashableDimensionKey.cpp b/cmds/statsd/src/HashableDimensionKey.cpp
index 7103034..af8b3af 100644
--- a/cmds/statsd/src/HashableDimensionKey.cpp
+++ b/cmds/statsd/src/HashableDimensionKey.cpp
@@ -65,8 +65,6 @@
for (const auto& value : values) {
for (size_t i = 0; i < matcherFields.size(); ++i) {
const auto& matcher = matcherFields[i];
- // TODO: potential optimization here to break early because all fields are naturally
- // sorted.
if (value.mField.matches(matcher)) {
output->addValue(value);
output->mutableValue(num_matches)->mField.setTag(value.mField.getTag());
@@ -196,4 +194,4 @@
} // namespace statsd
} // namespace os
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index ae44ee9..1119eb3 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -440,7 +440,6 @@
if (argCount == 2) {
// Automatically pick the UID
uid = IPCThreadState::self()->getCallingUid();
- // TODO: What if this isn't a binder call? Should we fail?
name.assign(args[1].c_str(), args[1].size());
good = true;
} else if (argCount == 3) {
@@ -493,7 +492,6 @@
if (argCount == 3) {
// Automatically pick the UID
uid = IPCThreadState::self()->getCallingUid();
- // TODO: What if this isn't a binder call? Should we fail?
name.assign(args[2].c_str(), args[2].size());
good = true;
} else if (argCount == 4) {
@@ -578,7 +576,6 @@
if (argCount == 2) {
// Automatically pick the UID
uid = IPCThreadState::self()->getCallingUid();
- // TODO: What if this isn't a binder call? Should we fail?
name.assign(args[1].c_str(), args[1].size());
good = true;
} else if (argCount == 3) {
@@ -604,7 +601,6 @@
vector<uint8_t> data;
mProcessor->onDumpReport(ConfigKey(uid, StrToInt64(name)), getElapsedRealtimeNs(),
false /* include_current_bucket*/, ADB_DUMP, &data);
- // TODO: print the returned StatsLogReport to file instead of printing to logcat.
if (proto) {
for (size_t i = 0; i < data.size(); i ++) {
fprintf(out, "%c", data[i]);
diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h
index 0a11f7c..ed90050 100644
--- a/cmds/statsd/src/StatsService.h
+++ b/cmds/statsd/src/StatsService.h
@@ -49,7 +49,6 @@
virtual ~StatsService();
/** The anomaly alarm registered with AlarmManager won't be updated by less than this. */
- // TODO: Consider making this configurable. And choose a good number.
const uint32_t MIN_DIFF_TO_UPDATE_REGISTERED_ALARM_SECS = 5;
virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags);
diff --git a/cmds/statsd/src/anomaly/AlarmMonitor.cpp b/cmds/statsd/src/anomaly/AlarmMonitor.cpp
index 78f0c2b..bc36dad 100644
--- a/cmds/statsd/src/anomaly/AlarmMonitor.cpp
+++ b/cmds/statsd/src/anomaly/AlarmMonitor.cpp
@@ -60,7 +60,7 @@
ALOGW("Asked to add a 0-time alarm.");
return;
}
- // TODO: Ensure that refractory period is respected.
+ // TODO(b/110563466): Ensure that refractory period is respected.
VLOG("Adding alarm with time %u", alarm->timestampSec);
mPq.push(alarm);
if (mRegisteredAlarmTimeSec < 1 ||
diff --git a/cmds/statsd/src/anomaly/AnomalyTracker.cpp b/cmds/statsd/src/anomaly/AnomalyTracker.cpp
index f32efee..ee111cd 100644
--- a/cmds/statsd/src/anomaly/AnomalyTracker.cpp
+++ b/cmds/statsd/src/anomaly/AnomalyTracker.cpp
@@ -208,7 +208,8 @@
}
void AnomalyTracker::declareAnomaly(const int64_t& timestampNs, const MetricDimensionKey& key) {
- // TODO: Why receive timestamp? RefractoryPeriod should always be based on real time right now.
+ // TODO(b/110563466): Why receive timestamp? RefractoryPeriod should always be based on
+ // real time right now.
if (isInRefractoryPeriod(timestampNs, key)) {
VLOG("Skipping anomaly declaration since within refractory period");
return;
@@ -216,7 +217,8 @@
if (mAlert.has_refractory_period_secs()) {
mRefractoryPeriodEndsSec[key] = ((timestampNs + NS_PER_SEC - 1) / NS_PER_SEC) // round up
+ mAlert.refractory_period_secs();
- // TODO: If we had access to the bucket_size_millis, consider calling resetStorage()
+ // TODO(b/110563466): If we had access to the bucket_size_millis, consider
+ // calling resetStorage()
// if (mAlert.refractory_period_secs() > mNumOfPastBuckets * bucketSizeNs) {resetStorage();}
}
@@ -230,7 +232,7 @@
StatsdStats::getInstance().noteAnomalyDeclared(mConfigKey, mAlert.id());
- // TODO: This should also take in the const MetricDimensionKey& key?
+ // TODO(b/110564268): This should also take in the const MetricDimensionKey& key?
android::util::stats_write(android::util::ANOMALY_DETECTED, mConfigKey.GetUid(),
mConfigKey.GetId(), mAlert.id());
}
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index cdb72ab..279ed02 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -29,6 +29,7 @@
import "frameworks/base/core/proto/android/telecomm/enums.proto";
import "frameworks/base/core/proto/android/telephony/enums.proto";
import "frameworks/base/core/proto/android/view/enums.proto";
+import "frameworks/base/proto/src/metrics_constants.proto";
/**
* The master atom class. This message defines all of the available
@@ -121,12 +122,11 @@
ANROccurred anr_occurred = 79;
WTFOccurred wtf_occurred = 80;
LowMemReported low_mem_reported = 81;
-
-
+ GenericAtom generic_atom = 82;
}
// Pulled events will start at field 10000.
- // Next: 10022
+ // Next: 10023
oneof pulled {
WifiBytesTransfer wifi_bytes_transfer = 10000;
WifiBytesTransferByFgBg wifi_bytes_transfer_by_fg_bg = 10001;
@@ -150,6 +150,7 @@
RemainingBatteryCapacity remaining_battery_capacity = 10019;
FullBatteryCapacity full_battery_capacity = 10020;
Temperature temperature = 10021;
+ BinderCalls binder_calls = 10022;
}
// DO NOT USE field numbers above 100,000 in AOSP. Field numbers above
@@ -1975,3 +1976,50 @@
// Temperature in tenths of a degree C.
optional int32 temperature_dC = 3;
}
+
+/**
+ * Pulls the statistics of calls to Binder.
+ *
+ * Binder stats are cumulative from boot unless somebody reset the data using
+ * > adb shell dumpsys binder_calls_stats --reset
+ */
+message BinderCalls {
+ // TODO(gaillard): figure out if binder call stats includes data from isolated uids, if a uid
+ // gets recycled and we have isolated uids, we might attribute the data incorrectly.
+ // TODO(gaillard): there is a high dimensions cardinality, figure out if we should drop the less
+ // commonly used APIs.
+ optional int32 uid = 1 [(is_uid) = true];
+ // Fully qualified class name of the API call.
+ optional string service_class_name = 2;
+ // Method name of the API call. It can also be a transaction code if we cannot resolve it to a
+ // name. See Binder#getTransactionName.
+ optional string service_method_name = 3;
+ // Total number of API calls.
+ optional int64 call_count = 4;
+ // Number of exceptions thrown by the API.
+ optional int64 exception_count = 5;
+ // Total latency of all API calls.
+ // Average can be computed using total_latency_micros / call_count.
+ optional int64 total_latency_micros = 6;
+ // Maximum latency of one API call.
+ optional int64 max_latency_micros = 7;
+ // Total CPU usage of all API calls.
+ optional int64 total_cpu_micros = 8;
+ // Maximum CPU usage of one API call.
+ optional int64 max_cpu_micros = 9;
+ // Maximum parcel reply size of one API call.
+ optional int64 max_reply_size_bytes = 10;
+ // Maximum parcel request size of one API call.
+ optional int64 max_request_size_bytes = 11;
+}
+
+/**
+ * An atom for generic metrics logging. Available from Android Q.
+ * One has to add an enum to frameworks/base/proto/src/metrics_constants.proto
+ * to extend another metric.
+ */
+message GenericAtom {
+ // Type of event. Previously it only indicated visual elements but now it
+ // is expanded to describe any type of event.
+ optional com_android_internal_logging.MetricsEvent.View view = 1;
+}
diff --git a/cmds/statsd/src/external/Perfetto.cpp b/cmds/statsd/src/external/Perfetto.cpp
index 0554483..e44351b 100644
--- a/cmds/statsd/src/external/Perfetto.cpp
+++ b/cmds/statsd/src/external/Perfetto.cpp
@@ -113,7 +113,7 @@
return false;
}
- std::string cfgProto = config.trace_config().SerializeAsString();
+ const std::string& cfgProto = config.trace_config();
size_t bytesWritten = fwrite(cfgProto.data(), 1, cfgProto.size(), writePipeStream);
fclose(writePipeStream);
if (bytesWritten != cfgProto.size() || cfgProto.size() == 0) {
diff --git a/cmds/statsd/src/external/Perfprofd.cpp b/cmds/statsd/src/external/Perfprofd.cpp
index ff237e8..1678f10 100644
--- a/cmds/statsd/src/external/Perfprofd.cpp
+++ b/cmds/statsd/src/external/Perfprofd.cpp
@@ -55,17 +55,8 @@
return false;
}
- // Add protobufs can't be described in AIDL, we need to re-serialize
- // the config proto to send it.
- std::vector<uint8_t> proto_serialized;
- {
- const auto& config_proto = config.perfprofd_config();
- int size = config_proto.ByteSize();
- proto_serialized.resize(size);
- ::google::protobuf::uint8* target_ptr =
- reinterpret_cast<::google::protobuf::uint8*>(proto_serialized.data());
- config_proto.SerializeWithCachedSizesToArray(target_ptr);
- }
+ auto* data = reinterpret_cast<const uint8_t*>(config.perfprofd_config().data());
+ std::vector<uint8_t> proto_serialized(data, data + config.perfprofd_config().size());
// TODO: alert-id etc?
diff --git a/cmds/statsd/src/external/ResourceHealthManagerPuller.cpp b/cmds/statsd/src/external/ResourceHealthManagerPuller.cpp
index 3741202..ae97d7a 100644
--- a/cmds/statsd/src/external/ResourceHealthManagerPuller.cpp
+++ b/cmds/statsd/src/external/ResourceHealthManagerPuller.cpp
@@ -54,7 +54,7 @@
ResourceHealthManagerPuller::ResourceHealthManagerPuller(int tagId) : StatsPuller(tagId) {
}
-// TODO: add other health atoms (eg. Temperature).
+// TODO(b/110565992): add other health atoms (eg. Temperature).
bool ResourceHealthManagerPuller::PullInternal(vector<shared_ptr<LogEvent>>* data) {
if (!getHealthHal()) {
ALOGE("Health Hal not loaded");
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index 06edff9..160d6e8 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -171,7 +171,14 @@
1 * NS_PER_SEC,
new StatsCompanionServicePuller(android::util::PROCESS_MEMORY_STATE)}},
// temperature
- {android::util::TEMPERATURE, {{}, {}, 1, new ResourceThermalManagerPuller()}}};
+ {android::util::TEMPERATURE, {{}, {}, 1, new ResourceThermalManagerPuller()}},
+ // binder_calls
+ {android::util::BINDER_CALLS,
+ {{4, 5, 6, 8},
+ {2, 3, 7, 9, 10, 11},
+ 1 * NS_PER_SEC,
+ new StatsCompanionServicePuller(android::util::BINDER_CALLS)}}
+ };
StatsPullerManager::StatsPullerManager() : mNextPullTimeNs(NO_ALARM_UPDATE) {
}
diff --git a/cmds/statsd/src/guardrail/StatsdStats.cpp b/cmds/statsd/src/guardrail/StatsdStats.cpp
index 764366f..a955511 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.cpp
+++ b/cmds/statsd/src/guardrail/StatsdStats.cpp
@@ -100,10 +100,10 @@
const int FIELD_ID_UID_MAP_DELETED_APPS = 4;
const std::map<int, std::pair<size_t, size_t>> StatsdStats::kAtomDimensionKeySizeLimitMap = {
+ {android::util::BINDER_CALLS, {6000, 10000}},
{android::util::CPU_TIME_PER_UID_FREQ, {6000, 10000}},
};
-// TODO: add stats for pulled atoms.
StatsdStats::StatsdStats() {
mPushedAtomStats.resize(android::util::kMaxPushedAtomId + 1);
mStartTimeSec = getWallClockSec();
diff --git a/cmds/statsd/src/guardrail/StatsdStats.h b/cmds/statsd/src/guardrail/StatsdStats.h
index 74541d3..9fb2cd8 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.h
+++ b/cmds/statsd/src/guardrail/StatsdStats.h
@@ -86,7 +86,6 @@
static StatsdStats& getInstance();
~StatsdStats(){};
- // TODO: set different limit if the device is low ram.
const static int kDimensionKeySizeSoftLimit = 500;
const static int kDimensionKeySizeHardLimit = 800;
diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp
index 4e4f146..04d34f3 100644
--- a/cmds/statsd/src/logd/LogEvent.cpp
+++ b/cmds/statsd/src/logd/LogEvent.cpp
@@ -273,7 +273,7 @@
}
int64_t LogEvent::GetLong(size_t key, status_t* err) const {
- // TODO: encapsulate the magical operations all in Field struct as a static function.
+ // TODO(b/110561208): encapsulate the magical operations in Field struct as static functions
int field = getSimpleField(key);
for (const auto& value : mValues) {
if (value.mField.getField() == field) {
diff --git a/cmds/statsd/src/main.cpp b/cmds/statsd/src/main.cpp
index 6aa68da..2f15d0f 100644
--- a/cmds/statsd/src/main.cpp
+++ b/cmds/statsd/src/main.cpp
@@ -82,7 +82,6 @@
if (err != NO_ERROR) {
return err;
}
- // TODO: Do we need to tweak thread priority?
err = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
if (err != NO_ERROR) {
pthread_attr_destroy(&attr);
diff --git a/cmds/statsd/src/matchers/LogMatchingTracker.h b/cmds/statsd/src/matchers/LogMatchingTracker.h
index 4f30a04..88ab4e6 100644
--- a/cmds/statsd/src/matchers/LogMatchingTracker.h
+++ b/cmds/statsd/src/matchers/LogMatchingTracker.h
@@ -86,8 +86,6 @@
// The collection of the event tag ids that this LogMatchingTracker cares. So we can quickly
// return kNotMatched when we receive an event with an id not in the list. This is especially
// useful when we have a complex CombinationLogMatcherTracker.
- // TODO: Consider use an array instead of stl set. In reality, the number of the tag ids a
- // LogMatchingTracker cares is only a few.
std::set<int> mAtomIds;
};
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp
index 43f53e0..a894782 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp
@@ -68,7 +68,6 @@
const sp<ConditionWizard>& wizard,
const int64_t startTimeNs)
: MetricProducer(metric.id(), key, startTimeNs, conditionIndex, wizard) {
- // TODO: evaluate initial conditions. and set mConditionMet.
if (metric.has_bucket()) {
mBucketSizeNs =
TimeUnitToBucketSizeInMillisGuardrailed(key.GetUid(), metric.bucket()) * 1000000;
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.h b/cmds/statsd/src/metrics/CountMetricProducer.h
index 139c083..520d5de 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.h
+++ b/cmds/statsd/src/metrics/CountMetricProducer.h
@@ -40,7 +40,6 @@
class CountMetricProducer : public MetricProducer {
public:
- // TODO: Pass in the start time from MetricsManager, it should be consistent for all metrics.
CountMetricProducer(const ConfigKey& key, const CountMetric& countMetric,
const int conditionIndex, const sp<ConditionWizard>& wizard,
const int64_t startTimeNs);
@@ -80,7 +79,6 @@
void flushCurrentBucketLocked(const int64_t& eventTimeNs) override;
- // TODO: Add a lock to mPastBuckets.
std::unordered_map<MetricDimensionKey, std::vector<CountBucket>> mPastBuckets;
// The current bucket (may be a partial bucket).
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
index 62237bc..a19eb0b 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
@@ -76,9 +76,6 @@
mStopAllIndex(stopAllIndex),
mNested(nesting),
mContainANYPositionInInternalDimensions(false) {
- // TODO: The following boiler plate code appears in all MetricProducers, but we can't abstract
- // them in the base class, because the proto generated CountMetric, and DurationMetric are
- // not related. Maybe we should add a template in the future??
if (metric.has_bucket()) {
mBucketSizeNs =
TimeUnitToBucketSizeInMillisGuardrailed(key.GetUid(), metric.bucket()) * 1000000;
@@ -434,8 +431,6 @@
VLOG("Metric %lld onConditionChanged", (long long)mMetricId);
mCondition = conditionMet;
flushIfNeededLocked(eventTime);
- // TODO: need to populate the condition change time from the event which triggers the condition
- // change, instead of using current time.
for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
for (auto& pair : whatIt.second) {
pair.second->onConditionChanged(conditionMet, eventTime);
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h
index 88e455a..c496b12 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.h
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.h
@@ -115,7 +115,6 @@
ConditionState mUnSlicedPartCondition;
// Save the past buckets and we can clear when the StatsLogReport is dumped.
- // TODO: Add a lock to mPastBuckets.
std::unordered_map<MetricDimensionKey, std::vector<DurationBucket>> mPastBuckets;
// The duration trackers in the current bucket.
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.h b/cmds/statsd/src/metrics/EventMetricProducer.h
index 62d1105..7f7aa37 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.h
+++ b/cmds/statsd/src/metrics/EventMetricProducer.h
@@ -33,7 +33,6 @@
class EventMetricProducer : public MetricProducer {
public:
- // TODO: Pass in the start time from MetricsManager, it should be consistent for all metrics.
EventMetricProducer(const ConfigKey& key, const EventMetric& eventMetric,
const int conditionIndex, const sp<ConditionWizard>& wizard,
const int64_t startTimeNs);
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index d75bb10..a779410 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -101,7 +101,6 @@
translateFieldMatcher(metric.gauge_fields_filter().fields(), &mFieldMatchers);
}
- // TODO: use UidMap if uid->pkg_name is required
if (metric.has_dimensions_in_what()) {
translateFieldMatcher(metric.dimensions_in_what(), &mDimensionsInWhat);
mContainANYPositionInDimensionsInWhat = HasPositionANY(metric.dimensions_in_what());
@@ -299,7 +298,6 @@
protoOutput->end(protoToken);
mPastBuckets.clear();
- // TODO: Clear mDimensionKeyMap once the report is dumped.
}
void GaugeMetricProducer::pullLocked(const int64_t timestampNs) {
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.h b/cmds/statsd/src/metrics/GaugeMetricProducer.h
index 62ec27eb..6984aa2 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.h
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.h
@@ -33,7 +33,7 @@
namespace statsd {
struct GaugeAtom {
- GaugeAtom(std::shared_ptr<vector<FieldValue>> fields, int64_t elapsedTimeNs, int wallClockNs)
+ GaugeAtom(std::shared_ptr<vector<FieldValue>> fields, int64_t elapsedTimeNs, int64_t wallClockNs)
: mFields(fields), mElapsedTimestamps(elapsedTimeNs), mWallClockTimestampNs(wallClockNs) {
}
std::shared_ptr<vector<FieldValue>> mFields;
@@ -122,7 +122,6 @@
const int mPullTagId;
// Save the past buckets and we can clear when the StatsLogReport is dumped.
- // TODO: Add a lock to mPastBuckets.
std::unordered_map<MetricDimensionKey, std::vector<GaugeBucket>> mPastBuckets;
// The current partial bucket.
diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp
index 213f9ab..0e5ef4d 100644
--- a/cmds/statsd/src/metrics/MetricsManager.cpp
+++ b/cmds/statsd/src/metrics/MetricsManager.cpp
@@ -238,7 +238,6 @@
if (event.GetTagId() == android::util::APP_BREADCRUMB_REPORTED) {
// Check that app breadcrumb reported fields are valid.
- // TODO: Find a way to make these checks easier to maintain.
status_t err = NO_ERROR;
// Uid is 3rd from last field and must match the caller's uid,
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index ef8b6cc..c6f7bb4 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define DEBUG false // STOPSHIP if true
+#define DEBUG true // STOPSHIP if true
#include "Log.h"
#include "ValueMetricProducer.h"
@@ -27,7 +27,7 @@
using android::util::FIELD_COUNT_REPEATED;
using android::util::FIELD_TYPE_BOOL;
-using android::util::FIELD_TYPE_FLOAT;
+using android::util::FIELD_TYPE_DOUBLE;
using android::util::FIELD_TYPE_INT32;
using android::util::FIELD_TYPE_INT64;
using android::util::FIELD_TYPE_MESSAGE;
@@ -64,7 +64,8 @@
const int FIELD_ID_DIMENSION_LEAF_IN_WHAT = 4;
const int FIELD_ID_DIMENSION_LEAF_IN_CONDITION = 5;
// for ValueBucketInfo
-const int FIELD_ID_VALUE = 3;
+const int FIELD_ID_VALUE_LONG = 3;
+const int FIELD_ID_VALUE_DOUBLE = 7;
const int FIELD_ID_BUCKET_NUM = 4;
const int FIELD_ID_START_BUCKET_ELAPSED_MILLIS = 5;
const int FIELD_ID_END_BUCKET_ELAPSED_MILLIS = 6;
@@ -79,6 +80,7 @@
mPullerManager(pullerManager),
mValueField(metric.value_field()),
mPullTagId(pullTagId),
+ mIsPulled(pullTagId != -1),
mMinBucketSizeNs(metric.min_bucket_size_nanos()),
mDimensionSoftLimit(StatsdStats::kAtomDimensionKeySizeLimitMap.find(pullTagId) !=
StatsdStats::kAtomDimensionKeySizeLimitMap.end()
@@ -88,8 +90,9 @@
StatsdStats::kAtomDimensionKeySizeLimitMap.end()
? StatsdStats::kAtomDimensionKeySizeLimitMap.at(pullTagId).second
: StatsdStats::kDimensionKeySizeHardLimit),
- mUseAbsoluteValueOnReset(metric.use_absolute_value_on_reset()) {
- // TODO: valuemetric for pushed events may need unlimited bucket length
+ mUseAbsoluteValueOnReset(metric.use_absolute_value_on_reset()),
+ mAggregationType(metric.aggregation_type()),
+ mValueType(metric.aggregation_type() == ValueMetric::AVG ? DOUBLE : LONG) {
int64_t bucketSizeMills = 0;
if (metric.has_bucket()) {
bucketSizeMills = TimeUnitToBucketSizeInMillisGuardrailed(key.GetUid(), metric.bucket());
@@ -124,9 +127,9 @@
mSliceByPositionALL = HasPositionALL(metric.dimensions_in_what()) ||
HasPositionALL(metric.dimensions_in_condition());
- // Kicks off the puller immediately.
flushIfNeededLocked(startTimestampNs);
- if (mPullTagId != -1) {
+ // Kicks off the puller immediately.
+ if (mIsPulled) {
mPullerManager->RegisterReceiver(mPullTagId, this,
mCurrentBucketStartTimeNs + mBucketSizeNs, mBucketSizeNs);
}
@@ -137,7 +140,7 @@
ValueMetricProducer::~ValueMetricProducer() {
VLOG("~ValueMetricProducer() called");
- if (mPullTagId != -1) {
+ if (mIsPulled) {
mPullerManager->UnRegisterReceiver(mPullTagId, this);
}
}
@@ -246,11 +249,15 @@
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_NUM,
(long long)(getBucketNumFromEndTimeNs(bucket.mBucketEndNs)));
}
-
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_VALUE, (long long)bucket.mValue);
+ if (mValueType == LONG) {
+ protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_VALUE_LONG,
+ (long long)bucket.mValueLong);
+ } else {
+ protoOutput->write(FIELD_TYPE_DOUBLE | FIELD_ID_VALUE_DOUBLE, bucket.mValueDouble);
+ }
protoOutput->end(bucketInfoToken);
- VLOG("\t bucket [%lld - %lld] count: %lld", (long long)bucket.mBucketStartNs,
- (long long)bucket.mBucketEndNs, (long long)bucket.mValue);
+ VLOG("\t bucket [%lld - %lld] count: %lld, %.2f", (long long)bucket.mBucketStartNs,
+ (long long)bucket.mBucketEndNs, (long long)bucket.mValueLong, bucket.mValueDouble);
}
protoOutput->end(wrapperToken);
}
@@ -272,7 +279,7 @@
flushIfNeededLocked(eventTimeNs);
- if (mPullTagId != -1) {
+ if (mIsPulled) {
vector<shared_ptr<LogEvent>> allData;
if (mPullerManager->Pull(mPullTagId, eventTimeNs, &allData)) {
if (allData.size() == 0) {
@@ -322,10 +329,10 @@
(unsigned long)mCurrentSlicedBucket.size());
if (verbose) {
for (const auto& it : mCurrentSlicedBucket) {
- fprintf(out, "\t(what)%s\t(condition)%s (value)%lld\n",
- it.first.getDimensionKeyInWhat().toString().c_str(),
- it.first.getDimensionKeyInCondition().toString().c_str(),
- (unsigned long long)it.second.sum);
+ fprintf(out, "\t(what)%s\t(condition)%s (value)%s\n",
+ it.first.getDimensionKeyInWhat().toString().c_str(),
+ it.first.getDimensionKeyInCondition().toString().c_str(),
+ it.second.value.toString().c_str());
}
}
}
@@ -350,6 +357,27 @@
return false;
}
+const Value getDoubleOrLong(const Value& value) {
+ Value v;
+ switch (value.type) {
+ case INT:
+ v.setLong(value.int_value);
+ break;
+ case LONG:
+ v.setLong(value.long_value);
+ break;
+ case FLOAT:
+ v.setDouble(value.float_value);
+ break;
+ case DOUBLE:
+ v.setDouble(value.double_value);
+ break;
+ default:
+ break;
+ }
+ return v;
+}
+
void ValueMetricProducer::onMatchedLogEventInternalLocked(
const size_t matcherIndex, const MetricDimensionKey& eventKey,
const ConditionKey& conditionKey, bool condition,
@@ -368,19 +396,25 @@
}
Interval& interval = mCurrentSlicedBucket[eventKey];
- int error = 0;
- const int64_t value = event.GetLong(mField, &error);
- if (error < 0) {
+ if (mField > event.size()) {
+ VLOG("Failed to extract value field %d from atom %s. %d", mField, event.ToString().c_str(),
+ (int)event.size());
return;
}
+ Value value = getDoubleOrLong(event.getValues()[mField - 1].mValue);
- if (mPullTagId != -1) { // for pulled events
+ Value diff;
+ bool hasDiff = false;
+ if (mIsPulled) {
+ // Always require condition for pulled events. In the case of no condition, only pull
+ // on bucket boundaries, in which we fake condition changes.
if (mCondition == true) {
if (!interval.startUpdated) {
interval.start = value;
interval.startUpdated = true;
} else {
- // skip it if there is already value recorded for the start
+ // Skip it if there is already value recorded for the start. Happens when puller
+ // takes too long to finish. In this case we take the previous value.
VLOG("Already recorded value for this dimension %s", eventKey.toString().c_str());
}
} else {
@@ -388,31 +422,55 @@
// If not, take absolute value or drop it, based on config.
if (interval.startUpdated) {
if (value >= interval.start) {
- interval.sum += (value - interval.start);
- interval.hasValue = true;
+ diff = (value - interval.start);
+ hasDiff = true;
} else {
if (mUseAbsoluteValueOnReset) {
- interval.sum += value;
- interval.hasValue = true;
+ diff = value;
+ hasDiff = true;
} else {
- VLOG("Dropping data for atom %d, prev: %lld, now: %lld", mPullTagId,
- (long long)interval.start, (long long)value);
+ VLOG("Dropping data for atom %d, prev: %s, now: %s", mPullTagId,
+ interval.start.toString().c_str(), value.toString().c_str());
}
}
interval.startUpdated = false;
} else {
- VLOG("No start for matching end %lld", (long long)value);
- interval.tainted += 1;
+ VLOG("No start for matching end %s", value.toString().c_str());
}
}
- } else { // for pushed events, only accumulate when condition is true
- if (mCondition == true || mConditionTrackerIndex < 0) {
- interval.sum += value;
- interval.hasValue = true;
+ } else {
+ // for pushed events, only aggregate when sliced condition is true
+ if (condition == true || mConditionTrackerIndex < 0) {
+ diff = value;
+ hasDiff = true;
}
}
+ if (hasDiff) {
+ if (interval.hasValue) {
+ switch (mAggregationType) {
+ case ValueMetric::SUM:
+ // for AVG, we add up and take average when flushing the bucket
+ case ValueMetric::AVG:
+ interval.value += diff;
+ break;
+ case ValueMetric::MIN:
+ interval.value = diff < interval.value ? diff : interval.value;
+ break;
+ case ValueMetric::MAX:
+ interval.value = diff > interval.value ? diff : interval.value;
+ break;
+ default:
+ break;
+ }
+ } else {
+ interval.value = diff;
+ interval.hasValue = true;
+ }
+ interval.sampleSize += 1;
+ }
- long wholeBucketVal = interval.sum;
+ // TODO: propgate proper values down stream when anomaly support doubles
+ long wholeBucketVal = interval.value.long_value;
auto prev = mCurrentFullBucket.find(eventKey);
if (prev != mCurrentFullBucket.end()) {
wholeBucketVal += prev->second;
@@ -459,18 +517,15 @@
if (info.mBucketEndNs - mCurrentBucketStartTimeNs >= mMinBucketSizeNs) {
// The current bucket is large enough to keep.
- int tainted = 0;
for (const auto& slice : mCurrentSlicedBucket) {
- tainted += slice.second.tainted;
- tainted += slice.second.startUpdated;
if (slice.second.hasValue) {
- info.mValue = slice.second.sum;
+ info.mValueLong = slice.second.value.long_value;
+ info.mValueDouble = (double)slice.second.value.long_value / slice.second.sampleSize;
// it will auto create new vector of ValuebucketInfo if the key is not found.
auto& bucketList = mPastBuckets[slice.first];
bucketList.push_back(info);
}
}
- VLOG("%d tainted pairs in the bucket", tainted);
} else {
mSkippedBuckets.emplace_back(info.mBucketStartNs, info.mBucketEndNs);
}
@@ -479,7 +534,8 @@
// Accumulate partial buckets with current value and then send to anomaly tracker.
if (mCurrentFullBucket.size() > 0) {
for (const auto& slice : mCurrentSlicedBucket) {
- mCurrentFullBucket[slice.first] += slice.second.sum;
+ // TODO: fix this when anomaly can accept double values
+ mCurrentFullBucket[slice.first] += slice.second.value.long_value;
}
for (const auto& slice : mCurrentFullBucket) {
for (auto& tracker : mAnomalyTrackers) {
@@ -494,7 +550,9 @@
for (const auto& slice : mCurrentSlicedBucket) {
for (auto& tracker : mAnomalyTrackers) {
if (tracker != nullptr) {
- tracker->addPastBucket(slice.first, slice.second.sum, mCurrentBucketNum);
+ // TODO: fix this when anomaly can accept double values
+ tracker->addPastBucket(slice.first, slice.second.value.long_value,
+ mCurrentBucketNum);
}
}
}
@@ -502,7 +560,8 @@
} else {
// Accumulate partial bucket.
for (const auto& slice : mCurrentSlicedBucket) {
- mCurrentFullBucket[slice.first] += slice.second.sum;
+ // TODO: fix this when anomaly can accept double values
+ mCurrentFullBucket[slice.first] += slice.second.value.long_value;
}
}
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h
index 75f3113..188e3de 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.h
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.h
@@ -23,6 +23,7 @@
#include "../condition/ConditionTracker.h"
#include "../external/PullDataReceiver.h"
#include "../external/StatsPullerManager.h"
+#include "../stats_log_util.h"
#include "MetricProducer.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
@@ -33,7 +34,8 @@
struct ValueBucket {
int64_t mBucketStartNs;
int64_t mBucketEndNs;
- int64_t mValue;
+ int64_t mValueLong;
+ double mValueDouble;
};
class ValueMetricProducer : public virtual MetricProducer, public virtual PullDataReceiver {
@@ -45,6 +47,7 @@
virtual ~ValueMetricProducer();
+ // Process data pulled on bucket boundary.
void onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& data) override;
// ValueMetric needs special logic if it's a pulled atom.
@@ -120,20 +123,22 @@
// tagId for pulled data. -1 if this is not pulled
const int mPullTagId;
+ // if this is pulled metric
+ const bool mIsPulled;
+
int mField;
// internal state of a bucket.
typedef struct {
// Pulled data always come in pair of <start, end>. This holds the value
- // for start. The diff (end - start) is added to sum.
- int64_t start;
+ // for start. The diff (end - start) is taken as the real value.
+ Value start;
// Whether the start data point is updated
bool startUpdated;
- // If end data point comes before the start, record this pair as tainted
- // and the value is not added to the running sum.
- int tainted;
- // Running sum of known pairs in this bucket
- int64_t sum;
+ // Current value, depending on the aggregation type.
+ Value value;
+ // Number of samples collected.
+ int sampleSize;
// If this dimension has any non-tainted value. If not, don't report the
// dimension.
bool hasValue;
@@ -144,7 +149,6 @@
std::unordered_map<MetricDimensionKey, int64_t> mCurrentFullBucket;
// Save the past buckets and we can clear when the StatsLogReport is dumped.
- // TODO: Add a lock to mPastBuckets.
std::unordered_map<MetricDimensionKey, std::vector<ValueBucket>> mPastBuckets;
// Pairs of (elapsed start, elapsed end) denoting buckets that were skipped.
@@ -163,6 +167,10 @@
const bool mUseAbsoluteValueOnReset;
+ const ValueMetric::AggregationType mAggregationType;
+
+ const Type mValueType;
+
FRIEND_TEST(ValueMetricProducerTest, TestNonDimensionalEvents);
FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsTakeAbsoluteValueOnReset);
FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsTakeZeroOnReset);
@@ -177,6 +185,11 @@
FRIEND_TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition);
FRIEND_TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition2);
FRIEND_TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition3);
+ FRIEND_TEST(ValueMetricProducerTest, TestPushedAggregateMin);
+ FRIEND_TEST(ValueMetricProducerTest, TestPushedAggregateMax);
+ FRIEND_TEST(ValueMetricProducerTest, TestPushedAggregateAvg);
+ FRIEND_TEST(ValueMetricProducerTest, TestPushedAggregateSum);
+ FRIEND_TEST(ValueMetricProducerTest, TestPushedAggregateSumSliced);
};
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
index 149b318..ccb1d43 100644
--- a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
+++ b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
@@ -44,7 +44,6 @@
int64_t lastStartTime;
// existing duration in current bucket.
int64_t lastDuration;
- // TODO: Optimize the way we track sliced condition in duration metrics.
// cache the HashableDimensionKeys we need to query the condition for this duration event.
ConditionKey conditionKeys;
diff --git a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
index b833dfc..956383a 100644
--- a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
+++ b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
@@ -326,7 +326,6 @@
int64_t OringDurationTracker::predictAnomalyTimestampNs(
const DurationAnomalyTracker& anomalyTracker, const int64_t eventTimestampNs) const {
- // TODO: Unit-test this and see if it can be done more efficiently (e.g. use int32).
// The anomaly threshold.
const int64_t thresholdNs = anomalyTracker.getAnomalyThreshold();
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp
index deb9893..e03edb3 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.cpp
+++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp
@@ -103,7 +103,6 @@
}
allConditionTrackers[condition_it->second]->setSliced(true);
allConditionTrackers[it->second]->setSliced(true);
- // TODO: We need to verify the link is valid.
}
conditionIndex = condition_it->second;
@@ -169,7 +168,6 @@
bool isStateTracker(const SimplePredicate& simplePredicate, vector<Matcher>* primaryKeys) {
// 1. must not have "stop". must have "dimension"
if (!simplePredicate.has_stop() && simplePredicate.has_dimensions()) {
- // TODO: need to check the start atom matcher too.
auto it = android::util::AtomsInfo::kStateAtomsFieldOptions.find(
simplePredicate.dimensions().field());
// 2. must be based on a state atom.
diff --git a/cmds/statsd/src/metrics_constants/metrics_constants.proto b/cmds/statsd/src/metrics_constants/metrics_constants.proto
new file mode 120000
index 0000000..8366db7
--- /dev/null
+++ b/cmds/statsd/src/metrics_constants/metrics_constants.proto
@@ -0,0 +1 @@
+../../../../proto/src/metrics_constants.proto
\ No newline at end of file
diff --git a/cmds/statsd/src/packages/UidMap.h b/cmds/statsd/src/packages/UidMap.h
index 514eec7..91f2030 100644
--- a/cmds/statsd/src/packages/UidMap.h
+++ b/cmds/statsd/src/packages/UidMap.h
@@ -146,7 +146,6 @@
void getListenerListCopyLocked(std::vector<wp<PackageInfoListener>>* output);
- // TODO: Use shared_mutex for improved read-locking if a library can be found in Android.
mutable mutex mMutex;
mutable mutex mIsolatedMutex;
diff --git a/cmds/statsd/src/perfetto/perfetto_config.proto b/cmds/statsd/src/perfetto/perfetto_config.proto
deleted file mode 100644
index 56d12f8..0000000
--- a/cmds/statsd/src/perfetto/perfetto_config.proto
+++ /dev/null
@@ -1,60 +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.
- */
-
-syntax = "proto2";
-
-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/perfprofd/perfprofd_config.proto b/cmds/statsd/src/perfprofd/perfprofd_config.proto
deleted file mode 120000
index 2e3305f..0000000
--- a/cmds/statsd/src/perfprofd/perfprofd_config.proto
+++ /dev/null
@@ -1 +0,0 @@
-../../../../../../system/extras/perfprofd/binder_interface/perfprofd_config.proto
\ No newline at end of file
diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto
index 2fe17da..cfd6269 100644
--- a/cmds/statsd/src/stats_log.proto
+++ b/cmds/statsd/src/stats_log.proto
@@ -106,7 +106,11 @@
optional int64 end_bucket_elapsed_nanos = 2;
- optional int64 value = 3;
+ oneof values {
+ int64 value_long = 3;
+
+ double value_double = 7;
+ }
optional int64 bucket_num = 4;
diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto
index 638fbb9..26dfda3 100644
--- a/cmds/statsd/src/statsd_config.proto
+++ b/cmds/statsd/src/statsd_config.proto
@@ -21,9 +21,6 @@
option java_package = "com.android.internal.os";
option java_outer_classname = "StatsdConfigProto";
-import "frameworks/base/cmds/statsd/src/perfetto/perfetto_config.proto";
-import "frameworks/base/cmds/statsd/src/perfprofd/perfprofd_config.proto";
-
enum Position {
POSITION_UNKNOWN = 0;
@@ -262,6 +259,9 @@
enum AggregationType {
SUM = 1;
+ MIN = 2;
+ MAX = 3;
+ AVG = 4;
}
optional AggregationType aggregation_type = 8 [default = SUM];
@@ -301,11 +301,21 @@
}
message PerfettoDetails {
- optional perfetto.protos.TraceConfig trace_config = 1;
+ // The |trace_config| field is a proto-encoded message of type
+ // perfetto.protos.TraceConfig defined in
+ // //external/perfetto/protos/perfetto/config/. On device,
+ // statsd doesn't need to deserialize the message as it's just
+ // passed binary-encoded to the perfetto cmdline client.
+ optional bytes trace_config = 1;
}
message PerfprofdDetails {
- optional android.perfprofd.ProfilingConfig perfprofd_config = 1;
+ // The |perfprofd_config| field is a proto-encoded message of type
+ // android.perfprofd.ProfilingConfig defined in
+ // //system/extras/perfprofd/. On device, statsd doesn't need to
+ // deserialize the message as it's just passed binary-encoded to
+ // the perfprofd service.
+ optional bytes perfprofd_config = 1;
}
message BroadcastSubscriberDetails {
diff --git a/cmds/statsd/src/storage/StorageManager.cpp b/cmds/statsd/src/storage/StorageManager.cpp
index 1f81812..3ebc8a4 100644
--- a/cmds/statsd/src/storage/StorageManager.cpp
+++ b/cmds/statsd/src/storage/StorageManager.cpp
@@ -57,7 +57,7 @@
}
// When index ends before hitting 3, file name is corrupted. We
// intentionally put -1 at index 0 to indicate the error to caller.
- // TODO: consider removing files with unexpected name format.
+ // TODO(b/110563137): consider removing files with unexpected name format.
if (index < 3) {
result[0] = -1;
}
diff --git a/cmds/statsd/tests/FieldValue_test.cpp b/cmds/statsd/tests/FieldValue_test.cpp
index c253bc1..a9305ac 100644
--- a/cmds/statsd/tests/FieldValue_test.cpp
+++ b/cmds/statsd/tests/FieldValue_test.cpp
@@ -312,7 +312,8 @@
dim.addValue(FieldValue(field4, value4));
SubscriberReporter::getStatsDimensionsValue(dim);
- // TODO: can't test anything here because SubscriberReport class doesn't have any read api.
+ // TODO(b/110562792): can't test anything here because StatsDimensionsValue class doesn't
+ // have any read api.
}
TEST(AtomMatcherTest, TestWriteDimensionToProto) {
@@ -483,4 +484,4 @@
} // namespace android
#else
GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
\ No newline at end of file
+#endif
diff --git a/cmds/statsd/tests/MetricsManager_test.cpp b/cmds/statsd/tests/MetricsManager_test.cpp
index 4de9986..8fbb58a 100644
--- a/cmds/statsd/tests/MetricsManager_test.cpp
+++ b/cmds/statsd/tests/MetricsManager_test.cpp
@@ -39,8 +39,6 @@
#ifdef __ANDROID__
-// TODO: ADD MORE TEST CASES.
-
const ConfigKey kConfigKey(0, 12345);
const long timeBaseSec = 1000;
diff --git a/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp b/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp
index 11aaab0..cc8894b 100644
--- a/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp
@@ -99,7 +99,6 @@
// If we want to test multiple dump data, we must do it in separate tests, because in the e2e tests,
// we should use the real API which will clear the data after dump data is called.
-// TODO: better refactor the code so that the tests are not so verbose.
TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks1) {
auto config = CreateStatsdConfig();
uint64_t bucketStartTimeNs = 10000000000;
diff --git a/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp b/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp
index 744828e..f2e8f58 100644
--- a/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp
@@ -141,23 +141,23 @@
EXPECT_EQ(baseTimeNs + 2 * bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
- EXPECT_TRUE(data.bucket_info(0).has_value());
+ EXPECT_TRUE(data.bucket_info(0).has_value_long());
EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(1).start_bucket_elapsed_nanos());
EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, data.bucket_info(1).end_bucket_elapsed_nanos());
- EXPECT_TRUE(data.bucket_info(1).has_value());
+ EXPECT_TRUE(data.bucket_info(1).has_value_long());
EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, data.bucket_info(2).start_bucket_elapsed_nanos());
EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs, data.bucket_info(2).end_bucket_elapsed_nanos());
- EXPECT_TRUE(data.bucket_info(2).has_value());
+ EXPECT_TRUE(data.bucket_info(2).has_value_long());
EXPECT_EQ(baseTimeNs + 6 * bucketSizeNs, data.bucket_info(3).start_bucket_elapsed_nanos());
EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs, data.bucket_info(3).end_bucket_elapsed_nanos());
- EXPECT_TRUE(data.bucket_info(3).has_value());
+ EXPECT_TRUE(data.bucket_info(3).has_value_long());
EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs, data.bucket_info(4).start_bucket_elapsed_nanos());
EXPECT_EQ(baseTimeNs + 8 * bucketSizeNs, data.bucket_info(4).end_bucket_elapsed_nanos());
- EXPECT_TRUE(data.bucket_info(4).has_value());
+ EXPECT_TRUE(data.bucket_info(4).has_value_long());
}
TEST(ValueMetricE2eTest, TestPulledEvents_LateAlarm) {
@@ -248,15 +248,15 @@
EXPECT_EQ(baseTimeNs + 2 * bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
- EXPECT_TRUE(data.bucket_info(0).has_value());
+ EXPECT_TRUE(data.bucket_info(0).has_value_long());
EXPECT_EQ(baseTimeNs + 8 * bucketSizeNs, data.bucket_info(1).start_bucket_elapsed_nanos());
EXPECT_EQ(baseTimeNs + 9 * bucketSizeNs, data.bucket_info(1).end_bucket_elapsed_nanos());
- EXPECT_TRUE(data.bucket_info(1).has_value());
+ EXPECT_TRUE(data.bucket_info(1).has_value_long());
EXPECT_EQ(baseTimeNs + 9 * bucketSizeNs, data.bucket_info(2).start_bucket_elapsed_nanos());
EXPECT_EQ(baseTimeNs + 10 * bucketSizeNs, data.bucket_info(2).end_bucket_elapsed_nanos());
- EXPECT_TRUE(data.bucket_info(2).has_value());
+ EXPECT_TRUE(data.bucket_info(2).has_value_long());
}
#else
diff --git a/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp b/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp
index 3a15466..d2fd95c 100644
--- a/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp
@@ -54,8 +54,8 @@
eventProducer.onMatchedLogEvent(1 /*matcher index*/, event1);
eventProducer.onMatchedLogEvent(1 /*matcher index*/, event2);
- // TODO: get the report and check the content after the ProtoOutputStream change is done.
- // eventProducer.onDumpReport();
+ // TODO(b/110561136): get the report and check the content after the ProtoOutputStream change
+ // is done eventProducer.onDumpReport();
}
TEST(EventMetricProducerTest, TestEventsWithNonSlicedCondition) {
diff --git a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
index c7e72f9..19c9f77 100644
--- a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
@@ -59,8 +59,6 @@
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- // TODO: pending refactor of StatsPullerManager
- // For now we still need this so that it doesn't do real pulling.
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
index d93b46f..3559a7c 100644
--- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
@@ -60,8 +60,6 @@
metric.mutable_value_field()->add_child()->set_field(2);
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- // TODO: pending refactor of StatsPullerManager
- // For now we still need this so that it doesn't do real pulling.
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
@@ -82,13 +80,11 @@
// has one slice
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
- valueProducer.setBucketSize(60 * NS_PER_SEC);
- // startUpdated:true tainted:0 sum:0 start:11
+ // startUpdated:true sum:0 start:11
EXPECT_EQ(true, curInterval.startUpdated);
- EXPECT_EQ(0, curInterval.tainted);
- EXPECT_EQ(0, curInterval.sum);
- EXPECT_EQ(11, curInterval.start);
+ EXPECT_EQ(false, curInterval.hasValue);
+ EXPECT_EQ(11, curInterval.start.long_value);
EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
allData.clear();
@@ -101,13 +97,12 @@
// has one slice
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
- // tartUpdated:false tainted:0 sum:12
+ // tartUpdated:false sum:12
EXPECT_EQ(true, curInterval.startUpdated);
- EXPECT_EQ(0, curInterval.tainted);
- EXPECT_EQ(0, curInterval.sum);
+ EXPECT_EQ(0, curInterval.value.long_value);
EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
- EXPECT_EQ(12, valueProducer.mPastBuckets.begin()->second.back().mValue);
+ EXPECT_EQ(12, valueProducer.mPastBuckets.begin()->second.back().mValueLong);
allData.clear();
event = make_shared<LogEvent>(tagId, bucket4StartTimeNs + 1);
@@ -118,13 +113,12 @@
valueProducer.onDataPulled(allData);
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
- // startUpdated:false tainted:0 sum:12
+ // startUpdated:false sum:12
EXPECT_EQ(true, curInterval.startUpdated);
- EXPECT_EQ(0, curInterval.tainted);
- EXPECT_EQ(0, curInterval.sum);
+ EXPECT_EQ(0, curInterval.value.long_value);
EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
EXPECT_EQ(2UL, valueProducer.mPastBuckets.begin()->second.size());
- EXPECT_EQ(13, valueProducer.mPastBuckets.begin()->second.back().mValue);
+ EXPECT_EQ(13, valueProducer.mPastBuckets.begin()->second.back().mValueLong);
}
/*
@@ -159,12 +153,10 @@
// has one slice
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
- valueProducer.setBucketSize(60 * NS_PER_SEC);
EXPECT_EQ(true, curInterval.startUpdated);
- EXPECT_EQ(0, curInterval.tainted);
- EXPECT_EQ(0, curInterval.sum);
- EXPECT_EQ(11, curInterval.start);
+ EXPECT_EQ(false, curInterval.hasValue);
+ EXPECT_EQ(11, curInterval.start.long_value);
EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
allData.clear();
@@ -178,11 +170,10 @@
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
EXPECT_EQ(true, curInterval.startUpdated);
- EXPECT_EQ(0, curInterval.tainted);
- EXPECT_EQ(0, curInterval.sum);
+ EXPECT_EQ(false, curInterval.hasValue);
EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
- EXPECT_EQ(10, valueProducer.mPastBuckets.begin()->second.back().mValue);
+ EXPECT_EQ(10, valueProducer.mPastBuckets.begin()->second.back().mValueLong);
allData.clear();
event = make_shared<LogEvent>(tagId, bucket4StartTimeNs + 1);
@@ -194,11 +185,10 @@
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
EXPECT_EQ(true, curInterval.startUpdated);
- EXPECT_EQ(0, curInterval.tainted);
- EXPECT_EQ(0, curInterval.sum);
+ EXPECT_EQ(false, curInterval.hasValue);
EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
EXPECT_EQ(2UL, valueProducer.mPastBuckets.begin()->second.size());
- EXPECT_EQ(26, valueProducer.mPastBuckets.begin()->second.back().mValue);
+ EXPECT_EQ(26, valueProducer.mPastBuckets.begin()->second.back().mValueLong);
}
/*
@@ -232,12 +222,10 @@
// has one slice
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
- valueProducer.setBucketSize(60 * NS_PER_SEC);
EXPECT_EQ(true, curInterval.startUpdated);
- EXPECT_EQ(0, curInterval.tainted);
- EXPECT_EQ(0, curInterval.sum);
- EXPECT_EQ(11, curInterval.start);
+ EXPECT_EQ(false, curInterval.hasValue);
+ EXPECT_EQ(11, curInterval.start.long_value);
EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
allData.clear();
@@ -251,8 +239,7 @@
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
EXPECT_EQ(true, curInterval.startUpdated);
- EXPECT_EQ(0, curInterval.tainted);
- EXPECT_EQ(0, curInterval.sum);
+ EXPECT_EQ(false, curInterval.hasValue);
EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
allData.clear();
@@ -265,11 +252,10 @@
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
EXPECT_EQ(true, curInterval.startUpdated);
- EXPECT_EQ(0, curInterval.tainted);
- EXPECT_EQ(0, curInterval.sum);
+ EXPECT_EQ(false, curInterval.hasValue);
EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
- EXPECT_EQ(26, valueProducer.mPastBuckets.begin()->second.back().mValue);
+ EXPECT_EQ(26, valueProducer.mPastBuckets.begin()->second.back().mValueLong);
}
/*
@@ -318,11 +304,10 @@
// has one slice
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
- // startUpdated:false tainted:0 sum:0 start:100
- EXPECT_EQ(100, curInterval.start);
+ // startUpdated:false sum:0 start:100
+ EXPECT_EQ(100, curInterval.start.long_value);
EXPECT_EQ(true, curInterval.startUpdated);
- EXPECT_EQ(0, curInterval.tainted);
- EXPECT_EQ(0, curInterval.sum);
+ EXPECT_EQ(false, curInterval.hasValue);
EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
vector<shared_ptr<LogEvent>> allData;
@@ -337,19 +322,19 @@
// has one slice
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
- // startUpdated:false tainted:0 sum:0 start:110
- EXPECT_EQ(110, curInterval.start);
+ // startUpdated:false sum:0 start:110
+ EXPECT_EQ(110, curInterval.start.long_value);
EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
- EXPECT_EQ(10, valueProducer.mPastBuckets.begin()->second.back().mValue);
+ EXPECT_EQ(10, valueProducer.mPastBuckets.begin()->second.back().mValueLong);
valueProducer.onConditionChanged(false, bucket2StartTimeNs + 1);
// has one slice
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
- // startUpdated:false tainted:0 sum:0 start:110
- EXPECT_EQ(10, curInterval.sum);
+ // startUpdated:false sum:0 start:110
+ EXPECT_EQ(10, curInterval.value.long_value);
EXPECT_EQ(false, curInterval.startUpdated);
}
@@ -435,7 +420,7 @@
valueProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1);
EXPECT_EQ(1UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
EXPECT_EQ(eventUpgradeTimeNs, valueProducer.mCurrentBucketStartTimeNs);
- EXPECT_EQ(20L, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mValue);
+ EXPECT_EQ(20L, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mValueLong);
allData.clear();
event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
@@ -446,7 +431,7 @@
valueProducer.onDataPulled(allData);
EXPECT_EQ(2UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
EXPECT_EQ(bucket2StartTimeNs, valueProducer.mCurrentBucketStartTimeNs);
- EXPECT_EQ(30L, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][1].mValue);
+ EXPECT_EQ(30L, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][1].mValueLong);
}
TEST(ValueMetricProducerTest, TestPulledValueWithUpgradeWhileConditionFalse) {
@@ -495,7 +480,7 @@
EXPECT_EQ(bucket2StartTimeNs-50, valueProducer.mCurrentBucketStartTimeNs);
EXPECT_EQ(1UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
EXPECT_EQ(bucketStartTimeNs, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketStartNs);
- EXPECT_EQ(20L, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mValue);
+ EXPECT_EQ(20L, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mValueLong);
EXPECT_FALSE(valueProducer.mCondition);
}
@@ -525,19 +510,20 @@
// has one slice
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
- EXPECT_EQ(10, curInterval.sum);
+ EXPECT_EQ(10, curInterval.value.long_value);
+ EXPECT_EQ(true, curInterval.hasValue);
valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event2);
// has one slice
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
- EXPECT_EQ(30, curInterval.sum);
+ EXPECT_EQ(30, curInterval.value.long_value);
valueProducer.flushIfNeededLocked(bucket3StartTimeNs);
EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
- EXPECT_EQ(30, valueProducer.mPastBuckets.begin()->second.back().mValue);
+ EXPECT_EQ(30, valueProducer.mPastBuckets.begin()->second.back().mValueLong);
}
TEST(ValueMetricProducerTest, TestPushedEventsWithCondition) {
@@ -574,7 +560,7 @@
// has one slice
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
- EXPECT_EQ(20, curInterval.sum);
+ EXPECT_EQ(20, curInterval.value.long_value);
shared_ptr<LogEvent> event3 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 30);
event3->write(1);
@@ -585,7 +571,7 @@
// has one slice
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
- EXPECT_EQ(50, curInterval.sum);
+ EXPECT_EQ(50, curInterval.value.long_value);
valueProducer.onConditionChangedLocked(false, bucketStartTimeNs + 35);
shared_ptr<LogEvent> event4 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 40);
@@ -597,12 +583,12 @@
// has one slice
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
- EXPECT_EQ(50, curInterval.sum);
+ EXPECT_EQ(50, curInterval.value.long_value);
valueProducer.flushIfNeededLocked(bucket3StartTimeNs);
EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
- EXPECT_EQ(50, valueProducer.mPastBuckets.begin()->second.back().mValue);
+ EXPECT_EQ(50, valueProducer.mPastBuckets.begin()->second.back().mValueLong);
}
TEST(ValueMetricProducerTest, TestAnomalyDetection) {
@@ -720,11 +706,10 @@
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
- // startUpdated:true tainted:0 sum:0 start:11
+ // startUpdated:true sum:0 start:11
EXPECT_EQ(true, curInterval.startUpdated);
- EXPECT_EQ(0, curInterval.tainted);
- EXPECT_EQ(0, curInterval.sum);
- EXPECT_EQ(11, curInterval.start);
+ EXPECT_EQ(false, curInterval.hasValue);
+ EXPECT_EQ(11, curInterval.start.long_value);
EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
// pull 2 at correct time
@@ -738,13 +723,12 @@
// has one slice
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
- // tartUpdated:false tainted:0 sum:12
+ // tartUpdated:false sum:12
EXPECT_EQ(true, curInterval.startUpdated);
- EXPECT_EQ(0, curInterval.tainted);
- EXPECT_EQ(0, curInterval.sum);
+ EXPECT_EQ(false, curInterval.hasValue);
EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
- EXPECT_EQ(12, valueProducer.mPastBuckets.begin()->second.back().mValue);
+ EXPECT_EQ(12, valueProducer.mPastBuckets.begin()->second.back().mValueLong);
// pull 3 come late.
// The previous bucket gets closed with error. (Has start value 23, no ending)
@@ -759,14 +743,13 @@
valueProducer.onDataPulled(allData);
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
- // startUpdated:false tainted:0 sum:12
+ // startUpdated:false sum:12
EXPECT_EQ(true, curInterval.startUpdated);
- EXPECT_EQ(0, curInterval.tainted);
- EXPECT_EQ(36, curInterval.start);
- EXPECT_EQ(0, curInterval.sum);
+ EXPECT_EQ(36, curInterval.start.long_value);
+ EXPECT_EQ(false, curInterval.hasValue);
EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
- EXPECT_EQ(12, valueProducer.mPastBuckets.begin()->second.back().mValue);
+ EXPECT_EQ(12, valueProducer.mPastBuckets.begin()->second.back().mValueLong);
}
/*
@@ -818,19 +801,17 @@
// has one slice
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
- // startUpdated:false tainted:0 sum:0 start:100
- EXPECT_EQ(100, curInterval.start);
+ // startUpdated:false sum:0 start:100
+ EXPECT_EQ(100, curInterval.start.long_value);
EXPECT_EQ(true, curInterval.startUpdated);
- EXPECT_EQ(0, curInterval.tainted);
- EXPECT_EQ(0, curInterval.sum);
+ EXPECT_EQ(false, curInterval.hasValue);
EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
// pull on bucket boundary come late, condition change happens before it
valueProducer.onConditionChanged(false, bucket2StartTimeNs + 1);
curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
EXPECT_EQ(false, curInterval.startUpdated);
- EXPECT_EQ(1, curInterval.tainted);
- EXPECT_EQ(0, curInterval.sum);
+ EXPECT_EQ(false, curInterval.hasValue);
EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
// Now the alarm is delivered.
@@ -846,8 +827,7 @@
curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
EXPECT_EQ(false, curInterval.startUpdated);
- EXPECT_EQ(1, curInterval.tainted);
- EXPECT_EQ(0, curInterval.sum);
+ EXPECT_EQ(false, curInterval.hasValue);
EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
}
@@ -911,28 +891,25 @@
// has one slice
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
- // startUpdated:false tainted:0 sum:0 start:100
- EXPECT_EQ(100, curInterval.start);
+ // startUpdated:false sum:0 start:100
+ EXPECT_EQ(100, curInterval.start.long_value);
EXPECT_EQ(true, curInterval.startUpdated);
- EXPECT_EQ(0, curInterval.tainted);
- EXPECT_EQ(0, curInterval.sum);
+ EXPECT_EQ(false, curInterval.hasValue);
EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
// pull on bucket boundary come late, condition change happens before it
valueProducer.onConditionChanged(false, bucket2StartTimeNs + 1);
curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
EXPECT_EQ(false, curInterval.startUpdated);
- EXPECT_EQ(1, curInterval.tainted);
- EXPECT_EQ(0, curInterval.sum);
+ EXPECT_EQ(false, curInterval.hasValue);
EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
// condition changed to true again, before the pull alarm is delivered
valueProducer.onConditionChanged(true, bucket2StartTimeNs + 25);
curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
EXPECT_EQ(true, curInterval.startUpdated);
- EXPECT_EQ(130, curInterval.start);
- EXPECT_EQ(1, curInterval.tainted);
- EXPECT_EQ(0, curInterval.sum);
+ EXPECT_EQ(130, curInterval.start.long_value);
+ EXPECT_EQ(false, curInterval.hasValue);
EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
// Now the alarm is delivered, but it is considered late, it has no effect
@@ -947,9 +924,8 @@
curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
EXPECT_EQ(true, curInterval.startUpdated);
- EXPECT_EQ(130, curInterval.start);
- EXPECT_EQ(1, curInterval.tainted);
- EXPECT_EQ(0, curInterval.sum);
+ EXPECT_EQ(130, curInterval.start.long_value);
+ EXPECT_EQ(false, curInterval.hasValue);
EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
}
@@ -1002,11 +978,10 @@
// has one slice
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
- // startUpdated:false tainted:0 sum:0 start:100
- EXPECT_EQ(100, curInterval.start);
+ // startUpdated:false sum:0 start:100
+ EXPECT_EQ(100, curInterval.start.long_value);
EXPECT_EQ(true, curInterval.startUpdated);
- EXPECT_EQ(0, curInterval.tainted);
- EXPECT_EQ(0, curInterval.sum);
+ EXPECT_EQ(false, curInterval.hasValue);
EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
// pull on bucket boundary come late, condition change happens before it.
@@ -1014,8 +989,7 @@
valueProducer.onConditionChanged(false, bucket2StartTimeNs + 1);
curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
EXPECT_EQ(false, curInterval.startUpdated);
- EXPECT_EQ(1, curInterval.tainted);
- EXPECT_EQ(0, curInterval.sum);
+ EXPECT_EQ(false, curInterval.hasValue);
EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
// Alarm is delivered in time, but the pull is very slow, and pullers are called in order,
@@ -1031,11 +1005,247 @@
curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
EXPECT_EQ(false, curInterval.startUpdated);
- EXPECT_EQ(1, curInterval.tainted);
- EXPECT_EQ(0, curInterval.sum);
+ EXPECT_EQ(false, curInterval.hasValue);
EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
}
+TEST(ValueMetricProducerTest, TestPushedAggregateMin) {
+ ValueMetric metric;
+ metric.set_id(metricId);
+ metric.set_bucket(ONE_MINUTE);
+ metric.mutable_value_field()->set_field(tagId);
+ metric.mutable_value_field()->add_child()->set_field(2);
+ metric.set_aggregation_type(ValueMetric::MIN);
+
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+
+ ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, -1, bucketStartTimeNs,
+ bucketStartTimeNs, pullerManager);
+ valueProducer.setBucketSize(60 * NS_PER_SEC);
+
+ shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
+ event1->write(1);
+ event1->write(10);
+ event1->init();
+ shared_ptr<LogEvent> event2 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 20);
+ event2->write(1);
+ event2->write(20);
+ event2->init();
+ valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event1);
+ // has one slice
+ EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+ EXPECT_EQ(10, curInterval.value.long_value);
+ EXPECT_EQ(true, curInterval.hasValue);
+
+ valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event2);
+
+ // has one slice
+ EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+ EXPECT_EQ(10, curInterval.value.long_value);
+
+ valueProducer.flushIfNeededLocked(bucket3StartTimeNs);
+ EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
+ EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
+ EXPECT_EQ(10, valueProducer.mPastBuckets.begin()->second.back().mValueLong);
+}
+
+TEST(ValueMetricProducerTest, TestPushedAggregateMax) {
+ ValueMetric metric;
+ metric.set_id(metricId);
+ metric.set_bucket(ONE_MINUTE);
+ metric.mutable_value_field()->set_field(tagId);
+ metric.mutable_value_field()->add_child()->set_field(2);
+ metric.set_aggregation_type(ValueMetric::MAX);
+
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+
+ ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, -1, bucketStartTimeNs,
+ bucketStartTimeNs, pullerManager);
+ valueProducer.setBucketSize(60 * NS_PER_SEC);
+
+ shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
+ event1->write(1);
+ event1->write(10);
+ event1->init();
+ shared_ptr<LogEvent> event2 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 20);
+ event2->write(1);
+ event2->write(20);
+ event2->init();
+ valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event1);
+ // has one slice
+ EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+ EXPECT_EQ(10, curInterval.value.long_value);
+ EXPECT_EQ(true, curInterval.hasValue);
+
+ valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event2);
+
+ // has one slice
+ EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+ EXPECT_EQ(20, curInterval.value.long_value);
+
+ valueProducer.flushIfNeededLocked(bucket3StartTimeNs);
+ EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
+ EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
+ EXPECT_EQ(20, valueProducer.mPastBuckets.begin()->second.back().mValueLong);
+}
+
+TEST(ValueMetricProducerTest, TestPushedAggregateAvg) {
+ ValueMetric metric;
+ metric.set_id(metricId);
+ metric.set_bucket(ONE_MINUTE);
+ metric.mutable_value_field()->set_field(tagId);
+ metric.mutable_value_field()->add_child()->set_field(2);
+ metric.set_aggregation_type(ValueMetric::AVG);
+
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+
+ ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, -1, bucketStartTimeNs,
+ bucketStartTimeNs, pullerManager);
+ valueProducer.setBucketSize(60 * NS_PER_SEC);
+
+ shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
+ event1->write(1);
+ event1->write(10);
+ event1->init();
+ shared_ptr<LogEvent> event2 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 20);
+ event2->write(1);
+ event2->write(15);
+ event2->init();
+ valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event1);
+ // has one slice
+ EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ ValueMetricProducer::Interval curInterval;
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+ EXPECT_EQ(10, curInterval.value.long_value);
+ EXPECT_EQ(true, curInterval.hasValue);
+ EXPECT_EQ(1, curInterval.sampleSize);
+
+ valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event2);
+
+ // has one slice
+ EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+ EXPECT_EQ(25, curInterval.value.long_value);
+ EXPECT_EQ(2, curInterval.sampleSize);
+
+ valueProducer.flushIfNeededLocked(bucket3StartTimeNs);
+ EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
+ EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
+ EXPECT_EQ(12.5, valueProducer.mPastBuckets.begin()->second.back().mValueDouble);
+}
+
+TEST(ValueMetricProducerTest, TestPushedAggregateSum) {
+ ValueMetric metric;
+ metric.set_id(metricId);
+ metric.set_bucket(ONE_MINUTE);
+ metric.mutable_value_field()->set_field(tagId);
+ metric.mutable_value_field()->add_child()->set_field(2);
+ metric.set_aggregation_type(ValueMetric::SUM);
+
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+
+ ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, -1, bucketStartTimeNs,
+ bucketStartTimeNs, pullerManager);
+ valueProducer.setBucketSize(60 * NS_PER_SEC);
+
+ shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
+ event1->write(1);
+ event1->write(10);
+ event1->init();
+ shared_ptr<LogEvent> event2 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 20);
+ event2->write(1);
+ event2->write(15);
+ event2->init();
+ valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event1);
+ // has one slice
+ EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+ EXPECT_EQ(10, curInterval.value.long_value);
+ EXPECT_EQ(true, curInterval.hasValue);
+
+ valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event2);
+
+ // has one slice
+ EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+ EXPECT_EQ(25, curInterval.value.long_value);
+
+ valueProducer.flushIfNeededLocked(bucket3StartTimeNs);
+ EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
+ EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
+ EXPECT_EQ(25, valueProducer.mPastBuckets.begin()->second.back().mValueLong);
+}
+
+TEST(ValueMetricProducerTest, TestPushedAggregateSumSliced) {
+ string slicedConditionName = "UID";
+ const int conditionTagId = 2;
+ ValueMetric metric;
+ metric.set_id(metricId);
+ metric.set_bucket(ONE_MINUTE);
+ metric.mutable_value_field()->set_field(tagId);
+ metric.mutable_value_field()->add_child()->set_field(1);
+ metric.set_aggregation_type(ValueMetric::SUM);
+
+ metric.set_condition(StringToId(slicedConditionName));
+ MetricConditionLink* link = metric.add_links();
+ link->set_condition(StringToId(slicedConditionName));
+ buildSimpleAtomFieldMatcher(tagId, 2, link->mutable_fields_in_what());
+ buildSimpleAtomFieldMatcher(conditionTagId, 2, link->mutable_fields_in_condition());
+
+ LogEvent event1(tagId, bucketStartTimeNs + 10);
+ event1.write(10); // value
+ event1.write("111"); // uid
+ event1.init();
+ ConditionKey key1;
+ key1[StringToId(slicedConditionName)] =
+ {getMockedDimensionKey(conditionTagId, 2, "111")};
+
+ LogEvent event2(tagId, bucketStartTimeNs + 20);
+ event2.write(15);
+ event2.write("222");
+ event2.init();
+ ConditionKey key2;
+ key2[StringToId(slicedConditionName)] =
+ {getMockedDimensionKey(conditionTagId, 2, "222")};
+
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ EXPECT_CALL(*wizard, query(_, key1, _, _, _, _)).WillOnce(Return(ConditionState::kFalse));
+
+ EXPECT_CALL(*wizard, query(_, key2, _, _, _, _)).WillOnce(Return(ConditionState::kTrue));
+
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+
+ ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, -1, bucketStartTimeNs,
+ bucketStartTimeNs, pullerManager);
+ valueProducer.setBucketSize(60 * NS_PER_SEC);
+
+ valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
+
+ EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+ EXPECT_EQ(false, curInterval.hasValue);
+
+ valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
+
+ // has one slice
+ EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+ EXPECT_EQ(15, curInterval.value.long_value);
+
+ valueProducer.flushIfNeededLocked(bucket3StartTimeNs);
+ EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
+ EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
+ EXPECT_EQ(15, valueProducer.mPastBuckets.begin()->second.back().mValueLong);
+}
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/core/ShellUiAutomatorBridge.java b/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/core/ShellUiAutomatorBridge.java
index 6538515..950a258 100644
--- a/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/core/ShellUiAutomatorBridge.java
+++ b/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/core/ShellUiAutomatorBridge.java
@@ -62,7 +62,7 @@
IBinder token = new Binder();
try {
ContentProviderHolder holder = activityManager.getContentProviderExternal(
- providerName, UserHandle.USER_SYSTEM, token);
+ providerName, UserHandle.USER_SYSTEM, token, "*uiautomator*");
if (holder == null) {
throw new IllegalStateException("Could not find provider: " + providerName);
}
diff --git a/config/hiddenapi-light-greylist.txt b/config/hiddenapi-light-greylist.txt
index cc17ea8..d81e2dc 100644
--- a/config/hiddenapi-light-greylist.txt
+++ b/config/hiddenapi-light-greylist.txt
@@ -137,6 +137,7 @@
Landroid/app/ActivityManager$StackInfo;->visible:Z
Landroid/app/ActivityManager$TaskDescription;->getBackgroundColor()I
Landroid/app/ActivityManager$TaskDescription;->getInMemoryIcon()Landroid/graphics/Bitmap;
+Landroid/app/ActivityManager$TaskDescription;->setIcon(Landroid/graphics/Bitmap;)V
Landroid/app/ActivityManager$TaskSnapshot;->getContentInsets()Landroid/graphics/Rect;
Landroid/app/ActivityManager$TaskSnapshot;->getOrientation()I
Landroid/app/ActivityManager$TaskSnapshot;->getScale()F
@@ -856,9 +857,6 @@
Landroid/app/PictureInPictureArgs;-><init>()V
Landroid/app/PictureInPictureArgs;->setActions(Ljava/util/List;)V
Landroid/app/PictureInPictureArgs;->setAspectRatio(F)V
-Landroid/app/PictureInPictureParams;->getActions()Ljava/util/List;
-Landroid/app/PictureInPictureParams;->getAspectRatio()F
-Landroid/app/PictureInPictureParams;->getSourceRectHint()Landroid/graphics/Rect;
Landroid/app/Presentation;->createPresentationContext(Landroid/content/Context;Landroid/view/Display;I)Landroid/content/Context;
Landroid/app/ProgressDialog;->mMessageView:Landroid/widget/TextView;
Landroid/app/ProgressDialog;->mProgress:Landroid/widget/ProgressBar;
@@ -1217,6 +1215,7 @@
Landroid/bluetooth/IBluetooth$Stub;-><init>()V
Landroid/bluetooth/IBluetooth$Stub;->asInterface(Landroid/os/IBinder;)Landroid/bluetooth/IBluetooth;
Landroid/bluetooth/IBluetooth$Stub;->TRANSACTION_enable:I
+Landroid/bluetooth/IBluetooth;->fetchRemoteUuids(Landroid/bluetooth/BluetoothDevice;)Z
Landroid/bluetooth/IBluetooth;->getAddress()Ljava/lang/String;
Landroid/bluetooth/IBluetooth;->getRemoteAlias(Landroid/bluetooth/BluetoothDevice;)Ljava/lang/String;
Landroid/bluetooth/IBluetooth;->isEnabled()Z
@@ -1445,8 +1444,6 @@
Landroid/content/pm/ApplicationInfo;->isPackageUnavailable(Landroid/content/pm/PackageManager;)Z
Landroid/content/pm/ApplicationInfo;->nativeLibraryRootDir:Ljava/lang/String;
Landroid/content/pm/ApplicationInfo;->primaryCpuAbi:Ljava/lang/String;
-Landroid/content/pm/ApplicationInfo;->privateFlags:I
-Landroid/content/pm/ApplicationInfo;->PRIVATE_FLAG_PRIVILEGED:I
Landroid/content/pm/ApplicationInfo;->resourceDirs:[Ljava/lang/String;
Landroid/content/pm/ApplicationInfo;->scanPublicSourceDir:Ljava/lang/String;
Landroid/content/pm/ApplicationInfo;->scanSourceDir:Ljava/lang/String;
@@ -1491,6 +1488,7 @@
Landroid/content/pm/IPackageManager$Stub$Proxy;->getAppOpPermissionPackages(Ljava/lang/String;)[Ljava/lang/String;
Landroid/content/pm/IPackageManager$Stub$Proxy;->getInstalledPackages(II)Landroid/content/pm/ParceledListSlice;
Landroid/content/pm/IPackageManager$Stub$Proxy;->getInstallLocation()I
+Landroid/content/pm/IPackageManager$Stub$Proxy;->getLastChosenActivity(Landroid/content/Intent;Ljava/lang/String;I)Landroid/content/pm/ResolveInfo;
Landroid/content/pm/IPackageManager$Stub$Proxy;->getPackageInfo(Ljava/lang/String;II)Landroid/content/pm/PackageInfo;
Landroid/content/pm/IPackageManager$Stub$Proxy;->getPackagesForUid(I)[Ljava/lang/String;
Landroid/content/pm/IPackageManager$Stub$Proxy;->getSystemSharedLibraryNames()[Ljava/lang/String;
@@ -1721,6 +1719,7 @@
Landroid/content/pm/PackageParser;->collectCertificates(Landroid/content/pm/PackageParser$Package;Ljava/io/File;Z)V
Landroid/content/pm/PackageParser;->collectCertificates(Landroid/content/pm/PackageParser$Package;Z)V
Landroid/content/pm/PackageParser;->generateActivityInfo(Landroid/content/pm/PackageParser$Activity;ILandroid/content/pm/PackageUserState;I)Landroid/content/pm/ActivityInfo;
+Landroid/content/pm/PackageParser;->generateApplicationInfo(Landroid/content/pm/PackageParser$Package;ILandroid/content/pm/PackageUserState;)Landroid/content/pm/ApplicationInfo;
Landroid/content/pm/PackageParser;->generateApplicationInfo(Landroid/content/pm/PackageParser$Package;ILandroid/content/pm/PackageUserState;I)Landroid/content/pm/ApplicationInfo;
Landroid/content/pm/PackageParser;->generateInstrumentationInfo(Landroid/content/pm/PackageParser$Instrumentation;I)Landroid/content/pm/InstrumentationInfo;
Landroid/content/pm/PackageParser;->generatePackageInfo(Landroid/content/pm/PackageParser$Package;[IIJJLjava/util/Set;Landroid/content/pm/PackageUserState;)Landroid/content/pm/PackageInfo;
@@ -1732,6 +1731,7 @@
Landroid/content/pm/PackageParser;->mCallback:Landroid/content/pm/PackageParser$Callback;
Landroid/content/pm/PackageParser;->NEW_PERMISSIONS:[Landroid/content/pm/PackageParser$NewPermissionInfo;
Landroid/content/pm/PackageParser;->parseBaseApk(Ljava/lang/String;Landroid/content/res/Resources;Landroid/content/res/XmlResourceParser;I[Ljava/lang/String;)Landroid/content/pm/PackageParser$Package;
+Landroid/content/pm/PackageParser;->parseBaseApplication(Landroid/content/pm/PackageParser$Package;Landroid/content/res/Resources;Landroid/content/res/XmlResourceParser;I[Ljava/lang/String;)Z
Landroid/content/pm/PackageParser;->parseMonolithicPackage(Ljava/io/File;I)Landroid/content/pm/PackageParser$Package;
Landroid/content/pm/PackageParser;->parsePackage(Ljava/io/File;I)Landroid/content/pm/PackageParser$Package;
Landroid/content/pm/PackageParser;->parsePackage(Ljava/io/File;IZ)Landroid/content/pm/PackageParser$Package;
@@ -1815,6 +1815,7 @@
Landroid/content/res/AssetManager;->setConfiguration(IILjava/lang/String;IIIIIIIIIIIIIII)V
Landroid/content/res/AssetManager;->sSystem:Landroid/content/res/AssetManager;
Landroid/content/res/ColorStateList$ColorStateListFactory;-><init>(Landroid/content/res/ColorStateList;)V
+Landroid/content/res/ColorStateList;-><init>()V
Landroid/content/res/ColorStateList;->canApplyTheme()Z
Landroid/content/res/ColorStateList;->getColors()[I
Landroid/content/res/ColorStateList;->getStates()[[I
@@ -1877,6 +1878,7 @@
Landroid/content/res/Resources;->updateSystemConfiguration(Landroid/content/res/Configuration;Landroid/util/DisplayMetrics;Landroid/content/res/CompatibilityInfo;)V
Landroid/content/res/ResourcesImpl;-><init>(Landroid/content/res/AssetManager;Landroid/util/DisplayMetrics;Landroid/content/res/Configuration;Landroid/view/DisplayAdjustments;)V
Landroid/content/res/ResourcesImpl;->getAssets()Landroid/content/res/AssetManager;
+Landroid/content/res/ResourcesImpl;->getDisplayMetrics()Landroid/util/DisplayMetrics;
Landroid/content/res/ResourcesImpl;->getValue(ILandroid/util/TypedValue;Z)V
Landroid/content/res/ResourcesImpl;->mAccessLock:Ljava/lang/Object;
Landroid/content/res/ResourcesImpl;->mAnimatorCache:Landroid/content/res/ConfigurationBoundResourceCache;
@@ -2132,6 +2134,7 @@
Landroid/graphics/CanvasProperty;->createPaint(Landroid/graphics/Paint;)Landroid/graphics/CanvasProperty;
Landroid/graphics/ColorMatrixColorFilter;->mMatrix:Landroid/graphics/ColorMatrix;
Landroid/graphics/ColorMatrixColorFilter;->setColorMatrix(Landroid/graphics/ColorMatrix;)V
+Landroid/graphics/ColorMatrixColorFilter;->setColorMatrixArray([F)V
Landroid/graphics/drawable/AnimatedImageDrawable;->onAnimationEnd()V
Landroid/graphics/drawable/AnimatedRotateDrawable;->setFramesCount(I)V
Landroid/graphics/drawable/AnimatedRotateDrawable;->setFramesDuration(I)V
@@ -2485,8 +2488,6 @@
Landroid/hardware/display/WifiDisplayStatus;->mActiveDisplay:Landroid/hardware/display/WifiDisplay;
Landroid/hardware/display/WifiDisplayStatus;->mDisplays:[Landroid/hardware/display/WifiDisplay;
Landroid/hardware/display/WifiDisplayStatus;->SCAN_STATE_NOT_SCANNING:I
-Landroid/hardware/fingerprint/Fingerprint;->getFingerId()I
-Landroid/hardware/fingerprint/Fingerprint;->getName()Ljava/lang/CharSequence;
Landroid/hardware/fingerprint/FingerprintManager$AuthenticationResult;->getFingerprint()Landroid/hardware/fingerprint/Fingerprint;
Landroid/hardware/fingerprint/FingerprintManager;->getAuthenticatorId()J
Landroid/hardware/fingerprint/FingerprintManager;->getEnrolledFingerprints()Ljava/util/List;
@@ -3922,7 +3923,6 @@
Landroid/os/Environment;->getDataSystemDirectory()Ljava/io/File;
Landroid/os/Environment;->getLegacyExternalStorageObbDirectory()Ljava/io/File;
Landroid/os/Environment;->getOemDirectory()Ljava/io/File;
-Landroid/os/Environment;->getStorageDirectory()Ljava/io/File;
Landroid/os/Environment;->getVendorDirectory()Ljava/io/File;
Landroid/os/Environment;->initForCurrentUser()V
Landroid/os/Environment;->maybeTranslateEmulatedPathToInternal(Ljava/io/File;)Ljava/io/File;
@@ -3949,18 +3949,6 @@
Landroid/os/Handler;->mCallback:Landroid/os/Handler$Callback;
Landroid/os/Handler;->mLooper:Landroid/os/Looper;
Landroid/os/Handler;->mMessenger:Landroid/os/IMessenger;
-Landroid/os/health/HealthKeys$Constants;-><init>(Ljava/lang/Class;)V
-Landroid/os/health/HealthStats;-><init>(Landroid/os/Parcel;)V
-Landroid/os/health/HealthStatsParceler;-><init>(Landroid/os/health/HealthStatsWriter;)V
-Landroid/os/health/HealthStatsParceler;-><init>(Landroid/os/Parcel;)V
-Landroid/os/health/HealthStatsParceler;->getHealthStats()Landroid/os/health/HealthStats;
-Landroid/os/health/HealthStatsWriter;-><init>(Landroid/os/health/HealthKeys$Constants;)V
-Landroid/os/health/HealthStatsWriter;->addMeasurement(IJ)V
-Landroid/os/health/HealthStatsWriter;->addMeasurements(ILjava/lang/String;J)V
-Landroid/os/health/HealthStatsWriter;->addStats(ILjava/lang/String;Landroid/os/health/HealthStatsWriter;)V
-Landroid/os/health/HealthStatsWriter;->addTimer(IIJ)V
-Landroid/os/health/HealthStatsWriter;->addTimers(ILjava/lang/String;Landroid/os/health/TimerStat;)V
-Landroid/os/health/HealthStatsWriter;->flattenToParcel(Landroid/os/Parcel;)V
Landroid/os/health/SystemHealthManager;-><init>()V
Landroid/os/health/SystemHealthManager;->from(Landroid/content/Context;)Landroid/os/health/SystemHealthManager;
Landroid/os/HwParcel;-><init>(Z)V
@@ -4036,8 +4024,6 @@
Landroid/os/MessageQueue;->mQuitAllowed:Z
Landroid/os/MessageQueue;->nativePollOnce(JI)V
Landroid/os/MessageQueue;->next()Landroid/os/Message;
-Landroid/os/MessageQueue;->postSyncBarrier()I
-Landroid/os/MessageQueue;->removeSyncBarrier(I)V
Landroid/os/Parcel$ReadWriteHelper;-><init>()V
Landroid/os/Parcel;->getGlobalAllocCount()J
Landroid/os/Parcel;->getGlobalAllocSize()J
@@ -4049,13 +4035,11 @@
Landroid/os/Parcel;->readCreator(Landroid/os/Parcelable$Creator;Ljava/lang/ClassLoader;)Landroid/os/Parcelable;
Landroid/os/Parcel;->readExceptionCode()I
Landroid/os/Parcel;->readParcelableCreator(Ljava/lang/ClassLoader;)Landroid/os/Parcelable$Creator;
-Landroid/os/Parcel;->readParcelableList(Ljava/util/List;Ljava/lang/ClassLoader;)Ljava/util/List;
Landroid/os/Parcel;->readRawFileDescriptor()Ljava/io/FileDescriptor;
Landroid/os/Parcel;->writeArrayMap(Landroid/util/ArrayMap;)V
Landroid/os/Parcel;->writeArraySet(Landroid/util/ArraySet;)V
Landroid/os/Parcel;->writeCharSequence(Ljava/lang/CharSequence;)V
Landroid/os/Parcel;->writeParcelableCreator(Landroid/os/Parcelable;)V
-Landroid/os/Parcel;->writeParcelableList(Ljava/util/List;I)V
Landroid/os/ParcelableParcel;-><init>(Ljava/lang/ClassLoader;)V
Landroid/os/ParcelableParcel;->CREATOR:Landroid/os/Parcelable$ClassLoaderCreator;
Landroid/os/ParcelableParcel;->getClassLoader()Ljava/lang/ClassLoader;
@@ -4139,6 +4123,7 @@
Landroid/os/SELinux;->restoreconRecursive(Ljava/io/File;)Z
Landroid/os/ServiceManager;-><init>()V
Landroid/os/ServiceManager;->addService(Ljava/lang/String;Landroid/os/IBinder;)V
+Landroid/os/ServiceManager;->addService(Ljava/lang/String;Landroid/os/IBinder;Z)V
Landroid/os/ServiceManager;->addService(Ljava/lang/String;Landroid/os/IBinder;ZI)V
Landroid/os/ServiceManager;->checkService(Ljava/lang/String;)Landroid/os/IBinder;
Landroid/os/ServiceManager;->getIServiceManager()Landroid/os/IServiceManager;
@@ -4202,7 +4187,6 @@
Landroid/os/storage/StorageVolume;->getFatVolumeId()I
Landroid/os/storage/StorageVolume;->getMaxFileSize()J
Landroid/os/storage/StorageVolume;->getOwner()Landroid/os/UserHandle;
-Landroid/os/storage/StorageVolume;->getPath()Ljava/lang/String;
Landroid/os/storage/StorageVolume;->getPathFile()Ljava/io/File;
Landroid/os/storage/StorageVolume;->getUserLabel()Ljava/lang/String;
Landroid/os/storage/StorageVolume;->mDescription:Ljava/lang/String;
@@ -4223,6 +4207,7 @@
Landroid/os/storage/VolumeInfo;->getDiskId()Ljava/lang/String;
Landroid/os/storage/VolumeInfo;->getEnvironmentForState(I)Ljava/lang/String;
Landroid/os/storage/VolumeInfo;->getFsUuid()Ljava/lang/String;
+Landroid/os/storage/VolumeInfo;->getInternalPath()Ljava/io/File;
Landroid/os/storage/VolumeInfo;->getInternalPathForUser(I)Ljava/io/File;
Landroid/os/storage/VolumeInfo;->getMountUserId()I
Landroid/os/storage/VolumeInfo;->getPath()Ljava/io/File;
@@ -4247,7 +4232,6 @@
Landroid/os/StrictMode$ThreadPolicy;->mask:I
Landroid/os/StrictMode$VmPolicy$Builder;->mMask:I
Landroid/os/StrictMode$VmPolicy;->mask:I
-Landroid/os/StrictMode;->conditionallyCheckInstanceCounts()V
Landroid/os/StrictMode;->disableDeathOnFileUriExposure()V
Landroid/os/StrictMode;->enableDeathOnFileUriExposure()V
Landroid/os/StrictMode;->enterCriticalSpan(Ljava/lang/String;)Landroid/os/StrictMode$Span;
@@ -4258,6 +4242,7 @@
Landroid/os/StrictMode;->sLastVmViolationTime:Ljava/util/HashMap;
Landroid/os/StrictMode;->sWindowManager:Landroid/util/Singleton;
Landroid/os/StrictMode;->violationsBeingTimed:Ljava/lang/ThreadLocal;
+Landroid/os/SystemClock;-><init>()V
Landroid/os/SystemClock;->currentThreadTimeMicro()J
Landroid/os/SystemClock;->currentTimeMicro()J
Landroid/os/SystemProperties;-><init>()V
@@ -4342,6 +4327,7 @@
Landroid/os/UserManager;->getUserStartRealtime()J
Landroid/os/UserManager;->getUserUnlockRealtime()J
Landroid/os/UserManager;->hasBaseUserRestriction(Ljava/lang/String;Landroid/os/UserHandle;)Z
+Landroid/os/UserManager;->isDeviceInDemoMode(Landroid/content/Context;)Z
Landroid/os/UserManager;->isGuestUser(I)Z
Landroid/os/UserManager;->isLinkedUser()Z
Landroid/os/UserManager;->isUserAdmin(I)Z
@@ -4361,19 +4347,11 @@
Landroid/os/VintfRuntimeInfo;->getOsName()Ljava/lang/String;
Landroid/os/VintfRuntimeInfo;->getOsRelease()Ljava/lang/String;
Landroid/os/VintfRuntimeInfo;->getOsVersion()Ljava/lang/String;
-Landroid/os/WorkSource;-><init>(I)V
Landroid/os/WorkSource;-><init>(Landroid/os/Parcel;)V
-Landroid/os/WorkSource;->add(I)Z
-Landroid/os/WorkSource;->add(ILjava/lang/String;)Z
-Landroid/os/WorkSource;->addReturningNewbs(Landroid/os/WorkSource;)Landroid/os/WorkSource;
-Landroid/os/WorkSource;->get(I)I
-Landroid/os/WorkSource;->getName(I)Ljava/lang/String;
Landroid/os/WorkSource;->mNames:[Ljava/lang/String;
Landroid/os/WorkSource;->mNum:I
Landroid/os/WorkSource;->mUids:[I
-Landroid/os/WorkSource;->setReturningDiffs(Landroid/os/WorkSource;)[Landroid/os/WorkSource;
Landroid/os/WorkSource;->sGoneWork:Landroid/os/WorkSource;
-Landroid/os/WorkSource;->size()I
Landroid/os/WorkSource;->sNewbWork:Landroid/os/WorkSource;
Landroid/os/WorkSource;->sTmpWorkSource:Landroid/os/WorkSource;
Landroid/os/WorkSource;->updateLocked(Landroid/os/WorkSource;ZZ)Z
@@ -4547,6 +4525,7 @@
Landroid/provider/Settings$Global;->ZEN_MODE_IMPORTANT_INTERRUPTIONS:I
Landroid/provider/Settings$Global;->ZEN_MODE_NO_INTERRUPTIONS:I
Landroid/provider/Settings$Global;->ZEN_MODE_OFF:I
+Landroid/provider/Settings$NameValueCache;->getStringForUser(Landroid/content/ContentResolver;Ljava/lang/String;I)Ljava/lang/String;
Landroid/provider/Settings$NameValueCache;->mProviderHolder:Landroid/provider/Settings$ContentProviderHolder;
Landroid/provider/Settings$Secure;->ACCESSIBILITY_AUTOCLICK_ENABLED:Ljava/lang/String;
Landroid/provider/Settings$Secure;->ACCESSIBILITY_CAPTIONING_TYPEFACE:Ljava/lang/String;
@@ -4585,10 +4564,12 @@
Landroid/provider/Settings$Secure;->SETTINGS_TO_BACKUP:[Ljava/lang/String;
Landroid/provider/Settings$Secure;->SMS_DEFAULT_APPLICATION:Ljava/lang/String;
Landroid/provider/Settings$Secure;->sNameValueCache:Landroid/provider/Settings$NameValueCache;
+Landroid/provider/Settings$Secure;->sProviderHolder:Landroid/provider/Settings$ContentProviderHolder;
Landroid/provider/Settings$Secure;->VOICE_RECOGNITION_SERVICE:Ljava/lang/String;
Landroid/provider/Settings$System;->AIRPLANE_MODE_TOGGLEABLE_RADIOS:Ljava/lang/String;
Landroid/provider/Settings$System;->CAR_DOCK_SOUND:Ljava/lang/String;
Landroid/provider/Settings$System;->CAR_UNDOCK_SOUND:Ljava/lang/String;
+Landroid/provider/Settings$System;->CLONE_TO_MANAGED_PROFILE:Ljava/util/Set;
Landroid/provider/Settings$System;->DESK_DOCK_SOUND:Ljava/lang/String;
Landroid/provider/Settings$System;->DESK_UNDOCK_SOUND:Ljava/lang/String;
Landroid/provider/Settings$System;->DOCK_SOUNDS_ENABLED:Ljava/lang/String;
@@ -4600,6 +4581,9 @@
Landroid/provider/Settings$System;->LOCKSCREEN_SOUNDS_ENABLED:Ljava/lang/String;
Landroid/provider/Settings$System;->LOCK_SOUND:Ljava/lang/String;
Landroid/provider/Settings$System;->MASTER_MONO:Ljava/lang/String;
+Landroid/provider/Settings$System;->MOVED_TO_GLOBAL:Ljava/util/HashSet;
+Landroid/provider/Settings$System;->MOVED_TO_SECURE:Ljava/util/HashSet;
+Landroid/provider/Settings$System;->MOVED_TO_SECURE_THEN_GLOBAL:Ljava/util/HashSet;
Landroid/provider/Settings$System;->NOTIFICATION_LIGHT_PULSE:Ljava/lang/String;
Landroid/provider/Settings$System;->POINTER_LOCATION:Ljava/lang/String;
Landroid/provider/Settings$System;->POINTER_SPEED:Ljava/lang/String;
@@ -4614,6 +4598,7 @@
Landroid/provider/Settings$System;->sProviderHolder:Landroid/provider/Settings$ContentProviderHolder;
Landroid/provider/Settings$System;->TTY_MODE:Ljava/lang/String;
Landroid/provider/Settings$System;->UNLOCK_SOUND:Ljava/lang/String;
+Landroid/provider/Settings$System;->VALIDATORS:Ljava/util/Map;
Landroid/provider/Settings$System;->VIBRATE_IN_SILENT:Ljava/lang/String;
Landroid/provider/Settings;->ACTION_TRUSTED_CREDENTIALS_USER:Ljava/lang/String;
Landroid/provider/Settings;->ACTION_USER_DICTIONARY_INSERT:Ljava/lang/String;
@@ -5357,6 +5342,7 @@
Landroid/telephony/ServiceState;->mCdmaRoamingIndicator:I
Landroid/telephony/ServiceState;->mCssIndicator:Z
Landroid/telephony/ServiceState;->mIsManualNetworkSelection:Z
+Landroid/telephony/ServiceState;->mIsUsingCarrierAggregation:Z
Landroid/telephony/ServiceState;->mNetworkId:I
Landroid/telephony/ServiceState;->mSystemId:I
Landroid/telephony/ServiceState;->newFromBundle(Landroid/os/Bundle;)Landroid/telephony/ServiceState;
@@ -5720,6 +5706,7 @@
Landroid/transition/Scene;->setCurrentScene(Landroid/view/View;Landroid/transition/Scene;)V
Landroid/transition/Transition;->cancel()V
Landroid/transition/Transition;->end()V
+Landroid/transition/Transition;->getRunningAnimators()Landroid/util/ArrayMap;
Landroid/transition/TransitionManager;->getRunningTransitions()Landroid/util/ArrayMap;
Landroid/transition/TransitionManager;->sPendingTransitions:Ljava/util/ArrayList;
Landroid/transition/TransitionManager;->sRunningTransitions:Ljava/lang/ThreadLocal;
@@ -6166,6 +6153,7 @@
Landroid/view/SurfaceControl;->HIDDEN:I
Landroid/view/SurfaceControl;->hide()V
Landroid/view/SurfaceControl;->openTransaction()V
+Landroid/view/SurfaceControl;->screenshot(Landroid/graphics/Rect;III)Landroid/graphics/Bitmap;
Landroid/view/SurfaceControl;->screenshot(Landroid/graphics/Rect;IIIIZI)Landroid/graphics/Bitmap;
Landroid/view/SurfaceControl;->screenshot(Landroid/os/IBinder;Landroid/view/Surface;Landroid/graphics/Rect;IIIIZZ)V
Landroid/view/SurfaceControl;->setDisplayLayerStack(Landroid/os/IBinder;I)V
@@ -6959,6 +6947,7 @@
Landroid/widget/ListView;->fillSpecific(II)Landroid/view/View;
Landroid/widget/ListView;->fillUp(II)Landroid/view/View;
Landroid/widget/ListView;->getHeightForPosition(I)I
+Landroid/widget/ListView;->isDirectChildHeaderOrFooter(Landroid/view/View;)Z
Landroid/widget/ListView;->makeAndAddView(IIZIZ)Landroid/view/View;
Landroid/widget/ListView;->mAreAllItemsSelectable:Z
Landroid/widget/ListView;->mDivider:Landroid/graphics/drawable/Drawable;
@@ -7507,6 +7496,7 @@
Lcom/android/internal/appwidget/IAppWidgetService;->bindAppWidgetId(Ljava/lang/String;IILandroid/content/ComponentName;Landroid/os/Bundle;)Z
Lcom/android/internal/appwidget/IAppWidgetService;->bindRemoteViewsService(Ljava/lang/String;ILandroid/content/Intent;Landroid/app/IApplicationThread;Landroid/os/IBinder;Landroid/app/IServiceConnection;I)Z
Lcom/android/internal/appwidget/IAppWidgetService;->getAppWidgetIds(Landroid/content/ComponentName;)[I
+Lcom/android/internal/appwidget/IAppWidgetService;->getAppWidgetViews(Ljava/lang/String;I)Landroid/widget/RemoteViews;
Lcom/android/internal/backup/IBackupTransport$Stub;-><init>()V
Lcom/android/internal/backup/IBackupTransport$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/backup/IBackupTransport;
Lcom/android/internal/backup/IBackupTransport;->clearBackupData(Landroid/content/pm/PackageInfo;)I
@@ -7614,7 +7604,6 @@
Lcom/android/internal/os/BatteryStatsImpl$Uid;->getWifiRunningTime(JI)J
Lcom/android/internal/os/BatteryStatsImpl$Uid;->getWifiScanTime(JI)J
Lcom/android/internal/os/BatteryStatsImpl;-><init>(Landroid/os/Parcel;)V
-Lcom/android/internal/os/BatteryStatsImpl;->commitPendingDataToDisk()V
Lcom/android/internal/os/BatteryStatsImpl;->computeBatteryRealtime(JI)J
Lcom/android/internal/os/BatteryStatsImpl;->computeBatteryTimeRemaining(J)J
Lcom/android/internal/os/BatteryStatsImpl;->computeBatteryUptime(JI)J
@@ -7723,6 +7712,9 @@
Lcom/android/internal/R$attr;->text:I
Lcom/android/internal/R$attr;->title:I
Lcom/android/internal/R$attr;->webViewStyle:I
+Lcom/android/internal/R$bool;-><init>()V
+Lcom/android/internal/R$bool;->config_automatic_brightness_available:I
+Lcom/android/internal/R$bool;->config_intrusiveNotificationLed:I
Lcom/android/internal/R$bool;->config_mms_content_disposition_support:I
Lcom/android/internal/R$bool;->config_showNavigationBar:I
Lcom/android/internal/R$dimen;-><init>()V
@@ -8146,6 +8138,8 @@
Lcom/android/internal/R$styleable;->Window:[I
Lcom/android/internal/R$styleable;->WindowAnimation:[I
Lcom/android/internal/R$styleable;->Window_windowActionBarFullscreenDecorLayout:I
+Lcom/android/internal/R$styleable;->Window_windowBackground:I
+Lcom/android/internal/R$styleable;->Window_windowFullscreen:I
Lcom/android/internal/R$styleable;->Window_windowIsFloating:I
Lcom/android/internal/R$styleable;->Window_windowIsTranslucent:I
Lcom/android/internal/R$styleable;->Window_windowShowWallpaper:I
@@ -8215,6 +8209,7 @@
Lcom/android/internal/telephony/IPhoneStateListener;->onSignalStrengthChanged(I)V
Lcom/android/internal/telephony/IPhoneStateListener;->onSignalStrengthsChanged(Landroid/telephony/SignalStrength;)V
Lcom/android/internal/telephony/IPhoneSubInfo$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
+Lcom/android/internal/telephony/IPhoneSubInfo$Stub$Proxy;->getDeviceId(Ljava/lang/String;)Ljava/lang/String;
Lcom/android/internal/telephony/IPhoneSubInfo$Stub;-><init>()V
Lcom/android/internal/telephony/IPhoneSubInfo$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/telephony/IPhoneSubInfo;
Lcom/android/internal/telephony/IPhoneSubInfo$Stub;->TRANSACTION_getDeviceId:I
@@ -8361,11 +8356,6 @@
Lcom/android/internal/util/AsyncChannel;->sendMessageSynchronously(Landroid/os/Message;)Landroid/os/Message;
Lcom/android/internal/util/AsyncChannel;->STATUS_SUCCESSFUL:I
Lcom/android/internal/util/FastPrintWriter;-><init>(Ljava/io/OutputStream;)V
-Lcom/android/internal/util/JournaledFile;-><init>(Ljava/io/File;Ljava/io/File;)V
-Lcom/android/internal/util/JournaledFile;->chooseForRead()Ljava/io/File;
-Lcom/android/internal/util/JournaledFile;->chooseForWrite()Ljava/io/File;
-Lcom/android/internal/util/JournaledFile;->commit()V
-Lcom/android/internal/util/JournaledFile;->rollback()V
Lcom/android/internal/util/XmlUtils;->convertValueToBoolean(Ljava/lang/CharSequence;Z)Z
Lcom/android/internal/util/XmlUtils;->convertValueToInt(Ljava/lang/CharSequence;I)I
Lcom/android/internal/util/XmlUtils;->readMapXml(Ljava/io/InputStream;)Ljava/util/HashMap;
@@ -8400,6 +8390,8 @@
Lcom/android/internal/view/menu/MenuBuilder;->addMenuPresenter(Lcom/android/internal/view/menu/MenuPresenter;Landroid/content/Context;)V
Lcom/android/internal/view/menu/MenuBuilder;->collapseItemActionView(Lcom/android/internal/view/menu/MenuItemImpl;)Z
Lcom/android/internal/view/menu/MenuBuilder;->getContext()Landroid/content/Context;
+Lcom/android/internal/view/menu/MenuBuilder;->getHeaderIcon()Landroid/graphics/drawable/Drawable;
+Lcom/android/internal/view/menu/MenuBuilder;->getHeaderTitle()Ljava/lang/CharSequence;
Lcom/android/internal/view/menu/MenuBuilder;->getNonActionItems()Ljava/util/ArrayList;
Lcom/android/internal/view/menu/MenuBuilder;->getRootMenu()Lcom/android/internal/view/menu/MenuBuilder;
Lcom/android/internal/view/menu/MenuBuilder;->getVisibleItems()Ljava/util/ArrayList;
@@ -8417,11 +8409,14 @@
Lcom/android/internal/view/menu/MenuItemImpl;->requestsActionButton()Z
Lcom/android/internal/view/menu/MenuItemImpl;->requiresActionButton()Z
Lcom/android/internal/view/menu/MenuItemImpl;->setActionViewExpanded(Z)V
+Lcom/android/internal/view/menu/MenuItemImpl;->setExclusiveCheckable(Z)V
Lcom/android/internal/view/menu/MenuItemImpl;->setMenuInfo(Landroid/view/ContextMenu$ContextMenuInfo;)V
Lcom/android/internal/view/menu/MenuPopupHelper;-><init>(Landroid/content/Context;Lcom/android/internal/view/menu/MenuBuilder;)V
Lcom/android/internal/view/menu/MenuPopupHelper;-><init>(Landroid/content/Context;Lcom/android/internal/view/menu/MenuBuilder;Landroid/view/View;)V
Lcom/android/internal/view/menu/MenuPopupHelper;->dismiss()V
+Lcom/android/internal/view/menu/MenuPopupHelper;->getPopup()Lcom/android/internal/view/menu/MenuPopup;
Lcom/android/internal/view/menu/MenuPopupHelper;->mForceShowIcon:Z
+Lcom/android/internal/view/menu/MenuPopupHelper;->setAnchorView(Landroid/view/View;)V
Lcom/android/internal/view/menu/MenuPopupHelper;->setForceShowIcon(Z)V
Lcom/android/internal/view/menu/MenuPopupHelper;->setGravity(I)V
Lcom/android/internal/view/menu/MenuPopupHelper;->show()V
@@ -8751,6 +8746,7 @@
Ljava/lang/reflect/Field;->getOffset()I
Ljava/lang/reflect/Parameter;-><init>(Ljava/lang/String;ILjava/lang/reflect/Executable;I)V
Ljava/lang/reflect/Proxy;->invoke(Ljava/lang/reflect/Proxy;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;
+Ljava/lang/Runtime;-><init>()V
Ljava/lang/Runtime;->load(Ljava/lang/String;Ljava/lang/ClassLoader;)V
Ljava/lang/Runtime;->loadLibrary(Ljava/lang/String;Ljava/lang/ClassLoader;)V
Ljava/lang/Runtime;->loadLibrary0(Ljava/lang/ClassLoader;Ljava/lang/String;)V
@@ -9119,6 +9115,7 @@
Lorg/xml/sax/helpers/NamespaceSupport;->contexts:[Lorg/xml/sax/helpers/NamespaceSupport$Context;
Lorg/xml/sax/helpers/NamespaceSupport;->currentContext:Lorg/xml/sax/helpers/NamespaceSupport$Context;
Lorg/xml/sax/helpers/NamespaceSupport;->EMPTY_ENUMERATION:Ljava/util/Enumeration;
+Lorg/xml/sax/helpers/NamespaceSupport;->namespaceDeclUris:Z
Lorg/xml/sax/helpers/ParserAdapter;->attAdapter:Lorg/xml/sax/helpers/ParserAdapter$AttributeListAdapter;
Lorg/xml/sax/helpers/ParserAdapter;->atts:Lorg/xml/sax/helpers/AttributesImpl;
Lorg/xml/sax/helpers/ParserAdapter;->checkNotParsing(Ljava/lang/String;Ljava/lang/String;)V
@@ -9138,6 +9135,7 @@
Lorg/xml/sax/helpers/ParserAdapter;->reportError(Ljava/lang/String;)V
Lorg/xml/sax/helpers/ParserAdapter;->setup(Lorg/xml/sax/Parser;)V
Lorg/xml/sax/helpers/ParserAdapter;->setupParser()V
+Lorg/xml/sax/helpers/ParserAdapter;->uris:Z
Lorg/xml/sax/helpers/XMLFilterImpl;->contentHandler:Lorg/xml/sax/ContentHandler;
Lorg/xml/sax/helpers/XMLFilterImpl;->dtdHandler:Lorg/xml/sax/DTDHandler;
Lorg/xml/sax/helpers/XMLFilterImpl;->entityResolver:Lorg/xml/sax/EntityResolver;
diff --git a/config/hiddenapi-p-light-greylist.txt b/config/hiddenapi-p-light-greylist.txt
index 7f3c93a..e360879 100644
--- a/config/hiddenapi-p-light-greylist.txt
+++ b/config/hiddenapi-p-light-greylist.txt
@@ -1626,8 +1626,6 @@
Landroid/hardware/display/IDisplayManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/hardware/display/IDisplayManager;
Landroid/hardware/display/WifiDisplayStatus;->mActiveDisplay:Landroid/hardware/display/WifiDisplay;
Landroid/hardware/display/WifiDisplayStatus;->mDisplays:[Landroid/hardware/display/WifiDisplay;
-Landroid/hardware/fingerprint/Fingerprint;->getFingerId()I
-Landroid/hardware/fingerprint/Fingerprint;->getName()Ljava/lang/CharSequence;
Landroid/hardware/fingerprint/IFingerprintService$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
Landroid/hardware/input/IInputManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
Landroid/hardware/input/IInputManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/hardware/input/IInputManager;
diff --git a/core/java/android/animation/IntEvaluator.java b/core/java/android/animation/IntEvaluator.java
index 34fb0dc..1de2ae7 100644
--- a/core/java/android/animation/IntEvaluator.java
+++ b/core/java/android/animation/IntEvaluator.java
@@ -24,7 +24,7 @@
/**
* This function returns the result of linearly interpolating the start and end values, with
* <code>fraction</code> representing the proportion between the start and end values. The
- * calculation is a simple parametric calculation: <code>result = x0 + t * (v1 - v0)</code>,
+ * calculation is a simple parametric calculation: <code>result = x0 + t * (x1 - x0)</code>,
* where <code>x0</code> is <code>startValue</code>, <code>x1</code> is <code>endValue</code>,
* and <code>t</code> is <code>fraction</code>.
*
@@ -39,4 +39,4 @@
int startInt = startValue;
return (int)(startInt + fraction * (endValue - startInt));
}
-}
\ No newline at end of file
+}
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 8e0fa13..6638dd9 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -1086,7 +1086,7 @@
*
* @param savedInstanceState contains the saved state
*/
- final void performRestoreInstanceState(Bundle savedInstanceState) {
+ final void performRestoreInstanceState(@NonNull Bundle savedInstanceState) {
onRestoreInstanceState(savedInstanceState);
restoreManagedDialogs(savedInstanceState);
}
@@ -1100,8 +1100,8 @@
* @param savedInstanceState contains the saved state
* @param persistentState contains the persistable saved state
*/
- final void performRestoreInstanceState(Bundle savedInstanceState,
- PersistableBundle persistentState) {
+ final void performRestoreInstanceState(@Nullable Bundle savedInstanceState,
+ @Nullable PersistableBundle persistentState) {
onRestoreInstanceState(savedInstanceState, persistentState);
if (savedInstanceState != null) {
restoreManagedDialogs(savedInstanceState);
@@ -1128,7 +1128,7 @@
* @see #onResume
* @see #onSaveInstanceState
*/
- protected void onRestoreInstanceState(Bundle savedInstanceState) {
+ protected void onRestoreInstanceState(@NonNull Bundle savedInstanceState) {
if (mWindow != null) {
Bundle windowState = savedInstanceState.getBundle(WINDOW_HIERARCHY_TAG);
if (windowState != null) {
@@ -1149,8 +1149,12 @@
*
* <p>If this method is called {@link #onRestoreInstanceState(Bundle)} will not be called.
*
- * @param savedInstanceState the data most recently supplied in {@link #onSaveInstanceState}.
- * @param persistentState the data most recently supplied in {@link #onSaveInstanceState}.
+ * <p>At least one of {@code savedInstanceState} or {@code persistentState} will not be null.
+ *
+ * @param savedInstanceState the data most recently supplied in {@link #onSaveInstanceState}
+ * or null.
+ * @param persistentState the data most recently supplied in {@link #onSaveInstanceState}
+ * or null.
*
* @see #onRestoreInstanceState(Bundle)
* @see #onCreate
@@ -1158,8 +1162,8 @@
* @see #onResume
* @see #onSaveInstanceState
*/
- public void onRestoreInstanceState(Bundle savedInstanceState,
- PersistableBundle persistentState) {
+ public void onRestoreInstanceState(@Nullable Bundle savedInstanceState,
+ @Nullable PersistableBundle persistentState) {
if (savedInstanceState != null) {
onRestoreInstanceState(savedInstanceState);
}
@@ -1545,7 +1549,7 @@
*
* @param outState The bundle to save the state to.
*/
- final void performSaveInstanceState(Bundle outState) {
+ final void performSaveInstanceState(@NonNull Bundle outState) {
onSaveInstanceState(outState);
saveManagedDialogs(outState);
mActivityTransitionState.saveState(outState);
@@ -1562,7 +1566,8 @@
* @param outState The bundle to save the state to.
* @param outPersistentState The bundle to save persistent state to.
*/
- final void performSaveInstanceState(Bundle outState, PersistableBundle outPersistentState) {
+ final void performSaveInstanceState(@NonNull Bundle outState,
+ @NonNull PersistableBundle outPersistentState) {
onSaveInstanceState(outState, outPersistentState);
saveManagedDialogs(outState);
storeHasCurrentPermissionRequest(outState);
@@ -1618,7 +1623,7 @@
* @see #onRestoreInstanceState
* @see #onPause
*/
- protected void onSaveInstanceState(Bundle outState) {
+ protected void onSaveInstanceState(@NonNull Bundle outState) {
outState.putBundle(WINDOW_HIERARCHY_TAG, mWindow.saveHierarchyState());
outState.putInt(LAST_AUTOFILL_ID, mLastAutofillId);
@@ -1648,7 +1653,8 @@
* @see #onRestoreInstanceState(Bundle, PersistableBundle)
* @see #onPause
*/
- public void onSaveInstanceState(Bundle outState, PersistableBundle outPersistentState) {
+ public void onSaveInstanceState(@NonNull Bundle outState,
+ @NonNull PersistableBundle outPersistentState) {
onSaveInstanceState(outState);
}
@@ -4406,9 +4412,9 @@
/**
* Requests permissions to be granted to this application. These permissions
* must be requested in your manifest, they should not be granted to your app,
- * and they should have protection level {@link android.content.pm.PermissionInfo
- * #PROTECTION_DANGEROUS dangerous}, regardless whether they are declared by
- * the platform or a third-party app.
+ * and they should have protection level {@link
+ * android.content.pm.PermissionInfo#PROTECTION_DANGEROUS dangerous}, regardless
+ * whether they are declared by the platform or a third-party app.
* <p>
* Normal permissions {@link android.content.pm.PermissionInfo#PROTECTION_NORMAL}
* are granted at install time if requested in the manifest. Signature permissions
@@ -4452,7 +4458,7 @@
* result callbacks including {@link #onRequestPermissionsResult(int, String[], int[])}.
* </p>
* <p>
- * The <a href="http://developer.android.com/samples/RuntimePermissions/index.html">
+ * The <a href="https://github.com/googlesamples/android-RuntimePermissions">
* RuntimePermissions</a> sample app demonstrates how to use this method to
* request permissions at run time.
* </p>
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index f27b286..3f579bc 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -3080,16 +3080,32 @@
*/
public int processState;
+ /**
+ * Whether the app is focused in multi-window environment.
+ * @hide
+ */
+ public boolean isFocused;
+
+ /**
+ * Copy of {@link com.android.server.am.ProcessRecord#lastActivityTime} of the process.
+ * @hide
+ */
+ public long lastActivityTime;
+
public RunningAppProcessInfo() {
importance = IMPORTANCE_FOREGROUND;
importanceReasonCode = REASON_UNKNOWN;
processState = PROCESS_STATE_IMPORTANT_FOREGROUND;
+ isFocused = false;
+ lastActivityTime = 0;
}
public RunningAppProcessInfo(String pProcessName, int pPid, String pArr[]) {
processName = pProcessName;
pid = pPid;
pkgList = pArr;
+ isFocused = false;
+ lastActivityTime = 0;
}
public int describeContents() {
@@ -3110,6 +3126,8 @@
ComponentName.writeToParcel(importanceReasonComponent, dest);
dest.writeInt(importanceReasonImportance);
dest.writeInt(processState);
+ dest.writeInt(isFocused ? 1 : 0);
+ dest.writeLong(lastActivityTime);
}
public void readFromParcel(Parcel source) {
@@ -3126,6 +3144,8 @@
importanceReasonComponent = ComponentName.readFromParcel(source);
importanceReasonImportance = source.readInt();
processState = source.readInt();
+ isFocused = source.readInt() != 0;
+ lastActivityTime = source.readLong();
}
public static final Creator<RunningAppProcessInfo> CREATOR =
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 4e6cc7e..2e4404c 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -21,6 +21,8 @@
import android.content.ComponentName;
import android.content.IIntentSender;
import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.UserInfo;
import android.content.res.Configuration;
import android.os.Bundle;
import android.os.IBinder;
@@ -31,6 +33,7 @@
import com.android.internal.app.IVoiceInteractor;
+import java.util.ArrayList;
import java.util.List;
/**
@@ -41,6 +44,11 @@
public abstract class ActivityManagerInternal {
+ // Access modes for handleIncomingUser.
+ public static final int ALLOW_NON_FULL = 0;
+ public static final int ALLOW_NON_FULL_IN_PROFILE = 1;
+ public static final int ALLOW_FULL_ONLY = 2;
+
/**
* Grant Uri permissions from one app to another. This method only extends
* permission grants if {@code callingUid} has permission to them.
@@ -63,15 +71,6 @@
String processName, String abiOverride, int uid, Runnable crashHandler);
/**
- * Called when a user has been deleted. This can happen during normal device usage
- * or just at startup, when partially removed users are purged. Any state persisted by the
- * ActivityManager should be purged now.
- *
- * @param userId The user being cleaned up.
- */
- public abstract void onUserRemoved(int userId);
-
- /**
* Kill foreground apps from the specified user.
*/
public abstract void killForegroundAppsForUser(int userHandle);
@@ -95,15 +94,6 @@
boolean adding);
/**
- * Updates and persists the {@link Configuration} for a given user.
- *
- * @param values the configuration to update
- * @param userId the user to update the configuration for
- */
- public abstract void updatePersistentConfigurationForUser(@NonNull Configuration values,
- int userId);
-
- /**
* Get the procstate for the UID. The return value will be between
* {@link ActivityManager#MIN_PROCESS_STATE} and {@link ActivityManager#MAX_PROCESS_STATE}.
* Note if the UID doesn't exist, it'll return {@link ActivityManager#PROCESS_STATE_NONEXISTENT}
@@ -155,17 +145,6 @@
public abstract void clearSavedANRState();
/**
- * Set a uid that is allowed to bypass stopped app switches, launching an app
- * whenever it wants.
- *
- * @param type Type of the caller -- unique string the caller supplies to identify itself
- * and disambiguate with other calles.
- * @param uid The uid of the app to be allowed, or -1 to clear the uid for this type.
- * @param userId The user it is allowed for.
- */
- public abstract void setAllowAppSwitches(@NonNull String type, int uid, int userId);
-
- /**
* @return true if runtime was restarted, false if it's normal boot
*/
public abstract boolean isRuntimeRestarted();
@@ -199,4 +178,61 @@
* Returns a list that contains the memory stats for currently running processes.
*/
public abstract List<ProcessMemoryState> getMemoryStateForProcesses();
+
+ /**
+ * Checks to see if the calling pid is allowed to handle the user. Returns adjusted user id as
+ * needed.
+ */
+ public abstract int handleIncomingUser(int callingPid, int callingUid, int userId,
+ boolean allowAll, int allowMode, String name, String callerPackage);
+
+ /** Checks if the calling binder pid as the permission. */
+ public abstract void enforceCallingPermission(String permission, String func);
+
+ /** Returns the current user id. */
+ public abstract int getCurrentUserId();
+
+ /** Returns true if the user is running. */
+ public abstract boolean isUserRunning(int userId, int flags);
+
+ /** Trims memory usage in the system by removing/stopping unused application processes. */
+ public abstract void trimApplications();
+
+ /** Returns the screen compatibility mode for the given application. */
+ public abstract int getPackageScreenCompatMode(ApplicationInfo ai);
+
+ /** Sets the screen compatibility mode for the given application. */
+ public abstract void setPackageScreenCompatMode(ApplicationInfo ai, int mode);
+
+ /** Closes all system dialogs. */
+ public abstract void closeSystemDialogs(String reason);
+
+ /** Kill the processes in the list due to their tasks been removed. */
+ public abstract void killProcessesForRemovedTask(ArrayList<Object> procsToKill);
+
+ /**
+ * Returns {@code true} if {@code uid} is running an activity from {@code packageName}.
+ */
+ public abstract boolean hasRunningActivity(int uid, @Nullable String packageName);
+
+ public abstract void updateOomAdj();
+ public abstract void sendForegroundProfileChanged(int userId);
+
+ /**
+ * Returns whether the given user requires credential entry at this time. This is used to
+ * intercept activity launches for work apps when the Work Challenge is present.
+ */
+ public abstract boolean shouldConfirmCredentials(int userId);
+
+ /**
+ * @return The intent used to launch the home activity.
+ */
+ public abstract Intent getHomeIntent();
+
+ public abstract int[] getCurrentProfileIds();
+ public abstract UserInfo getCurrentUser();
+ public abstract void ensureNotSpecialUser(int userId);
+ public abstract boolean isCurrentProfile(int userId);
+ public abstract boolean hasStartedUserState(int userId);
+ public abstract void finishUserSwitch(Object uss);
}
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index 89408cc..8914535 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -302,7 +302,6 @@
private int mResultCode;
private int mExitCoordinatorIndex;
private PendingIntent mUsageTimeReport;
- private boolean mLockTaskMode = false;
private int mLaunchDisplayId = INVALID_DISPLAY;
@WindowConfiguration.WindowingMode
private int mLaunchWindowingMode = WINDOWING_MODE_UNDEFINED;
@@ -310,6 +309,7 @@
private int mLaunchActivityType = ACTIVITY_TYPE_UNDEFINED;
private int mLaunchTaskId = -1;
private int mSplitScreenCreateMode = SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
+ private boolean mLockTaskMode = false;
private boolean mDisallowEnterPictureInPictureWhileLaunching;
private boolean mTaskOverlay;
private boolean mTaskOverlayCanResume;
@@ -946,7 +946,7 @@
mAnimationFinishedListener = IRemoteCallback.Stub.asInterface(
opts.getBinder(KEY_ANIMATION_FINISHED_LISTENER));
}
- mRotationAnimationHint = opts.getInt(KEY_ROTATION_ANIMATION_HINT);
+ mRotationAnimationHint = opts.getInt(KEY_ROTATION_ANIMATION_HINT, -1);
mAppVerificationBundle = opts.getBundle(KEY_INSTANT_APP_VERIFICATION_BUNDLE);
if (opts.containsKey(KEY_SPECS_FUTURE)) {
mSpecsFuture = IAppTransitionAnimationSpecsFuture.Stub.asInterface(opts.getBinder(
@@ -1442,17 +1442,37 @@
b.putInt(KEY_EXIT_COORDINATOR_INDEX, mExitCoordinatorIndex);
break;
}
- b.putBoolean(KEY_LOCK_TASK_MODE, mLockTaskMode);
- b.putInt(KEY_LAUNCH_DISPLAY_ID, mLaunchDisplayId);
- b.putInt(KEY_LAUNCH_WINDOWING_MODE, mLaunchWindowingMode);
- b.putInt(KEY_LAUNCH_ACTIVITY_TYPE, mLaunchActivityType);
- b.putInt(KEY_LAUNCH_TASK_ID, mLaunchTaskId);
- b.putBoolean(KEY_TASK_OVERLAY, mTaskOverlay);
- b.putBoolean(KEY_TASK_OVERLAY_CAN_RESUME, mTaskOverlayCanResume);
- b.putBoolean(KEY_AVOID_MOVE_TO_FRONT, mAvoidMoveToFront);
- b.putInt(KEY_SPLIT_SCREEN_CREATE_MODE, mSplitScreenCreateMode);
- b.putBoolean(KEY_DISALLOW_ENTER_PICTURE_IN_PICTURE_WHILE_LAUNCHING,
- mDisallowEnterPictureInPictureWhileLaunching);
+ if (mLockTaskMode) {
+ b.putBoolean(KEY_LOCK_TASK_MODE, mLockTaskMode);
+ }
+ if (mLaunchDisplayId != INVALID_DISPLAY) {
+ b.putInt(KEY_LAUNCH_DISPLAY_ID, mLaunchDisplayId);
+ }
+ if (mLaunchWindowingMode != WINDOWING_MODE_UNDEFINED) {
+ b.putInt(KEY_LAUNCH_WINDOWING_MODE, mLaunchWindowingMode);
+ }
+ if (mLaunchActivityType != ACTIVITY_TYPE_UNDEFINED) {
+ b.putInt(KEY_LAUNCH_ACTIVITY_TYPE, mLaunchActivityType);
+ }
+ if (mLaunchTaskId != -1) {
+ b.putInt(KEY_LAUNCH_TASK_ID, mLaunchTaskId);
+ }
+ if (mTaskOverlay) {
+ b.putBoolean(KEY_TASK_OVERLAY, mTaskOverlay);
+ }
+ if (mTaskOverlayCanResume) {
+ b.putBoolean(KEY_TASK_OVERLAY_CAN_RESUME, mTaskOverlayCanResume);
+ }
+ if (mAvoidMoveToFront) {
+ b.putBoolean(KEY_AVOID_MOVE_TO_FRONT, mAvoidMoveToFront);
+ }
+ if (mSplitScreenCreateMode != SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT) {
+ b.putInt(KEY_SPLIT_SCREEN_CREATE_MODE, mSplitScreenCreateMode);
+ }
+ if (mDisallowEnterPictureInPictureWhileLaunching) {
+ b.putBoolean(KEY_DISALLOW_ENTER_PICTURE_IN_PICTURE_WHILE_LAUNCHING,
+ mDisallowEnterPictureInPictureWhileLaunching);
+ }
if (mAnimSpecs != null) {
b.putParcelableArray(KEY_ANIM_SPECS, mAnimSpecs);
}
@@ -1462,7 +1482,9 @@
if (mSpecsFuture != null) {
b.putBinder(KEY_SPECS_FUTURE, mSpecsFuture.asBinder());
}
- b.putInt(KEY_ROTATION_ANIMATION_HINT, mRotationAnimationHint);
+ if (mRotationAnimationHint != -1) {
+ b.putInt(KEY_ROTATION_ANIMATION_HINT, mRotationAnimationHint);
+ }
if (mAppVerificationBundle != null) {
b.putBundle(KEY_INSTANT_APP_VERIFICATION_BUNDLE, mAppVerificationBundle);
}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index f3c6731..2daa577 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -35,6 +35,7 @@
import android.app.servertransaction.ActivityRelaunchItem;
import android.app.servertransaction.ActivityResultItem;
import android.app.servertransaction.ClientTransaction;
+import android.app.servertransaction.ClientTransactionItem;
import android.app.servertransaction.PendingTransactionActions;
import android.app.servertransaction.PendingTransactionActions.StopInfo;
import android.app.servertransaction.TransactionExecutor;
@@ -176,6 +177,7 @@
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Map;
@@ -257,6 +259,8 @@
final H mH = new H();
final Executor mExecutor = new HandlerExecutor(mH);
final ArrayMap<IBinder, ActivityClientRecord> mActivities = new ArrayMap<>();
+ final Map<IBinder, ClientTransactionItem> mActivitiesToBeDestroyed =
+ Collections.synchronizedMap(new ArrayMap<IBinder, ClientTransactionItem>());
// List of new activities (via ActivityRecord.nextIdle) that should
// be reported when next we idle.
ActivityClientRecord mNewActivities = null;
@@ -4474,6 +4478,11 @@
}
@Override
+ public Map<IBinder, ClientTransactionItem> getActivitiesToBeDestroyed() {
+ return mActivitiesToBeDestroyed;
+ }
+
+ @Override
public void handleDestroyActivity(IBinder token, boolean finishing, int configChanges,
boolean getNonConfigInstance, String reason) {
ActivityClientRecord r = performDestroyActivity(token, finishing,
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 8639849..c3404a5 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -350,8 +350,10 @@
public static final int OP_START_FOREGROUND = 76;
/** @hide */
public static final int OP_BLUETOOTH_SCAN = 77;
+ /** @hide Use the face authentication API. */
+ public static final int OP_USE_FACE = 78;
/** @hide */
- public static final int _NUM_OP = 78;
+ public static final int _NUM_OP = 79;
/** Access to coarse location information. */
public static final String OPSTR_COARSE_LOCATION = "android:coarse_location";
@@ -596,6 +598,9 @@
/** @hide */
public static final String OPSTR_BLUETOOTH_SCAN = "android:bluetooth_scan";
+ /** @hide Use the face authentication API. */
+ public static final String OPSTR_USE_FACE = "android:use_face";
+
// Warning: If an permission is added here it also has to be added to
// com.android.packageinstaller.permission.utils.EventLogger
private static final int[] RUNTIME_AND_APPOP_PERMISSIONS_OPS = {
@@ -733,6 +738,7 @@
OP_MANAGE_IPSEC_TUNNELS, // MANAGE_IPSEC_HANDOVERS
OP_START_FOREGROUND, // START_FOREGROUND
OP_COARSE_LOCATION, // BLUETOOTH_SCAN
+ OP_USE_FACE, // FACE
};
/**
@@ -817,6 +823,7 @@
OPSTR_MANAGE_IPSEC_TUNNELS,
OPSTR_START_FOREGROUND,
OPSTR_BLUETOOTH_SCAN,
+ OPSTR_USE_FACE,
};
/**
@@ -902,6 +909,7 @@
"MANAGE_IPSEC_TUNNELS",
"START_FOREGROUND",
"BLUETOOTH_SCAN",
+ "USE_FACE",
};
/**
@@ -987,6 +995,7 @@
null, // no permission for OP_MANAGE_IPSEC_TUNNELS
Manifest.permission.FOREGROUND_SERVICE,
null, // no permission for OP_BLUETOOTH_SCAN
+ Manifest.permission.USE_BIOMETRIC,
};
/**
@@ -1073,6 +1082,7 @@
null, // MANAGE_IPSEC_TUNNELS
null, // START_FOREGROUND
null, // maybe should be UserManager.DISALLOW_SHARE_LOCATION, //BLUETOOTH_SCAN
+ null, // USE_FACE
};
/**
@@ -1158,6 +1168,7 @@
false, // MANAGE_IPSEC_HANDOVERS
false, // START_FOREGROUND
true, // BLUETOOTH_SCAN
+ false, // USE_FACE
};
/**
@@ -1242,6 +1253,7 @@
AppOpsManager.MODE_ERRORED, // MANAGE_IPSEC_TUNNELS
AppOpsManager.MODE_ALLOWED, // OP_START_FOREGROUND
AppOpsManager.MODE_ALLOWED, // OP_BLUETOOTH_SCAN
+ AppOpsManager.MODE_ALLOWED, // USE_FACE
};
/**
@@ -1330,6 +1342,7 @@
false, // MANAGE_IPSEC_TUNNELS
false, // START_FOREGROUND
false, // BLUETOOTH_SCAN
+ false, // USE_FACE
};
/**
@@ -2474,7 +2487,7 @@
*/
public int noteProxyOpNoThrow(int op, String proxiedPackageName) {
try {
- return mService.noteProxyOperation(op, mContext.getOpPackageName(),
+ return mService.noteProxyOperation(op, Process.myUid(), mContext.getOpPackageName(),
Binder.getCallingUid(), proxiedPackageName);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
diff --git a/core/java/android/app/AppOpsManagerInternal.java b/core/java/android/app/AppOpsManagerInternal.java
index 24c5d23..f5d5e6e 100644
--- a/core/java/android/app/AppOpsManagerInternal.java
+++ b/core/java/android/app/AppOpsManagerInternal.java
@@ -18,12 +18,55 @@
import android.util.SparseIntArray;
+import com.android.internal.util.function.QuadFunction;
+import com.android.internal.util.function.TriFunction;
+
/**
* App ops service local interface.
*
* @hide Only for use within the system server.
*/
public abstract class AppOpsManagerInternal {
+ /** Interface to override app ops checks via composition */
+ public interface CheckOpsDelegate {
+ /**
+ * Allows overriding check operation behavior.
+ *
+ * @param code The op code to check.
+ * @param uid The UID for which to check.
+ * @param packageName The package for which to check.
+ * @param superImpl The super implementation.
+ * @return The app op check result.
+ */
+ int checkOperation(int code, int uid, String packageName,
+ TriFunction<Integer, Integer, String, Integer> superImpl);
+
+ /**
+ * Allows overriding check audio operation behavior.
+ *
+ * @param code The op code to check.
+ * @param usage The audio op usage.
+ * @param uid The UID for which to check.
+ * @param packageName The package for which to check.
+ * @param superImpl The super implementation.
+ * @return The app op check result.
+ */
+ int checkAudioOperation(int code, int usage, int uid, String packageName,
+ QuadFunction<Integer, Integer, Integer, String, Integer> superImpl);
+
+ /**
+ * Allows overriding note operation behavior.
+ *
+ * @param code The op code to note.
+ * @param uid The UID for which to note.
+ * @param packageName The package for which to note.
+ * @param superImpl The super implementation.
+ * @return The app op note result.
+ */
+ int noteOperation(int code, int uid, String packageName,
+ TriFunction<Integer, Integer, String, Integer> superImpl);
+ }
+
/**
* Set the currently configured device and profile owners. Specifies the package uid (value)
* that has been configured for each user (key) that has one. These will be allowed privileged
diff --git a/core/java/android/app/Application.java b/core/java/android/app/Application.java
index 4531f53..6a58d9b 100644
--- a/core/java/android/app/Application.java
+++ b/core/java/android/app/Application.java
@@ -17,6 +17,8 @@
package android.app;
import android.annotation.CallSuper;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.ComponentCallbacks;
import android.content.ComponentCallbacks2;
import android.content.Context;
@@ -58,13 +60,13 @@
public LoadedApk mLoadedApk;
public interface ActivityLifecycleCallbacks {
- void onActivityCreated(Activity activity, Bundle savedInstanceState);
- void onActivityStarted(Activity activity);
- void onActivityResumed(Activity activity);
- void onActivityPaused(Activity activity);
- void onActivityStopped(Activity activity);
- void onActivitySaveInstanceState(Activity activity, Bundle outState);
- void onActivityDestroyed(Activity activity);
+ void onActivityCreated(@NonNull Activity activity, @Nullable Bundle savedInstanceState);
+ void onActivityStarted(@NonNull Activity activity);
+ void onActivityResumed(@NonNull Activity activity);
+ void onActivityPaused(@NonNull Activity activity);
+ void onActivityStopped(@NonNull Activity activity);
+ void onActivitySaveInstanceState(@NonNull Activity activity, @NonNull Bundle outState);
+ void onActivityDestroyed(@NonNull Activity activity);
}
/**
@@ -213,7 +215,8 @@
mLoadedApk = ContextImpl.getImpl(context).mPackageInfo;
}
- /* package */ void dispatchActivityCreated(Activity activity, Bundle savedInstanceState) {
+ /* package */ void dispatchActivityCreated(@NonNull Activity activity,
+ @Nullable Bundle savedInstanceState) {
Object[] callbacks = collectActivityLifecycleCallbacks();
if (callbacks != null) {
for (int i=0; i<callbacks.length; i++) {
@@ -223,7 +226,7 @@
}
}
- /* package */ void dispatchActivityStarted(Activity activity) {
+ /* package */ void dispatchActivityStarted(@NonNull Activity activity) {
Object[] callbacks = collectActivityLifecycleCallbacks();
if (callbacks != null) {
for (int i=0; i<callbacks.length; i++) {
@@ -232,7 +235,7 @@
}
}
- /* package */ void dispatchActivityResumed(Activity activity) {
+ /* package */ void dispatchActivityResumed(@NonNull Activity activity) {
Object[] callbacks = collectActivityLifecycleCallbacks();
if (callbacks != null) {
for (int i=0; i<callbacks.length; i++) {
@@ -241,7 +244,7 @@
}
}
- /* package */ void dispatchActivityPaused(Activity activity) {
+ /* package */ void dispatchActivityPaused(@NonNull Activity activity) {
Object[] callbacks = collectActivityLifecycleCallbacks();
if (callbacks != null) {
for (int i=0; i<callbacks.length; i++) {
@@ -250,7 +253,7 @@
}
}
- /* package */ void dispatchActivityStopped(Activity activity) {
+ /* package */ void dispatchActivityStopped(@NonNull Activity activity) {
Object[] callbacks = collectActivityLifecycleCallbacks();
if (callbacks != null) {
for (int i=0; i<callbacks.length; i++) {
@@ -259,7 +262,8 @@
}
}
- /* package */ void dispatchActivitySaveInstanceState(Activity activity, Bundle outState) {
+ /* package */ void dispatchActivitySaveInstanceState(@NonNull Activity activity,
+ @NonNull Bundle outState) {
Object[] callbacks = collectActivityLifecycleCallbacks();
if (callbacks != null) {
for (int i=0; i<callbacks.length; i++) {
@@ -269,7 +273,7 @@
}
}
- /* package */ void dispatchActivityDestroyed(Activity activity) {
+ /* package */ void dispatchActivityDestroyed(@NonNull Activity activity) {
Object[] callbacks = collectActivityLifecycleCallbacks();
if (callbacks != null) {
for (int i=0; i<callbacks.length; i++) {
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 0e44833..344610a 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -73,6 +73,7 @@
import android.os.PersistableBundle;
import android.os.Process;
import android.os.RemoteException;
+import android.os.StrictMode;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
@@ -149,15 +150,16 @@
@Override
public PackageInfo getPackageInfo(String packageName, int flags)
throws NameNotFoundException {
- return getPackageInfoAsUser(packageName, flags, mContext.getUserId());
+ return getPackageInfoAsUser(packageName, flags, getUserId());
}
@Override
public PackageInfo getPackageInfo(VersionedPackage versionedPackage, int flags)
throws NameNotFoundException {
+ final int userId = getUserId();
try {
- PackageInfo pi = mPM.getPackageInfoVersioned(versionedPackage, flags,
- mContext.getUserId());
+ PackageInfo pi = mPM.getPackageInfoVersioned(versionedPackage,
+ updateFlagsForPackage(flags, userId), userId);
if (pi != null) {
return pi;
}
@@ -171,7 +173,8 @@
public PackageInfo getPackageInfoAsUser(String packageName, int flags, int userId)
throws NameNotFoundException {
try {
- PackageInfo pi = mPM.getPackageInfo(packageName, flags, userId);
+ PackageInfo pi = mPM.getPackageInfo(packageName,
+ updateFlagsForPackage(flags, userId), userId);
if (pi != null) {
return pi;
}
@@ -262,8 +265,10 @@
@Override
public int[] getPackageGids(String packageName, int flags)
throws NameNotFoundException {
+ final int userId = getUserId();
try {
- int[] gids = mPM.getPackageGids(packageName, flags, mContext.getUserId());
+ int[] gids = mPM.getPackageGids(packageName,
+ updateFlagsForPackage(flags, userId), userId);
if (gids != null) {
return gids;
}
@@ -276,7 +281,7 @@
@Override
public int getPackageUid(String packageName, int flags) throws NameNotFoundException {
- return getPackageUidAsUser(packageName, flags, mContext.getUserId());
+ return getPackageUidAsUser(packageName, flags, getUserId());
}
@Override
@@ -288,7 +293,8 @@
public int getPackageUidAsUser(String packageName, int flags, int userId)
throws NameNotFoundException {
try {
- int uid = mPM.getPackageUid(packageName, flags, userId);
+ int uid = mPM.getPackageUid(packageName,
+ updateFlagsForPackage(flags, userId), userId);
if (uid >= 0) {
return uid;
}
@@ -374,14 +380,15 @@
@Override
public ApplicationInfo getApplicationInfo(String packageName, int flags)
throws NameNotFoundException {
- return getApplicationInfoAsUser(packageName, flags, mContext.getUserId());
+ return getApplicationInfoAsUser(packageName, flags, getUserId());
}
@Override
public ApplicationInfo getApplicationInfoAsUser(String packageName, int flags, int userId)
throws NameNotFoundException {
try {
- ApplicationInfo ai = mPM.getApplicationInfo(packageName, flags, userId);
+ ApplicationInfo ai = mPM.getApplicationInfo(packageName,
+ updateFlagsForApplication(flags, userId), userId);
if (ai != null) {
// This is a temporary hack. Callers must use
// createPackageContext(packageName).getApplicationInfo() to
@@ -423,8 +430,10 @@
@Override
public ActivityInfo getActivityInfo(ComponentName className, int flags)
throws NameNotFoundException {
+ final int userId = getUserId();
try {
- ActivityInfo ai = mPM.getActivityInfo(className, flags, mContext.getUserId());
+ ActivityInfo ai = mPM.getActivityInfo(className,
+ updateFlagsForComponent(flags, userId, null), userId);
if (ai != null) {
return ai;
}
@@ -438,8 +447,10 @@
@Override
public ActivityInfo getReceiverInfo(ComponentName className, int flags)
throws NameNotFoundException {
+ final int userId = getUserId();
try {
- ActivityInfo ai = mPM.getReceiverInfo(className, flags, mContext.getUserId());
+ ActivityInfo ai = mPM.getReceiverInfo(className,
+ updateFlagsForComponent(flags, userId, null), userId);
if (ai != null) {
return ai;
}
@@ -453,8 +464,10 @@
@Override
public ServiceInfo getServiceInfo(ComponentName className, int flags)
throws NameNotFoundException {
+ final int userId = getUserId();
try {
- ServiceInfo si = mPM.getServiceInfo(className, flags, mContext.getUserId());
+ ServiceInfo si = mPM.getServiceInfo(className,
+ updateFlagsForComponent(flags, userId, null), userId);
if (si != null) {
return si;
}
@@ -468,8 +481,10 @@
@Override
public ProviderInfo getProviderInfo(ComponentName className, int flags)
throws NameNotFoundException {
+ final int userId = getUserId();
try {
- ProviderInfo pi = mPM.getProviderInfo(className, flags, mContext.getUserId());
+ ProviderInfo pi = mPM.getProviderInfo(className,
+ updateFlagsForComponent(flags, userId, null), userId);
if (pi != null) {
return pi;
}
@@ -492,7 +507,7 @@
/** @hide */
@Override
public @NonNull List<SharedLibraryInfo> getSharedLibraries(int flags) {
- return getSharedLibrariesAsUser(flags, mContext.getUserId());
+ return getSharedLibrariesAsUser(flags, getUserId());
}
/** @hide */
@@ -535,7 +550,7 @@
@Override
public ChangedPackages getChangedPackages(int sequenceNumber) {
try {
- return mPM.getChangedPackages(sequenceNumber, mContext.getUserId());
+ return mPM.getChangedPackages(sequenceNumber, getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -578,7 +593,7 @@
@Override
public int checkPermission(String permName, String pkgName) {
try {
- return mPM.checkPermission(permName, pkgName, mContext.getUserId());
+ return mPM.checkPermission(permName, pkgName, getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -587,7 +602,7 @@
@Override
public boolean isPermissionRevokedByPolicy(String permName, String pkgName) {
try {
- return mPM.isPermissionRevokedByPolicy(permName, pkgName, mContext.getUserId());
+ return mPM.isPermissionRevokedByPolicy(permName, pkgName, getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -681,7 +696,7 @@
public boolean shouldShowRequestPermissionRationale(String permission) {
try {
return mPM.shouldShowRequestPermissionRationale(permission,
- mContext.getPackageName(), mContext.getUserId());
+ mContext.getPackageName(), getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -769,7 +784,7 @@
@SuppressWarnings("unchecked")
@Override
public List<PackageInfo> getInstalledPackages(int flags) {
- return getInstalledPackagesAsUser(flags, mContext.getUserId());
+ return getInstalledPackagesAsUser(flags, getUserId());
}
/** @hide */
@@ -778,7 +793,7 @@
public List<PackageInfo> getInstalledPackagesAsUser(int flags, int userId) {
try {
ParceledListSlice<PackageInfo> parceledList =
- mPM.getInstalledPackages(flags, userId);
+ mPM.getInstalledPackages(updateFlagsForPackage(flags, userId), userId);
if (parceledList == null) {
return Collections.emptyList();
}
@@ -792,10 +807,11 @@
@Override
public List<PackageInfo> getPackagesHoldingPermissions(
String[] permissions, int flags) {
- final int userId = mContext.getUserId();
+ final int userId = getUserId();
try {
ParceledListSlice<PackageInfo> parceledList =
- mPM.getPackagesHoldingPermissions(permissions, flags, userId);
+ mPM.getPackagesHoldingPermissions(permissions,
+ updateFlagsForPackage(flags, userId), userId);
if (parceledList == null) {
return Collections.emptyList();
}
@@ -808,7 +824,7 @@
@SuppressWarnings("unchecked")
@Override
public List<ApplicationInfo> getInstalledApplications(int flags) {
- return getInstalledApplicationsAsUser(flags, mContext.getUserId());
+ return getInstalledApplicationsAsUser(flags, getUserId());
}
/** @hide */
@@ -817,7 +833,7 @@
public List<ApplicationInfo> getInstalledApplicationsAsUser(int flags, int userId) {
try {
ParceledListSlice<ApplicationInfo> parceledList =
- mPM.getInstalledApplications(flags, userId);
+ mPM.getInstalledApplications(updateFlagsForApplication(flags, userId), userId);
if (parceledList == null) {
return Collections.emptyList();
}
@@ -832,8 +848,7 @@
@Override
public List<InstantAppInfo> getInstantApps() {
try {
- ParceledListSlice<InstantAppInfo> slice =
- mPM.getInstantApps(mContext.getUserId());
+ ParceledListSlice<InstantAppInfo> slice = mPM.getInstantApps(getUserId());
if (slice != null) {
return slice.getList();
}
@@ -847,8 +862,7 @@
@Override
public Drawable getInstantAppIcon(String packageName) {
try {
- Bitmap bitmap = mPM.getInstantAppIcon(
- packageName, mContext.getUserId());
+ Bitmap bitmap = mPM.getInstantAppIcon(packageName, getUserId());
if (bitmap != null) {
return new BitmapDrawable(null, bitmap);
}
@@ -866,7 +880,7 @@
@Override
public boolean isInstantApp(String packageName) {
try {
- return mPM.isInstantApp(packageName, mContext.getUserId());
+ return mPM.isInstantApp(packageName, getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -886,8 +900,7 @@
@Override
public @NonNull byte[] getInstantAppCookie() {
try {
- final byte[] cookie = mPM.getInstantAppCookie(
- mContext.getPackageName(), mContext.getUserId());
+ final byte[] cookie = mPM.getInstantAppCookie(mContext.getPackageName(), getUserId());
if (cookie != null) {
return cookie;
} else {
@@ -910,8 +923,7 @@
+ getInstantAppCookieMaxBytes());
}
try {
- mPM.setInstantAppCookie(mContext.getPackageName(),
- cookie, mContext.getUserId());
+ mPM.setInstantAppCookie(mContext.getPackageName(), cookie, getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -920,8 +932,7 @@
@Override
public boolean setInstantAppCookie(@NonNull byte[] cookie) {
try {
- return mPM.setInstantAppCookie(mContext.getPackageName(),
- cookie, mContext.getUserId());
+ return mPM.setInstantAppCookie(mContext.getPackageName(), cookie, getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -929,7 +940,7 @@
@Override
public ResolveInfo resolveActivity(Intent intent, int flags) {
- return resolveActivityAsUser(intent, flags, mContext.getUserId());
+ return resolveActivityAsUser(intent, flags, getUserId());
}
@Override
@@ -938,7 +949,7 @@
return mPM.resolveIntent(
intent,
intent.resolveTypeIfNeeded(mContext.getContentResolver()),
- flags,
+ updateFlagsForComponent(flags, userId, intent),
userId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -948,7 +959,7 @@
@Override
public List<ResolveInfo> queryIntentActivities(Intent intent,
int flags) {
- return queryIntentActivitiesAsUser(intent, flags, mContext.getUserId());
+ return queryIntentActivitiesAsUser(intent, flags, getUserId());
}
/** @hide Same as above but for a specific user */
@@ -957,10 +968,11 @@
public List<ResolveInfo> queryIntentActivitiesAsUser(Intent intent,
int flags, int userId) {
try {
- ParceledListSlice<ResolveInfo> parceledList =
- mPM.queryIntentActivities(intent,
- intent.resolveTypeIfNeeded(mContext.getContentResolver()),
- flags, userId);
+ ParceledListSlice<ResolveInfo> parceledList = mPM.queryIntentActivities(
+ intent,
+ intent.resolveTypeIfNeeded(mContext.getContentResolver()),
+ updateFlagsForComponent(flags, userId, intent),
+ userId);
if (parceledList == null) {
return Collections.emptyList();
}
@@ -972,9 +984,9 @@
@Override
@SuppressWarnings("unchecked")
- public List<ResolveInfo> queryIntentActivityOptions(
- ComponentName caller, Intent[] specifics, Intent intent,
- int flags) {
+ public List<ResolveInfo> queryIntentActivityOptions(ComponentName caller, Intent[] specifics,
+ Intent intent, int flags) {
+ final int userId = getUserId();
final ContentResolver resolver = mContext.getContentResolver();
String[] specificTypes = null;
@@ -995,9 +1007,14 @@
}
try {
- ParceledListSlice<ResolveInfo> parceledList =
- mPM.queryIntentActivityOptions(caller, specifics, specificTypes, intent,
- intent.resolveTypeIfNeeded(resolver), flags, mContext.getUserId());
+ ParceledListSlice<ResolveInfo> parceledList = mPM.queryIntentActivityOptions(
+ caller,
+ specifics,
+ specificTypes,
+ intent,
+ intent.resolveTypeIfNeeded(resolver),
+ updateFlagsForComponent(flags, userId, intent),
+ userId);
if (parceledList == null) {
return Collections.emptyList();
}
@@ -1014,10 +1031,11 @@
@SuppressWarnings("unchecked")
public List<ResolveInfo> queryBroadcastReceiversAsUser(Intent intent, int flags, int userId) {
try {
- ParceledListSlice<ResolveInfo> parceledList =
- mPM.queryIntentReceivers(intent,
- intent.resolveTypeIfNeeded(mContext.getContentResolver()),
- flags, userId);
+ ParceledListSlice<ResolveInfo> parceledList = mPM.queryIntentReceivers(
+ intent,
+ intent.resolveTypeIfNeeded(mContext.getContentResolver()),
+ updateFlagsForComponent(flags, userId, intent),
+ userId);
if (parceledList == null) {
return Collections.emptyList();
}
@@ -1029,7 +1047,7 @@
@Override
public List<ResolveInfo> queryBroadcastReceivers(Intent intent, int flags) {
- return queryBroadcastReceiversAsUser(intent, flags, mContext.getUserId());
+ return queryBroadcastReceiversAsUser(intent, flags, getUserId());
}
@Override
@@ -1039,7 +1057,7 @@
return mPM.resolveService(
intent,
intent.resolveTypeIfNeeded(mContext.getContentResolver()),
- flags,
+ updateFlagsForComponent(flags, userId, intent),
userId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -1048,17 +1066,18 @@
@Override
public ResolveInfo resolveService(Intent intent, int flags) {
- return resolveServiceAsUser(intent, flags, mContext.getUserId());
+ return resolveServiceAsUser(intent, flags, getUserId());
}
@Override
@SuppressWarnings("unchecked")
public List<ResolveInfo> queryIntentServicesAsUser(Intent intent, int flags, int userId) {
try {
- ParceledListSlice<ResolveInfo> parceledList =
- mPM.queryIntentServices(intent,
+ ParceledListSlice<ResolveInfo> parceledList = mPM.queryIntentServices(
+ intent,
intent.resolveTypeIfNeeded(mContext.getContentResolver()),
- flags, userId);
+ updateFlagsForComponent(flags, userId, intent),
+ userId);
if (parceledList == null) {
return Collections.emptyList();
}
@@ -1070,7 +1089,7 @@
@Override
public List<ResolveInfo> queryIntentServices(Intent intent, int flags) {
- return queryIntentServicesAsUser(intent, flags, mContext.getUserId());
+ return queryIntentServicesAsUser(intent, flags, getUserId());
}
@Override
@@ -1078,10 +1097,11 @@
public List<ResolveInfo> queryIntentContentProvidersAsUser(
Intent intent, int flags, int userId) {
try {
- ParceledListSlice<ResolveInfo> parceledList =
- mPM.queryIntentContentProviders(intent,
- intent.resolveTypeIfNeeded(mContext.getContentResolver()),
- flags, userId);
+ ParceledListSlice<ResolveInfo> parceledList = mPM.queryIntentContentProviders(
+ intent,
+ intent.resolveTypeIfNeeded(mContext.getContentResolver()),
+ updateFlagsForComponent(flags, userId, intent),
+ userId);
if (parceledList == null) {
return Collections.emptyList();
}
@@ -1093,19 +1113,20 @@
@Override
public List<ResolveInfo> queryIntentContentProviders(Intent intent, int flags) {
- return queryIntentContentProvidersAsUser(intent, flags, mContext.getUserId());
+ return queryIntentContentProvidersAsUser(intent, flags, getUserId());
}
@Override
public ProviderInfo resolveContentProvider(String name, int flags) {
- return resolveContentProviderAsUser(name, flags, mContext.getUserId());
+ return resolveContentProviderAsUser(name, flags, getUserId());
}
/** @hide **/
@Override
public ProviderInfo resolveContentProviderAsUser(String name, int flags, int userId) {
try {
- return mPM.resolveContentProvider(name, flags, userId);
+ return mPM.resolveContentProvider(name,
+ updateFlagsForComponent(flags, userId, null), userId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1122,8 +1143,8 @@
public List<ProviderInfo> queryContentProviders(String processName,
int uid, int flags, String metaDataKey) {
try {
- ParceledListSlice<ProviderInfo> slice =
- mPM.queryContentProviders(processName, uid, flags, metaDataKey);
+ ParceledListSlice<ProviderInfo> slice = mPM.queryContentProviders(processName, uid,
+ updateFlagsForComponent(flags, UserHandle.getUserId(uid), null), metaDataKey);
return slice != null ? slice.getList() : Collections.<ProviderInfo>emptyList();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -1517,6 +1538,73 @@
mPM = pm;
}
+ /**
+ * Update given flags when being used to request {@link PackageInfo}.
+ */
+ private int updateFlagsForPackage(int flags, int userId) {
+ if ((flags & (GET_ACTIVITIES | GET_RECEIVERS | GET_SERVICES | GET_PROVIDERS)) != 0) {
+ // Caller is asking for component details, so they'd better be
+ // asking for specific Direct Boot matching behavior
+ if ((flags & (MATCH_DIRECT_BOOT_UNAWARE
+ | MATCH_DIRECT_BOOT_AWARE
+ | MATCH_DIRECT_BOOT_AUTO)) == 0) {
+ onImplicitDirectBoot(userId);
+ }
+ }
+ return flags;
+ }
+
+ /**
+ * Update given flags when being used to request {@link ApplicationInfo}.
+ */
+ private int updateFlagsForApplication(int flags, int userId) {
+ return updateFlagsForPackage(flags, userId);
+ }
+
+ /**
+ * Update given flags when being used to request {@link ComponentInfo}.
+ */
+ private int updateFlagsForComponent(int flags, int userId, Intent intent) {
+ if (intent != null) {
+ if ((intent.getFlags() & Intent.FLAG_DIRECT_BOOT_AUTO) != 0) {
+ flags |= MATCH_DIRECT_BOOT_AUTO;
+ }
+ }
+
+ // Caller is asking for component details, so they'd better be
+ // asking for specific Direct Boot matching behavior
+ if ((flags & (MATCH_DIRECT_BOOT_UNAWARE
+ | MATCH_DIRECT_BOOT_AWARE
+ | MATCH_DIRECT_BOOT_AUTO)) == 0) {
+ onImplicitDirectBoot(userId);
+ }
+ return flags;
+ }
+
+ private void onImplicitDirectBoot(int userId) {
+ // Only report if someone is relying on implicit behavior while the user
+ // is locked; code running when unlocked is going to see both aware and
+ // unaware components.
+ if (StrictMode.vmImplicitDirectBootEnabled()) {
+ // We can cache the unlocked state for the userId we're running as,
+ // since any relocking of that user will always result in our
+ // process being killed to release any CE FDs we're holding onto.
+ if (userId == UserHandle.myUserId()) {
+ if (mUserUnlocked) {
+ return;
+ } else if (mContext.getSystemService(UserManager.class)
+ .isUserUnlockingOrUnlocked(userId)) {
+ mUserUnlocked = true;
+ } else {
+ StrictMode.onImplicitDirectBoot();
+ }
+ } else if (!mContext.getSystemService(UserManager.class)
+ .isUserUnlockingOrUnlocked(userId)) {
+ StrictMode.onImplicitDirectBoot();
+ }
+ }
+ }
+
@Nullable
private Drawable getCachedIcon(@NonNull ResourceName name) {
synchronized (sSync) {
@@ -1730,7 +1818,7 @@
@Override
public int installExistingPackage(String packageName, int installReason)
throws NameNotFoundException {
- return installExistingPackageAsUser(packageName, installReason, mContext.getUserId());
+ return installExistingPackageAsUser(packageName, installReason, getUserId());
}
@Override
@@ -2089,7 +2177,7 @@
@Override
public void deletePackage(String packageName, IPackageDeleteObserver observer, int flags) {
- deletePackageAsUser(packageName, observer, flags, mContext.getUserId());
+ deletePackageAsUser(packageName, observer, flags, getUserId());
}
@Override
@@ -2107,7 +2195,7 @@
public void clearApplicationUserData(String packageName,
IPackageDataObserver observer) {
try {
- mPM.clearApplicationUserData(packageName, observer, mContext.getUserId());
+ mPM.clearApplicationUserData(packageName, observer, getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -2158,7 +2246,7 @@
try {
return mPM.setPackagesSuspendedAsUser(packageNames, suspended, appExtras,
launcherExtras, dialogMessage, mContext.getOpPackageName(),
- mContext.getUserId());
+ getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -2169,7 +2257,7 @@
final PersistableBundle extras;
try {
extras = mPM.getSuspendedPackageAppExtras(mContext.getOpPackageName(),
- mContext.getUserId());
+ getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -2189,7 +2277,7 @@
@Override
public boolean isPackageSuspended(String packageName) throws NameNotFoundException {
try {
- return isPackageSuspendedForUser(packageName, mContext.getUserId());
+ return isPackageSuspendedForUser(packageName, getUserId());
} catch (IllegalArgumentException ie) {
throw new NameNotFoundException(packageName);
}
@@ -2197,7 +2285,7 @@
@Override
public boolean isPackageSuspended() {
- return isPackageSuspendedForUser(mContext.getOpPackageName(), mContext.getUserId());
+ return isPackageSuspendedForUser(mContext.getOpPackageName(), getUserId());
}
/** @hide */
@@ -2247,7 +2335,7 @@
public void addPreferredActivity(IntentFilter filter,
int match, ComponentName[] set, ComponentName activity) {
try {
- mPM.addPreferredActivity(filter, match, set, activity, mContext.getUserId());
+ mPM.addPreferredActivity(filter, match, set, activity, getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -2267,7 +2355,7 @@
public void replacePreferredActivity(IntentFilter filter,
int match, ComponentName[] set, ComponentName activity) {
try {
- mPM.replacePreferredActivity(filter, match, set, activity, mContext.getUserId());
+ mPM.replacePreferredActivity(filter, match, set, activity, getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -2316,7 +2404,7 @@
public void setComponentEnabledSetting(ComponentName componentName,
int newState, int flags) {
try {
- mPM.setComponentEnabledSetting(componentName, newState, flags, mContext.getUserId());
+ mPM.setComponentEnabledSetting(componentName, newState, flags, getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -2325,7 +2413,7 @@
@Override
public int getComponentEnabledSetting(ComponentName componentName) {
try {
- return mPM.getComponentEnabledSetting(componentName, mContext.getUserId());
+ return mPM.getComponentEnabledSetting(componentName, getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -2336,7 +2424,7 @@
int newState, int flags) {
try {
mPM.setApplicationEnabledSetting(packageName, newState, flags,
- mContext.getUserId(), mContext.getOpPackageName());
+ getUserId(), mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -2345,7 +2433,7 @@
@Override
public int getApplicationEnabledSetting(String packageName) {
try {
- return mPM.getApplicationEnabledSetting(packageName, mContext.getUserId());
+ return mPM.getApplicationEnabledSetting(packageName, getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -2457,7 +2545,7 @@
if (mInstaller == null) {
try {
mInstaller = new PackageInstaller(mPM.getPackageInstaller(),
- mContext.getPackageName(), mContext.getUserId());
+ mContext.getPackageName(), getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -2469,7 +2557,7 @@
@Override
public boolean isPackageAvailable(String packageName) {
try {
- return mPM.isPackageAvailable(packageName, mContext.getUserId());
+ return mPM.isPackageAvailable(packageName, getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -2509,7 +2597,7 @@
if (itemInfo.showUserIcon != UserHandle.USER_NULL) {
return dr;
}
- return getUserBadgedIcon(dr, new UserHandle(mContext.getUserId()));
+ return getUserBadgedIcon(dr, new UserHandle(getUserId()));
}
/**
@@ -2657,6 +2745,9 @@
private final ContextImpl mContext;
private final IPackageManager mPM;
+ /** Assume locked until we hear otherwise */
+ private volatile boolean mUserUnlocked = false;
+
private static final Object sSync = new Object();
private static ArrayMap<ResourceName, WeakReference<Drawable.ConstantState>> sIconCache
= new ArrayMap<ResourceName, WeakReference<Drawable.ConstantState>>();
@@ -2701,7 +2792,7 @@
@Override
public boolean canRequestPackageInstalls() {
try {
- return mPM.canRequestPackageInstalls(mContext.getPackageName(), mContext.getUserId());
+ return mPM.canRequestPackageInstalls(mContext.getPackageName(), getUserId());
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
@@ -2811,7 +2902,7 @@
@Override
public CharSequence getHarmfulAppWarning(String packageName) {
try {
- return mPM.getHarmfulAppWarning(packageName, mContext.getUserId());
+ return mPM.getHarmfulAppWarning(packageName, getUserId());
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
@@ -2820,7 +2911,7 @@
@Override
public void setHarmfulAppWarning(String packageName, CharSequence warning) {
try {
- mPM.setHarmfulAppWarning(packageName, warning, mContext.getUserId());
+ mPM.setHarmfulAppWarning(packageName, warning, getUserId());
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
diff --git a/core/java/android/app/ClientTransactionHandler.java b/core/java/android/app/ClientTransactionHandler.java
index d9c7cf3..193f933 100644
--- a/core/java/android/app/ClientTransactionHandler.java
+++ b/core/java/android/app/ClientTransactionHandler.java
@@ -16,6 +16,7 @@
package android.app;
import android.app.servertransaction.ClientTransaction;
+import android.app.servertransaction.ClientTransactionItem;
import android.app.servertransaction.PendingTransactionActions;
import android.app.servertransaction.TransactionExecutor;
import android.content.Intent;
@@ -29,6 +30,7 @@
import com.android.internal.content.ReferrerIntent;
import java.util.List;
+import java.util.Map;
/**
* Defines operations that a {@link android.app.servertransaction.ClientTransaction} or its items
@@ -78,6 +80,9 @@
// Execute phase related logic and handlers. Methods here execute actual lifecycle transactions
// and deliver callbacks.
+ /** Get activity and its corresponding transaction item which are going to destroy. */
+ public abstract Map<IBinder, ClientTransactionItem> getActivitiesToBeDestroyed();
+
/** Destroy the activity. */
public abstract void handleDestroyActivity(IBinder token, boolean finishing, int configChanges,
boolean getNonConfigInstance, String reason);
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 82088dc..2eafb32 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -80,6 +80,8 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.Preconditions;
+import dalvik.system.BlockGuard;
+
import libcore.io.Memory;
import java.io.File;
@@ -2521,7 +2523,11 @@
private File makeFilename(File base, String name) {
if (name.indexOf(File.separatorChar) < 0) {
- return new File(base, name);
+ final File res = new File(base, name);
+ // We report as filesystem access here to give us the best shot at
+ // detecting apps that will pass the path down to native code.
+ BlockGuard.getVmPolicy().onPathAccess(res.getPath());
+ return res;
}
throw new IllegalArgumentException(
"File " + name + " contains a path separator");
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index b39eb9b..19d7c83 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -237,11 +237,8 @@
// running.
List<ApplicationInfo> getRunningExternalApplications();
void finishHeavyWeightApp();
- // A StrictMode violation to be handled. The violationMask is a
- // subset of the original StrictMode policy bitmask, with only the
- // bit violated and penalty bits to be executed by the
- // ActivityManagerService remaining set.
- void handleApplicationStrictModeViolation(in IBinder app, int violationMask,
+ // A StrictMode violation to be handled.
+ void handleApplicationStrictModeViolation(in IBinder app, int penaltyMask,
in StrictMode.ViolationInfo crashInfo);
boolean isTopActivityImmersive();
void crashApplication(int uid, int initialPid, in String packageName, int userId, in String message);
@@ -271,7 +268,7 @@
void showBootMessage(in CharSequence msg, boolean always);
void killAllBackgroundProcesses();
ContentProviderHolder getContentProviderExternal(in String name, int userId,
- in IBinder token);
+ in IBinder token, String tag);
void removeContentProviderExternal(in String name, in IBinder token);
// Get memory information about the calling process.
void getMyMemoryState(out ActivityManager.RunningAppProcessInfo outInfo);
@@ -396,7 +393,6 @@
void setDumpHeapDebugLimit(in String processName, int uid, long maxMemSize,
in String reportPackage);
void dumpHeapFinished(in String path);
- void setVoiceKeepAwake(in IVoiceInteractionSession session, boolean keepAwake);
void updateLockTaskPackages(int userId, in String[] packages);
void noteAlarmStart(in IIntentSender sender, in WorkSource workSource, int sourceUid, in String tag);
void noteAlarmFinish(in IIntentSender sender, in WorkSource workSource, int sourceUid, in String tag);
@@ -502,4 +498,19 @@
* user unlock progress.
*/
boolean startUserInBackgroundWithListener(int userid, IProgressListener unlockProgressListener);
+
+ /**
+ * Method for the shell UID to start deletating its permission identity to an
+ * active instrumenation. The shell can delegate permissions only to one active
+ * instrumentation at a time. An active instrumentation is one running and
+ * started from the shell.
+ */
+ void startDelegateShellPermissionIdentity(int uid);
+
+ /**
+ * Method for the shell UID to stop deletating its permission identity to an
+ * active instrumenation. An active instrumentation is one running and
+ * started from the shell.
+ */
+ void stopDelegateShellPermissionIdentity();
}
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index a508289..3dbc312 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -128,7 +128,7 @@
in boolean stopProfiling);
void activityResumed(in IBinder token);
void activityPaused(in IBinder token);
- oneway void activityStopped(in IBinder token, in Bundle state,
+ void activityStopped(in IBinder token, in Bundle state,
in PersistableBundle persistentState, in CharSequence description);
oneway void activityDestroyed(in IBinder token);
void activityRelaunched(in IBinder token);
@@ -418,4 +418,8 @@
void setVrThread(int tid);
void setPersistentVrThread(int tid);
+ void stopAppSwitches();
+ void resumeAppSwitches();
+ void setActivityController(in IActivityController watcher, boolean imAMonkey);
+ void setVoiceKeepAwake(in IVoiceInteractionSession session, boolean keepAwake);
}
diff --git a/core/java/android/app/IUiAutomationConnection.aidl b/core/java/android/app/IUiAutomationConnection.aidl
index d01938b..ac4bf7d 100644
--- a/core/java/android/app/IUiAutomationConnection.aidl
+++ b/core/java/android/app/IUiAutomationConnection.aidl
@@ -47,7 +47,8 @@
in ParcelFileDescriptor source);
void grantRuntimePermission(String packageName, String permission, int userId);
void revokeRuntimePermission(String packageName, String permission, int userId);
-
+ void adoptShellPermissionIdentity(int uid);
+ void dropShellPermissionIdentity();
// Called from the system process.
oneway void shutdown();
}
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index 34be41b6..d9969a7 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -1313,7 +1313,8 @@
* @param activity The activity being restored.
* @param savedInstanceState The previously saved state being restored.
*/
- public void callActivityOnRestoreInstanceState(Activity activity, Bundle savedInstanceState) {
+ public void callActivityOnRestoreInstanceState(@NonNull Activity activity,
+ @NonNull Bundle savedInstanceState) {
activity.performRestoreInstanceState(savedInstanceState);
}
@@ -1322,11 +1323,12 @@
* method. The default implementation simply calls through to that method.
*
* @param activity The activity being restored.
- * @param savedInstanceState The previously saved state being restored.
+ * @param savedInstanceState The previously saved state being restored (or null).
* @param persistentState The previously persisted state (or null)
*/
- public void callActivityOnRestoreInstanceState(Activity activity, Bundle savedInstanceState,
- PersistableBundle persistentState) {
+ public void callActivityOnRestoreInstanceState(@NonNull Activity activity,
+ @Nullable Bundle savedInstanceState,
+ @Nullable PersistableBundle persistentState) {
activity.performRestoreInstanceState(savedInstanceState, persistentState);
}
@@ -1335,11 +1337,12 @@
* The default implementation simply calls through to that method.
*
* @param activity The activity being created.
- * @param icicle The previously frozen state (or null) to pass through to
+ * @param savedInstanceState The previously saved state (or null) to pass through to
* onPostCreate().
*/
- public void callActivityOnPostCreate(Activity activity, Bundle icicle) {
- activity.onPostCreate(icicle);
+ public void callActivityOnPostCreate(@NonNull Activity activity,
+ @Nullable Bundle savedInstanceState) {
+ activity.onPostCreate(savedInstanceState);
}
/**
@@ -1347,12 +1350,14 @@
* The default implementation simply calls through to that method.
*
* @param activity The activity being created.
- * @param icicle The previously frozen state (or null) to pass through to
+ * @param savedInstanceState The previously frozen state (or null) to pass through to
* onPostCreate().
+ * @param persistentState The previously persisted state (or null)
*/
- public void callActivityOnPostCreate(Activity activity, Bundle icicle,
- PersistableBundle persistentState) {
- activity.onPostCreate(icicle, persistentState);
+ public void callActivityOnPostCreate(@NonNull Activity activity,
+ @Nullable Bundle savedInstanceState,
+ @Nullable PersistableBundle persistentState) {
+ activity.onPostCreate(savedInstanceState, persistentState);
}
/**
@@ -1439,7 +1444,8 @@
* @param activity The activity being saved.
* @param outState The bundle to pass to the call.
*/
- public void callActivityOnSaveInstanceState(Activity activity, Bundle outState) {
+ public void callActivityOnSaveInstanceState(@NonNull Activity activity,
+ @NonNull Bundle outState) {
activity.performSaveInstanceState(outState);
}
@@ -1450,8 +1456,8 @@
* @param outState The bundle to pass to the call.
* @param outPersistentState The persistent bundle to pass to the call.
*/
- public void callActivityOnSaveInstanceState(Activity activity, Bundle outState,
- PersistableBundle outPersistentState) {
+ public void callActivityOnSaveInstanceState(@NonNull Activity activity,
+ @NonNull Bundle outState, @NonNull PersistableBundle outPersistentState) {
activity.performSaveInstanceState(outState, outPersistentState);
}
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 05bf6bf..74d3c0d 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -202,6 +202,11 @@
*/
private static final int MAX_REPLY_HISTORY = 5;
+ /**
+ * Maximum numbers of action buttons in a notification.
+ * @hide
+ */
+ public static final int MAX_ACTION_BUTTONS = 3;
/**
* If the notification contained an unsent draft for a RemoteInput when the user clicked on it,
@@ -3151,8 +3156,6 @@
public static final String EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT
= "android.rebuild.hudViewActionCount";
- private static final int MAX_ACTION_BUTTONS = 3;
-
private static final boolean USE_ONLY_TITLE_IN_LOW_PRIORITY_SUMMARY =
SystemProperties.getBoolean("notifications.only_title", true);
@@ -4473,9 +4476,9 @@
mTextColorsAreForBackground = backgroundColor;
if (!hasForegroundColor() || !isColorized()) {
mPrimaryTextColor = ContrastColorUtil.resolvePrimaryColor(mContext,
- backgroundColor);
+ backgroundColor, mInNightMode);
mSecondaryTextColor = ContrastColorUtil.resolveSecondaryColor(mContext,
- backgroundColor);
+ backgroundColor, mInNightMode);
if (backgroundColor != COLOR_DEFAULT && isColorized()) {
mPrimaryTextColor = ContrastColorUtil.findAlphaToMeetContrast(
mPrimaryTextColor, backgroundColor, 4.5);
@@ -5260,7 +5263,7 @@
// background color
background = outResultColor[0].getDefaultColor();
int textColor = ContrastColorUtil.resolvePrimaryColor(mContext,
- background);
+ background, mInNightMode);
button.setTextColor(R.id.action0, textColor);
rippleColor = textColor;
} else if (mN.color != COLOR_DEFAULT && !isColorized() && mTintActionButtons) {
@@ -5440,7 +5443,7 @@
com.android.internal.R.color.notification_material_background_color);
if (mN.color == COLOR_DEFAULT) {
ensureColors();
- color = ContrastColorUtil.resolveDefaultColor(mContext, background);
+ color = ContrastColorUtil.resolveDefaultColor(mContext, background, mInNightMode);
} else {
color = ContrastColorUtil.resolveContrastColor(mContext, mN.color,
background, mInNightMode);
@@ -5459,7 +5462,8 @@
}
int background = mContext.getColor(
com.android.internal.R.color.notification_material_background_color);
- mNeutralColor = ContrastColorUtil.resolveDefaultColor(mContext, background);
+ mNeutralColor = ContrastColorUtil.resolveDefaultColor(mContext, background,
+ mInNightMode);
if (Color.alpha(mNeutralColor) < 255) {
// alpha doesn't go well for color filters, so let's blend it manually
mNeutralColor = ContrastColorUtil.compositeColors(mNeutralColor, background);
@@ -7162,8 +7166,8 @@
}
public static final class Message {
-
- static final String KEY_TEXT = "text";
+ /** @hide */
+ public static final String KEY_TEXT = "text";
static final String KEY_TIMESTAMP = "time";
static final String KEY_SENDER = "sender";
static final String KEY_SENDER_PERSON = "sender_person";
@@ -7830,10 +7834,13 @@
// If the action buttons should not be tinted, then just use the default
// notification color. Otherwise, just use the passed-in color.
+ Configuration currentConfig = mBuilder.mContext.getResources().getConfiguration();
+ boolean inNightMode = (currentConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK)
+ == Configuration.UI_MODE_NIGHT_YES;
int tintColor = mBuilder.shouldTintActionButtons() || mBuilder.isColorized()
? color
: ContrastColorUtil.resolveColor(mBuilder.mContext,
- Notification.COLOR_DEFAULT);
+ Notification.COLOR_DEFAULT, inNightMode);
button.setDrawableTint(R.id.action0, false, tintColor,
PorterDuff.Mode.SRC_ATOP);
diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java
index ba355f9..03fd139 100644
--- a/core/java/android/app/NotificationChannel.java
+++ b/core/java/android/app/NotificationChannel.java
@@ -38,6 +38,7 @@
import org.xmlpull.v1.XmlSerializer;
import java.io.IOException;
+import java.io.PrintWriter;
import java.util.Arrays;
/**
@@ -644,7 +645,7 @@
@Nullable
private Uri restoreSoundUri(Context context, @Nullable Uri uri) {
- if (uri == null) {
+ if (uri == null || Uri.EMPTY.equals(uri)) {
return null;
}
ContentResolver contentResolver = context.getContentResolver();
@@ -680,7 +681,7 @@
private Uri getSoundForBackup(Context context) {
Uri sound = getSound();
- if (sound == null) {
+ if (sound == null || Uri.EMPTY.equals(sound)) {
return null;
}
Uri canonicalSound = context.getContentResolver().canonicalize(sound);
@@ -942,6 +943,32 @@
return result;
}
+ /** @hide */
+ public void dump(PrintWriter pw, String prefix, boolean redacted) {
+ String redactedName = redacted ? TextUtils.trimToLengthWithEllipsis(mName, 3) : mName;
+ String output = "NotificationChannel{"
+ + "mId='" + mId + '\''
+ + ", mName=" + redactedName
+ + ", mDescription=" + (!TextUtils.isEmpty(mDesc) ? "hasDescription " : "")
+ + ", mImportance=" + mImportance
+ + ", mBypassDnd=" + mBypassDnd
+ + ", mLockscreenVisibility=" + mLockscreenVisibility
+ + ", mSound=" + mSound
+ + ", mLights=" + mLights
+ + ", mLightColor=" + mLightColor
+ + ", mVibration=" + Arrays.toString(mVibration)
+ + ", mUserLockedFields=" + Integer.toHexString(mUserLockedFields)
+ + ", mFgServiceShown=" + mFgServiceShown
+ + ", mVibrationEnabled=" + mVibrationEnabled
+ + ", mShowBadge=" + mShowBadge
+ + ", mDeleted=" + mDeleted
+ + ", mGroup='" + mGroup + '\''
+ + ", mAudioAttributes=" + mAudioAttributes
+ + ", mBlockableSystem=" + mBlockableSystem
+ + '}';
+ pw.println(prefix + output);
+ }
+
@Override
public String toString() {
return "NotificationChannel{"
diff --git a/core/java/android/app/PictureInPictureParams.java b/core/java/android/app/PictureInPictureParams.java
index 7313b0d..edaae75 100644
--- a/core/java/android/app/PictureInPictureParams.java
+++ b/core/java/android/app/PictureInPictureParams.java
@@ -17,6 +17,7 @@
package android.app;
import android.annotation.Nullable;
+import android.annotation.TestApi;
import android.graphics.Rect;
import android.os.Parcel;
import android.os.Parcelable;
@@ -181,6 +182,7 @@
* @return the aspect ratio. If none is set, return 0.
* @hide
*/
+ @TestApi
public float getAspectRatio() {
if (mAspectRatio != null) {
return mAspectRatio.floatValue();
@@ -205,6 +207,7 @@
* @return the set of user actions.
* @hide
*/
+ @TestApi
public List<RemoteAction> getActions() {
return mUserActions;
}
@@ -231,6 +234,7 @@
* @return the source rect hint
* @hide
*/
+ @TestApi
public Rect getSourceRectHint() {
return mSourceRectHint;
}
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index e5f143c..b432baa 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -26,6 +26,7 @@
import android.app.slice.SliceManager;
import android.app.timedetector.TimeDetector;
import android.app.timezone.RulesManager;
+import android.app.timezonedetector.TimeZoneDetector;
import android.app.trust.TrustManager;
import android.app.usage.IStorageStatsManager;
import android.app.usage.IUsageStatsManager;
@@ -54,6 +55,8 @@
import android.hardware.SystemSensorManager;
import android.hardware.camera2.CameraManager;
import android.hardware.display.DisplayManager;
+import android.hardware.face.FaceManager;
+import android.hardware.face.IFaceService;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.IFingerprintService;
import android.hardware.hdmi.HdmiControlManager;
@@ -791,6 +794,22 @@
return new FingerprintManager(ctx.getOuterContext(), service);
}});
+ registerService(Context.FACE_SERVICE, FaceManager.class,
+ new CachedServiceFetcher<FaceManager>() {
+ @Override
+ public FaceManager createService(ContextImpl ctx)
+ throws ServiceNotFoundException {
+ final IBinder binder;
+ if (ctx.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.O) {
+ binder = ServiceManager.getServiceOrThrow(Context.FACE_SERVICE);
+ } else {
+ binder = ServiceManager.getService(Context.FACE_SERVICE);
+ }
+ IFaceService service = IFaceService.Stub.asInterface(binder);
+ return new FaceManager(ctx.getOuterContext(), service);
+ }
+ });
+
registerService(Context.TV_INPUT_SERVICE, TvInputManager.class,
new CachedServiceFetcher<TvInputManager>() {
@Override
@@ -1015,6 +1034,13 @@
throws ServiceNotFoundException {
return new TimeDetector();
}});
+ registerService(Context.TIME_ZONE_DETECTOR_SERVICE, TimeZoneDetector.class,
+ new CachedServiceFetcher<TimeZoneDetector>() {
+ @Override
+ public TimeZoneDetector createService(ContextImpl ctx)
+ throws ServiceNotFoundException {
+ return new TimeZoneDetector();
+ }});
}
/**
diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java
index 5662aea..44f28796 100644
--- a/core/java/android/app/UiAutomation.java
+++ b/core/java/android/app/UiAutomation.java
@@ -33,6 +33,7 @@
import android.os.IBinder;
import android.os.Looper;
import android.os.ParcelFileDescriptor;
+import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
@@ -50,6 +51,7 @@
import android.view.accessibility.IAccessibilityInteractionConnection;
import com.android.internal.util.function.pooled.PooledLambda;
+
import libcore.io.IoUtils;
import java.io.IOException;
@@ -347,6 +349,46 @@
}
/**
+ * Adopt the permission identity of the shell UID. This allows you to call APIs protected
+ * permissions which normal apps cannot hold but are granted to the shell UID. If you
+ * already adopted the shell permission identity this method would be a no-op.
+ * Note that your permission state becomes that of the shell UID and it is not a
+ * combination of your and the shell UID permissions.
+ *
+ * @see #dropShellPermissionIdentity()
+ */
+ public void adoptShellPermissionIdentity() {
+ synchronized (mLock) {
+ throwIfNotConnectedLocked();
+ }
+ try {
+ // Calling out without a lock held.
+ mUiAutomationConnection.adoptShellPermissionIdentity(Process.myUid());
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error executing adopting shell permission identity!", re);
+ }
+ }
+
+ /**
+ * Drop the shell permission identity adopted by a previous call to
+ * {@link #adoptShellPermissionIdentity()}. If you did not adopt the shell permission
+ * identity this method would be a no-op.
+ *
+ * @see #adoptShellPermissionIdentity()
+ */
+ public void dropShellPermissionIdentity() {
+ synchronized (mLock) {
+ throwIfNotConnectedLocked();
+ }
+ try {
+ // Calling out without a lock held.
+ mUiAutomationConnection.dropShellPermissionIdentity();
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error executing dropping shell permission identity!", re);
+ }
+ }
+
+ /**
* Performs a global action. Such an action can be performed at any moment
* regardless of the current application or user location in that application.
* For example going back, going home, opening recents, etc.
@@ -999,6 +1041,8 @@
*
* @param command The command to execute.
* @return A file descriptor to the standard output stream.
+ *
+ * @see #adoptShellPermissionIdentity()
*/
public ParcelFileDescriptor executeShellCommand(String command) {
synchronized (mLock) {
@@ -1081,22 +1125,6 @@
return result;
}
- private static float getDegreesForRotation(int value) {
- switch (value) {
- case Surface.ROTATION_90: {
- return 360f - 90f;
- }
- case Surface.ROTATION_180: {
- return 360f - 180f;
- }
- case Surface.ROTATION_270: {
- return 360f - 270f;
- } default: {
- return 0;
- }
- }
- }
-
private boolean isConnectedLocked() {
return mConnectionId != CONNECTION_ID_UNDEFINED;
}
diff --git a/core/java/android/app/UiAutomationConnection.java b/core/java/android/app/UiAutomationConnection.java
index d3828ab..ac3f2e7 100644
--- a/core/java/android/app/UiAutomationConnection.java
+++ b/core/java/android/app/UiAutomationConnection.java
@@ -30,6 +30,7 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
+import android.util.Log;
import android.view.IWindowManager;
import android.view.InputEvent;
import android.view.SurfaceControl;
@@ -37,7 +38,6 @@
import android.view.WindowContentFrameStats;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.IAccessibilityManager;
-import android.util.Log;
import libcore.io.IoUtils;
@@ -71,6 +71,9 @@
private final IPackageManager mPackageManager = IPackageManager.Stub
.asInterface(ServiceManager.getService("package"));
+ private final IActivityManager mActivityManager = IActivityManager.Stub
+ .asInterface(ServiceManager.getService("activity"));
+
private final Object mLock = new Object();
private final Binder mToken = new Binder();
@@ -274,6 +277,36 @@
}
}
+ @Override
+ public void adoptShellPermissionIdentity(int uid) throws RemoteException {
+ synchronized (mLock) {
+ throwIfCalledByNotTrustedUidLocked();
+ throwIfShutdownLocked();
+ throwIfNotConnectedLocked();
+ }
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ mActivityManager.startDelegateShellPermissionIdentity(uid);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
+ public void dropShellPermissionIdentity() throws RemoteException {
+ synchronized (mLock) {
+ throwIfCalledByNotTrustedUidLocked();
+ throwIfShutdownLocked();
+ throwIfNotConnectedLocked();
+ }
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ mActivityManager.stopDelegateShellPermissionIdentity();
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
public class Repeater implements Runnable {
// Continuously read readFrom and write back to writeTo until EOF is encountered
private final InputStream readFrom;
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index fde756c..6ad6c25 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -996,17 +996,29 @@
}
/**
- * If the current wallpaper is a live wallpaper component, return the
- * information about that wallpaper. Otherwise, if it is a static image,
- * simply return null.
+ * Returns the information about the wallpaper if the current wallpaper is
+ * a live wallpaper component. Otherwise, if the wallpaper is a static image,
+ * this returns null.
*/
public WallpaperInfo getWallpaperInfo() {
+ return getWallpaperInfo(mContext.getUserId());
+ }
+
+ /**
+ * Returns the information about the wallpaper if the current wallpaper is
+ * a live wallpaper component. Otherwise, if the wallpaper is a static image,
+ * this returns null.
+ *
+ * @param userId Owner of the wallpaper.
+ * @hide
+ */
+ public WallpaperInfo getWallpaperInfo(int userId) {
try {
if (sGlobals.mService == null) {
Log.w(TAG, "WallpaperService not running");
throw new RuntimeException(new DeadSystemException());
} else {
- return sGlobals.mService.getWallpaperInfo(mContext.getUserId());
+ return sGlobals.mService.getWallpaperInfo(userId);
}
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
diff --git a/core/java/android/app/WindowConfiguration.java b/core/java/android/app/WindowConfiguration.java
index 09c7981..e6fb5dc 100644
--- a/core/java/android/app/WindowConfiguration.java
+++ b/core/java/android/app/WindowConfiguration.java
@@ -59,11 +59,6 @@
/** The current windowing mode of the configuration. */
private @WindowingMode int mWindowingMode;
- private int mFlags;
-
- /** Indicates that this window should always be on top of the other windows. */
- private static final int PFLAG_ALWAYS_ON_TOP = 1 << 0;
-
/** Windowing mode is currently not defined. */
public static final int WINDOWING_MODE_UNDEFINED = 0;
/** Occupies the full area of the screen or the parent container. */
@@ -129,6 +124,24 @@
})
public @interface ActivityType {}
+ /** The current always on top status of the configuration. */
+ private @AlwaysOnTop int mAlwaysOnTop;
+
+ /** Always on top is currently not defined. */
+ private static final int ALWAYS_ON_TOP_UNDEFINED = 0;
+ /** Always on top is currently on for this configuration. */
+ private static final int ALWAYS_ON_TOP_ON = 1;
+ /** Always on top is currently off for this configuration. */
+ private static final int ALWAYS_ON_TOP_OFF = 2;
+
+ /** @hide */
+ @IntDef(prefix = { "ALWAYS_ON_TOP_" }, value = {
+ ALWAYS_ON_TOP_UNDEFINED,
+ ALWAYS_ON_TOP_ON,
+ ALWAYS_ON_TOP_OFF,
+ })
+ private @interface AlwaysOnTop {}
+
/** Bit that indicates that the {@link #mBounds} changed.
* @hide */
public static final int WINDOW_CONFIG_BOUNDS = 1 << 0;
@@ -141,16 +154,16 @@
/** Bit that indicates that the {@link #mActivityType} changed.
* @hide */
public static final int WINDOW_CONFIG_ACTIVITY_TYPE = 1 << 3;
- /** Bit that indicates that the {@link #mFlags} changed.
+ /** Bit that indicates that the {@link #mAlwaysOnTop} changed.
* @hide */
- public static final int WINDOW_CONFIG_FLAGS = 1 << 4;
+ public static final int WINDOW_CONFIG_ALWAYS_ON_TOP = 1 << 4;
/** @hide */
@IntDef(flag = true, prefix = { "WINDOW_CONFIG_" }, value = {
WINDOW_CONFIG_BOUNDS,
WINDOW_CONFIG_APP_BOUNDS,
WINDOW_CONFIG_WINDOWING_MODE,
WINDOW_CONFIG_ACTIVITY_TYPE,
- WINDOW_CONFIG_FLAGS
+ WINDOW_CONFIG_ALWAYS_ON_TOP,
})
public @interface WindowConfig {}
@@ -176,7 +189,7 @@
dest.writeParcelable(mAppBounds, flags);
dest.writeInt(mWindowingMode);
dest.writeInt(mActivityType);
- dest.writeInt(mFlags);
+ dest.writeInt(mAlwaysOnTop);
}
private void readFromParcel(Parcel source) {
@@ -184,7 +197,7 @@
mAppBounds = source.readParcelable(Rect.class.getClassLoader());
mWindowingMode = source.readInt();
mActivityType = source.readInt();
- mFlags = source.readInt();
+ mAlwaysOnTop = source.readInt();
}
@Override
@@ -232,9 +245,7 @@
setAppBounds(rect.left, rect.top, rect.right, rect.bottom);
}
- private void setFlags(int flags) {
- mFlags = flags;
- }
+
/**
* Sets whether this window should be always on top.
@@ -242,11 +253,11 @@
* @hide
*/
public void setAlwaysOnTop(boolean alwaysOnTop) {
- if (alwaysOnTop) {
- mFlags |= PFLAG_ALWAYS_ON_TOP;
- } else {
- mFlags &= ~PFLAG_ALWAYS_ON_TOP;
- }
+ mAlwaysOnTop = alwaysOnTop ? ALWAYS_ON_TOP_ON : ALWAYS_ON_TOP_OFF;
+ }
+
+ private void setAlwaysOnTop(@AlwaysOnTop int alwaysOnTop) {
+ mAlwaysOnTop = alwaysOnTop;
}
/**
@@ -308,7 +319,7 @@
setAppBounds(other.mAppBounds);
setWindowingMode(other.mWindowingMode);
setActivityType(other.mActivityType);
- setFlags(other.mFlags);
+ setAlwaysOnTop(other.mAlwaysOnTop);
}
/** Set this object to completely undefined.
@@ -323,7 +334,7 @@
setBounds(null);
setWindowingMode(WINDOWING_MODE_UNDEFINED);
setActivityType(ACTIVITY_TYPE_UNDEFINED);
- setFlags(0);
+ setAlwaysOnTop(ALWAYS_ON_TOP_UNDEFINED);
}
/**
@@ -341,10 +352,6 @@
changed |= WINDOW_CONFIG_BOUNDS;
setBounds(delta.mBounds);
}
- if (delta.mFlags != mFlags) {
- changed |= WINDOW_CONFIG_FLAGS;
- setFlags(delta.mFlags);
- }
if (delta.mAppBounds != null && !delta.mAppBounds.equals(mAppBounds)) {
changed |= WINDOW_CONFIG_APP_BOUNDS;
setAppBounds(delta.mAppBounds);
@@ -359,6 +366,11 @@
changed |= WINDOW_CONFIG_ACTIVITY_TYPE;
setActivityType(delta.mActivityType);
}
+ if (delta.mAlwaysOnTop != ALWAYS_ON_TOP_UNDEFINED
+ && mAlwaysOnTop != delta.mAlwaysOnTop) {
+ changed |= WINDOW_CONFIG_ALWAYS_ON_TOP;
+ setAlwaysOnTop(delta.mAlwaysOnTop);
+ }
return changed;
}
@@ -380,10 +392,6 @@
changes |= WINDOW_CONFIG_BOUNDS;
}
- if (mFlags != other.mFlags) {
- changes |= WINDOW_CONFIG_FLAGS;
- }
-
// Make sure that one of the values is not null and that they are not equal.
if ((compareUndefined || other.mAppBounds != null)
&& mAppBounds != other.mAppBounds
@@ -401,6 +409,11 @@
changes |= WINDOW_CONFIG_ACTIVITY_TYPE;
}
+ if ((compareUndefined || other.mAlwaysOnTop != ALWAYS_ON_TOP_UNDEFINED)
+ && mAlwaysOnTop != other.mAlwaysOnTop) {
+ changes |= WINDOW_CONFIG_ALWAYS_ON_TOP;
+ }
+
return changes;
}
@@ -435,8 +448,7 @@
if (n != 0) return n;
n = mActivityType - that.mActivityType;
if (n != 0) return n;
-
- n = mFlags - that.mFlags;
+ n = mAlwaysOnTop - that.mAlwaysOnTop;
if (n != 0) return n;
// if (n != 0) return n;
@@ -465,7 +477,7 @@
result = 31 * result + mWindowingMode;
result = 31 * result + mActivityType;
- result = 31 * result + mFlags;
+ result = 31 * result + mAlwaysOnTop;
return result;
}
@@ -476,7 +488,7 @@
+ " mAppBounds=" + mAppBounds
+ " mWindowingMode=" + windowingModeToString(mWindowingMode)
+ " mActivityType=" + activityTypeToString(mActivityType)
- + " mFlags=0x" + Integer.toHexString(mFlags)
+ + " mAlwaysOnTop=" + alwaysOnTopToString(mAlwaysOnTop)
+ "}";
}
@@ -563,7 +575,8 @@
* @hide
*/
public boolean isAlwaysOnTop() {
- return mWindowingMode == WINDOWING_MODE_PINNED || (mFlags & PFLAG_ALWAYS_ON_TOP) != 0;
+ return mWindowingMode == WINDOWING_MODE_PINNED
+ || (mWindowingMode == WINDOWING_MODE_FREEFORM && mAlwaysOnTop == ALWAYS_ON_TOP_ON);
}
/**
@@ -640,4 +653,14 @@
}
return String.valueOf(applicationType);
}
+
+ /** @hide */
+ public static String alwaysOnTopToString(@AlwaysOnTop int alwaysOnTop) {
+ switch (alwaysOnTop) {
+ case ALWAYS_ON_TOP_UNDEFINED: return "undefined";
+ case ALWAYS_ON_TOP_ON: return "on";
+ case ALWAYS_ON_TOP_OFF: return "off";
+ }
+ return String.valueOf(alwaysOnTop);
+ }
}
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index ff38c1f..cbd8741 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -72,6 +72,7 @@
import android.security.keystore.KeyAttestationException;
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.ParcelableKeyGenParameterSpec;
+import android.security.keystore.StrongBoxUnavailableException;
import android.service.restrictions.RestrictionsReceiver;
import android.telephony.TelephonyManager;
import android.telephony.data.ApnSetting;
@@ -1774,6 +1775,13 @@
public static final int ID_TYPE_MEID = 8;
/**
+ * Service-specific error code for {@link #generateKeyPair}:
+ * Indicates the call has failed due to StrongBox unavailability.
+ * @hide
+ */
+ public static final int KEY_GEN_STRONGBOX_UNAVAILABLE = 1;
+
+ /**
* Specifies that the calling app should be granted access to the installed credentials
* immediately. Otherwise, access to the credentials will be gated by user approval.
* For use with {@link #installKeyPair(ComponentName, PrivateKey, Certificate[], String, int)}
@@ -3237,8 +3245,8 @@
/**
* Called by a device/profile owner to set the timeout after which unlocking with secondary, non
- * strong auth (e.g. fingerprint, trust agents) times out, i.e. the user has to use a strong
- * authentication method like password, pin or pattern.
+ * strong auth (e.g. fingerprint, face, trust agents) times out, i.e. the user has to use a
+ * strong authentication method like password, pin or pattern.
*
* <p>This timeout is used internally to reset the timer to require strong auth again after
* specified timeout each time it has been successfully used.
@@ -3710,7 +3718,6 @@
| DevicePolicyManager.KEYGUARD_DISABLE_IRIS
| DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT;
-
/**
* Disable all current and future keyguard customizations.
*/
@@ -4154,8 +4161,11 @@
* Called by a device or profile owner, or delegated certificate installer, to generate a
* new private/public key pair. If the device supports key generation via secure hardware,
* this method is useful for creating a key in KeyChain that never left the secure hardware.
- *
* Access to the key is controlled the same way as in {@link #installKeyPair}.
+ *
+ * <p>Because this method might take several seconds to complete, it should only be called from
+ * a worker thread. This method returns {@code null} when called from the main thread.
+ *
* @param admin Which {@link DeviceAdminReceiver} this request is associated with, or
* {@code null} if calling from a delegated certificate installer.
* @param algorithm The key generation algorithm, see {@link java.security.KeyPairGenerator}.
@@ -4188,6 +4198,8 @@
* {@code keySpec} does not contain an attestation challenge.
* @throws UnsupportedOperationException if Device ID attestation was requested but the
* underlying hardware does not support it.
+ * @throws StrongBoxUnavailableException if the use of StrongBox for key generation was
+ * specified in {@code keySpec} but the device does not have one.
* @see KeyGenParameterSpec.Builder#setAttestationChallenge(byte[])
*/
public AttestedKeyPair generateKeyPair(@Nullable ComponentName admin,
@@ -4228,6 +4240,15 @@
} catch (InterruptedException e) {
Log.w(TAG, "Interrupted while generating key", e);
Thread.currentThread().interrupt();
+ } catch (ServiceSpecificException e) {
+ Log.w(TAG, String.format("Key Generation failure: %d", e.errorCode));
+ switch (e.errorCode) {
+ case KEY_GEN_STRONGBOX_UNAVAILABLE:
+ throw new StrongBoxUnavailableException("No StrongBox for key generation.");
+ default:
+ throw new RuntimeException(
+ String.format("Unknown error while generating key: %d", e.errorCode));
+ }
}
return null;
}
@@ -4898,10 +4919,10 @@
/**
* @hide
*/
- public void reportFailedFingerprintAttempt(int userHandle) {
+ public void reportFailedBiometricAttempt(int userHandle) {
if (mService != null) {
try {
- mService.reportFailedFingerprintAttempt(userHandle);
+ mService.reportFailedBiometricAttempt(userHandle);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -4911,10 +4932,10 @@
/**
* @hide
*/
- public void reportSuccessfulFingerprintAttempt(int userHandle) {
+ public void reportSuccessfulBiometricAttempt(int userHandle) {
if (mService != null) {
try {
- mService.reportSuccessfulFingerprintAttempt(userHandle);
+ mService.reportSuccessfulBiometricAttempt(userHandle);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -8300,6 +8321,22 @@
}
/**
+ * Makes all accumulated network logs available to DPC in a new batch.
+ * Only callable by ADB. If throttled, returns time to wait in milliseconds, otherwise 0.
+ * @hide
+ */
+ public long forceNetworkLogs() {
+ if (mService == null) {
+ return -1;
+ }
+ try {
+ return mService.forceNetworkLogs();
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Forces a batch of security logs to be fetched from logd and makes it available for DPC.
* Only callable by ADB. If throttled, returns time to wait in milliseconds, otherwise 0.
* @hide
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 37508cd..c95bc5b 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -132,8 +132,8 @@
void reportPasswordChanged(int userId);
void reportFailedPasswordAttempt(int userHandle);
void reportSuccessfulPasswordAttempt(int userHandle);
- void reportFailedFingerprintAttempt(int userHandle);
- void reportSuccessfulFingerprintAttempt(int userHandle);
+ void reportFailedBiometricAttempt(int userHandle);
+ void reportSuccessfulBiometricAttempt(int userHandle);
void reportKeyguardDismissed(int userHandle);
void reportKeyguardSecured(int userHandle);
@@ -347,6 +347,7 @@
boolean isSecurityLoggingEnabled(in ComponentName admin);
ParceledListSlice retrieveSecurityLogs(in ComponentName admin);
ParceledListSlice retrievePreRebootSecurityLogs(in ComponentName admin);
+ long forceNetworkLogs();
long forceSecurityLogs();
boolean isUninstallInQueue(String packageName);
diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java
index ef41b10..2099e75 100644
--- a/core/java/android/app/assist/AssistStructure.java
+++ b/core/java/android/app/assist/AssistStructure.java
@@ -2167,13 +2167,13 @@
if (autofillId == null) {
Log.i(TAG, prefix + " NO autofill ID");
} else {
- Log.i(TAG, prefix + "Autofill info: id= " + autofillId
+ Log.i(TAG, prefix + " Autofill info: id= " + autofillId
+ ", type=" + node.getAutofillType()
+ ", options=" + Arrays.toString(node.getAutofillOptions())
+ ", hints=" + Arrays.toString(node.getAutofillHints())
+ ", value=" + node.getAutofillValue()
+ ", sanitized=" + node.isSanitized()
- + ", importantFor=" + node.getImportantForAutofill());
+ + ", important=" + node.getImportantForAutofill());
}
final int NCHILDREN = node.getChildCount();
diff --git a/core/java/android/app/backup/BackupAgent.java b/core/java/android/app/backup/BackupAgent.java
index 9657922..ec2cf0c 100644
--- a/core/java/android/app/backup/BackupAgent.java
+++ b/core/java/android/app/backup/BackupAgent.java
@@ -43,7 +43,6 @@
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
-import java.util.Collection;
import java.util.LinkedList;
import java.util.Map;
import java.util.Set;
@@ -833,7 +832,7 @@
}
if (excludes != null &&
- isFileSpecifiedInPathList(destination, excludes)) {
+ BackupUtils.isFileSpecifiedInPathList(destination, excludes)) {
if (Log.isLoggable(FullBackup.TAG_XML_PARSER, Log.VERBOSE)) {
Log.v(FullBackup.TAG_XML_PARSER,
"onRestoreFile: \"" + destinationCanonicalPath + "\": listed in"
@@ -847,7 +846,8 @@
// it's a small list), we'll go through and look for it.
boolean explicitlyIncluded = false;
for (Set<PathWithRequiredFlags> domainIncludes : includes.values()) {
- explicitlyIncluded |= isFileSpecifiedInPathList(destination, domainIncludes);
+ explicitlyIncluded |=
+ BackupUtils.isFileSpecifiedInPathList(destination, domainIncludes);
if (explicitlyIncluded) {
break;
}
@@ -866,33 +866,6 @@
}
/**
- * @return True if the provided file is either directly in the provided list, or the provided
- * file is within a directory in the list.
- */
- private boolean isFileSpecifiedInPathList(File file,
- Collection<PathWithRequiredFlags> canonicalPathList) throws IOException {
- for (PathWithRequiredFlags canonical : canonicalPathList) {
- String canonicalPath = canonical.getPath();
- File fileFromList = new File(canonicalPath);
- if (fileFromList.isDirectory()) {
- if (file.isDirectory()) {
- // If they are both directories check exact equals.
- return file.equals(fileFromList);
- } else {
- // O/w we have to check if the file is within the directory from the list.
- return file.getCanonicalPath().startsWith(canonicalPath);
- }
- } else {
- if (file.equals(fileFromList)) {
- // Need to check the explicit "equals" so we don't end up with substrings.
- return true;
- }
- }
- }
- return false;
- }
-
- /**
* Only specialized platform agents should overload this entry point to support
* restores to crazy non-app locations.
* @hide
diff --git a/core/java/android/app/backup/BackupUtils.java b/core/java/android/app/backup/BackupUtils.java
new file mode 100644
index 0000000..8cf8a84
--- /dev/null
+++ b/core/java/android/app/backup/BackupUtils.java
@@ -0,0 +1,58 @@
+/*
+ * 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.app.backup;
+
+import android.app.backup.FullBackup.BackupScheme.PathWithRequiredFlags;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Collection;
+
+/** @hide */
+public class BackupUtils {
+
+ private BackupUtils() {}
+
+ /**
+ * Returns {@code true} if {@code file} is either directly in {@code canonicalPathList} or is a
+ * file contained in a directory in the list.
+ */
+ public static boolean isFileSpecifiedInPathList(
+ File file, Collection<PathWithRequiredFlags> canonicalPathList) throws IOException {
+ for (PathWithRequiredFlags canonical : canonicalPathList) {
+ String canonicalPath = canonical.getPath();
+ File fileFromList = new File(canonicalPath);
+ if (fileFromList.isDirectory()) {
+ if (file.isDirectory()) {
+ // If they are both directories check exact equals.
+ if (file.equals(fileFromList)) {
+ return true;
+ }
+ } else {
+ // O/w we have to check if the file is within the directory from the list.
+ if (file.toPath().startsWith(canonicalPath)) {
+ return true;
+ }
+ }
+ } else if (file.equals(fileFromList)) {
+ // Need to check the explicit "equals" so we don't end up with substrings.
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/core/java/android/app/servertransaction/ClientTransaction.java b/core/java/android/app/servertransaction/ClientTransaction.java
index 08ad2f0..2c1e59b 100644
--- a/core/java/android/app/servertransaction/ClientTransaction.java
+++ b/core/java/android/app/servertransaction/ClientTransaction.java
@@ -164,6 +164,31 @@
ObjectPool.recycle(this);
}
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder(64);
+ sb.append("ClientTransaction{");
+ if (mActivityToken != null) {
+ sb.append(" a:").append(Integer.toHexString(System.identityHashCode(mActivityToken)));
+ }
+ if (mActivityCallbacks != null && !mActivityCallbacks.isEmpty()) {
+ sb.append(" c:");
+ final int size = mActivityCallbacks.size();
+ for (int i = 0; i < size; i++) {
+ sb.append(mActivityCallbacks.get(i).getClass().getSimpleName());
+ if (i < size - 1) {
+ sb.append(",");
+ }
+ }
+ }
+ if (mLifecycleStateRequest != null) {
+ sb.append(" s:");
+ sb.append(mLifecycleStateRequest.getClass().getSimpleName());
+ }
+ sb.append(" }");
+ return sb.toString();
+ }
+
// Parcelable implementation
diff --git a/core/java/android/app/servertransaction/DestroyActivityItem.java b/core/java/android/app/servertransaction/DestroyActivityItem.java
index b443166..5941486 100644
--- a/core/java/android/app/servertransaction/DestroyActivityItem.java
+++ b/core/java/android/app/servertransaction/DestroyActivityItem.java
@@ -33,6 +33,11 @@
private int mConfigChanges;
@Override
+ public void preExecute(ClientTransactionHandler client, IBinder token) {
+ client.getActivitiesToBeDestroyed().put(token, this);
+ }
+
+ @Override
public void execute(ClientTransactionHandler client, IBinder token,
PendingTransactionActions pendingActions) {
Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityDestroy");
diff --git a/core/java/android/app/servertransaction/TransactionExecutor.java b/core/java/android/app/servertransaction/TransactionExecutor.java
index 43a2b4c..503e18b 100644
--- a/core/java/android/app/servertransaction/TransactionExecutor.java
+++ b/core/java/android/app/servertransaction/TransactionExecutor.java
@@ -35,6 +35,7 @@
import com.android.internal.annotations.VisibleForTesting;
import java.util.List;
+import java.util.Map;
/**
* Class that manages transaction execution in the correct order.
@@ -63,6 +64,24 @@
*/
public void execute(ClientTransaction transaction) {
final IBinder token = transaction.getActivityToken();
+ if (token != null) {
+ final Map<IBinder, ClientTransactionItem> activitiesToBeDestroyed =
+ mTransactionHandler.getActivitiesToBeDestroyed();
+ final ClientTransactionItem destroyItem = activitiesToBeDestroyed.get(token);
+ if (destroyItem != null) {
+ if (transaction.getLifecycleStateRequest() == destroyItem) {
+ // It is going to execute the transaction that will destroy activity with the
+ // token, so the corresponding to-be-destroyed record can be removed.
+ activitiesToBeDestroyed.remove(token);
+ }
+ if (mTransactionHandler.getActivityClient(token) == null) {
+ // The activity has not been created but has been requested to destroy, so all
+ // transactions for the token are just like being cancelled.
+ Slog.w(TAG, "Skip pre-destroyed " + transaction);
+ return;
+ }
+ }
+ }
log("Start resolving transaction for client: " + mTransactionHandler + ", token: " + token);
executeCallbacks(transaction);
@@ -76,7 +95,7 @@
@VisibleForTesting
public void executeCallbacks(ClientTransaction transaction) {
final List<ClientTransactionItem> callbacks = transaction.getCallbacks();
- if (callbacks == null) {
+ if (callbacks == null || callbacks.isEmpty()) {
// No callbacks to execute, return early.
return;
}
diff --git a/core/java/android/app/timezone/RulesState.aidl b/core/java/android/app/timezone/RulesState.aidl
index f789120..665220d 100644
--- a/core/java/android/app/timezone/RulesState.aidl
+++ b/core/java/android/app/timezone/RulesState.aidl
@@ -14,4 +14,6 @@
* limitations under the License.
*/
+package android.app.timezone;
+
parcelable RulesState;
\ No newline at end of file
diff --git a/core/java/android/app/timezonedetector/ITimeZoneDetectorService.aidl b/core/java/android/app/timezonedetector/ITimeZoneDetectorService.aidl
new file mode 100644
index 0000000..ef2cbab
--- /dev/null
+++ b/core/java/android/app/timezonedetector/ITimeZoneDetectorService.aidl
@@ -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 android.app.timezonedetector;
+
+/**
+ * System private API to comunicate with time zone detector service.
+ *
+ * <p>Used by parts of the Android system with signals associated with the device's time zone to
+ * provide information to the Time Zone Detector Service.
+ *
+ * <p>Use the {@link android.app.timezonedetector.TimeZoneDetector} class rather than going through
+ * this Binder interface directly. See {@link android.app.timezonedetector.TimeZoneDetectorService}
+ * for more complete documentation.
+ *
+ *
+ * {@hide}
+ */
+interface ITimeZoneDetectorService {
+ void stubbedCall();
+}
diff --git a/core/java/android/app/timezonedetector/TimeZoneDetector.java b/core/java/android/app/timezonedetector/TimeZoneDetector.java
new file mode 100644
index 0000000..be3c764
--- /dev/null
+++ b/core/java/android/app/timezonedetector/TimeZoneDetector.java
@@ -0,0 +1,57 @@
+/*
+ * 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.app.timezonedetector;
+
+import android.annotation.SystemService;
+import android.content.Context;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.ServiceManager.ServiceNotFoundException;
+import android.util.Log;
+
+/**
+ * The interface through which system components can send signals to the TimeZoneDetectorService.
+ * @hide
+ */
+@SystemService(Context.TIME_ZONE_DETECTOR_SERVICE)
+public final class TimeZoneDetector {
+
+ private static final String TAG = "timezonedetector.TimeZoneDetector";
+ private static final boolean DEBUG = false;
+
+ private final ITimeZoneDetectorService mITimeZoneDetectorService;
+
+ public TimeZoneDetector() throws ServiceNotFoundException {
+ mITimeZoneDetectorService = ITimeZoneDetectorService.Stub.asInterface(
+ ServiceManager.getServiceOrThrow(Context.TIME_ZONE_DETECTOR_SERVICE));
+
+ }
+ /**
+ * Does nothing.
+ * TODO: Remove this when the service implementation is built out.
+ */
+ public void stubbedCall() {
+ if (DEBUG) {
+ Log.d(TAG, "stubbedCall called");
+ }
+ try {
+ mITimeZoneDetectorService.stubbedCall();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+}
diff --git a/core/java/android/app/trust/ITrustManager.aidl b/core/java/android/app/trust/ITrustManager.aidl
index 6d65e3e..9985cc0 100644
--- a/core/java/android/app/trust/ITrustManager.aidl
+++ b/core/java/android/app/trust/ITrustManager.aidl
@@ -17,6 +17,7 @@
package android.app.trust;
import android.app.trust.ITrustListener;
+import android.hardware.biometrics.BiometricSourceType;
/**
* System private API to comunicate with trust service.
@@ -34,6 +35,6 @@
boolean isDeviceLocked(int userId);
boolean isDeviceSecure(int userId);
boolean isTrustUsuallyManaged(int userId);
- void unlockedByFingerprintForUser(int userId);
- void clearAllFingerprints();
+ void unlockedByBiometricForUser(int userId, in BiometricSourceType source);
+ void clearAllBiometricRecognized(in BiometricSourceType target);
}
diff --git a/core/java/android/app/trust/TrustManager.java b/core/java/android/app/trust/TrustManager.java
index 8ab0b70..fb27bed 100644
--- a/core/java/android/app/trust/TrustManager.java
+++ b/core/java/android/app/trust/TrustManager.java
@@ -20,6 +20,7 @@
import android.annotation.RequiresPermission;
import android.annotation.SystemService;
import android.content.Context;
+import android.hardware.biometrics.BiometricSourceType;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -195,26 +196,28 @@
}
/**
- * Updates the trust state for the user due to the user unlocking via fingerprint.
- * Should only be called if user authenticated via fingerprint and bouncer can be skipped.
+ * Updates the trust state for the user due to the user unlocking via a biometric sensor.
+ * Should only be called if user authenticated via fingerprint, face, or iris and bouncer
+ * can be skipped.
+ *
* @param userId
*/
@RequiresPermission(Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE)
- public void unlockedByFingerprintForUser(int userId) {
+ public void unlockedByBiometricForUser(int userId, BiometricSourceType source) {
try {
- mService.unlockedByFingerprintForUser(userId);
+ mService.unlockedByBiometricForUser(userId, source);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
- * Clears authenticated fingerprints for all users.
+ * Clears authentication by the specified biometric type for all users.
*/
@RequiresPermission(Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE)
- public void clearAllFingerprints() {
+ public void clearAllBiometricRecognized(BiometricSourceType source) {
try {
- mService.clearAllFingerprints();
+ mService.clearAllBiometricRecognized(source);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/content/ContentProviderClient.java b/core/java/android/content/ContentProviderClient.java
index 2d490a0..9d8c318 100644
--- a/core/java/android/content/ContentProviderClient.java
+++ b/core/java/android/content/ContentProviderClient.java
@@ -39,6 +39,8 @@
import dalvik.system.CloseGuard;
+import libcore.io.IoUtils;
+
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -560,14 +562,17 @@
return ContentProvider.coerceToLocalContentProvider(mContentProvider);
}
+ /**
+ * Closes the given object quietly, ignoring any checked exceptions. Does
+ * nothing if the given object is {@code null}.
+ */
+ public static void closeQuietly(ContentProviderClient client) {
+ IoUtils.closeQuietly(client);
+ }
+
/** {@hide} */
public static void releaseQuietly(ContentProviderClient client) {
- if (client != null) {
- try {
- client.release();
- } catch (Exception ignored) {
- }
- }
+ IoUtils.closeQuietly(client);
}
private class NotRespondingRunnable implements Runnable {
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index af09f15..f923738 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -260,6 +260,13 @@
*/
public static final String QUERY_ARG_SQL_SORT_ORDER = "android:query-arg-sql-sort-order";
+ /** {@hide} */
+ public static final String QUERY_ARG_SQL_GROUP_BY = "android:query-arg-sql-group-by";
+ /** {@hide} */
+ public static final String QUERY_ARG_SQL_HAVING = "android:query-arg-sql-having";
+ /** {@hide} */
+ public static final String QUERY_ARG_SQL_LIMIT = "android:query-arg-sql-limit";
+
/**
* Specifies the list of columns against which to sort results. When first column values
* are identical, records are then sorted based on second column values, and so on.
diff --git a/core/java/android/content/ContentValues.java b/core/java/android/content/ContentValues.java
index 6f93798..93fa403 100644
--- a/core/java/android/content/ContentValues.java
+++ b/core/java/android/content/ContentValues.java
@@ -18,6 +18,7 @@
import android.os.Parcel;
import android.os.Parcelable;
+import android.util.ArrayMap;
import android.util.Log;
import java.util.ArrayList;
@@ -32,16 +33,20 @@
public final class ContentValues implements Parcelable {
public static final String TAG = "ContentValues";
- /** Holds the actual values */
+ /**
+ * @hide
+ * @deprecated kept around for lame people doing reflection
+ */
+ @Deprecated
private HashMap<String, Object> mValues;
+ private final ArrayMap<String, Object> mMap;
+
/**
* Creates an empty set of values using the default initial size
*/
public ContentValues() {
- // Choosing a default size of 8 based on analysis of typical
- // consumption by applications.
- mValues = new HashMap<String, Object>(8);
+ mMap = new ArrayMap<>();
}
/**
@@ -50,7 +55,7 @@
* @param size the initial size of the set of values
*/
public ContentValues(int size) {
- mValues = new HashMap<String, Object>(size, 1.0f);
+ mMap = new ArrayMap<>(size);
}
/**
@@ -59,18 +64,23 @@
* @param from the values to copy
*/
public ContentValues(ContentValues from) {
- mValues = new HashMap<String, Object>(from.mValues);
+ mMap = new ArrayMap<>(from.mMap);
}
/**
- * Creates a set of values copied from the given HashMap. This is used
- * by the Parcel unmarshalling code.
- *
- * @param values the values to start with
- * {@hide}
+ * @hide
+ * @deprecated kept around for lame people doing reflection
*/
- private ContentValues(HashMap<String, Object> values) {
- mValues = values;
+ @Deprecated
+ private ContentValues(HashMap<String, Object> from) {
+ mMap = new ArrayMap<>();
+ mMap.putAll(from);
+ }
+
+ /** {@hide} */
+ private ContentValues(Parcel in) {
+ mMap = new ArrayMap<>(in.readInt());
+ in.readArrayMap(mMap, null);
}
@Override
@@ -78,12 +88,17 @@
if (!(object instanceof ContentValues)) {
return false;
}
- return mValues.equals(((ContentValues) object).mValues);
+ return mMap.equals(((ContentValues) object).mMap);
+ }
+
+ /** {@hide} */
+ public ArrayMap<String, Object> getValues() {
+ return mMap;
}
@Override
public int hashCode() {
- return mValues.hashCode();
+ return mMap.hashCode();
}
/**
@@ -93,7 +108,7 @@
* @param value the data for the value to put
*/
public void put(String key, String value) {
- mValues.put(key, value);
+ mMap.put(key, value);
}
/**
@@ -102,7 +117,7 @@
* @param other the ContentValues from which to copy
*/
public void putAll(ContentValues other) {
- mValues.putAll(other.mValues);
+ mMap.putAll(other.mMap);
}
/**
@@ -112,7 +127,7 @@
* @param value the data for the value to put
*/
public void put(String key, Byte value) {
- mValues.put(key, value);
+ mMap.put(key, value);
}
/**
@@ -122,7 +137,7 @@
* @param value the data for the value to put
*/
public void put(String key, Short value) {
- mValues.put(key, value);
+ mMap.put(key, value);
}
/**
@@ -132,7 +147,7 @@
* @param value the data for the value to put
*/
public void put(String key, Integer value) {
- mValues.put(key, value);
+ mMap.put(key, value);
}
/**
@@ -142,7 +157,7 @@
* @param value the data for the value to put
*/
public void put(String key, Long value) {
- mValues.put(key, value);
+ mMap.put(key, value);
}
/**
@@ -152,7 +167,7 @@
* @param value the data for the value to put
*/
public void put(String key, Float value) {
- mValues.put(key, value);
+ mMap.put(key, value);
}
/**
@@ -162,7 +177,7 @@
* @param value the data for the value to put
*/
public void put(String key, Double value) {
- mValues.put(key, value);
+ mMap.put(key, value);
}
/**
@@ -172,7 +187,7 @@
* @param value the data for the value to put
*/
public void put(String key, Boolean value) {
- mValues.put(key, value);
+ mMap.put(key, value);
}
/**
@@ -182,7 +197,7 @@
* @param value the data for the value to put
*/
public void put(String key, byte[] value) {
- mValues.put(key, value);
+ mMap.put(key, value);
}
/**
@@ -191,7 +206,7 @@
* @param key the name of the value to make null
*/
public void putNull(String key) {
- mValues.put(key, null);
+ mMap.put(key, null);
}
/**
@@ -200,7 +215,7 @@
* @return the number of values
*/
public int size() {
- return mValues.size();
+ return mMap.size();
}
/**
@@ -211,7 +226,7 @@
* TODO: consider exposing this new method publicly
*/
public boolean isEmpty() {
- return mValues.isEmpty();
+ return mMap.isEmpty();
}
/**
@@ -220,14 +235,14 @@
* @param key the name of the value to remove
*/
public void remove(String key) {
- mValues.remove(key);
+ mMap.remove(key);
}
/**
* Removes all values.
*/
public void clear() {
- mValues.clear();
+ mMap.clear();
}
/**
@@ -237,7 +252,7 @@
* @return {@code true} if the value is present, {@code false} otherwise
*/
public boolean containsKey(String key) {
- return mValues.containsKey(key);
+ return mMap.containsKey(key);
}
/**
@@ -249,7 +264,7 @@
* was previously added with the given {@code key}
*/
public Object get(String key) {
- return mValues.get(key);
+ return mMap.get(key);
}
/**
@@ -259,7 +274,7 @@
* @return the String for the value
*/
public String getAsString(String key) {
- Object value = mValues.get(key);
+ Object value = mMap.get(key);
return value != null ? value.toString() : null;
}
@@ -270,7 +285,7 @@
* @return the Long value, or {@code null} if the value is missing or cannot be converted
*/
public Long getAsLong(String key) {
- Object value = mValues.get(key);
+ Object value = mMap.get(key);
try {
return value != null ? ((Number) value).longValue() : null;
} catch (ClassCastException e) {
@@ -295,7 +310,7 @@
* @return the Integer value, or {@code null} if the value is missing or cannot be converted
*/
public Integer getAsInteger(String key) {
- Object value = mValues.get(key);
+ Object value = mMap.get(key);
try {
return value != null ? ((Number) value).intValue() : null;
} catch (ClassCastException e) {
@@ -320,7 +335,7 @@
* @return the Short value, or {@code null} if the value is missing or cannot be converted
*/
public Short getAsShort(String key) {
- Object value = mValues.get(key);
+ Object value = mMap.get(key);
try {
return value != null ? ((Number) value).shortValue() : null;
} catch (ClassCastException e) {
@@ -345,7 +360,7 @@
* @return the Byte value, or {@code null} if the value is missing or cannot be converted
*/
public Byte getAsByte(String key) {
- Object value = mValues.get(key);
+ Object value = mMap.get(key);
try {
return value != null ? ((Number) value).byteValue() : null;
} catch (ClassCastException e) {
@@ -370,7 +385,7 @@
* @return the Double value, or {@code null} if the value is missing or cannot be converted
*/
public Double getAsDouble(String key) {
- Object value = mValues.get(key);
+ Object value = mMap.get(key);
try {
return value != null ? ((Number) value).doubleValue() : null;
} catch (ClassCastException e) {
@@ -395,7 +410,7 @@
* @return the Float value, or {@code null} if the value is missing or cannot be converted
*/
public Float getAsFloat(String key) {
- Object value = mValues.get(key);
+ Object value = mMap.get(key);
try {
return value != null ? ((Number) value).floatValue() : null;
} catch (ClassCastException e) {
@@ -420,7 +435,7 @@
* @return the Boolean value, or {@code null} if the value is missing or cannot be converted
*/
public Boolean getAsBoolean(String key) {
- Object value = mValues.get(key);
+ Object value = mMap.get(key);
try {
return (Boolean) value;
} catch (ClassCastException e) {
@@ -448,7 +463,7 @@
* {@code byte[]}
*/
public byte[] getAsByteArray(String key) {
- Object value = mValues.get(key);
+ Object value = mMap.get(key);
if (value instanceof byte[]) {
return (byte[]) value;
} else {
@@ -462,7 +477,7 @@
* @return a set of all of the keys and values
*/
public Set<Map.Entry<String, Object>> valueSet() {
- return mValues.entrySet();
+ return mMap.entrySet();
}
/**
@@ -471,30 +486,31 @@
* @return a set of all of the keys
*/
public Set<String> keySet() {
- return mValues.keySet();
+ return mMap.keySet();
}
public static final Parcelable.Creator<ContentValues> CREATOR =
new Parcelable.Creator<ContentValues>() {
- @SuppressWarnings({"deprecation", "unchecked"})
+ @Override
public ContentValues createFromParcel(Parcel in) {
- // TODO - what ClassLoader should be passed to readHashMap?
- HashMap<String, Object> values = in.readHashMap(null);
- return new ContentValues(values);
+ return new ContentValues(in);
}
+ @Override
public ContentValues[] newArray(int size) {
return new ContentValues[size];
}
};
+ @Override
public int describeContents() {
return 0;
}
- @SuppressWarnings("deprecation")
+ @Override
public void writeToParcel(Parcel parcel, int flags) {
- parcel.writeMap(mValues);
+ parcel.writeInt(mMap.size());
+ parcel.writeArrayMap(mMap);
}
/**
@@ -503,7 +519,7 @@
*/
@Deprecated
public void putStringArrayList(String key, ArrayList<String> value) {
- mValues.put(key, value);
+ mMap.put(key, value);
}
/**
@@ -513,7 +529,7 @@
@SuppressWarnings("unchecked")
@Deprecated
public ArrayList<String> getStringArrayList(String key) {
- return (ArrayList<String>) mValues.get(key);
+ return (ArrayList<String>) mMap.get(key);
}
/**
@@ -523,7 +539,7 @@
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
- for (String name : mValues.keySet()) {
+ for (String name : mMap.keySet()) {
String value = getAsString(name);
if (sb.length() > 0) sb.append(" ");
sb.append(name + "=" + value);
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index c0cfb90..c515bce 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3005,6 +3005,7 @@
NSD_SERVICE,
AUDIO_SERVICE,
FINGERPRINT_SERVICE,
+ //@hide: FACE_SERVICE,
MEDIA_ROUTER_SERVICE,
TELEPHONY_SERVICE,
TELEPHONY_SUBSCRIPTION_SERVICE,
@@ -3060,6 +3061,7 @@
CROSS_PROFILE_APPS_SERVICE,
//@hide: SYSTEM_UPDATE_SERVICE,
//@hide: TIME_DETECTOR_SERVICE,
+ //@hide: TIME_ZONE_DETECTOR_SERVICE,
})
@Retention(RetentionPolicy.SOURCE)
public @interface ServiceName {}
@@ -3652,6 +3654,18 @@
/**
* Use with {@link #getSystemService(String)} to retrieve a
+ * Use with {@link #getSystemService} to retrieve a
+ * {@link android.hardware.face.FaceManager} for handling management
+ * of face authentication.
+ *
+ * @hide
+ * @see #getSystemService
+ * @see android.hardware.face.FaceManager
+ */
+ public static final String FACE_SERVICE = "face";
+
+ /**
+ * Use with {@link #getSystemService} to retrieve a
* {@link android.media.MediaRouter} for controlling and managing
* routing of media.
*
@@ -4231,6 +4245,15 @@
public static final String TIME_DETECTOR_SERVICE = "time_detector";
/**
+ * Use with {@link #getSystemService(String)} to retrieve an
+ * {@link android.app.timezonedetector.ITimeZoneDetectorService}.
+ * @hide
+ *
+ * @see #getSystemService(String)
+ */
+ public static final String TIME_ZONE_DETECTOR_SERVICE = "time_zone_detector";
+
+ /**
* Determine whether the given permission is allowed for a particular
* process and user ID running in the system.
*
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 631c4b7..78738e9 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -5372,19 +5372,23 @@
public static final int FLAG_GRANT_PREFIX_URI_PERMISSION = 0x00000080;
/**
- * Internal flag used to indicate that a system component has done their
- * homework and verified that they correctly handle packages and components
- * that come and go over time. In particular:
- * <ul>
- * <li>Apps installed on external storage, which will appear to be
- * uninstalled while the the device is ejected.
- * <li>Apps with encryption unaware components, which will appear to not
- * exist while the device is locked.
- * </ul>
- *
- * @hide
+ * Flag used to automatically match intents based on their Direct Boot
+ * awareness and the current user state.
+ * <p>
+ * Since the default behavior is to automatically apply the current user
+ * state, this is effectively a sentinel value that doesn't change the
+ * output of any queries based on its presence or absence.
+ * <p>
+ * Instead, this value can be useful in conjunction with
+ * {@link android.os.StrictMode.VmPolicy.Builder#detectImplicitDirectBoot()}
+ * to detect when a caller is relying on implicit automatic matching,
+ * instead of confirming the explicit behavior they want.
*/
- public static final int FLAG_DEBUG_TRIAGED_MISSING = 0x00000100;
+ public static final int FLAG_DIRECT_BOOT_AUTO = 0x00000100;
+
+ /** {@hide} */
+ @Deprecated
+ public static final int FLAG_DEBUG_TRIAGED_MISSING = FLAG_DIRECT_BOOT_AUTO;
/**
* Internal flag used to indicate ephemeral applications should not be
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index d65e051..13b67fd 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -477,6 +477,7 @@
*
* {@hide}
*/
+ @TestApi
public static final int PRIVATE_FLAG_PRIVILEGED = 1<<3;
/**
@@ -649,6 +650,7 @@
* Private/hidden flags. See {@code PRIVATE_FLAG_...} constants.
* @hide
*/
+ @TestApi
public @ApplicationInfoPrivateFlags int privateFlags;
/**
@@ -1120,6 +1122,9 @@
/** @hide */
public String[] splitClassLoaderNames;
+ /** @hide */
+ public boolean hiddenUntilInstalled;
+
/**
* Represents the default policy. The actual policy used will depend on other properties of
* the application, e.g. the target SDK version.
@@ -1460,6 +1465,7 @@
compileSdkVersion = orig.compileSdkVersion;
compileSdkVersionCodename = orig.compileSdkVersionCodename;
mHiddenApiPolicy = orig.mHiddenApiPolicy;
+ hiddenUntilInstalled = orig.hiddenUntilInstalled;
}
public String toString() {
@@ -1534,6 +1540,7 @@
dest.writeString(compileSdkVersionCodename);
dest.writeString(appComponentFactory);
dest.writeInt(mHiddenApiPolicy);
+ dest.writeInt(hiddenUntilInstalled ? 1 : 0);
}
public static final Parcelable.Creator<ApplicationInfo> CREATOR
@@ -1605,6 +1612,7 @@
compileSdkVersionCodename = source.readString();
appComponentFactory = source.readString();
mHiddenApiPolicy = source.readInt();
+ hiddenUntilInstalled = source.readInt() != 0;
}
/**
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index c988fa9..bc5b32c 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -598,6 +598,9 @@
boolean setApplicationHiddenSettingAsUser(String packageName, boolean hidden, int userId);
boolean getApplicationHiddenSettingAsUser(String packageName, int userId);
+ void setSystemAppHiddenUntilInstalled(String packageName, boolean hidden);
+ boolean setSystemAppInstallState(String packageName, boolean installed, int userId);
+
IPackageInstaller getPackageInstaller();
boolean setBlockUninstallForUser(String packageName, boolean blockUninstall, int userId);
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 43b6984..ce551ee 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -147,6 +147,7 @@
GET_DISABLED_COMPONENTS,
GET_DISABLED_UNTIL_USED_COMPONENTS,
GET_UNINSTALLED_PACKAGES,
+ MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS,
})
@Retention(RetentionPolicy.SOURCE)
public @interface PackageInfoFlags {}
@@ -164,6 +165,7 @@
MATCH_STATIC_SHARED_LIBRARIES,
GET_DISABLED_UNTIL_USED_COMPONENTS,
GET_UNINSTALLED_PACKAGES,
+ MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS,
})
@Retention(RetentionPolicy.SOURCE)
public @interface ApplicationInfoFlags {}
@@ -177,6 +179,7 @@
MATCH_DEFAULT_ONLY,
MATCH_DISABLED_COMPONENTS,
MATCH_DISABLED_UNTIL_USED_COMPONENTS,
+ MATCH_DIRECT_BOOT_AUTO,
MATCH_DIRECT_BOOT_AWARE,
MATCH_DIRECT_BOOT_UNAWARE,
MATCH_SYSTEM_ONLY,
@@ -200,6 +203,7 @@
MATCH_DISABLED_COMPONENTS,
MATCH_DISABLED_UNTIL_USED_COMPONENTS,
MATCH_DEFAULT_ONLY,
+ MATCH_DIRECT_BOOT_AUTO,
MATCH_DIRECT_BOOT_AWARE,
MATCH_DIRECT_BOOT_UNAWARE,
MATCH_SYSTEM_ONLY,
@@ -504,22 +508,35 @@
public static final int GET_SIGNING_CERTIFICATES = 0x08000000;
/**
- * Internal flag used to indicate that a system component has done their
- * homework and verified that they correctly handle packages and components
- * that come and go over time. In particular:
+ * Querying flag: automatically match components based on their Direct Boot
+ * awareness and the current user state.
+ * <p>
+ * Since the default behavior is to automatically apply the current user
+ * state, this is effectively a sentinel value that doesn't change the
+ * output of any queries based on its presence or absence.
+ * <p>
+ * Instead, this value can be useful in conjunction with
+ * {@link android.os.StrictMode.VmPolicy.Builder#detectImplicitDirectBoot()}
+ * to detect when a caller is relying on implicit automatic matching,
+ * instead of confirming the explicit behavior they want, using a
+ * combination of these flags:
* <ul>
- * <li>Apps installed on external storage, which will appear to be
- * uninstalled while the the device is ejected.
- * <li>Apps with encryption unaware components, which will appear to not
- * exist while the device is locked.
+ * <li>{@link #MATCH_DIRECT_BOOT_AWARE}
+ * <li>{@link #MATCH_DIRECT_BOOT_UNAWARE}
+ * <li>{@link #MATCH_DIRECT_BOOT_AUTO}
* </ul>
- *
- * @see #MATCH_UNINSTALLED_PACKAGES
- * @see #MATCH_DIRECT_BOOT_AWARE
- * @see #MATCH_DIRECT_BOOT_UNAWARE
+ */
+ public static final int MATCH_DIRECT_BOOT_AUTO = 0x10000000;
+
+ /** @hide */
+ @Deprecated
+ public static final int MATCH_DEBUG_TRIAGED_MISSING = MATCH_DIRECT_BOOT_AUTO;
+
+ /**
+ * Internal flag used to indicate that a package is a hidden system app.
* @hide
*/
- public static final int MATCH_DEBUG_TRIAGED_MISSING = 0x10000000;
+ public static final int MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS = 0x20000000;
/**
* Flag for {@link #addCrossProfileIntentFilter}: if this flag is set: when
@@ -2245,12 +2262,20 @@
/**
* Feature for {@link #getSystemAvailableFeatures} and
* {@link #hasSystemFeature}: The device has biometric hardware to detect a fingerprint.
- */
+ */
@SdkConstant(SdkConstantType.FEATURE)
public static final String FEATURE_FINGERPRINT = "android.hardware.fingerprint";
/**
* Feature for {@link #getSystemAvailableFeatures} and
+ * {@link #hasSystemFeature}: The device has biometric hardware to perform face authentication.
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_FACE = "android.hardware.face";
+
+ /**
+ * Feature for {@link #getSystemAvailableFeatures} and
* {@link #hasSystemFeature}: The device supports portrait orientation
* screens. For backwards compatibility, you can assume that if neither
* this nor {@link #FEATURE_SCREEN_LANDSCAPE} is set then the device supports
@@ -2912,6 +2937,7 @@
*
* @hide
*/
+ @TestApi
public static final String SYSTEM_SHARED_LIBRARY_SERVICES = "android.ext.services";
/**
@@ -2923,6 +2949,7 @@
*
* @hide
*/
+ @TestApi
public static final String SYSTEM_SHARED_LIBRARY_SHARED = "android.ext.shared";
/**
@@ -3555,6 +3582,7 @@
*
* @hide
*/
+ @TestApi
@SystemApi
@RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS)
public abstract void grantRuntimePermission(@NonNull String packageName,
@@ -3581,6 +3609,7 @@
*
* @hide
*/
+ @TestApi
@SystemApi
@RequiresPermission(android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS)
public abstract void revokeRuntimePermission(@NonNull String packageName,
@@ -4847,7 +4876,8 @@
* on the system for other users, also install it for the specified user.
* @hide
*/
- @RequiresPermission(anyOf = {
+ @RequiresPermission(anyOf = {
+ Manifest.permission.INSTALL_EXISTING_PACKAGES,
Manifest.permission.INSTALL_PACKAGES,
Manifest.permission.INTERACT_ACROSS_USERS_FULL})
public abstract int installExistingPackageAsUser(String packageName, @UserIdInt int userId)
diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java
index f30b3fe..ee752f8 100644
--- a/core/java/android/content/pm/PackageManagerInternal.java
+++ b/core/java/android/content/pm/PackageManagerInternal.java
@@ -29,9 +29,12 @@
import android.os.PersistableBundle;
import android.util.SparseArray;
+import com.android.internal.util.function.TriFunction;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.List;
+import java.util.function.BiFunction;
/**
* Package manager local system service interface.
@@ -64,6 +67,32 @@
void onPackageRemoved(@NonNull String packageName);
}
+ /** Interface to override permission checks via composition */
+ public interface CheckPermissionDelegate {
+ /**
+ * Allows overriding check permission behavior.
+ *
+ * @param permName The permission to check.
+ * @param pkgName The package for which to check.
+ * @param userId The user for which to check.
+ * @param superImpl The super implementation.
+ * @return The check permission result.
+ */
+ int checkPermission(String permName, String pkgName, int userId,
+ TriFunction<String, String, Integer, Integer> superImpl);
+
+ /**
+ * Allows overriding check UID permission behavior.
+ *
+ * @param permName The permission to check.
+ * @param uid The UID for which to check.
+ * @param superImpl The super implementation.
+ * @return The check permission result.
+ */
+ int checkUidPermission(String permName, int uid,
+ BiFunction<String, Integer, Integer> superImpl);
+ }
+
/**
* Provider for package names.
*/
@@ -628,4 +657,18 @@
*/
public abstract boolean hasSignatureCapability(int serverUid, int clientUid,
@PackageParser.SigningDetails.CertCapabilities int capability);
+
+ /**
+ * Get the delegate to influence permission checking.
+ *
+ * @return The delegate instance or null to clear.
+ */
+ public abstract @Nullable CheckPermissionDelegate getCheckPermissionDelegate();
+
+ /**
+ * Set a delegate to influence permission checking.
+ *
+ * @param delegate A delegate instance or null to clear.
+ */
+ public abstract void setCheckPermissionDelegate(@Nullable CheckPermissionDelegate delegate);
}
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 950070e..54d383a 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -646,11 +646,19 @@
*/
private static boolean checkUseInstalledOrHidden(int flags, PackageUserState state,
ApplicationInfo appInfo) {
+ // Returns false if the package is hidden system app until installed.
+ if ((flags & PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS) == 0
+ && !state.installed
+ && appInfo != null && appInfo.hiddenUntilInstalled) {
+ return false;
+ }
+
// If available for the target user, or trying to match uninstalled packages and it's
// a system app.
return state.isAvailable(flags)
|| (appInfo != null && appInfo.isSystemApp()
- && (flags & PackageManager.MATCH_KNOWN_PACKAGES) != 0);
+ && ((flags & PackageManager.MATCH_KNOWN_PACKAGES) != 0
+ || (flags & PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS) != 0));
}
public static boolean isAvailable(PackageUserState state) {
@@ -2206,10 +2214,10 @@
com.android.internal.R.styleable.AndroidManifestUsesSdk_minSdkVersion);
if (val != null) {
if (val.type == TypedValue.TYPE_STRING && val.string != null) {
- targetCode = minCode = val.string.toString();
+ minCode = val.string.toString();
} else {
// If it's not a string, it's an integer.
- targetVers = minVers = val.data;
+ minVers = val.data;
}
}
@@ -2225,6 +2233,9 @@
// If it's not a string, it's an integer.
targetVers = val.data;
}
+ } else {
+ targetVers = minVers;
+ targetCode = minCode;
}
sa.recycle();
@@ -3097,6 +3108,14 @@
0);
perm.info.requestRes = sa.getResourceId(
com.android.internal.R.styleable.AndroidManifestPermissionGroup_request, 0);
+ perm.info.requestDetailResourceId = sa.getResourceId(
+ com.android.internal.R.styleable.AndroidManifestPermissionGroup_requestDetail, 0);
+ perm.info.backgroundRequestResourceId = sa.getResourceId(
+ com.android.internal.R.styleable.AndroidManifestPermissionGroup_backgroundRequest,
+ 0);
+ perm.info.backgroundRequestDetailResourceId = sa.getResourceId(
+ com.android.internal.R.styleable
+ .AndroidManifestPermissionGroup_backgroundRequestDetail, 0);
perm.info.flags = sa.getInt(
com.android.internal.R.styleable.AndroidManifestPermissionGroup_permissionGroupFlags, 0);
perm.info.priority = sa.getInt(
@@ -3151,6 +3170,19 @@
perm.info.requestRes = sa.getResourceId(
com.android.internal.R.styleable.AndroidManifestPermission_request, 0);
+ if (sa.hasValue(
+ com.android.internal.R.styleable.AndroidManifestPermission_backgroundPermission)) {
+ if ("android".equals(owner.packageName)) {
+ perm.info.backgroundPermission = sa.getNonResourceString(
+ com.android.internal.R.styleable
+ .AndroidManifestPermission_backgroundPermission);
+ } else {
+ Slog.w(TAG, owner.packageName + " defines permission '" + perm.info.name
+ + "' with a background permission. Only the 'android' package can do "
+ + "that.");
+ }
+ }
+
perm.info.protectionLevel = sa.getInt(
com.android.internal.R.styleable.AndroidManifestPermission_protectionLevel,
PermissionInfo.PROTECTION_NORMAL);
diff --git a/core/java/android/content/pm/PermissionGroupInfo.java b/core/java/android/content/pm/PermissionGroupInfo.java
index 7c4478d..8cf66d8 100644
--- a/core/java/android/content/pm/PermissionGroupInfo.java
+++ b/core/java/android/content/pm/PermissionGroupInfo.java
@@ -45,6 +45,42 @@
public @StringRes int requestRes;
/**
+ * A string resource identifier (in the package's resources) used as subtitle when requesting
+ * only access while in the foreground.
+ *
+ * From the "requestDetail" attribute or, if not set, {@link
+ * android.content.res.ResourceId#ID_NULL}.
+ *
+ * @hide
+ */
+ @SystemApi
+ public @StringRes int requestDetailResourceId;
+
+ /**
+ * A string resource identifier (in the package's resources) used when requesting background
+ * access. Also used when requesting both foreground and background access.
+ *
+ * From the "backgroundRequest" attribute or, if not set, {@link
+ * android.content.res.ResourceId#ID_NULL}.
+ *
+ * @hide
+ */
+ @SystemApi
+ public @StringRes int backgroundRequestResourceId;
+
+ /**
+ * A string resource identifier (in the package's resources) used as subtitle when requesting
+ * background access.
+ *
+ * From the "backgroundRequestDetail" attribute or, if not set, {@link
+ * android.content.res.ResourceId#ID_NULL}.
+ *
+ * @hide
+ */
+ @SystemApi
+ public @StringRes int backgroundRequestDetailResourceId;
+
+ /**
* The description string provided in the AndroidManifest file, if any. You
* probably don't want to use this, since it will be null if the description
* is in a resource. You probably want
@@ -76,6 +112,9 @@
super(orig);
descriptionRes = orig.descriptionRes;
requestRes = orig.requestRes;
+ requestDetailResourceId = orig.requestDetailResourceId;
+ backgroundRequestResourceId = orig.backgroundRequestResourceId;
+ backgroundRequestDetailResourceId = orig.backgroundRequestDetailResourceId;
nonLocalizedDescription = orig.nonLocalizedDescription;
flags = orig.flags;
priority = orig.priority;
@@ -119,6 +158,9 @@
super.writeToParcel(dest, parcelableFlags);
dest.writeInt(descriptionRes);
dest.writeInt(requestRes);
+ dest.writeInt(requestDetailResourceId);
+ dest.writeInt(backgroundRequestResourceId);
+ dest.writeInt(backgroundRequestDetailResourceId);
TextUtils.writeToParcel(nonLocalizedDescription, dest, parcelableFlags);
dest.writeInt(flags);
dest.writeInt(priority);
@@ -138,6 +180,9 @@
super(source);
descriptionRes = source.readInt();
requestRes = source.readInt();
+ requestDetailResourceId = source.readInt();
+ backgroundRequestResourceId = source.readInt();
+ backgroundRequestDetailResourceId = source.readInt();
nonLocalizedDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
flags = source.readInt();
priority = source.readInt();
diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java
index 938409a..535ef00 100644
--- a/core/java/android/content/pm/PermissionInfo.java
+++ b/core/java/android/content/pm/PermissionInfo.java
@@ -285,6 +285,21 @@
public int requestRes;
/**
+ * Some permissions only grant access while the app is in foreground. Some of these permissions
+ * allow to add background capabilities by adding another permission.
+ *
+ * If this is such a permission, this is the name of the permission adding the background
+ * access.
+ *
+ * From the "backgroundPermission" attribute or, if not set null
+ *
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ public String backgroundPermission;
+
+ /**
* The description string provided in the AndroidManifest file, if any. You
* probably don't want to use this, since it will be null if the description
* is in a resource. You probably want
@@ -373,6 +388,7 @@
protectionLevel = orig.protectionLevel;
flags = orig.flags;
group = orig.group;
+ backgroundPermission = orig.backgroundPermission;
descriptionRes = orig.descriptionRes;
requestRes = orig.requestRes;
nonLocalizedDescription = orig.nonLocalizedDescription;
@@ -436,6 +452,7 @@
dest.writeInt(protectionLevel);
dest.writeInt(flags);
dest.writeString(group);
+ dest.writeString(backgroundPermission);
dest.writeInt(descriptionRes);
dest.writeInt(requestRes);
TextUtils.writeToParcel(nonLocalizedDescription, dest, parcelableFlags);
@@ -475,6 +492,7 @@
protectionLevel = source.readInt();
flags = source.readInt();
group = source.readString();
+ backgroundPermission = source.readString();
descriptionRes = source.readInt();
requestRes = source.readInt();
nonLocalizedDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index 7adea6a8..ef0dce3 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -642,8 +642,7 @@
* tool. This integer encodes the package, type, and resource
* entry. The value 0 is an invalid identifier.
*
- * @return Resource dimension value multiplied by the appropriate
- * metric.
+ * @return Resource dimension value multiplied by the appropriate metric to convert to pixels.
*
* @throws NotFoundException Throws NotFoundException if the given ID does not exist.
*
diff --git a/core/java/android/database/OWNERS b/core/java/android/database/OWNERS
index 84e81e4..592a852 100644
--- a/core/java/android/database/OWNERS
+++ b/core/java/android/database/OWNERS
@@ -1,2 +1,3 @@
-fkupolov@google.com
-omakoto@google.com
\ No newline at end of file
+omakoto@google.com
+jsharkey@google.com
+yamasani@google.com
\ No newline at end of file
diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java
index 6adae25..a4b989a 100644
--- a/core/java/android/database/sqlite/SQLiteDatabase.java
+++ b/core/java/android/database/sqlite/SQLiteDatabase.java
@@ -189,7 +189,8 @@
*/
public static final int CONFLICT_NONE = 0;
- private static final String[] CONFLICT_VALUES = new String[]
+ /** {@hide} */
+ public static final String[] CONFLICT_VALUES = new String[]
{"", " OR ROLLBACK ", " OR ABORT ", " OR FAIL ", " OR IGNORE ", " OR REPLACE "};
/**
@@ -1748,7 +1749,8 @@
executeSql(sql, bindArgs);
}
- private int executeSql(String sql, Object[] bindArgs) throws SQLException {
+ /** {@hide} */
+ public int executeSql(String sql, Object[] bindArgs) throws SQLException {
acquireReference();
try {
final int statementType = DatabaseUtils.getSqlStatementType(sql);
diff --git a/core/java/android/database/sqlite/SQLiteStatementBuilder.java b/core/java/android/database/sqlite/SQLiteStatementBuilder.java
new file mode 100644
index 0000000..e2efb2f
--- /dev/null
+++ b/core/java/android/database/sqlite/SQLiteStatementBuilder.java
@@ -0,0 +1,1036 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.database.sqlite;
+
+import static android.content.ContentResolver.QUERY_ARG_SQL_GROUP_BY;
+import static android.content.ContentResolver.QUERY_ARG_SQL_HAVING;
+import static android.content.ContentResolver.QUERY_ARG_SQL_LIMIT;
+import static android.content.ContentResolver.QUERY_ARG_SQL_SELECTION;
+import static android.content.ContentResolver.QUERY_ARG_SQL_SELECTION_ARGS;
+import static android.content.ContentResolver.QUERY_ARG_SQL_SORT_ORDER;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.database.DatabaseUtils;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.os.OperationCanceledException;
+import android.provider.BaseColumns;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.Log;
+
+import com.android.internal.util.ArrayUtils;
+
+import dalvik.system.VMRuntime;
+
+import libcore.util.EmptyArray;
+
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Objects;
+import java.util.Set;
+import java.util.regex.Pattern;
+
+/**
+ * This is a convenience class that helps build SQL queries to be sent to
+ * {@link SQLiteDatabase} objects.
+ * @hide
+ */
+public class SQLiteStatementBuilder {
+ private static final String TAG = "SQLiteStatementBuilder";
+ private static final Pattern sLimitPattern =
+ Pattern.compile("\\s*\\d+\\s*(,\\s*\\d+\\s*)?");
+
+ private Map<String, String> mProjectionMap = null;
+ private String mTables = "";
+ private StringBuilder mWhereClause = null; // lazily created
+ private String[] mWhereArgs = EmptyArray.STRING;
+ private boolean mDistinct;
+ private SQLiteDatabase.CursorFactory mFactory;
+ private boolean mStrict;
+
+ public SQLiteStatementBuilder() {
+ mDistinct = false;
+ mFactory = null;
+ }
+
+ /**
+ * Mark the query as DISTINCT.
+ *
+ * @param distinct if true the query is DISTINCT, otherwise it isn't
+ */
+ public void setDistinct(boolean distinct) {
+ mDistinct = distinct;
+ }
+
+ /**
+ * Returns the list of tables being queried
+ *
+ * @return the list of tables being queried
+ */
+ public String getTables() {
+ return mTables;
+ }
+
+ /**
+ * Sets the list of tables to query. Multiple tables can be specified to perform a join.
+ * For example:
+ * setTables("foo, bar")
+ * setTables("foo LEFT OUTER JOIN bar ON (foo.id = bar.foo_id)")
+ *
+ * @param inTables the list of tables to query on
+ */
+ public void setTables(String inTables) {
+ mTables = inTables;
+ }
+
+ /** {@hide} */
+ public @Nullable String getWhere() {
+ return (mWhereClause != null) ? mWhereClause.toString() : null;
+ }
+
+ /** {@hide} */
+ public String[] getWhereArgs() {
+ return mWhereArgs;
+ }
+
+ /**
+ * Append a chunk to the {@code WHERE} clause of the query. All chunks
+ * appended are surrounded by parenthesis and {@code AND}ed with the
+ * selection passed to {@link #query}. The final {@code WHERE} clause looks
+ * like:
+ *
+ * <pre>
+ * WHERE (<append chunk 1><append chunk2>) AND (<query() selection parameter>)
+ * </pre>
+ *
+ * @param inWhere the chunk of text to append to the {@code WHERE} clause.
+ */
+ public void appendWhere(@NonNull CharSequence inWhere) {
+ appendWhere(inWhere, EmptyArray.STRING);
+ }
+
+ /**
+ * Append a chunk to the {@code WHERE} clause of the query. All chunks
+ * appended are surrounded by parenthesis and {@code AND}ed with the
+ * selection passed to {@link #query}. The final {@code WHERE} clause looks
+ * like:
+ *
+ * <pre>
+ * WHERE (<append chunk 1><append chunk2>) AND (<query() selection parameter>)
+ * </pre>
+ *
+ * @param inWhere the chunk of text to append to the {@code WHERE} clause.
+ * @param inWhereArgs list of arguments to be bound to any '?' occurrences
+ * in the where clause.
+ */
+ public void appendWhere(@NonNull CharSequence inWhere, String... inWhereArgs) {
+ if (mWhereClause == null) {
+ mWhereClause = new StringBuilder(inWhere.length() + 16);
+ }
+ mWhereClause.append(inWhere);
+ mWhereArgs = ArrayUtils.concat(String.class, mWhereArgs, inWhereArgs);
+ }
+
+ /**
+ * Append a standalone expression to the {@code WHERE} clause of this query.
+ * <p>
+ * This method differs from {@link #appendWhere(CharSequence)} in that it
+ * automatically appends {@code AND} to any existing {@code WHERE} clause
+ * already under construction before appending the given standalone
+ * expression.
+ *
+ * @param inWhere the standalone expression to append to the {@code WHERE}
+ * clause. It will be wrapped in parentheses when it's appended.
+ */
+ public void appendWhereExpression(@NonNull CharSequence inWhere) {
+ appendWhereExpression(inWhere, EmptyArray.STRING);
+ }
+
+ /**
+ * Append a standalone expression to the {@code WHERE} clause of this query.
+ * <p>
+ * This method differs from {@link #appendWhere(CharSequence)} in that it
+ * automatically appends {@code AND} to any existing {@code WHERE} clause
+ * already under construction before appending the given standalone
+ * expression.
+ *
+ * @param inWhere the standalone expression to append to the {@code WHERE}
+ * clause. It will be wrapped in parentheses when it's appended.
+ * @param inWhereArgs list of arguments to be bound to any '?' occurrences
+ * in the standalone expression.
+ */
+ public void appendWhereExpression(@NonNull CharSequence inWhere, String... inWhereArgs) {
+ if (mWhereClause == null) {
+ mWhereClause = new StringBuilder(inWhere.length() + 16);
+ }
+ if (mWhereClause.length() > 0) {
+ mWhereClause.append(" AND ");
+ }
+ mWhereClause.append('(').append(inWhere).append(')');
+ mWhereArgs = ArrayUtils.concat(String.class, mWhereArgs, inWhereArgs);
+ }
+
+ /**
+ * Append a chunk to the {@code WHERE} clause of the query. All chunks
+ * appended are surrounded by parenthesis and {@code AND}ed with the
+ * selection passed to {@link #query}. The final {@code WHERE} clause looks
+ * like this:
+ *
+ * <pre>
+ * WHERE (<append chunk 1><append chunk2>) AND (<query() selection parameter>)
+ * </pre>
+ *
+ * @param inWhere the chunk of text to append to the {@code WHERE} clause.
+ * It will be escaped to avoid SQL injection attacks.
+ */
+ public void appendWhereEscapeString(@NonNull String inWhere) {
+ appendWhereEscapeString(inWhere, EmptyArray.STRING);
+ }
+
+ /**
+ * Append a chunk to the {@code WHERE} clause of the query. All chunks
+ * appended are surrounded by parenthesis and {@code AND}ed with the
+ * selection passed to {@link #query}. The final {@code WHERE} clause looks
+ * like this:
+ *
+ * <pre>
+ * WHERE (<append chunk 1><append chunk2>) AND (<query() selection parameter>)
+ * </pre>
+ *
+ * @param inWhere the chunk of text to append to the {@code WHERE} clause.
+ * It will be escaped to avoid SQL injection attacks.
+ * @param inWhereArgs list of arguments to be bound to any '?' occurrences
+ * in the where clause.
+ */
+ public void appendWhereEscapeString(@NonNull String inWhere, String... inWhereArgs) {
+ if (mWhereClause == null) {
+ mWhereClause = new StringBuilder(inWhere.length() + 16);
+ }
+ DatabaseUtils.appendEscapedSQLString(mWhereClause, inWhere);
+ mWhereArgs = ArrayUtils.concat(String.class, mWhereArgs, inWhereArgs);
+ }
+
+ /**
+ * Sets the projection map for the query. The projection map maps
+ * from column names that the caller passes into query to database
+ * column names. This is useful for renaming columns as well as
+ * disambiguating column names when doing joins. For example you
+ * could map "name" to "people.name". If a projection map is set
+ * it must contain all column names the user may request, even if
+ * the key and value are the same.
+ *
+ * @param columnMap maps from the user column names to the database column names
+ */
+ public void setProjectionMap(Map<String, String> columnMap) {
+ mProjectionMap = columnMap;
+ }
+
+ /**
+ * Sets the cursor factory to be used for the query. You can use
+ * one factory for all queries on a database but it is normally
+ * easier to specify the factory when doing this query.
+ *
+ * @param factory the factory to use.
+ */
+ public void setCursorFactory(SQLiteDatabase.CursorFactory factory) {
+ mFactory = factory;
+ }
+
+ /**
+ * When set, the selection is verified against malicious arguments.
+ * When using this class to create a statement using
+ * {@link #buildQueryString(boolean, String, String[], String, String, String, String, String)},
+ * non-numeric limits will raise an exception. If a projection map is specified, fields
+ * not in that map will be ignored.
+ * If this class is used to execute the statement directly using
+ * {@link #query(SQLiteDatabase, String[], String, String[], String, String, String)}
+ * or
+ * {@link #query(SQLiteDatabase, String[], String, String[], String, String, String, String)},
+ * additionally also parenthesis escaping selection are caught.
+ *
+ * To summarize: To get maximum protection against malicious third party apps (for example
+ * content provider consumers), make sure to do the following:
+ * <ul>
+ * <li>Set this value to true</li>
+ * <li>Use a projection map</li>
+ * <li>Use one of the query overloads instead of getting the statement as a sql string</li>
+ * </ul>
+ * By default, this value is false.
+ */
+ public void setStrict(boolean strict) {
+ mStrict = strict;
+ }
+
+ /**
+ * Build an SQL query string from the given clauses.
+ *
+ * @param distinct true if you want each row to be unique, false otherwise.
+ * @param tables The table names to compile the query against.
+ * @param columns A list of which columns to return. Passing null will
+ * return all columns, which is discouraged to prevent reading
+ * data from storage that isn't going to be used.
+ * @param where A filter declaring which rows to return, formatted as an SQL
+ * WHERE clause (excluding the WHERE itself). Passing null will
+ * return all rows for the given URL.
+ * @param groupBy A filter declaring how to group rows, formatted as an SQL
+ * GROUP BY clause (excluding the GROUP BY itself). Passing null
+ * will cause the rows to not be grouped.
+ * @param having A filter declare which row groups to include in the cursor,
+ * if row grouping is being used, formatted as an SQL HAVING
+ * clause (excluding the HAVING itself). Passing null will cause
+ * all row groups to be included, and is required when row
+ * grouping is not being used.
+ * @param orderBy How to order the rows, formatted as an SQL ORDER BY clause
+ * (excluding the ORDER BY itself). Passing null will use the
+ * default sort order, which may be unordered.
+ * @param limit Limits the number of rows returned by the query,
+ * formatted as LIMIT clause. Passing null denotes no LIMIT clause.
+ * @return the SQL query string
+ */
+ public static String buildQueryString(
+ boolean distinct, String tables, String[] columns, String where,
+ String groupBy, String having, String orderBy, String limit) {
+ if (TextUtils.isEmpty(groupBy) && !TextUtils.isEmpty(having)) {
+ throw new IllegalArgumentException(
+ "HAVING clauses are only permitted when using a groupBy clause");
+ }
+ if (!TextUtils.isEmpty(limit) && !sLimitPattern.matcher(limit).matches()) {
+ throw new IllegalArgumentException("invalid LIMIT clauses:" + limit);
+ }
+
+ StringBuilder query = new StringBuilder(120);
+
+ query.append("SELECT ");
+ if (distinct) {
+ query.append("DISTINCT ");
+ }
+ if (columns != null && columns.length != 0) {
+ appendColumns(query, columns);
+ } else {
+ query.append("* ");
+ }
+ query.append("FROM ");
+ query.append(tables);
+ appendClause(query, " WHERE ", where);
+ appendClause(query, " GROUP BY ", groupBy);
+ appendClause(query, " HAVING ", having);
+ appendClause(query, " ORDER BY ", orderBy);
+ appendClause(query, " LIMIT ", limit);
+
+ return query.toString();
+ }
+
+ private static void appendClause(StringBuilder s, String name, String clause) {
+ if (!TextUtils.isEmpty(clause)) {
+ s.append(name);
+ s.append(clause);
+ }
+ }
+
+ /**
+ * Add the names that are non-null in columns to s, separating
+ * them with commas.
+ */
+ public static void appendColumns(StringBuilder s, String[] columns) {
+ int n = columns.length;
+
+ for (int i = 0; i < n; i++) {
+ String column = columns[i];
+
+ if (column != null) {
+ if (i > 0) {
+ s.append(", ");
+ }
+ s.append(column);
+ }
+ }
+ s.append(' ');
+ }
+
+ /**
+ * Perform a query by combining all current settings and the
+ * information passed into this method.
+ *
+ * @param db the database to query on
+ * @param projection A list of which columns to return. Passing
+ * null will return all columns, which is discouraged to prevent
+ * reading data from storage that isn't going to be used.
+ * @param selection A filter declaring which rows to return,
+ * formatted as an SQL WHERE clause (excluding the WHERE
+ * itself). Passing null will return all rows for the given URL.
+ * @param selectionArgs You may include ?s in selection, which
+ * will be replaced by the values from selectionArgs, in order
+ * that they appear in the selection. The values will be bound
+ * as Strings.
+ * @param groupBy A filter declaring how to group rows, formatted
+ * as an SQL GROUP BY clause (excluding the GROUP BY
+ * itself). Passing null will cause the rows to not be grouped.
+ * @param having A filter declare which row groups to include in
+ * the cursor, if row grouping is being used, formatted as an
+ * SQL HAVING clause (excluding the HAVING itself). Passing
+ * null will cause all row groups to be included, and is
+ * required when row grouping is not being used.
+ * @param sortOrder How to order the rows, formatted as an SQL
+ * ORDER BY clause (excluding the ORDER BY itself). Passing null
+ * will use the default sort order, which may be unordered.
+ * @return a cursor over the result set
+ * @see android.content.ContentResolver#query(android.net.Uri, String[],
+ * String, String[], String)
+ */
+ public @Nullable Cursor query(@NonNull SQLiteDatabase db,
+ @Nullable String[] projection,
+ @Nullable String selection,
+ @Nullable String[] selectionArgs,
+ @Nullable String groupBy,
+ @Nullable String having,
+ @Nullable String sortOrder) {
+ return query(db, projection, selection, selectionArgs, groupBy, having, sortOrder,
+ null /* limit */, null /* cancellationSignal */);
+ }
+
+ /**
+ * Perform a query by combining all current settings and the
+ * information passed into this method.
+ *
+ * @param db the database to query on
+ * @param projection A list of which columns to return. Passing
+ * null will return all columns, which is discouraged to prevent
+ * reading data from storage that isn't going to be used.
+ * @param selection A filter declaring which rows to return,
+ * formatted as an SQL WHERE clause (excluding the WHERE
+ * itself). Passing null will return all rows for the given URL.
+ * @param selectionArgs You may include ?s in selection, which
+ * will be replaced by the values from selectionArgs, in order
+ * that they appear in the selection. The values will be bound
+ * as Strings.
+ * @param groupBy A filter declaring how to group rows, formatted
+ * as an SQL GROUP BY clause (excluding the GROUP BY
+ * itself). Passing null will cause the rows to not be grouped.
+ * @param having A filter declare which row groups to include in
+ * the cursor, if row grouping is being used, formatted as an
+ * SQL HAVING clause (excluding the HAVING itself). Passing
+ * null will cause all row groups to be included, and is
+ * required when row grouping is not being used.
+ * @param sortOrder How to order the rows, formatted as an SQL
+ * ORDER BY clause (excluding the ORDER BY itself). Passing null
+ * will use the default sort order, which may be unordered.
+ * @param limit Limits the number of rows returned by the query,
+ * formatted as LIMIT clause. Passing null denotes no LIMIT clause.
+ * @return a cursor over the result set
+ * @see android.content.ContentResolver#query(android.net.Uri, String[],
+ * String, String[], String)
+ */
+ public @Nullable Cursor query(@NonNull SQLiteDatabase db,
+ @Nullable String[] projection,
+ @Nullable String selection,
+ @Nullable String[] selectionArgs,
+ @Nullable String groupBy,
+ @Nullable String having,
+ @Nullable String sortOrder,
+ @Nullable String limit) {
+ return query(db, projection, selection, selectionArgs,
+ groupBy, having, sortOrder, limit, null);
+ }
+
+ /**
+ * Perform a query by combining all current settings and the
+ * information passed into this method.
+ *
+ * @param db the database to query on
+ * @param projection A list of which columns to return. Passing
+ * null will return all columns, which is discouraged to prevent
+ * reading data from storage that isn't going to be used.
+ * @param selection A filter declaring which rows to return,
+ * formatted as an SQL WHERE clause (excluding the WHERE
+ * itself). Passing null will return all rows for the given URL.
+ * @param selectionArgs You may include ?s in selection, which
+ * will be replaced by the values from selectionArgs, in order
+ * that they appear in the selection. The values will be bound
+ * as Strings.
+ * @param sortOrder How to order the rows, formatted as an SQL
+ * ORDER BY clause (excluding the ORDER BY itself). Passing null
+ * will use the default sort order, which may be unordered.
+ * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
+ * If the operation is canceled, then {@link OperationCanceledException} will be thrown
+ * when the query is executed.
+ * @return a cursor over the result set
+ * @see android.content.ContentResolver#query(android.net.Uri, String[],
+ * String, String[], String)
+ */
+ public @Nullable Cursor query(@NonNull SQLiteDatabase db,
+ @Nullable String[] projection,
+ @Nullable String selection,
+ @Nullable String[] selectionArgs,
+ @Nullable String sortOrder,
+ @Nullable CancellationSignal cancellationSignal) {
+ return query(db, projection, selection, selectionArgs, null, null, sortOrder, null,
+ cancellationSignal);
+ }
+
+ /**
+ * Perform a query by combining all current settings and the
+ * information passed into this method.
+ *
+ * @param db the database to query on
+ * @param projection A list of which columns to return. Passing
+ * null will return all columns, which is discouraged to prevent
+ * reading data from storage that isn't going to be used.
+ * @param selection A filter declaring which rows to return,
+ * formatted as an SQL WHERE clause (excluding the WHERE
+ * itself). Passing null will return all rows for the given URL.
+ * @param selectionArgs You may include ?s in selection, which
+ * will be replaced by the values from selectionArgs, in order
+ * that they appear in the selection. The values will be bound
+ * as Strings.
+ * @param groupBy A filter declaring how to group rows, formatted
+ * as an SQL GROUP BY clause (excluding the GROUP BY
+ * itself). Passing null will cause the rows to not be grouped.
+ * @param having A filter declare which row groups to include in
+ * the cursor, if row grouping is being used, formatted as an
+ * SQL HAVING clause (excluding the HAVING itself). Passing
+ * null will cause all row groups to be included, and is
+ * required when row grouping is not being used.
+ * @param sortOrder How to order the rows, formatted as an SQL
+ * ORDER BY clause (excluding the ORDER BY itself). Passing null
+ * will use the default sort order, which may be unordered.
+ * @param limit Limits the number of rows returned by the query,
+ * formatted as LIMIT clause. Passing null denotes no LIMIT clause.
+ * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
+ * If the operation is canceled, then {@link OperationCanceledException} will be thrown
+ * when the query is executed.
+ * @return a cursor over the result set
+ * @see android.content.ContentResolver#query(android.net.Uri, String[],
+ * String, String[], String)
+ */
+ public @Nullable Cursor query(@NonNull SQLiteDatabase db,
+ @Nullable String[] projection,
+ @Nullable String selection,
+ @Nullable String[] selectionArgs,
+ @Nullable String groupBy,
+ @Nullable String having,
+ @Nullable String sortOrder,
+ @Nullable String limit,
+ @Nullable CancellationSignal cancellationSignal) {
+ final Bundle queryArgs = new Bundle();
+ maybePutString(queryArgs, QUERY_ARG_SQL_SELECTION, selection);
+ maybePutStringArray(queryArgs, QUERY_ARG_SQL_SELECTION_ARGS, selectionArgs);
+ maybePutString(queryArgs, QUERY_ARG_SQL_GROUP_BY, groupBy);
+ maybePutString(queryArgs, QUERY_ARG_SQL_HAVING, having);
+ maybePutString(queryArgs, QUERY_ARG_SQL_SORT_ORDER, sortOrder);
+ maybePutString(queryArgs, QUERY_ARG_SQL_LIMIT, limit);
+ return query(db, projection, queryArgs, cancellationSignal);
+ }
+
+ /**
+ * Perform a query by combining all current settings and the information
+ * passed into this method.
+ *
+ * @param db the database to query on
+ * @param projection A list of which columns to return. Passing null will
+ * return all columns, which is discouraged to prevent reading
+ * data from storage that isn't going to be used.
+ * @param queryArgs A collection of arguments for the query, defined using
+ * keys such as {@link ContentResolver#QUERY_ARG_SQL_SELECTION}
+ * and {@link ContentResolver#QUERY_ARG_SQL_SELECTION_ARGS}.
+ * @param cancellationSignal A signal to cancel the operation in progress,
+ * or null if none. If the operation is canceled, then
+ * {@link OperationCanceledException} will be thrown when the
+ * query is executed.
+ * @return a cursor over the result set
+ */
+ public Cursor query(@NonNull SQLiteDatabase db,
+ @Nullable String[] projection,
+ @Nullable Bundle queryArgs,
+ @Nullable CancellationSignal cancellationSignal) {
+ Objects.requireNonNull(db, "No database defined");
+
+ if (VMRuntime.getRuntime().getTargetSdkVersion() >= Build.VERSION_CODES.Q) {
+ Objects.requireNonNull(mTables, "No tables defined");
+ } else if (mTables == null) {
+ return null;
+ }
+
+ if (queryArgs == null) {
+ queryArgs = Bundle.EMPTY;
+ }
+
+ // Final SQL that we will execute
+ final String sql;
+
+ final String unwrappedSql = buildQuery(projection,
+ queryArgs.getString(QUERY_ARG_SQL_SELECTION),
+ queryArgs.getString(QUERY_ARG_SQL_GROUP_BY),
+ queryArgs.getString(QUERY_ARG_SQL_HAVING),
+ queryArgs.getString(QUERY_ARG_SQL_SORT_ORDER),
+ queryArgs.getString(QUERY_ARG_SQL_LIMIT));
+
+ if (mStrict) {
+ // Validate the user-supplied selection to detect syntactic anomalies
+ // in the selection string that could indicate a SQL injection attempt.
+ // The idea is to ensure that the selection clause is a valid SQL expression
+ // by compiling it twice: once wrapped in parentheses and once as
+ // originally specified. An attacker cannot create an expression that
+ // would escape the SQL expression while maintaining balanced parentheses
+ // in both the wrapped and original forms.
+
+ // NOTE: The ordering of the below operations is important; we must
+ // execute the wrapped query to ensure the untrusted clause has been
+ // fully isolated.
+
+ // TODO: decode SORT ORDER and LIMIT clauses, since they can contain
+ // "expr" inside that need to be validated
+
+ final String wrappedSql = buildQuery(projection,
+ wrap(queryArgs.getString(QUERY_ARG_SQL_SELECTION)),
+ queryArgs.getString(QUERY_ARG_SQL_GROUP_BY),
+ queryArgs.getString(QUERY_ARG_SQL_HAVING),
+ queryArgs.getString(QUERY_ARG_SQL_SORT_ORDER),
+ queryArgs.getString(QUERY_ARG_SQL_LIMIT));
+
+ // Validate the unwrapped query
+ db.validateSql(unwrappedSql, cancellationSignal);
+
+ // Execute wrapped query for extra protection
+ sql = wrappedSql;
+ } else {
+ // Execute unwrapped query
+ sql = unwrappedSql;
+ }
+
+ final String[] sqlArgs = ArrayUtils.concat(String.class,
+ queryArgs.getStringArray(QUERY_ARG_SQL_SELECTION_ARGS), mWhereArgs);
+
+ if (Build.IS_DEBUGGABLE && Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, sql + " with args " + Arrays.toString(sqlArgs));
+ }
+
+ return db.rawQueryWithFactory(
+ mFactory, sql, sqlArgs,
+ SQLiteDatabase.findEditTable(mTables),
+ cancellationSignal); // will throw if query is invalid
+ }
+
+ /**
+ * Perform an update by combining all current settings and the
+ * information passed into this method.
+ *
+ * @param db the database to update on
+ * @param selection A filter declaring which rows to return,
+ * formatted as an SQL WHERE clause (excluding the WHERE
+ * itself). Passing null will return all rows for the given URL.
+ * @param selectionArgs You may include ?s in selection, which
+ * will be replaced by the values from selectionArgs, in order
+ * that they appear in the selection. The values will be bound
+ * as Strings.
+ * @return the number of rows updated
+ */
+ public int update(@NonNull SQLiteDatabase db, @NonNull ContentValues values,
+ @Nullable String selection, @Nullable String[] selectionArgs) {
+ Objects.requireNonNull(mTables, "No tables defined");
+ Objects.requireNonNull(db, "No database defined");
+ Objects.requireNonNull(values, "No values defined");
+
+ if (mStrict) {
+ // Validate the user-supplied selection to detect syntactic anomalies
+ // in the selection string that could indicate a SQL injection attempt.
+ // The idea is to ensure that the selection clause is a valid SQL expression
+ // by compiling it twice: once wrapped in parentheses and once as
+ // originally specified. An attacker cannot create an expression that
+ // would escape the SQL expression while maintaining balanced parentheses
+ // in both the wrapped and original forms.
+ final String sql = buildUpdate(values, wrap(selection));
+ db.validateSql(sql, null); // will throw if query is invalid
+ }
+
+ final ArrayMap<String, Object> rawValues = values.getValues();
+ final String[] updateArgs = new String[rawValues.size()];
+ for (int i = 0; i < updateArgs.length; i++) {
+ final Object arg = rawValues.valueAt(i);
+ updateArgs[i] = (arg != null) ? arg.toString() : null;
+ }
+
+ final String sql = buildUpdate(values, selection);
+ final String[] sqlArgs = ArrayUtils.concat(String.class, updateArgs,
+ ArrayUtils.concat(String.class, selectionArgs, mWhereArgs));
+
+ if (Build.IS_DEBUGGABLE && Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, sql + " with args " + Arrays.toString(sqlArgs));
+ }
+
+ return db.executeSql(sql, sqlArgs);
+ }
+
+ /**
+ * Perform a delete by combining all current settings and the
+ * information passed into this method.
+ *
+ * @param db the database to delete on
+ * @param selection A filter declaring which rows to return,
+ * formatted as an SQL WHERE clause (excluding the WHERE
+ * itself). Passing null will return all rows for the given URL.
+ * @param selectionArgs You may include ?s in selection, which
+ * will be replaced by the values from selectionArgs, in order
+ * that they appear in the selection. The values will be bound
+ * as Strings.
+ * @return the number of rows deleted
+ */
+ public int delete(@NonNull SQLiteDatabase db, @Nullable String selection,
+ @Nullable String[] selectionArgs) {
+ Objects.requireNonNull(mTables, "No tables defined");
+ Objects.requireNonNull(db, "No database defined");
+
+ if (mStrict) {
+ // Validate the user-supplied selection to detect syntactic anomalies
+ // in the selection string that could indicate a SQL injection attempt.
+ // The idea is to ensure that the selection clause is a valid SQL expression
+ // by compiling it twice: once wrapped in parentheses and once as
+ // originally specified. An attacker cannot create an expression that
+ // would escape the SQL expression while maintaining balanced parentheses
+ // in both the wrapped and original forms.
+ final String sql = buildDelete(wrap(selection));
+ db.validateSql(sql, null); // will throw if query is invalid
+ }
+
+ final String sql = buildDelete(selection);
+ final String[] sqlArgs = ArrayUtils.concat(String.class, selectionArgs, mWhereArgs);
+
+ if (Build.IS_DEBUGGABLE && Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, sql + " with args " + Arrays.toString(sqlArgs));
+ }
+
+ return db.executeSql(sql, sqlArgs);
+ }
+
+ /**
+ * Construct a SELECT statement suitable for use in a group of
+ * SELECT statements that will be joined through UNION operators
+ * in buildUnionQuery.
+ *
+ * @param projectionIn A list of which columns to return. Passing
+ * null will return all columns, which is discouraged to
+ * prevent reading data from storage that isn't going to be
+ * used.
+ * @param selection A filter declaring which rows to return,
+ * formatted as an SQL WHERE clause (excluding the WHERE
+ * itself). Passing null will return all rows for the given
+ * URL.
+ * @param groupBy A filter declaring how to group rows, formatted
+ * as an SQL GROUP BY clause (excluding the GROUP BY itself).
+ * Passing null will cause the rows to not be grouped.
+ * @param having A filter declare which row groups to include in
+ * the cursor, if row grouping is being used, formatted as an
+ * SQL HAVING clause (excluding the HAVING itself). Passing
+ * null will cause all row groups to be included, and is
+ * required when row grouping is not being used.
+ * @param sortOrder How to order the rows, formatted as an SQL
+ * ORDER BY clause (excluding the ORDER BY itself). Passing null
+ * will use the default sort order, which may be unordered.
+ * @param limit Limits the number of rows returned by the query,
+ * formatted as LIMIT clause. Passing null denotes no LIMIT clause.
+ * @return the resulting SQL SELECT statement
+ */
+ public String buildQuery(
+ String[] projectionIn, String selection, String groupBy,
+ String having, String sortOrder, String limit) {
+ String[] projection = computeProjection(projectionIn);
+ String where = computeWhere(selection);
+
+ return buildQueryString(
+ mDistinct, mTables, projection, where,
+ groupBy, having, sortOrder, limit);
+ }
+
+ /**
+ * @deprecated This method's signature is misleading since no SQL parameter
+ * substitution is carried out. The selection arguments parameter does not get
+ * used at all. To avoid confusion, call
+ * {@link #buildQuery(String[], String, String, String, String, String)} instead.
+ */
+ @Deprecated
+ public String buildQuery(
+ String[] projectionIn, String selection, String[] selectionArgs,
+ String groupBy, String having, String sortOrder, String limit) {
+ return buildQuery(projectionIn, selection, groupBy, having, sortOrder, limit);
+ }
+
+ /** {@hide} */
+ public String buildUpdate(ContentValues values, String selection) {
+ if (values == null || values.isEmpty()) {
+ throw new IllegalArgumentException("Empty values");
+ }
+
+ StringBuilder sql = new StringBuilder(120);
+ sql.append("UPDATE ");
+ sql.append(mTables);
+ sql.append(" SET ");
+
+ final ArrayMap<String, Object> rawValues = values.getValues();
+ for (int i = 0; i < rawValues.size(); i++) {
+ if (i > 0) {
+ sql.append(',');
+ }
+ sql.append(rawValues.keyAt(i));
+ sql.append("=?");
+ }
+
+ final String where = computeWhere(selection);
+ appendClause(sql, " WHERE ", where);
+ return sql.toString();
+ }
+
+ /** {@hide} */
+ public String buildDelete(String selection) {
+ StringBuilder sql = new StringBuilder(120);
+ sql.append("DELETE FROM ");
+ sql.append(mTables);
+
+ final String where = computeWhere(selection);
+ appendClause(sql, " WHERE ", where);
+ return sql.toString();
+ }
+
+ /**
+ * Construct a SELECT statement suitable for use in a group of
+ * SELECT statements that will be joined through UNION operators
+ * in buildUnionQuery.
+ *
+ * @param typeDiscriminatorColumn the name of the result column
+ * whose cells will contain the name of the table from which
+ * each row was drawn.
+ * @param unionColumns the names of the columns to appear in the
+ * result. This may include columns that do not appear in the
+ * table this SELECT is querying (i.e. mTables), but that do
+ * appear in one of the other tables in the UNION query that we
+ * are constructing.
+ * @param columnsPresentInTable a Set of the names of the columns
+ * that appear in this table (i.e. in the table whose name is
+ * mTables). Since columns in unionColumns include columns that
+ * appear only in other tables, we use this array to distinguish
+ * which ones actually are present. Other columns will have
+ * NULL values for results from this subquery.
+ * @param computedColumnsOffset all columns in unionColumns before
+ * this index are included under the assumption that they're
+ * computed and therefore won't appear in columnsPresentInTable,
+ * e.g. "date * 1000 as normalized_date"
+ * @param typeDiscriminatorValue the value used for the
+ * type-discriminator column in this subquery
+ * @param selection A filter declaring which rows to return,
+ * formatted as an SQL WHERE clause (excluding the WHERE
+ * itself). Passing null will return all rows for the given
+ * URL.
+ * @param groupBy A filter declaring how to group rows, formatted
+ * as an SQL GROUP BY clause (excluding the GROUP BY itself).
+ * Passing null will cause the rows to not be grouped.
+ * @param having A filter declare which row groups to include in
+ * the cursor, if row grouping is being used, formatted as an
+ * SQL HAVING clause (excluding the HAVING itself). Passing
+ * null will cause all row groups to be included, and is
+ * required when row grouping is not being used.
+ * @return the resulting SQL SELECT statement
+ */
+ public String buildUnionSubQuery(
+ String typeDiscriminatorColumn,
+ String[] unionColumns,
+ Set<String> columnsPresentInTable,
+ int computedColumnsOffset,
+ String typeDiscriminatorValue,
+ String selection,
+ String groupBy,
+ String having) {
+ int unionColumnsCount = unionColumns.length;
+ String[] projectionIn = new String[unionColumnsCount];
+
+ for (int i = 0; i < unionColumnsCount; i++) {
+ String unionColumn = unionColumns[i];
+
+ if (unionColumn.equals(typeDiscriminatorColumn)) {
+ projectionIn[i] = "'" + typeDiscriminatorValue + "' AS "
+ + typeDiscriminatorColumn;
+ } else if (i <= computedColumnsOffset
+ || columnsPresentInTable.contains(unionColumn)) {
+ projectionIn[i] = unionColumn;
+ } else {
+ projectionIn[i] = "NULL AS " + unionColumn;
+ }
+ }
+ return buildQuery(
+ projectionIn, selection, groupBy, having,
+ null /* sortOrder */,
+ null /* limit */);
+ }
+
+ /**
+ * @deprecated This method's signature is misleading since no SQL parameter
+ * substitution is carried out. The selection arguments parameter does not get
+ * used at all. To avoid confusion, call
+ * {@link #buildUnionSubQuery}
+ * instead.
+ */
+ @Deprecated
+ public String buildUnionSubQuery(
+ String typeDiscriminatorColumn,
+ String[] unionColumns,
+ Set<String> columnsPresentInTable,
+ int computedColumnsOffset,
+ String typeDiscriminatorValue,
+ String selection,
+ String[] selectionArgs,
+ String groupBy,
+ String having) {
+ return buildUnionSubQuery(
+ typeDiscriminatorColumn, unionColumns, columnsPresentInTable,
+ computedColumnsOffset, typeDiscriminatorValue, selection,
+ groupBy, having);
+ }
+
+ /**
+ * Given a set of subqueries, all of which are SELECT statements,
+ * construct a query that returns the union of what those
+ * subqueries return.
+ * @param subQueries an array of SQL SELECT statements, all of
+ * which must have the same columns as the same positions in
+ * their results
+ * @param sortOrder How to order the rows, formatted as an SQL
+ * ORDER BY clause (excluding the ORDER BY itself). Passing
+ * null will use the default sort order, which may be unordered.
+ * @param limit The limit clause, which applies to the entire union result set
+ *
+ * @return the resulting SQL SELECT statement
+ */
+ public String buildUnionQuery(String[] subQueries, String sortOrder, String limit) {
+ StringBuilder query = new StringBuilder(128);
+ int subQueryCount = subQueries.length;
+ String unionOperator = mDistinct ? " UNION " : " UNION ALL ";
+
+ for (int i = 0; i < subQueryCount; i++) {
+ if (i > 0) {
+ query.append(unionOperator);
+ }
+ query.append(subQueries[i]);
+ }
+ appendClause(query, " ORDER BY ", sortOrder);
+ appendClause(query, " LIMIT ", limit);
+ return query.toString();
+ }
+
+ private @Nullable String[] computeProjection(@Nullable String[] projectionIn) {
+ if (projectionIn != null && projectionIn.length > 0) {
+ if (mProjectionMap != null) {
+ String[] projection = new String[projectionIn.length];
+ int length = projectionIn.length;
+
+ for (int i = 0; i < length; i++) {
+ String userColumn = projectionIn[i];
+ String column = mProjectionMap.get(userColumn);
+
+ if (column != null) {
+ projection[i] = column;
+ continue;
+ }
+
+ if (!mStrict &&
+ ( userColumn.contains(" AS ") || userColumn.contains(" as "))) {
+ /* A column alias already exist */
+ projection[i] = userColumn;
+ continue;
+ }
+
+ throw new IllegalArgumentException("Invalid column "
+ + projectionIn[i] + " from tables " + mTables);
+ }
+ return projection;
+ } else {
+ return projectionIn;
+ }
+ } else if (mProjectionMap != null) {
+ // Return all columns in projection map.
+ Set<Entry<String, String>> entrySet = mProjectionMap.entrySet();
+ String[] projection = new String[entrySet.size()];
+ Iterator<Entry<String, String>> entryIter = entrySet.iterator();
+ int i = 0;
+
+ while (entryIter.hasNext()) {
+ Entry<String, String> entry = entryIter.next();
+
+ // Don't include the _count column when people ask for no projection.
+ if (entry.getKey().equals(BaseColumns._COUNT)) {
+ continue;
+ }
+ projection[i++] = entry.getValue();
+ }
+ return projection;
+ }
+ return null;
+ }
+
+ private @NonNull String computeWhere(@Nullable String selection) {
+ final boolean hasUser = selection != null && selection.length() > 0;
+ final boolean hasInternal = mWhereClause != null && mWhereClause.length() > 0;
+
+ if (hasUser || hasInternal) {
+ final StringBuilder where = new StringBuilder();
+ if (hasUser) {
+ where.append('(').append(selection).append(')');
+ }
+ if (hasUser && hasInternal) {
+ where.append(" AND ");
+ }
+ if (hasInternal) {
+ where.append('(').append(mWhereClause.toString()).append(')');
+ }
+ return where.toString();
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Wrap given argument in parenthesis, unless it's {@code null} or
+ * {@code ()}, in which case return it verbatim.
+ */
+ private @Nullable String wrap(@Nullable String arg) {
+ if (arg == null) {
+ return null;
+ } else if (arg.equals("")) {
+ return arg;
+ } else {
+ return "(" + arg + ")";
+ }
+ }
+
+ private static void maybePutString(@NonNull Bundle bundle, @NonNull String key,
+ @Nullable String value) {
+ if (value != null) {
+ bundle.putString(key, value);
+ }
+ }
+
+ private static void maybePutStringArray(@NonNull Bundle bundle, @NonNull String key,
+ @Nullable String[] value) {
+ if (value != null) {
+ bundle.putStringArray(key, value);
+ }
+ }
+}
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index 9350aab..22e1f45 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -571,9 +571,7 @@
/**
* An empty Camera for testing purpose.
*/
- Camera() {
- initAppOps();
- }
+ Camera() {}
private void initAppOps() {
IBinder b = ServiceManager.getService(Context.APP_OPS_SERVICE);
diff --git a/core/java/android/hardware/biometrics/BiometricAuthenticator.java b/core/java/android/hardware/biometrics/BiometricAuthenticator.java
index c811999..e7c5116 100644
--- a/core/java/android/hardware/biometrics/BiometricAuthenticator.java
+++ b/core/java/android/hardware/biometrics/BiometricAuthenticator.java
@@ -33,7 +33,50 @@
* Container for biometric data
* @hide
*/
- abstract class BiometricIdentifier implements Parcelable {}
+ abstract class Identifier implements Parcelable {
+ private CharSequence mName;
+ private int mBiometricId;
+ private long mDeviceId; // physical device this is associated with
+
+ public Identifier() {}
+
+ public Identifier(CharSequence name, int biometricId, long deviceId) {
+ mName = name;
+ mBiometricId = biometricId;
+ mDeviceId = deviceId;
+ }
+
+ /**
+ * Gets the human-readable name for the given biometric.
+ * @return name given to the biometric
+ */
+ public CharSequence getName() {
+ return mName;
+ }
+
+ /**
+ * Gets the device-specific biometric id. Used by Settings to map a name to a specific
+ * biometric template.
+ */
+ public int getBiometricId() {
+ return mBiometricId;
+ }
+
+ /**
+ * Device this biometric belongs to.
+ */
+ public long getDeviceId() {
+ return mDeviceId;
+ }
+
+ public void setName(CharSequence name) {
+ mName = name;
+ }
+
+ public void setDeviceId(long deviceId) {
+ mDeviceId = deviceId;
+ }
+ }
/**
* Container for callback data from {@link BiometricAuthenticator#authenticate(
@@ -42,7 +85,7 @@
* AuthenticationCallback)}
*/
class AuthenticationResult {
- private BiometricIdentifier mIdentifier;
+ private Identifier mIdentifier;
private CryptoObject mCryptoObject;
private int mUserId;
@@ -58,7 +101,7 @@
* @param userId
* @hide
*/
- public AuthenticationResult(CryptoObject crypto, BiometricIdentifier identifier,
+ public AuthenticationResult(CryptoObject crypto, Identifier identifier,
int userId) {
mCryptoObject = crypto;
mIdentifier = identifier;
@@ -80,7 +123,7 @@
* operations.
* @hide
*/
- public BiometricIdentifier getId() {
+ public Identifier getId() {
return mIdentifier;
}
diff --git a/core/java/android/hardware/biometrics/BiometricFaceConstants.java b/core/java/android/hardware/biometrics/BiometricFaceConstants.java
new file mode 100644
index 0000000..008601c
--- /dev/null
+++ b/core/java/android/hardware/biometrics/BiometricFaceConstants.java
@@ -0,0 +1,215 @@
+/*
+ * 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.hardware.biometrics;
+
+import android.hardware.face.FaceManager;
+
+/**
+ * Interface containing all of the face-specific constants.
+ * @hide
+ */
+public interface BiometricFaceConstants {
+ //
+ // Error messages from face authentication hardware during initialization, enrollment,
+ // authentication or removal. Must agree with the list in HAL h file
+ //
+ /**
+ * The hardware is unavailable. Try again later.
+ */
+ public static final int FACE_ERROR_HW_UNAVAILABLE = 1;
+ /**
+ * Error state returned when the sensor was unable to process the current image.
+ */
+ public static final int FACE_ERROR_UNABLE_TO_PROCESS = 2;
+ /**
+ * Error state returned when the current request has been running too long. This is intended to
+ * prevent programs from waiting for the face authentication sensor indefinitely. The timeout is
+ * platform and sensor-specific, but is generally on the order of 30 seconds.
+ */
+ public static final int FACE_ERROR_TIMEOUT = 3;
+ /**
+ * Error state returned for operations like enrollment; the operation cannot be completed
+ * because there's not enough storage remaining to complete the operation.
+ */
+ public static final int FACE_ERROR_NO_SPACE = 4;
+ /**
+ * The operation was canceled because the face authentication sensor is unavailable. For
+ * example, this may happen when the user is switched, the device is locked or another pending
+ * operation prevents or disables it.
+ */
+ public static final int FACE_ERROR_CANCELED = 5;
+ /**
+ * The {@link FaceManager#remove} call failed. Typically this will happen when the
+ * provided face id was incorrect.
+ *
+ * @hide
+ */
+ public static final int FACE_ERROR_UNABLE_TO_REMOVE = 6;
+ /**
+ * The operation was canceled because the API is locked out due to too many attempts.
+ * This occurs after 5 failed attempts, and lasts for 30 seconds.
+ */
+ public static final int FACE_ERROR_LOCKOUT = 7;
+ /**
+ * Hardware vendors may extend this list if there are conditions that do not fall under one of
+ * the above categories. Vendors are responsible for providing error strings for these errors.
+ * These messages are typically reserved for internal operations such as enrollment, but may be
+ * used to express vendor errors not covered by the ones in HAL h file. Applications are
+ * expected to show the error message string if they happen, but are advised not to rely on the
+ * message id since they will be device and vendor-specific
+ */
+ public static final int FACE_ERROR_VENDOR = 8;
+
+ /**
+ * The operation was canceled because FACE_ERROR_LOCKOUT occurred too many times.
+ * Face authentication is disabled until the user unlocks with strong authentication
+ * (PIN/Pattern/Password)
+ */
+ public static final int FACE_ERROR_LOCKOUT_PERMANENT = 9;
+ /**
+ * The user canceled the operation. Upon receiving this, applications should use alternate
+ * authentication (e.g. a password). The application should also provide the means to return
+ * to face authentication, such as a "use face authentication" button.
+ */
+ public static final int FACE_ERROR_USER_CANCELED = 10;
+ /**
+ * The user does not have a face enrolled.
+ */
+ public static final int FACE_ERROR_NOT_ENROLLED = 11;
+ /**
+ * The device does not have a face sensor. This message will propagate if the calling app
+ * ignores the result from PackageManager.hasFeature(FEATURE_FACE) and calls
+ * this API anyway. Apps should always check for the feature before calling this API.
+ */
+ public static final int FACE_ERROR_HW_NOT_PRESENT = 12;
+ /**
+ * @hide
+ */
+ public static final int FACE_ERROR_VENDOR_BASE = 1000;
+
+ //
+ // Image acquisition messages. These will not be sent to the user, since they conflict with
+ // existing constants. These must agree with face@1.0/types.hal.
+ //
+
+ /**
+ * The image acquired was good.
+ */
+ public static final int FACE_ACQUIRED_GOOD = 0;
+
+ /**
+ * The face image was not good enough to process due to a detected condition.
+ * (See {@link #FACE_ACQUIRED_TOO_BRIGHT or @link #FACE_ACQUIRED_TOO_DARK}).
+ */
+ public static final int FACE_ACQUIRED_INSUFFICIENT = 1;
+
+ /**
+ * The face image was too bright due to too much ambient light.
+ * For example, it's reasonable to return this after multiple
+ * {@link #FACE_ACQUIRED_INSUFFICIENT}
+ * The user is expected to take action to retry in better lighting conditions
+ * when this is returned.
+ */
+ public static final int FACE_ACQUIRED_TOO_BRIGHT = 2;
+
+ /**
+ * The face image was too dark due to illumination light obscured.
+ * For example, it's reasonable to return this after multiple
+ * {@link #FACE_ACQUIRED_INSUFFICIENT}
+ * The user is expected to take action to retry in better lighting conditions
+ * when this is returned.
+ */
+ public static final int FACE_ACQUIRED_TOO_DARK = 3;
+
+ /**
+ * The detected face is too close to the sensor, and the image can't be processed.
+ * The user should be informed to move farther from the sensor when this is returned.
+ */
+ public static final int FACE_ACQUIRED_TOO_CLOSE = 4;
+
+ /**
+ * The detected face is too small, as the user might be too far from the sensor.
+ * The user should be informed to move closer to the sensor when this is returned.
+ */
+ public static final int FACE_ACQUIRED_TOO_FAR = 5;
+
+ /**
+ * Only the upper part of the face was detected. The sensor field of view is too high.
+ * The user should be informed to move up with respect to the sensor when this is returned.
+ */
+ public static final int FACE_ACQUIRED_TOO_HIGH = 6;
+
+ /**
+ * Only the lower part of the face was detected. The sensor field of view is too low.
+ * The user should be informed to move down with respect to the sensor when this is returned.
+ */
+ public static final int FACE_ACQUIRED_TOO_LOW = 7;
+
+ /**
+ * Only the right part of the face was detected. The sensor field of view is too far right.
+ * The user should be informed to move to the right with respect to the sensor
+ * when this is returned.
+ */
+ public static final int FACE_ACQUIRED_TOO_RIGHT = 8;
+
+ /**
+ * Only the left part of the face was detected. The sensor field of view is too far left.
+ * The user should be informed to move to the left with respect to the sensor
+ * when this is returned.
+ */
+ public static final int FACE_ACQUIRED_TOO_LEFT = 9;
+
+ /**
+ * The user's eyes have strayed away from the sensor. If this message is sent, the user should
+ * be informed to look at the device. If the user can't be found in the frame, one of the other
+ * acquisition messages should be sent, e.g. FACE_ACQUIRED_NOT_DETECTED.
+ */
+ public static final int FACE_ACQUIRED_POOR_GAZE = 10;
+
+ /**
+ * No face was detected in front of the sensor.
+ * The user should be informed to point the sensor to a face when this is returned.
+ */
+ public static final int FACE_ACQUIRED_NOT_DETECTED = 11;
+
+ /**
+ * Too much motion was detected.
+ * The user should be informed to keep their face steady relative to the
+ * sensor.
+ */
+ public static final int FACE_ACQUIRED_TOO_MUCH_MOTION = 12;
+
+ /**
+ * The sensor needs to be re-calibrated. This is an unexpected condition, and should only be
+ * sent if a serious, uncorrectable, and unrecoverable calibration issue is detected which
+ * requires user intervention, e.g. re-enrolling. The expected response to this message is to
+ * direct the user to re-enroll.
+ */
+ public static final int FACE_ACQUIRED_RECALIBRATE = 13;
+
+ /**
+ * Hardware vendors may extend this list if there are conditions that do not fall under one of
+ * the above categories. Vendors are responsible for providing error strings for these errors.
+ *
+ * @hide
+ */
+ public static final int FACE_ACQUIRED_VENDOR = 13;
+ /**
+ * @hide
+ */
+ public static final int FACE_ACQUIRED_VENDOR_BASE = 1000;
+}
diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java
index 1c9de45..bad418a 100644
--- a/core/java/android/hardware/biometrics/BiometricPrompt.java
+++ b/core/java/android/hardware/biometrics/BiometricPrompt.java
@@ -293,7 +293,7 @@
* @param userId
* @hide
*/
- public AuthenticationResult(CryptoObject crypto, BiometricIdentifier identifier,
+ public AuthenticationResult(CryptoObject crypto, Identifier identifier,
int userId) {
super(crypto, identifier, userId);
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionCategory.java b/core/java/android/hardware/biometrics/BiometricSourceType.aidl
similarity index 65%
rename from packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionCategory.java
rename to core/java/android/hardware/biometrics/BiometricSourceType.aidl
index 19e556a..15440d8 100644
--- a/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionCategory.java
+++ b/core/java/android/hardware/biometrics/BiometricSourceType.aidl
@@ -1,5 +1,5 @@
/*
- * 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.
@@ -14,12 +14,9 @@
* limitations under the License.
*/
-package com.android.settingslib.suggestions;
+package android.hardware.biometrics;
-public class SuggestionCategory {
- public String category;
- public String pkg;
- public boolean multiple;
- public boolean exclusive;
- public long exclusiveExpireDaysInMillis;
-}
+/**
+ * @hide
+ */
+parcelable BiometricSourceType;
\ No newline at end of file
diff --git a/core/java/android/hardware/biometrics/BiometricSourceType.java b/core/java/android/hardware/biometrics/BiometricSourceType.java
new file mode 100644
index 0000000..4a08cf2
--- /dev/null
+++ b/core/java/android/hardware/biometrics/BiometricSourceType.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 android.hardware.biometrics;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * @hide
+ */
+public enum BiometricSourceType implements Parcelable {
+ FINGERPRINT,
+ FACE,
+ IRIS;
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(name());
+ }
+
+ public static final Creator<BiometricSourceType> CREATOR = new Creator<BiometricSourceType>() {
+ @Override
+ public BiometricSourceType createFromParcel(final Parcel source) {
+ return BiometricSourceType.valueOf(source.readString());
+ }
+
+ @Override
+ public BiometricSourceType[] newArray(final int size) {
+ return new BiometricSourceType[size];
+ }
+ };
+}
diff --git a/core/java/android/hardware/fingerprint/IFingerprintServiceLockoutResetCallback.aidl b/core/java/android/hardware/biometrics/IBiometricServiceLockoutResetCallback.aidl
similarity index 81%
rename from core/java/android/hardware/fingerprint/IFingerprintServiceLockoutResetCallback.aidl
rename to core/java/android/hardware/biometrics/IBiometricServiceLockoutResetCallback.aidl
index 971e14c..ee033bf 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintServiceLockoutResetCallback.aidl
+++ b/core/java/android/hardware/biometrics/IBiometricServiceLockoutResetCallback.aidl
@@ -13,18 +13,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.hardware.fingerprint;
+package android.hardware.biometrics;
-import android.hardware.fingerprint.Fingerprint;
-import android.os.Bundle;
import android.os.IRemoteCallback;
-import android.os.UserHandle;
/**
* Callback when lockout period expired and clients are allowed to authenticate again.
* @hide
*/
-oneway interface IFingerprintServiceLockoutResetCallback {
+oneway interface IBiometricServiceLockoutResetCallback {
/**
* A wakelock will be held until the reciever calls back into {@param callback}
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 87c64cd..60e4ce2 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -2365,13 +2365,25 @@
* {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion}, is defined relative to the active array rectangle given in
* this field, with <code>(0, 0)</code> being the top-left of this rectangle.</p>
* <p>The active array may be smaller than the full pixel array, since the full array may
- * include black calibration pixels or other inactive regions, and geometric correction
- * resulting in scaling or cropping may have been applied.</p>
+ * include black calibration pixels or other inactive regions.</p>
+ * <p>For devices that do not support {@link CaptureRequest#DISTORTION_CORRECTION_MODE android.distortionCorrection.mode} control, the active
+ * array must be the same as {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}.</p>
+ * <p>For devices that support {@link CaptureRequest#DISTORTION_CORRECTION_MODE android.distortionCorrection.mode} control, the active array must
+ * be enclosed by {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}. The difference between
+ * pre-correction active array and active array accounts for scaling or cropping caused
+ * by lens geometric distortion correction.</p>
+ * <p>In general, application should always refer to active array size for controls like
+ * metering regions or crop region. Two exceptions are when the application is dealing with
+ * RAW image buffers (RAW_SENSOR, RAW10, RAW12 etc), or when application explicitly set
+ * {@link CaptureRequest#DISTORTION_CORRECTION_MODE android.distortionCorrection.mode} to OFF. In these cases, application should refer
+ * to {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}.</p>
* <p><b>Units</b>: Pixel coordinates on the image sensor</p>
* <p>This key is available on all devices.</p>
*
+ * @see CaptureRequest#DISTORTION_CORRECTION_MODE
* @see CaptureRequest#SCALER_CROP_REGION
* @see CameraCharacteristics#SENSOR_INFO_PIXEL_ARRAY_SIZE
+ * @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
*/
@PublicKey
public static final Key<android.graphics.Rect> SENSOR_INFO_ACTIVE_ARRAY_SIZE =
@@ -2616,9 +2628,9 @@
* <ol>
* <li>{@link CameraCharacteristics#LENS_DISTORTION android.lens.distortion}.</li>
* </ol>
- * <p>If all of the geometric distortion fields are no-ops, this rectangle will be the same
- * as the post-distortion-corrected rectangle given in
- * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.</p>
+ * <p>If the camera device doesn't support geometric distortion correction, or all of the
+ * geometric distortion fields are no-ops, this rectangle will be the same as the
+ * post-distortion-corrected rectangle given in {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.</p>
* <p>This rectangle is defined relative to the full pixel array; (0,0) is the top-left of
* the full pixel array, and the size of the full pixel array is given by
* {@link CameraCharacteristics#SENSOR_INFO_PIXEL_ARRAY_SIZE android.sensor.info.pixelArraySize}.</p>
@@ -3124,12 +3136,26 @@
* the following code snippet can be used:</p>
* <pre><code>// Returns true if the device supports the required hardware level, or better.
* boolean isHardwareLevelSupported(CameraCharacteristics c, int requiredLevel) {
+ * final int[] sortedHwLevels = {
+ * CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY,
+ * CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL,
+ * CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
+ * CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL,
+ * CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3
+ * };
* int deviceLevel = c.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
- * if (deviceLevel == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY) {
- * return requiredLevel == deviceLevel;
+ * if (requiredLevel == deviceLevel) {
+ * return true;
* }
- * // deviceLevel is not LEGACY, can use numerical sort
- * return requiredLevel <= deviceLevel;
+ *
+ * for (int sortedlevel : sortedHwLevels) {
+ * if (sortedlevel == requiredLevel) {
+ * return true;
+ * } else if (sortedlevel == deviceLevel) {
+ * return false;
+ * }
+ * }
+ * return false; // Should never reach here
* }
* </code></pre>
* <p>At a high level, the levels are:</p>
@@ -3143,6 +3169,8 @@
* post-processing settings, and image capture at a high rate.</li>
* <li><code>LEVEL_3</code> devices additionally support YUV reprocessing and RAW image capture, along
* with additional output stream configurations.</li>
+ * <li><code>EXTERNAL</code> devices are similar to <code>LIMITED</code> devices with exceptions like some sensor or
+ * lens information not reorted or less stable framerates.</li>
* </ul>
* <p>See the individual level enums for full descriptions of the supported capabilities. The
* {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} entry describes the device's capabilities at a
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index f47d464..ce88697 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -44,19 +44,27 @@
* {@link android.Manifest.permission#CAMERA Camera} permission in its manifest
* in order to access camera devices.</p>
*
- * <p>A given camera device may provide support at one of two levels: limited or
- * full. If a device only supports the limited level, then Camera2 exposes a
- * feature set that is roughly equivalent to the older
+ * <p>A given camera device may provide support at one of several levels defined
+ * in {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL}.
+ * If a device supports {@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY LEGACY} level,
+ * the camera device is running in backward compatibility mode and has minimum camera2 API support.
+ * If a device supports the {@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED LIMITED}
+ * level, then Camera2 exposes a feature set that is roughly equivalent to the older
* {@link android.hardware.Camera Camera} API, although with a cleaner and more
- * efficient interface. Devices that implement the full level of support
+ * efficient interface.
+ * If a device supports the {@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL EXTERNAL}
+ * level, then the device is a removable camera that provides similar but slightly less features
+ * as the {@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED LIMITED} level.
+ * Devices that implement the {@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_FULL FULL} or
+ * {@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_3 LEVEL3} level of support
* provide substantially improved capabilities over the older camera
- * API. Applications that target the limited level devices will run unchanged on
- * the full-level devices; if your application requires a full-level device for
+ * API. If your application requires a full-level device for
* proper operation, declare the "android.hardware.camera.level.full" feature in your
* manifest.</p>
*
* @see CameraManager#openCamera
* @see android.Manifest.permission#CAMERA
+ * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
*/
public abstract class CameraDevice implements AutoCloseable {
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index 411a97e..aca77a5 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -1269,11 +1269,26 @@
* Otherwise will always be present.</p>
* <p>The maximum number of regions supported by the device is determined by the value
* of {@link CameraCharacteristics#CONTROL_MAX_REGIONS_AE android.control.maxRegionsAe}.</p>
- * <p>The coordinate system is based on the active pixel array,
- * with (0,0) being the top-left pixel in the active pixel array, and
+ * <p>For devices not supporting {@link CaptureRequest#DISTORTION_CORRECTION_MODE android.distortionCorrection.mode} control, the coordinate
+ * system always follows that of {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, with (0,0) being
+ * the top-left pixel in the active pixel array, and
* ({@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.width - 1,
- * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.height - 1) being the
- * bottom-right pixel in the active pixel array.</p>
+ * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.height - 1) being the bottom-right pixel in the
+ * active pixel array.</p>
+ * <p>For devices supporting {@link CaptureRequest#DISTORTION_CORRECTION_MODE android.distortionCorrection.mode} control, the coordinate
+ * system depends on the mode being set.
+ * When the distortion correction mode is OFF, the coordinate system follows
+ * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}, with
+ * <code>(0, 0)</code> being the top-left pixel of the pre-correction active array, and
+ * ({@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}.width - 1,
+ * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}.height - 1) being the bottom-right
+ * pixel in the pre-correction active pixel array.
+ * When the distortion correction mode is not OFF, the coordinate system follows
+ * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, with
+ * <code>(0, 0)</code> being the top-left pixel of the active array, and
+ * ({@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.width - 1,
+ * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.height - 1) being the bottom-right pixel in the
+ * active pixel array.</p>
* <p>The weight must be within <code>[0, 1000]</code>, and represents a weight
* for every pixel in the area. This means that a large metering area
* with the same weight as a smaller area will have more effect in
@@ -1289,15 +1304,20 @@
* region and output only the intersection rectangle as the metering region in the result
* metadata. If the region is entirely outside the crop region, it will be ignored and
* not reported in the result metadata.</p>
- * <p><b>Units</b>: Pixel coordinates within {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}</p>
+ * <p><b>Units</b>: Pixel coordinates within {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize} or
+ * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize} depending on
+ * distortion correction capability and mode</p>
* <p><b>Range of valid values:</b><br>
* Coordinates must be between <code>[(0,0), (width, height))</code> of
- * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}</p>
+ * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize} or {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}
+ * depending on distortion correction capability and mode</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
*
* @see CameraCharacteristics#CONTROL_MAX_REGIONS_AE
+ * @see CaptureRequest#DISTORTION_CORRECTION_MODE
* @see CaptureRequest#SCALER_CROP_REGION
* @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
+ * @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
*/
@PublicKey
public static final Key<android.hardware.camera2.params.MeteringRectangle[]> CONTROL_AE_REGIONS =
@@ -1443,11 +1463,26 @@
* Otherwise will always be present.</p>
* <p>The maximum number of focus areas supported by the device is determined by the value
* of {@link CameraCharacteristics#CONTROL_MAX_REGIONS_AF android.control.maxRegionsAf}.</p>
- * <p>The coordinate system is based on the active pixel array,
- * with (0,0) being the top-left pixel in the active pixel array, and
+ * <p>For devices not supporting {@link CaptureRequest#DISTORTION_CORRECTION_MODE android.distortionCorrection.mode} control, the coordinate
+ * system always follows that of {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, with (0,0) being
+ * the top-left pixel in the active pixel array, and
* ({@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.width - 1,
- * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.height - 1) being the
- * bottom-right pixel in the active pixel array.</p>
+ * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.height - 1) being the bottom-right pixel in the
+ * active pixel array.</p>
+ * <p>For devices supporting {@link CaptureRequest#DISTORTION_CORRECTION_MODE android.distortionCorrection.mode} control, the coordinate
+ * system depends on the mode being set.
+ * When the distortion correction mode is OFF, the coordinate system follows
+ * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}, with
+ * <code>(0, 0)</code> being the top-left pixel of the pre-correction active array, and
+ * ({@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}.width - 1,
+ * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}.height - 1) being the bottom-right
+ * pixel in the pre-correction active pixel array.
+ * When the distortion correction mode is not OFF, the coordinate system follows
+ * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, with
+ * <code>(0, 0)</code> being the top-left pixel of the active array, and
+ * ({@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.width - 1,
+ * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.height - 1) being the bottom-right pixel in the
+ * active pixel array.</p>
* <p>The weight must be within <code>[0, 1000]</code>, and represents a weight
* for every pixel in the area. This means that a large metering area
* with the same weight as a smaller area will have more effect in
@@ -1464,15 +1499,20 @@
* region and output only the intersection rectangle as the metering region in the result
* metadata. If the region is entirely outside the crop region, it will be ignored and
* not reported in the result metadata.</p>
- * <p><b>Units</b>: Pixel coordinates within {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}</p>
+ * <p><b>Units</b>: Pixel coordinates within {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize} or
+ * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize} depending on
+ * distortion correction capability and mode</p>
* <p><b>Range of valid values:</b><br>
* Coordinates must be between <code>[(0,0), (width, height))</code> of
- * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}</p>
+ * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize} or {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}
+ * depending on distortion correction capability and mode</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
*
* @see CameraCharacteristics#CONTROL_MAX_REGIONS_AF
+ * @see CaptureRequest#DISTORTION_CORRECTION_MODE
* @see CaptureRequest#SCALER_CROP_REGION
* @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
+ * @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
*/
@PublicKey
public static final Key<android.hardware.camera2.params.MeteringRectangle[]> CONTROL_AF_REGIONS =
@@ -1612,11 +1652,26 @@
* Otherwise will always be present.</p>
* <p>The maximum number of regions supported by the device is determined by the value
* of {@link CameraCharacteristics#CONTROL_MAX_REGIONS_AWB android.control.maxRegionsAwb}.</p>
- * <p>The coordinate system is based on the active pixel array,
- * with (0,0) being the top-left pixel in the active pixel array, and
+ * <p>For devices not supporting {@link CaptureRequest#DISTORTION_CORRECTION_MODE android.distortionCorrection.mode} control, the coordinate
+ * system always follows that of {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, with (0,0) being
+ * the top-left pixel in the active pixel array, and
* ({@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.width - 1,
- * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.height - 1) being the
- * bottom-right pixel in the active pixel array.</p>
+ * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.height - 1) being the bottom-right pixel in the
+ * active pixel array.</p>
+ * <p>For devices supporting {@link CaptureRequest#DISTORTION_CORRECTION_MODE android.distortionCorrection.mode} control, the coordinate
+ * system depends on the mode being set.
+ * When the distortion correction mode is OFF, the coordinate system follows
+ * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}, with
+ * <code>(0, 0)</code> being the top-left pixel of the pre-correction active array, and
+ * ({@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}.width - 1,
+ * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}.height - 1) being the bottom-right
+ * pixel in the pre-correction active pixel array.
+ * When the distortion correction mode is not OFF, the coordinate system follows
+ * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, with
+ * <code>(0, 0)</code> being the top-left pixel of the active array, and
+ * ({@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.width - 1,
+ * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.height - 1) being the bottom-right pixel in the
+ * active pixel array.</p>
* <p>The weight must range from 0 to 1000, and represents a weight
* for every pixel in the area. This means that a large metering area
* with the same weight as a smaller area will have more effect in
@@ -1632,15 +1687,20 @@
* region and output only the intersection rectangle as the metering region in the result
* metadata. If the region is entirely outside the crop region, it will be ignored and
* not reported in the result metadata.</p>
- * <p><b>Units</b>: Pixel coordinates within {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}</p>
+ * <p><b>Units</b>: Pixel coordinates within {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize} or
+ * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize} depending on
+ * distortion correction capability and mode</p>
* <p><b>Range of valid values:</b><br>
* Coordinates must be between <code>[(0,0), (width, height))</code> of
- * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}</p>
+ * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize} or {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}
+ * depending on distortion correction capability and mode</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
*
* @see CameraCharacteristics#CONTROL_MAX_REGIONS_AWB
+ * @see CaptureRequest#DISTORTION_CORRECTION_MODE
* @see CaptureRequest#SCALER_CROP_REGION
* @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
+ * @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
*/
@PublicKey
public static final Key<android.hardware.camera2.params.MeteringRectangle[]> CONTROL_AWB_REGIONS =
@@ -2433,9 +2493,17 @@
/**
* <p>The desired region of the sensor to read out for this capture.</p>
* <p>This control can be used to implement digital zoom.</p>
- * <p>The crop region coordinate system is based off
- * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, with <code>(0, 0)</code> being the
- * top-left corner of the sensor active array.</p>
+ * <p>For devices not supporting {@link CaptureRequest#DISTORTION_CORRECTION_MODE android.distortionCorrection.mode} control, the coordinate
+ * system always follows that of {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, with <code>(0, 0)</code> being
+ * the top-left pixel of the active array.</p>
+ * <p>For devices supporting {@link CaptureRequest#DISTORTION_CORRECTION_MODE android.distortionCorrection.mode} control, the coordinate
+ * system depends on the mode being set.
+ * When the distortion correction mode is OFF, the coordinate system follows
+ * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}, with
+ * <code>(0, 0)</code> being the top-left pixel of the pre-correction active array.
+ * When the distortion correction mode is not OFF, the coordinate system follows
+ * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, with
+ * <code>(0, 0)</code> being the top-left pixel of the active array.</p>
* <p>Output streams use this rectangle to produce their output,
* cropping to a smaller region if necessary to maintain the
* stream's aspect ratio, then scaling the sensor input to
@@ -2454,20 +2522,30 @@
* outputs will crop horizontally (pillarbox), and 16:9
* streams will match exactly. These additional crops will
* be centered within the crop region.</p>
- * <p>The width and height of the crop region cannot
- * be set to be smaller than
+ * <p>If the coordinate system is android.sensor.info.activeArraysSize, the width and height
+ * of the crop region cannot be set to be smaller than
* <code>floor( activeArraySize.width / {@link CameraCharacteristics#SCALER_AVAILABLE_MAX_DIGITAL_ZOOM android.scaler.availableMaxDigitalZoom} )</code> and
* <code>floor( activeArraySize.height / {@link CameraCharacteristics#SCALER_AVAILABLE_MAX_DIGITAL_ZOOM android.scaler.availableMaxDigitalZoom} )</code>, respectively.</p>
+ * <p>If the coordinate system is {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}, the width
+ * and height of the crop region cannot be set to be smaller than
+ * <code>floor( preCorrectionActiveArraySize.width / {@link CameraCharacteristics#SCALER_AVAILABLE_MAX_DIGITAL_ZOOM android.scaler.availableMaxDigitalZoom} )</code>
+ * and
+ * <code>floor( preCorrectionActiveArraySize.height / {@link CameraCharacteristics#SCALER_AVAILABLE_MAX_DIGITAL_ZOOM android.scaler.availableMaxDigitalZoom} )</code>,
+ * respectively.</p>
* <p>The camera device may adjust the crop region to account
* for rounding and other hardware requirements; the final
* crop region used will be included in the output capture
* result.</p>
* <p><b>Units</b>: Pixel coordinates relative to
- * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}</p>
+ * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize} or
+ * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize} depending on distortion correction
+ * capability and mode</p>
* <p>This key is available on all devices.</p>
*
+ * @see CaptureRequest#DISTORTION_CORRECTION_MODE
* @see CameraCharacteristics#SCALER_AVAILABLE_MAX_DIGITAL_ZOOM
* @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
+ * @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
*/
@PublicKey
public static final Key<android.graphics.Rect> SCALER_CROP_REGION =
@@ -3186,15 +3264,14 @@
* any correction at all would slow down capture rate. Every output stream will have a
* similar amount of enhancement applied.</p>
* <p>The correction only applies to processed outputs such as YUV, JPEG, or DEPTH16; it is not
- * applied to any RAW output. Metadata coordinates such as face rectangles or metering
+ * applied to any RAW output. Metadata coordinates such as face rectangles or metering
* regions are also not affected by correction.</p>
- * <p>Applications enabling distortion correction need to pay extra attention when converting
- * image coordinates between corrected output buffers and the sensor array. For example, if
- * the app supports tap-to-focus and enables correction, it then has to apply the distortion
- * model described in {@link CameraCharacteristics#LENS_DISTORTION android.lens.distortion} to the image buffer tap coordinates to properly
- * calculate the tap position on the sensor active array to be used with
- * {@link CaptureRequest#CONTROL_AF_REGIONS android.control.afRegions}. The same applies in reverse to detected face rectangles if
- * they need to be drawn on top of the corrected output buffers.</p>
+ * <p>This control will be on by default on devices that support this control. Applications
+ * disabling distortion correction need to pay extra attention with the coordinate system of
+ * metering regions, crop region, and face rectangles. When distortion correction is OFF,
+ * metadata coordinates follow the coordinate system of
+ * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}. When distortion is not OFF, metadata
+ * coordinates follow the coordinate system of {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.</p>
* <p><b>Possible values:</b>
* <ul>
* <li>{@link #DISTORTION_CORRECTION_MODE_OFF OFF}</li>
@@ -3205,9 +3282,10 @@
* {@link CameraCharacteristics#DISTORTION_CORRECTION_AVAILABLE_MODES android.distortionCorrection.availableModes}</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
*
- * @see CaptureRequest#CONTROL_AF_REGIONS
* @see CameraCharacteristics#DISTORTION_CORRECTION_AVAILABLE_MODES
* @see CameraCharacteristics#LENS_DISTORTION
+ * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
+ * @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
* @see #DISTORTION_CORRECTION_MODE_OFF
* @see #DISTORTION_CORRECTION_MODE_FAST
* @see #DISTORTION_CORRECTION_MODE_HIGH_QUALITY
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index 361d83d..d003f9a 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -730,11 +730,26 @@
* Otherwise will always be present.</p>
* <p>The maximum number of regions supported by the device is determined by the value
* of {@link CameraCharacteristics#CONTROL_MAX_REGIONS_AE android.control.maxRegionsAe}.</p>
- * <p>The coordinate system is based on the active pixel array,
- * with (0,0) being the top-left pixel in the active pixel array, and
+ * <p>For devices not supporting {@link CaptureRequest#DISTORTION_CORRECTION_MODE android.distortionCorrection.mode} control, the coordinate
+ * system always follows that of {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, with (0,0) being
+ * the top-left pixel in the active pixel array, and
* ({@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.width - 1,
- * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.height - 1) being the
- * bottom-right pixel in the active pixel array.</p>
+ * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.height - 1) being the bottom-right pixel in the
+ * active pixel array.</p>
+ * <p>For devices supporting {@link CaptureRequest#DISTORTION_CORRECTION_MODE android.distortionCorrection.mode} control, the coordinate
+ * system depends on the mode being set.
+ * When the distortion correction mode is OFF, the coordinate system follows
+ * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}, with
+ * <code>(0, 0)</code> being the top-left pixel of the pre-correction active array, and
+ * ({@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}.width - 1,
+ * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}.height - 1) being the bottom-right
+ * pixel in the pre-correction active pixel array.
+ * When the distortion correction mode is not OFF, the coordinate system follows
+ * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, with
+ * <code>(0, 0)</code> being the top-left pixel of the active array, and
+ * ({@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.width - 1,
+ * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.height - 1) being the bottom-right pixel in the
+ * active pixel array.</p>
* <p>The weight must be within <code>[0, 1000]</code>, and represents a weight
* for every pixel in the area. This means that a large metering area
* with the same weight as a smaller area will have more effect in
@@ -750,15 +765,20 @@
* region and output only the intersection rectangle as the metering region in the result
* metadata. If the region is entirely outside the crop region, it will be ignored and
* not reported in the result metadata.</p>
- * <p><b>Units</b>: Pixel coordinates within {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}</p>
+ * <p><b>Units</b>: Pixel coordinates within {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize} or
+ * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize} depending on
+ * distortion correction capability and mode</p>
* <p><b>Range of valid values:</b><br>
* Coordinates must be between <code>[(0,0), (width, height))</code> of
- * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}</p>
+ * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize} or {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}
+ * depending on distortion correction capability and mode</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
*
* @see CameraCharacteristics#CONTROL_MAX_REGIONS_AE
+ * @see CaptureRequest#DISTORTION_CORRECTION_MODE
* @see CaptureRequest#SCALER_CROP_REGION
* @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
+ * @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
*/
@PublicKey
public static final Key<android.hardware.camera2.params.MeteringRectangle[]> CONTROL_AE_REGIONS =
@@ -1152,11 +1172,26 @@
* Otherwise will always be present.</p>
* <p>The maximum number of focus areas supported by the device is determined by the value
* of {@link CameraCharacteristics#CONTROL_MAX_REGIONS_AF android.control.maxRegionsAf}.</p>
- * <p>The coordinate system is based on the active pixel array,
- * with (0,0) being the top-left pixel in the active pixel array, and
+ * <p>For devices not supporting {@link CaptureRequest#DISTORTION_CORRECTION_MODE android.distortionCorrection.mode} control, the coordinate
+ * system always follows that of {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, with (0,0) being
+ * the top-left pixel in the active pixel array, and
* ({@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.width - 1,
- * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.height - 1) being the
- * bottom-right pixel in the active pixel array.</p>
+ * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.height - 1) being the bottom-right pixel in the
+ * active pixel array.</p>
+ * <p>For devices supporting {@link CaptureRequest#DISTORTION_CORRECTION_MODE android.distortionCorrection.mode} control, the coordinate
+ * system depends on the mode being set.
+ * When the distortion correction mode is OFF, the coordinate system follows
+ * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}, with
+ * <code>(0, 0)</code> being the top-left pixel of the pre-correction active array, and
+ * ({@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}.width - 1,
+ * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}.height - 1) being the bottom-right
+ * pixel in the pre-correction active pixel array.
+ * When the distortion correction mode is not OFF, the coordinate system follows
+ * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, with
+ * <code>(0, 0)</code> being the top-left pixel of the active array, and
+ * ({@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.width - 1,
+ * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.height - 1) being the bottom-right pixel in the
+ * active pixel array.</p>
* <p>The weight must be within <code>[0, 1000]</code>, and represents a weight
* for every pixel in the area. This means that a large metering area
* with the same weight as a smaller area will have more effect in
@@ -1173,15 +1208,20 @@
* region and output only the intersection rectangle as the metering region in the result
* metadata. If the region is entirely outside the crop region, it will be ignored and
* not reported in the result metadata.</p>
- * <p><b>Units</b>: Pixel coordinates within {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}</p>
+ * <p><b>Units</b>: Pixel coordinates within {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize} or
+ * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize} depending on
+ * distortion correction capability and mode</p>
* <p><b>Range of valid values:</b><br>
* Coordinates must be between <code>[(0,0), (width, height))</code> of
- * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}</p>
+ * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize} or {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}
+ * depending on distortion correction capability and mode</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
*
* @see CameraCharacteristics#CONTROL_MAX_REGIONS_AF
+ * @see CaptureRequest#DISTORTION_CORRECTION_MODE
* @see CaptureRequest#SCALER_CROP_REGION
* @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
+ * @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
*/
@PublicKey
public static final Key<android.hardware.camera2.params.MeteringRectangle[]> CONTROL_AF_REGIONS =
@@ -1730,11 +1770,26 @@
* Otherwise will always be present.</p>
* <p>The maximum number of regions supported by the device is determined by the value
* of {@link CameraCharacteristics#CONTROL_MAX_REGIONS_AWB android.control.maxRegionsAwb}.</p>
- * <p>The coordinate system is based on the active pixel array,
- * with (0,0) being the top-left pixel in the active pixel array, and
+ * <p>For devices not supporting {@link CaptureRequest#DISTORTION_CORRECTION_MODE android.distortionCorrection.mode} control, the coordinate
+ * system always follows that of {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, with (0,0) being
+ * the top-left pixel in the active pixel array, and
* ({@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.width - 1,
- * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.height - 1) being the
- * bottom-right pixel in the active pixel array.</p>
+ * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.height - 1) being the bottom-right pixel in the
+ * active pixel array.</p>
+ * <p>For devices supporting {@link CaptureRequest#DISTORTION_CORRECTION_MODE android.distortionCorrection.mode} control, the coordinate
+ * system depends on the mode being set.
+ * When the distortion correction mode is OFF, the coordinate system follows
+ * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}, with
+ * <code>(0, 0)</code> being the top-left pixel of the pre-correction active array, and
+ * ({@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}.width - 1,
+ * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}.height - 1) being the bottom-right
+ * pixel in the pre-correction active pixel array.
+ * When the distortion correction mode is not OFF, the coordinate system follows
+ * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, with
+ * <code>(0, 0)</code> being the top-left pixel of the active array, and
+ * ({@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.width - 1,
+ * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.height - 1) being the bottom-right pixel in the
+ * active pixel array.</p>
* <p>The weight must range from 0 to 1000, and represents a weight
* for every pixel in the area. This means that a large metering area
* with the same weight as a smaller area will have more effect in
@@ -1750,15 +1805,20 @@
* region and output only the intersection rectangle as the metering region in the result
* metadata. If the region is entirely outside the crop region, it will be ignored and
* not reported in the result metadata.</p>
- * <p><b>Units</b>: Pixel coordinates within {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}</p>
+ * <p><b>Units</b>: Pixel coordinates within {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize} or
+ * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize} depending on
+ * distortion correction capability and mode</p>
* <p><b>Range of valid values:</b><br>
* Coordinates must be between <code>[(0,0), (width, height))</code> of
- * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}</p>
+ * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize} or {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}
+ * depending on distortion correction capability and mode</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
*
* @see CameraCharacteristics#CONTROL_MAX_REGIONS_AWB
+ * @see CaptureRequest#DISTORTION_CORRECTION_MODE
* @see CaptureRequest#SCALER_CROP_REGION
* @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
+ * @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
*/
@PublicKey
public static final Key<android.hardware.camera2.params.MeteringRectangle[]> CONTROL_AWB_REGIONS =
@@ -3099,9 +3159,17 @@
/**
* <p>The desired region of the sensor to read out for this capture.</p>
* <p>This control can be used to implement digital zoom.</p>
- * <p>The crop region coordinate system is based off
- * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, with <code>(0, 0)</code> being the
- * top-left corner of the sensor active array.</p>
+ * <p>For devices not supporting {@link CaptureRequest#DISTORTION_CORRECTION_MODE android.distortionCorrection.mode} control, the coordinate
+ * system always follows that of {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, with <code>(0, 0)</code> being
+ * the top-left pixel of the active array.</p>
+ * <p>For devices supporting {@link CaptureRequest#DISTORTION_CORRECTION_MODE android.distortionCorrection.mode} control, the coordinate
+ * system depends on the mode being set.
+ * When the distortion correction mode is OFF, the coordinate system follows
+ * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}, with
+ * <code>(0, 0)</code> being the top-left pixel of the pre-correction active array.
+ * When the distortion correction mode is not OFF, the coordinate system follows
+ * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, with
+ * <code>(0, 0)</code> being the top-left pixel of the active array.</p>
* <p>Output streams use this rectangle to produce their output,
* cropping to a smaller region if necessary to maintain the
* stream's aspect ratio, then scaling the sensor input to
@@ -3120,20 +3188,30 @@
* outputs will crop horizontally (pillarbox), and 16:9
* streams will match exactly. These additional crops will
* be centered within the crop region.</p>
- * <p>The width and height of the crop region cannot
- * be set to be smaller than
+ * <p>If the coordinate system is android.sensor.info.activeArraysSize, the width and height
+ * of the crop region cannot be set to be smaller than
* <code>floor( activeArraySize.width / {@link CameraCharacteristics#SCALER_AVAILABLE_MAX_DIGITAL_ZOOM android.scaler.availableMaxDigitalZoom} )</code> and
* <code>floor( activeArraySize.height / {@link CameraCharacteristics#SCALER_AVAILABLE_MAX_DIGITAL_ZOOM android.scaler.availableMaxDigitalZoom} )</code>, respectively.</p>
+ * <p>If the coordinate system is {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}, the width
+ * and height of the crop region cannot be set to be smaller than
+ * <code>floor( preCorrectionActiveArraySize.width / {@link CameraCharacteristics#SCALER_AVAILABLE_MAX_DIGITAL_ZOOM android.scaler.availableMaxDigitalZoom} )</code>
+ * and
+ * <code>floor( preCorrectionActiveArraySize.height / {@link CameraCharacteristics#SCALER_AVAILABLE_MAX_DIGITAL_ZOOM android.scaler.availableMaxDigitalZoom} )</code>,
+ * respectively.</p>
* <p>The camera device may adjust the crop region to account
* for rounding and other hardware requirements; the final
* crop region used will be included in the output capture
* result.</p>
* <p><b>Units</b>: Pixel coordinates relative to
- * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}</p>
+ * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize} or
+ * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize} depending on distortion correction
+ * capability and mode</p>
* <p>This key is available on all devices.</p>
*
+ * @see CaptureRequest#DISTORTION_CORRECTION_MODE
* @see CameraCharacteristics#SCALER_AVAILABLE_MAX_DIGITAL_ZOOM
* @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
+ * @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
*/
@PublicKey
public static final Key<android.graphics.Rect> SCALER_CROP_REGION =
@@ -3624,12 +3702,23 @@
/**
* <p>List of landmarks for detected
* faces.</p>
- * <p>The coordinate system is that of {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, with
+ * <p>For devices not supporting {@link CaptureRequest#DISTORTION_CORRECTION_MODE android.distortionCorrection.mode} control, the coordinate
+ * system always follows that of {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, with <code>(0, 0)</code> being
+ * the top-left pixel of the active array.</p>
+ * <p>For devices supporting {@link CaptureRequest#DISTORTION_CORRECTION_MODE android.distortionCorrection.mode} control, the coordinate
+ * system depends on the mode being set.
+ * When the distortion correction mode is OFF, the coordinate system follows
+ * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}, with
+ * <code>(0, 0)</code> being the top-left pixel of the pre-correction active array.
+ * When the distortion correction mode is not OFF, the coordinate system follows
+ * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, with
* <code>(0, 0)</code> being the top-left pixel of the active array.</p>
* <p>Only available if {@link CaptureRequest#STATISTICS_FACE_DETECT_MODE android.statistics.faceDetectMode} == FULL
* This key is available on all devices.</p>
*
+ * @see CaptureRequest#DISTORTION_CORRECTION_MODE
* @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
+ * @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
* @see CaptureRequest#STATISTICS_FACE_DETECT_MODE
* @hide
*/
@@ -3639,12 +3728,23 @@
/**
* <p>List of the bounding rectangles for detected
* faces.</p>
- * <p>The coordinate system is that of {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, with
+ * <p>For devices not supporting {@link CaptureRequest#DISTORTION_CORRECTION_MODE android.distortionCorrection.mode} control, the coordinate
+ * system always follows that of {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, with <code>(0, 0)</code> being
+ * the top-left pixel of the active array.</p>
+ * <p>For devices supporting {@link CaptureRequest#DISTORTION_CORRECTION_MODE android.distortionCorrection.mode} control, the coordinate
+ * system depends on the mode being set.
+ * When the distortion correction mode is OFF, the coordinate system follows
+ * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}, with
+ * <code>(0, 0)</code> being the top-left pixel of the pre-correction active array.
+ * When the distortion correction mode is not OFF, the coordinate system follows
+ * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, with
* <code>(0, 0)</code> being the top-left pixel of the active array.</p>
* <p>Only available if {@link CaptureRequest#STATISTICS_FACE_DETECT_MODE android.statistics.faceDetectMode} != OFF
* This key is available on all devices.</p>
*
+ * @see CaptureRequest#DISTORTION_CORRECTION_MODE
* @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
+ * @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
* @see CaptureRequest#STATISTICS_FACE_DETECT_MODE
* @hide
*/
@@ -4478,15 +4578,14 @@
* any correction at all would slow down capture rate. Every output stream will have a
* similar amount of enhancement applied.</p>
* <p>The correction only applies to processed outputs such as YUV, JPEG, or DEPTH16; it is not
- * applied to any RAW output. Metadata coordinates such as face rectangles or metering
+ * applied to any RAW output. Metadata coordinates such as face rectangles or metering
* regions are also not affected by correction.</p>
- * <p>Applications enabling distortion correction need to pay extra attention when converting
- * image coordinates between corrected output buffers and the sensor array. For example, if
- * the app supports tap-to-focus and enables correction, it then has to apply the distortion
- * model described in {@link CameraCharacteristics#LENS_DISTORTION android.lens.distortion} to the image buffer tap coordinates to properly
- * calculate the tap position on the sensor active array to be used with
- * {@link CaptureRequest#CONTROL_AF_REGIONS android.control.afRegions}. The same applies in reverse to detected face rectangles if
- * they need to be drawn on top of the corrected output buffers.</p>
+ * <p>This control will be on by default on devices that support this control. Applications
+ * disabling distortion correction need to pay extra attention with the coordinate system of
+ * metering regions, crop region, and face rectangles. When distortion correction is OFF,
+ * metadata coordinates follow the coordinate system of
+ * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}. When distortion is not OFF, metadata
+ * coordinates follow the coordinate system of {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.</p>
* <p><b>Possible values:</b>
* <ul>
* <li>{@link #DISTORTION_CORRECTION_MODE_OFF OFF}</li>
@@ -4497,9 +4596,10 @@
* {@link CameraCharacteristics#DISTORTION_CORRECTION_AVAILABLE_MODES android.distortionCorrection.availableModes}</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
*
- * @see CaptureRequest#CONTROL_AF_REGIONS
* @see CameraCharacteristics#DISTORTION_CORRECTION_AVAILABLE_MODES
* @see CameraCharacteristics#LENS_DISTORTION
+ * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
+ * @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
* @see #DISTORTION_CORRECTION_MODE_OFF
* @see #DISTORTION_CORRECTION_MODE_FAST
* @see #DISTORTION_CORRECTION_MODE_HIGH_QUALITY
diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
index 4baf263..86bd30c 100644
--- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
+++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
@@ -747,6 +747,9 @@
if (faceDetectMode == null) {
Log.w(TAG, "Face detect mode metadata is null, assuming the mode is SIMPLE");
faceDetectMode = CaptureResult.STATISTICS_FACE_DETECT_MODE_SIMPLE;
+ } else if (faceDetectMode > CaptureResult.STATISTICS_FACE_DETECT_MODE_FULL) {
+ // Face detect mode is larger than FULL, assuming the mode is FULL
+ faceDetectMode = CaptureResult.STATISTICS_FACE_DETECT_MODE_FULL;
} else {
if (faceDetectMode == CaptureResult.STATISTICS_FACE_DETECT_MODE_OFF) {
return new Face[0];
diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java
index a040a09..1ee3c93 100644
--- a/core/java/android/hardware/camera2/params/OutputConfiguration.java
+++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java
@@ -82,9 +82,9 @@
*
* </ul>
*
- * <p> As of {@link android.os.Build.VERSION_CODES#P Android P}, all formats can be used for
- * sharing, subject to device support. On prior API levels, only {@link ImageFormat#PRIVATE}
- * format may be used.</p>
+ * <p> As of {@link android.os.Build.VERSION_CODES#P Android P}, all formats except
+ * {@link ImageFormat#JPEG} and {@link ImageFormat#RAW_PRIVATE} can be used for sharing, subject to
+ * device support. On prior API levels, only {@link ImageFormat#PRIVATE} format may be used.</p>
*
* @see CameraDevice#createCaptureSessionByOutputConfigurations
*
diff --git a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
index c56b685..414c463 100644
--- a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
+++ b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
@@ -725,7 +725,7 @@
* Get the minimum {@link CaptureRequest#SENSOR_FRAME_DURATION frame duration}
* for the class/size combination (in nanoseconds).
*
- * <p>This assumes a the {@code klass} is set up to use {@link ImageFormat#PRIVATE}.
+ * <p>This assumes that the {@code klass} is set up to use {@link ImageFormat#PRIVATE}.
* For user-defined formats, use {@link #getOutputMinFrameDuration(int, Size)}.</p>
*
* <p>{@code klass} should be one of the ones which is supported by
@@ -870,7 +870,7 @@
/**
* Get the stall duration for the class/size combination (in nanoseconds).
*
- * <p>This assumes a the {@code klass} is set up to use {@link ImageFormat#PRIVATE}.
+ * <p>This assumes that the {@code klass} is set up to use {@link ImageFormat#PRIVATE}.
* For user-defined formats, use {@link #getOutputMinFrameDuration(int, Size)}.</p>
*
* <p>{@code klass} should be one of the ones with a non-empty array returned by
diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java
index 504f840..7840fd0 100644
--- a/core/java/android/hardware/display/DisplayManagerInternal.java
+++ b/core/java/android/hardware/display/DisplayManagerInternal.java
@@ -23,6 +23,7 @@
import android.util.SparseArray;
import android.view.Display;
import android.view.DisplayInfo;
+import android.view.Surface;
import android.view.SurfaceControl;
/**
@@ -62,6 +63,15 @@
public abstract boolean isProximitySensorAvailable();
/**
+ * Take a screenshot of the specified display into the provided {@link Surface}.
+ *
+ * @param displayId The display id to take the screenshot of.
+ * @param outSurface The {@link Surface} to take the screenshot into.
+ * @return True if the screenshot is taken.
+ */
+ public abstract boolean screenshot(int displayId, Surface outSurface);
+
+ /**
* Returns information about the specified logical display.
*
* @param displayId The logical display id.
diff --git a/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionCategory.java b/core/java/android/hardware/face/Face.aidl
similarity index 65%
copy from packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionCategory.java
copy to core/java/android/hardware/face/Face.aidl
index 19e556a..a7c9141 100644
--- a/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionCategory.java
+++ b/core/java/android/hardware/face/Face.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,13 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package android.hardware.face;
-package com.android.settingslib.suggestions;
-
-public class SuggestionCategory {
- public String category;
- public String pkg;
- public boolean multiple;
- public boolean exclusive;
- public long exclusiveExpireDaysInMillis;
-}
+/**
+ * @hide
+ */
+parcelable Face;
diff --git a/core/java/android/hardware/face/Face.java b/core/java/android/hardware/face/Face.java
new file mode 100644
index 0000000..6a508ac
--- /dev/null
+++ b/core/java/android/hardware/face/Face.java
@@ -0,0 +1,100 @@
+/*
+ * 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.hardware.face;
+
+import android.hardware.biometrics.BiometricAuthenticator;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Container for face metadata.
+ *
+ * @hide
+ */
+public final class Face extends BiometricAuthenticator.Identifier {
+ private CharSequence mName;
+ private int mFaceId;
+ private long mDeviceId; // physical device this face is associated with
+
+ public Face(CharSequence name, int faceId, long deviceId) {
+ mName = name;
+ mFaceId = faceId;
+ mDeviceId = deviceId;
+ }
+
+ private Face(Parcel in) {
+ mName = in.readString();
+ mFaceId = in.readInt();
+ mDeviceId = in.readLong();
+ }
+
+ /**
+ * Gets the human-readable name for the given fingerprint.
+ * @return name given to finger
+ */
+ public CharSequence getName() {
+ return mName;
+ }
+
+ /**
+ * Gets the device-specific finger id. Used by Settings to map a name to a specific
+ * fingerprint template.
+ * @return device-specific id for this finger
+ * @hide
+ */
+ public int getFaceId() {
+ return mFaceId;
+ }
+
+ /**
+ * Device this face belongs to.
+ *
+ * @hide
+ */
+ public long getDeviceId() {
+ return mDeviceId;
+ }
+
+ /**
+ * Describes the contents.
+ * @return
+ */
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * Writes to a parcel.
+ * @param out
+ * @param flags Additional flags about how the object should be written.
+ */
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeString(mName.toString());
+ out.writeInt(mFaceId);
+ out.writeLong(mDeviceId);
+ }
+
+ public static final Parcelable.Creator<Face> CREATOR = new Parcelable.Creator<Face>() {
+ public Face createFromParcel(Parcel in) {
+ return new Face(in);
+ }
+
+ public Face[] newArray(int size) {
+ return new Face[size];
+ }
+ };
+}
diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java
new file mode 100644
index 0000000..3de9de3
--- /dev/null
+++ b/core/java/android/hardware/face/FaceManager.java
@@ -0,0 +1,1007 @@
+/**
+ * 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.hardware.face;
+
+import static android.Manifest.permission.INTERACT_ACROSS_USERS;
+import static android.Manifest.permission.MANAGE_FACE;
+import static android.Manifest.permission.USE_BIOMETRIC;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemService;
+import android.app.ActivityManager;
+import android.content.Context;
+import android.hardware.biometrics.BiometricAuthenticator;
+import android.hardware.biometrics.BiometricFaceConstants;
+import android.hardware.biometrics.BiometricPrompt;
+import android.hardware.biometrics.CryptoObject;
+import android.hardware.biometrics.IBiometricPromptReceiver;
+import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.os.CancellationSignal.OnCancelListener;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.IRemoteCallback;
+import android.os.Looper;
+import android.os.PowerManager;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.Log;
+import android.util.Slog;
+
+import com.android.internal.R;
+
+import java.util.List;
+import java.util.concurrent.Executor;
+
+/**
+ * A class that coordinates access to the face authentication hardware.
+ * @hide
+ */
+@SystemService(Context.FACE_SERVICE)
+public class FaceManager implements BiometricFaceConstants {
+
+
+ private static final String TAG = "FaceManager";
+ private static final boolean DEBUG = true;
+ private static final int MSG_ENROLL_RESULT = 100;
+ private static final int MSG_ACQUIRED = 101;
+ private static final int MSG_AUTHENTICATION_SUCCEEDED = 102;
+ private static final int MSG_AUTHENTICATION_FAILED = 103;
+ private static final int MSG_ERROR = 104;
+ private static final int MSG_REMOVED = 105;
+
+ private IFaceService mService;
+ private final Context mContext;
+ private IBinder mToken = new Binder();
+ private BiometricAuthenticator.AuthenticationCallback mAuthenticationCallback;
+ private EnrollmentCallback mEnrollmentCallback;
+ private RemovalCallback mRemovalCallback;
+ private CryptoObject mCryptoObject;
+ private Face mRemovalFace;
+ private Handler mHandler;
+ private Executor mExecutor;
+
+ private IFaceServiceReceiver mServiceReceiver = new IFaceServiceReceiver.Stub() {
+
+ @Override // binder call
+ public void onEnrollResult(long deviceId, int faceId, int remaining) {
+ mHandler.obtainMessage(MSG_ENROLL_RESULT, remaining, 0,
+ new Face(null, faceId, deviceId)).sendToTarget();
+ }
+
+ @Override // binder call
+ public void onAcquired(long deviceId, int acquireInfo, int vendorCode) {
+ mHandler.obtainMessage(MSG_ACQUIRED, acquireInfo, vendorCode, deviceId).sendToTarget();
+ }
+
+ @Override // binder call
+ public void onAuthenticationSucceeded(long deviceId, Face face) {
+ mHandler.obtainMessage(MSG_AUTHENTICATION_SUCCEEDED, face).sendToTarget();
+ }
+
+ @Override // binder call
+ public void onAuthenticationFailed(long deviceId) {
+ mHandler.obtainMessage(MSG_AUTHENTICATION_FAILED).sendToTarget();
+ }
+
+ @Override // binder call
+ public void onError(long deviceId, int error, int vendorCode) {
+ mHandler.obtainMessage(MSG_ERROR, error, vendorCode, deviceId).sendToTarget();
+ }
+
+ @Override // binder call
+ public void onRemoved(long deviceId, int faceId, int remaining) {
+ mHandler.obtainMessage(MSG_REMOVED, remaining, 0,
+ new Face(null, faceId, deviceId)).sendToTarget();
+ }
+ };
+
+ /**
+ * @hide
+ */
+ public FaceManager(Context context, IFaceService service) {
+ mContext = context;
+ mService = service;
+ if (mService == null) {
+ Slog.v(TAG, "FaceAuthenticationManagerService was null");
+ }
+ mHandler = new MyHandler(context);
+ }
+
+ /**
+ * Request authentication of a crypto object. This call operates the face recognition hardware
+ * and starts capturing images. It terminates when
+ * {@link AuthenticationCallback#onAuthenticationError(int, CharSequence)} or
+ * {@link AuthenticationCallback#onAuthenticationSucceeded(AuthenticationResult)} is called, at
+ * which point the object is no longer valid. The operation can be canceled by using the
+ * provided cancel object.
+ *
+ * @param crypto object associated with the call or null if none required.
+ * @param cancel an object that can be used to cancel authentication
+ * @param flags optional flags; should be 0
+ * @param callback an object to receive authentication events
+ * @param handler an optional handler to handle callback events
+ * @throws IllegalArgumentException if the crypto operation is not supported or is not backed
+ * by
+ * <a href="{@docRoot}training/articles/keystore.html">Android
+ * Keystore facility</a>.
+ * @throws IllegalStateException if the crypto primitive is not initialized.
+ * @hide
+ */
+ @RequiresPermission(USE_BIOMETRIC)
+ public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel,
+ int flags, @NonNull AuthenticationCallback callback, @Nullable Handler handler) {
+ if (callback == null) {
+ throw new IllegalArgumentException("Must supply an authentication callback");
+ }
+
+ if (cancel != null) {
+ if (cancel.isCanceled()) {
+ Log.w(TAG, "authentication already canceled");
+ return;
+ } else {
+ cancel.setOnCancelListener(new OnAuthenticationCancelListener(crypto));
+ }
+ }
+
+ if (mService != null) {
+ try {
+ useHandler(handler);
+ mAuthenticationCallback = callback;
+ mCryptoObject = crypto;
+ long sessionId = crypto != null ? crypto.getOpId() : 0;
+ mService.authenticate(mToken, sessionId, mServiceReceiver, flags,
+ mContext.getOpPackageName(), null /* bundle */, null /* receiver */);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Remote exception while authenticating: ", e);
+ if (callback != null) {
+ // Though this may not be a hardware issue, it will cause apps to give up or try
+ // again later.
+ callback.onAuthenticationError(FACE_ERROR_HW_UNAVAILABLE,
+ getErrorString(FACE_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */));
+ }
+ }
+ }
+ }
+
+ /**
+ * Use the provided handler thread for events.
+ */
+ private void useHandler(Handler handler) {
+ if (handler != null) {
+ mHandler = new MyHandler(handler.getLooper());
+ } else if (mHandler.getLooper() != mContext.getMainLooper()) {
+ mHandler = new MyHandler(mContext.getMainLooper());
+ }
+ }
+
+ /**
+ * This method invokes the BiometricPrompt.
+ */
+ private void authenticateWithPrompt(@Nullable android.hardware.biometrics.CryptoObject crypto,
+ @NonNull CancellationSignal cancel,
+ @NonNull Bundle bundle,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull IBiometricPromptReceiver receiver,
+ @NonNull BiometricAuthenticator.AuthenticationCallback callback) {
+ mCryptoObject = crypto;
+ if (cancel.isCanceled()) {
+ Slog.w(TAG, "authentication already canceled");
+ return;
+ } else {
+ cancel.setOnCancelListener(new OnAuthenticationCancelListener(crypto));
+ }
+
+ if (mService != null) {
+ try {
+ mExecutor = executor;
+ mAuthenticationCallback = callback;
+ final long sessionId = crypto != null ? crypto.getOpId() : 0;
+ mService.authenticate(mToken, sessionId, mServiceReceiver,
+ 0 /* flags */, mContext.getOpPackageName(), bundle, receiver);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Remote exception while authenticating", e);
+ mExecutor.execute(() -> {
+ callback.onAuthenticationError(FACE_ERROR_HW_UNAVAILABLE,
+ getErrorString(FACE_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */));
+ });
+ }
+ }
+ }
+
+ /**
+ * Private method, see {@link BiometricPrompt#authenticate(CancellationSignal, Executor,
+ * BiometricPrompt.AuthenticationCallback)}
+ * @param cancel
+ * @param executor
+ * @param callback
+ * @hide
+ */
+ public void authenticate(
+ @NonNull CancellationSignal cancel,
+ @NonNull Bundle bundle,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull IBiometricPromptReceiver receiver,
+ @NonNull BiometricAuthenticator.AuthenticationCallback callback) {
+ if (cancel == null) {
+ throw new IllegalArgumentException("Must supply a cancellation signal");
+ }
+ if (bundle == null) {
+ throw new IllegalArgumentException("Must supply a bundle");
+ }
+ if (executor == null) {
+ throw new IllegalArgumentException("Must supply an executor");
+ }
+ if (receiver == null) {
+ throw new IllegalArgumentException("Must supply a receiver");
+ }
+ if (callback == null) {
+ throw new IllegalArgumentException("Must supply a calback");
+ }
+ authenticateWithPrompt(null, cancel, bundle, executor, receiver, callback);
+ }
+
+ /**
+ * Private method, see {@link BiometricPrompt#authenticate(BiometricPrompt.CryptoObject,
+ * CancellationSignal, Executor, BiometricPrompt.AuthenticationCallback)}
+ * @param crypto
+ * @param cancel
+ * @param executor
+ * @param callback
+ * @hide
+ */
+ public void authenticate(@NonNull android.hardware.biometrics.CryptoObject crypto,
+ @NonNull CancellationSignal cancel,
+ @NonNull Bundle bundle,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull IBiometricPromptReceiver receiver,
+ @NonNull BiometricAuthenticator.AuthenticationCallback callback) {
+ if (crypto == null) {
+ throw new IllegalArgumentException("Must supply a crypto object");
+ }
+ if (cancel == null) {
+ throw new IllegalArgumentException("Must supply a cancellation signal");
+ }
+ if (bundle == null) {
+ throw new IllegalArgumentException("Must supply a bundle");
+ }
+ if (executor == null) {
+ throw new IllegalArgumentException("Must supply an executor");
+ }
+ if (receiver == null) {
+ throw new IllegalArgumentException("Must supply a receiver");
+ }
+ if (callback == null) {
+ throw new IllegalArgumentException("Must supply a callback");
+ }
+ authenticateWithPrompt(crypto, cancel, bundle, executor, receiver, callback);
+ }
+
+ /**
+ * Request face authentication enrollment. This call operates the face authentication hardware
+ * and starts capturing images. Progress will be indicated by callbacks to the
+ * {@link EnrollmentCallback} object. It terminates when
+ * {@link EnrollmentCallback#onEnrollmentError(int, CharSequence)} or
+ * {@link EnrollmentCallback#onEnrollmentProgress(int) is called with remaining == 0, at
+ * which point the object is no longer valid. The operation can be canceled by using the
+ * provided cancel object.
+ *
+ * @param token a unique token provided by a recent creation or verification of device
+ * credentials (e.g. pin, pattern or password).
+ * @param cancel an object that can be used to cancel enrollment
+ * @param flags optional flags
+ * @param userId the user to whom this face will belong to
+ * @param callback an object to receive enrollment events
+ * @hide
+ */
+ @RequiresPermission(MANAGE_FACE)
+ public void enroll(byte[] token, CancellationSignal cancel, int flags,
+ int userId, EnrollmentCallback callback) {
+ if (userId == UserHandle.USER_CURRENT) {
+ userId = getCurrentUserId();
+ }
+ if (callback == null) {
+ throw new IllegalArgumentException("Must supply an enrollment callback");
+ }
+
+ if (cancel != null) {
+ if (cancel.isCanceled()) {
+ Log.w(TAG, "enrollment already canceled");
+ return;
+ } else {
+ cancel.setOnCancelListener(new OnEnrollCancelListener());
+ }
+ }
+
+ if (mService != null) {
+ try {
+ mEnrollmentCallback = callback;
+ mService.enroll(mToken, token, userId, mServiceReceiver, flags,
+ mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ Log.w(TAG, "Remote exception in enroll: ", e);
+ if (callback != null) {
+ // Though this may not be a hardware issue, it will cause apps to give up or try
+ // again later.
+ callback.onEnrollmentError(FACE_ERROR_HW_UNAVAILABLE,
+ getErrorString(FACE_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */));
+ }
+ }
+ }
+ }
+
+ /**
+ * Requests a pre-enrollment auth token to tie enrollment to the confirmation of
+ * existing device credentials (e.g. pin/pattern/password).
+ *
+ * @hide
+ */
+ @RequiresPermission(MANAGE_FACE)
+ public long preEnroll() {
+ long result = 0;
+ if (mService != null) {
+ try {
+ result = mService.preEnroll(mToken);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Finishes enrollment and cancels the current auth token.
+ *
+ * @hide
+ */
+ @RequiresPermission(MANAGE_FACE)
+ public int postEnroll() {
+ int result = 0;
+ if (mService != null) {
+ try {
+ result = mService.postEnroll(mToken);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Sets the active user. This is meant to be used to select the current profile for enrollment
+ * to allow separate enrolled faces for a work profile
+ *
+ * @hide
+ */
+ @RequiresPermission(MANAGE_FACE)
+ public void setActiveUser(int userId) {
+ if (mService != null) {
+ try {
+ mService.setActiveUser(userId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
+ * Remove given face template from face hardware and/or protected storage.
+ *
+ * @param face the face item to remove
+ * @param userId the user who this face belongs to
+ * @param callback an optional callback to verify that face templates have been
+ * successfully removed. May be null if no callback is required.
+ * @hide
+ */
+ @RequiresPermission(MANAGE_FACE)
+ public void remove(Face face, int userId, RemovalCallback callback) {
+ if (mService != null) {
+ try {
+ mRemovalCallback = callback;
+ mRemovalFace = face;
+ mService.remove(mToken, face.getFaceId(), userId, mServiceReceiver);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Remote exception in remove: ", e);
+ if (callback != null) {
+ callback.onRemovalError(face, FACE_ERROR_HW_UNAVAILABLE,
+ getErrorString(FACE_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */));
+ }
+ }
+ }
+ }
+
+ /**
+ * Obtain the enrolled face template.
+ *
+ * @return the current face item
+ * @hide
+ */
+ @RequiresPermission(USE_BIOMETRIC)
+ public List<Face> getEnrolledFaces(int userId) {
+ if (mService != null) {
+ try {
+ return mService.getEnrolledFaces(userId, mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Obtain the enrolled face template.
+ *
+ * @return the current face item
+ * @hide
+ */
+ @RequiresPermission(USE_BIOMETRIC)
+ public List<Face> getEnrolledFaces() {
+ return getEnrolledFaces(UserHandle.myUserId());
+ }
+
+ /**
+ * Determine if there is a face enrolled.
+ *
+ * @return true if a face is enrolled, false otherwise
+ */
+ @RequiresPermission(USE_BIOMETRIC)
+ public boolean hasEnrolledFaces() {
+ if (mService != null) {
+ try {
+ return mService.hasEnrolledFaces(
+ UserHandle.myUserId(), mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ return false;
+ }
+
+ /**
+ * @hide
+ */
+ @RequiresPermission(allOf = {
+ USE_BIOMETRIC,
+ INTERACT_ACROSS_USERS})
+ public boolean hasEnrolledFaces(int userId) {
+ if (mService != null) {
+ try {
+ return mService.hasEnrolledFaces(userId, mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Determine if face authentication sensor hardware is present and functional.
+ *
+ * @return true if hardware is present and functional, false otherwise.
+ */
+ @RequiresPermission(USE_BIOMETRIC)
+ public boolean isHardwareDetected() {
+ if (mService != null) {
+ try {
+ long deviceId = 0; /* TODO: plumb hardware id to FPMS */
+ return mService.isHardwareDetected(deviceId, mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ } else {
+ Log.w(TAG, "isFaceHardwareDetected(): Service not connected!");
+ }
+ return false;
+ }
+
+ /**
+ * Retrieves the authenticator token for binding keys to the lifecycle
+ * of the calling user's face. Used only by internal clients.
+ *
+ * @hide
+ */
+ public long getAuthenticatorId() {
+ if (mService != null) {
+ try {
+ return mService.getAuthenticatorId(mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ } else {
+ Log.w(TAG, "getAuthenticatorId(): Service not connected!");
+ }
+ return 0;
+ }
+
+ /**
+ * Reset the lockout timer when asked to do so by keyguard.
+ *
+ * @param token an opaque token returned by password confirmation.
+ * @hide
+ */
+ public void resetTimeout(byte[] token) {
+ if (mService != null) {
+ try {
+ mService.resetTimeout(token);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ } else {
+ Log.w(TAG, "resetTimeout(): Service not connected!");
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public void addLockoutResetCallback(final LockoutResetCallback callback) {
+ if (mService != null) {
+ try {
+ final PowerManager powerManager = mContext.getSystemService(PowerManager.class);
+ mService.addLockoutResetCallback(
+ new IBiometricServiceLockoutResetCallback.Stub() {
+
+ @Override
+ public void onLockoutReset(long deviceId,
+ IRemoteCallback serverCallback)
+ throws RemoteException {
+ try {
+ final PowerManager.WakeLock wakeLock = powerManager.newWakeLock(
+ PowerManager.PARTIAL_WAKE_LOCK,
+ "faceLockoutResetCallback");
+ wakeLock.acquire();
+ mHandler.post(() -> {
+ try {
+ callback.onLockoutReset();
+ } finally {
+ wakeLock.release();
+ }
+ });
+ } finally {
+ serverCallback.sendResult(null /* data */);
+ }
+ }
+ });
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ } else {
+ Log.w(TAG, "addLockoutResetCallback(): Service not connected!");
+ }
+ }
+
+ private int getCurrentUserId() {
+ try {
+ return ActivityManager.getService().getCurrentUser().id;
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ private void cancelEnrollment() {
+ if (mService != null) {
+ try {
+ mService.cancelEnrollment(mToken);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ private void cancelAuthentication(CryptoObject cryptoObject) {
+ if (mService != null) {
+ try {
+ mService.cancelAuthentication(mToken, mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ private String getErrorString(int errMsg, int vendorCode) {
+ switch (errMsg) {
+ case FACE_ERROR_HW_UNAVAILABLE:
+ return mContext.getString(
+ com.android.internal.R.string.face_error_hw_not_available);
+ case FACE_ERROR_UNABLE_TO_PROCESS:
+ return mContext.getString(
+ com.android.internal.R.string.face_error_unable_to_process);
+ case FACE_ERROR_TIMEOUT:
+ return mContext.getString(com.android.internal.R.string.face_error_timeout);
+ case FACE_ERROR_NO_SPACE:
+ return mContext.getString(com.android.internal.R.string.face_error_no_space);
+ case FACE_ERROR_CANCELED:
+ return mContext.getString(com.android.internal.R.string.face_error_canceled);
+ case FACE_ERROR_LOCKOUT:
+ return mContext.getString(com.android.internal.R.string.face_error_lockout);
+ case FACE_ERROR_LOCKOUT_PERMANENT:
+ return mContext.getString(
+ com.android.internal.R.string.face_error_lockout_permanent);
+ case FACE_ERROR_NOT_ENROLLED:
+ return mContext.getString(com.android.internal.R.string.face_error_not_enrolled);
+ case FACE_ERROR_HW_NOT_PRESENT:
+ return mContext.getString(com.android.internal.R.string.face_error_hw_not_present);
+ case FACE_ERROR_VENDOR: {
+ String[] msgArray = mContext.getResources().getStringArray(
+ com.android.internal.R.array.face_error_vendor);
+ if (vendorCode < msgArray.length) {
+ return msgArray[vendorCode];
+ }
+ }
+ }
+ Slog.w(TAG, "Invalid error message: " + errMsg + ", " + vendorCode);
+ return null;
+ }
+
+ private String getAcquiredString(int acquireInfo, int vendorCode) {
+ switch (acquireInfo) {
+ case FACE_ACQUIRED_GOOD:
+ return null;
+ case FACE_ACQUIRED_INSUFFICIENT:
+ return mContext.getString(R.string.face_acquired_insufficient);
+ case FACE_ACQUIRED_TOO_BRIGHT:
+ return mContext.getString(R.string.face_acquired_too_bright);
+ case FACE_ACQUIRED_TOO_DARK:
+ return mContext.getString(R.string.face_acquired_too_dark);
+ case FACE_ACQUIRED_TOO_CLOSE:
+ return mContext.getString(R.string.face_acquired_too_close);
+ case FACE_ACQUIRED_TOO_FAR:
+ return mContext.getString(R.string.face_acquired_too_far);
+ case FACE_ACQUIRED_TOO_HIGH:
+ return mContext.getString(R.string.face_acquired_too_high);
+ case FACE_ACQUIRED_TOO_LOW:
+ return mContext.getString(R.string.face_acquired_too_low);
+ case FACE_ACQUIRED_TOO_RIGHT:
+ return mContext.getString(R.string.face_acquired_too_right);
+ case FACE_ACQUIRED_TOO_LEFT:
+ return mContext.getString(R.string.face_acquired_too_left);
+ case FACE_ACQUIRED_POOR_GAZE:
+ return mContext.getString(R.string.face_acquired_poor_gaze);
+ case FACE_ACQUIRED_NOT_DETECTED:
+ return mContext.getString(R.string.face_acquired_not_detected);
+ case FACE_ACQUIRED_VENDOR: {
+ String[] msgArray = mContext.getResources().getStringArray(
+ R.array.face_acquired_vendor);
+ if (vendorCode < msgArray.length) {
+ return msgArray[vendorCode];
+ }
+ }
+ }
+ Slog.w(TAG, "Invalid acquired message: " + acquireInfo + ", " + vendorCode);
+ return null;
+ }
+
+ /**
+ * Container for callback data from {@link FaceManager#authenticate(CryptoObject,
+ * CancellationSignal, int, AuthenticationCallback, Handler)}.
+ */
+ public static class AuthenticationResult {
+ private Face mFace;
+ private CryptoObject mCryptoObject;
+ private int mUserId;
+
+ /**
+ * Authentication result
+ *
+ * @param crypto the crypto object
+ * @param face the recognized face data, if allowed.
+ * @hide
+ */
+ public AuthenticationResult(CryptoObject crypto, Face face, int userId) {
+ mCryptoObject = crypto;
+ mFace = face;
+ mUserId = userId;
+ }
+
+ /**
+ * Obtain the crypto object associated with this transaction
+ *
+ * @return crypto object provided to {@link FaceManager#authenticate
+ * (CryptoObject,
+ * CancellationSignal, int, AuthenticationCallback, Handler)}.
+ */
+ public CryptoObject getCryptoObject() {
+ return mCryptoObject;
+ }
+
+ /**
+ * Obtain the Face associated with this operation. Applications are strongly
+ * discouraged from associating specific faces with specific applications or operations.
+ *
+ * @hide
+ */
+ public Face getFace() {
+ return mFace;
+ }
+
+ /**
+ * Obtain the userId for which this face was authenticated.
+ *
+ * @hide
+ */
+ public int getUserId() {
+ return mUserId;
+ }
+ }
+
+ /**
+ * Callback structure provided to {@link FaceManager#authenticate(CryptoObject,
+ * CancellationSignal, int, AuthenticationCallback, Handler)}. Users of {@link
+ * FaceManager#authenticate(CryptoObject, CancellationSignal,
+ * int, AuthenticationCallback, Handler) } must provide an implementation of this for listening
+ * to face events.
+ */
+ public abstract static class AuthenticationCallback
+ extends BiometricAuthenticator.AuthenticationCallback {
+
+ /**
+ * Called when an unrecoverable error has been encountered and the operation is complete.
+ * No further callbacks will be made on this object.
+ *
+ * @param errorCode An integer identifying the error message
+ * @param errString A human-readable error string that can be shown in UI
+ */
+ public void onAuthenticationError(int errorCode, CharSequence errString) {
+ }
+
+ /**
+ * Called when a recoverable error has been encountered during authentication. The help
+ * string is provided to give the user guidance for what went wrong, such as
+ * "Sensor dirty, please clean it."
+ *
+ * @param helpCode An integer identifying the error message
+ * @param helpString A human-readable string that can be shown in UI
+ */
+ public void onAuthenticationHelp(int helpCode, CharSequence helpString) {
+ }
+
+ /**
+ * Called when a face is recognized.
+ *
+ * @param result An object containing authentication-related data
+ */
+ public void onAuthenticationSucceeded(AuthenticationResult result) {
+ }
+
+ /**
+ * Called when a face is detected but not recognized.
+ */
+ public void onAuthenticationFailed() {
+ }
+
+ /**
+ * Called when a face image has been acquired, but wasn't processed yet.
+ *
+ * @param acquireInfo one of FACE_ACQUIRED_* constants
+ * @hide
+ */
+ public void onAuthenticationAcquired(int acquireInfo) {
+ }
+ }
+
+ /**
+ * Callback structure provided to {@link FaceManager#enroll(long,
+ * EnrollmentCallback, CancellationSignal, int). Users of {@link #FaceAuthenticationManager()}
+ * must provide an implementation of this to {@link FaceManager#enroll(long,
+ * CancellationSignal, int, EnrollmentCallback) for listening to face enrollment events.
+ *
+ * @hide
+ */
+ public abstract static class EnrollmentCallback {
+
+ /**
+ * Called when an unrecoverable error has been encountered and the operation is complete.
+ * No further callbacks will be made on this object.
+ *
+ * @param errMsgId An integer identifying the error message
+ * @param errString A human-readable error string that can be shown in UI
+ */
+ public void onEnrollmentError(int errMsgId, CharSequence errString) {
+ }
+
+ /**
+ * Called when a recoverable error has been encountered during enrollment. The help
+ * string is provided to give the user guidance for what went wrong, such as
+ * "Image too dark, uncover light source" or what they need to do next, such as
+ * "Rotate face up / down."
+ *
+ * @param helpMsgId An integer identifying the error message
+ * @param helpString A human-readable string that can be shown in UI
+ */
+ public void onEnrollmentHelp(int helpMsgId, CharSequence helpString) {
+ }
+
+ /**
+ * Called as each enrollment step progresses. Enrollment is considered complete when
+ * remaining reaches 0. This function will not be called if enrollment fails. See
+ * {@link EnrollmentCallback#onEnrollmentError(int, CharSequence)}
+ *
+ * @param remaining The number of remaining steps
+ */
+ public void onEnrollmentProgress(int remaining) {
+ }
+ }
+
+ /**
+ * Callback structure provided to {@link #remove}. Users of {@link FaceManager}
+ * may
+ * optionally provide an implementation of this to
+ * {@link #remove(Face, int, RemovalCallback)} for listening to face template
+ * removal events.
+ *
+ * @hide
+ */
+ public abstract static class RemovalCallback {
+
+ /**
+ * Called when the given face can't be removed.
+ *
+ * @param face The face that the call attempted to remove
+ * @param errMsgId An associated error message id
+ * @param errString An error message indicating why the face id can't be removed
+ */
+ public void onRemovalError(Face face, int errMsgId, CharSequence errString) {
+ }
+
+ /**
+ * Called when a given face is successfully removed.
+ *
+ * @param face The face template that was removed.
+ */
+ public void onRemovalSucceeded(Face face, int remaining) {
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public abstract static class LockoutResetCallback {
+
+ /**
+ * Called when lockout period expired and clients are allowed to listen for face
+ * authentication
+ * again.
+ */
+ public void onLockoutReset() {
+ }
+ }
+
+ private class OnEnrollCancelListener implements OnCancelListener {
+ @Override
+ public void onCancel() {
+ cancelEnrollment();
+ }
+ }
+
+ private class OnAuthenticationCancelListener implements OnCancelListener {
+ private CryptoObject mCrypto;
+
+ OnAuthenticationCancelListener(CryptoObject crypto) {
+ mCrypto = crypto;
+ }
+
+ @Override
+ public void onCancel() {
+ cancelAuthentication(mCrypto);
+ }
+ }
+
+ private class MyHandler extends Handler {
+ private MyHandler(Context context) {
+ super(context.getMainLooper());
+ }
+
+ private MyHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(android.os.Message msg) {
+ switch (msg.what) {
+ case MSG_ENROLL_RESULT:
+ sendEnrollResult((Face) msg.obj, msg.arg1 /* remaining */);
+ break;
+ case MSG_ACQUIRED:
+ sendAcquiredResult((Long) msg.obj /* deviceId */, msg.arg1 /* acquire info */,
+ msg.arg2 /* vendorCode */);
+ break;
+ case MSG_AUTHENTICATION_SUCCEEDED:
+ sendAuthenticatedSucceeded((Face) msg.obj, msg.arg1 /* userId */);
+ break;
+ case MSG_AUTHENTICATION_FAILED:
+ sendAuthenticatedFailed();
+ break;
+ case MSG_ERROR:
+ sendErrorResult((Long) msg.obj /* deviceId */, msg.arg1 /* errMsgId */,
+ msg.arg2 /* vendorCode */);
+ break;
+ case MSG_REMOVED:
+ sendRemovedResult((Face) msg.obj, msg.arg1 /* remaining */);
+ break;
+ }
+ }
+ };
+
+ private void sendRemovedResult(Face face, int remaining) {
+ if (mRemovalCallback == null) {
+ return;
+ }
+ if (face == null) {
+ Log.e(TAG, "Received MSG_REMOVED, but face is null");
+ return;
+ }
+ mRemovalCallback.onRemovalSucceeded(face, remaining);
+ }
+
+ private void sendErrorResult(long deviceId, int errMsgId, int vendorCode) {
+ // emulate HAL 2.1 behavior and send real errMsgId
+ final int clientErrMsgId = errMsgId == FACE_ERROR_VENDOR
+ ? (vendorCode + FACE_ERROR_VENDOR_BASE) : errMsgId;
+ if (mEnrollmentCallback != null) {
+ mEnrollmentCallback.onEnrollmentError(clientErrMsgId,
+ getErrorString(errMsgId, vendorCode));
+ } else if (mAuthenticationCallback != null) {
+ mAuthenticationCallback.onAuthenticationError(clientErrMsgId,
+ getErrorString(errMsgId, vendorCode));
+ } else if (mRemovalCallback != null) {
+ mRemovalCallback.onRemovalError(mRemovalFace, clientErrMsgId,
+ getErrorString(errMsgId, vendorCode));
+ }
+ }
+
+ private void sendEnrollResult(Face face, int remaining) {
+ if (mEnrollmentCallback != null) {
+ mEnrollmentCallback.onEnrollmentProgress(remaining);
+ }
+ }
+
+ private void sendAuthenticatedSucceeded(Face face, int userId) {
+ if (mAuthenticationCallback != null) {
+ final BiometricAuthenticator.AuthenticationResult result =
+ new BiometricAuthenticator.AuthenticationResult(mCryptoObject, face, userId);
+ mAuthenticationCallback.onAuthenticationSucceeded(result);
+ }
+ }
+
+ private void sendAuthenticatedFailed() {
+ if (mAuthenticationCallback != null) {
+ mAuthenticationCallback.onAuthenticationFailed();
+ }
+ }
+
+ private void sendAcquiredResult(long deviceId, int acquireInfo, int vendorCode) {
+ if (mAuthenticationCallback != null) {
+ mAuthenticationCallback.onAuthenticationAcquired(acquireInfo);
+ }
+ final String msg = getAcquiredString(acquireInfo, vendorCode);
+ if (msg == null) {
+ return;
+ }
+ final int clientInfo = acquireInfo == FACE_ACQUIRED_VENDOR
+ ? (vendorCode + FACE_ACQUIRED_VENDOR_BASE) : acquireInfo;
+ if (mEnrollmentCallback != null) {
+ mEnrollmentCallback.onEnrollmentHelp(clientInfo, msg);
+ } else if (mAuthenticationCallback != null) {
+ mAuthenticationCallback.onAuthenticationHelp(clientInfo, msg);
+ }
+ }
+}
diff --git a/core/java/android/hardware/face/IFaceService.aidl b/core/java/android/hardware/face/IFaceService.aidl
new file mode 100644
index 0000000..03bb7ae
--- /dev/null
+++ b/core/java/android/hardware/face/IFaceService.aidl
@@ -0,0 +1,85 @@
+/*
+ * 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.hardware.face;
+
+import android.os.Bundle;
+import android.hardware.biometrics.IBiometricPromptReceiver;
+import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
+import android.hardware.face.IFaceServiceReceiver;
+import android.hardware.face.Face;
+
+/**
+ * Communication channel from client to the face service.
+ * @hide
+ */
+interface IFaceService {
+ // Authenticate the given sessionId with a face
+ void authenticate(IBinder token, long sessionId,
+ IFaceServiceReceiver receiver, int flags, String opPackageName,
+ in Bundle bundle, IBiometricPromptReceiver dialogReceiver);
+
+ // Cancel authentication for the given sessionId
+ void cancelAuthentication(IBinder token, String opPackageName);
+
+ // Start face enrollment
+ void enroll(IBinder token, in byte [] cryptoToken, int userId, IFaceServiceReceiver receiver,
+ int flags, String opPackageName);
+
+ // Cancel enrollment in progress
+ void cancelEnrollment(IBinder token);
+
+ // Any errors resulting from this call will be returned to the listener
+ void remove(IBinder token, int faceId, int userId, IFaceServiceReceiver receiver);
+
+ // Rename the face specified by faceId to the given name
+ void rename(int faceId, String name);
+
+ // Get the enrolled face for user.
+ List<Face> getEnrolledFaces(int userId, String opPackageName);
+
+ // Determine if HAL is loaded and ready
+ boolean isHardwareDetected(long deviceId, String opPackageName);
+
+ // Get a pre-enrollment authentication token
+ long preEnroll(IBinder token);
+
+ // Finish an enrollment sequence and invalidate the authentication token
+ int postEnroll(IBinder token);
+
+ // Determine if a user has at least one enrolled face
+ boolean hasEnrolledFaces(int userId, String opPackageName);
+
+ // Gets the number of hardware devices
+ // int getHardwareDeviceCount();
+
+ // Gets the unique device id for hardware enumerated at i
+ // long getHardwareDevice(int i);
+
+ // Gets the authenticator ID for face
+ long getAuthenticatorId(String opPackageName);
+
+ // Reset the timeout when user authenticates with strong auth (e.g. PIN, pattern or password)
+ void resetTimeout(in byte [] cryptoToken);
+
+ // Add a callback which gets notified when the face lockout period expired.
+ void addLockoutResetCallback(IBiometricServiceLockoutResetCallback callback);
+
+ // Explicitly set the active user (for enrolling work profile)
+ void setActiveUser(int uid);
+
+ // Enumerate all faces
+ void enumerate(IBinder token, int userId, IFaceServiceReceiver receiver);
+}
diff --git a/core/java/android/hardware/face/IFaceServiceReceiver.aidl b/core/java/android/hardware/face/IFaceServiceReceiver.aidl
new file mode 100644
index 0000000..16fb690
--- /dev/null
+++ b/core/java/android/hardware/face/IFaceServiceReceiver.aidl
@@ -0,0 +1,33 @@
+/*
+ * 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.hardware.face;
+
+import android.hardware.face.Face;
+import android.os.Bundle;
+import android.os.UserHandle;
+
+/**
+ * Communication channel from the FaceService back to FaceAuthenticationManager.
+ * @hide
+ */
+oneway interface IFaceServiceReceiver {
+ void onEnrollResult(long deviceId, int faceId, int remaining);
+ void onAcquired(long deviceId, int acquiredInfo, int vendorCode);
+ void onAuthenticationSucceeded(long deviceId, in Face face);
+ void onAuthenticationFailed(long deviceId);
+ void onError(long deviceId, int error, int vendorCode);
+ void onRemoved(long deviceId, int faceId, int remaining);
+}
diff --git a/core/java/android/hardware/fingerprint/Fingerprint.java b/core/java/android/hardware/fingerprint/Fingerprint.java
index c7ce8fa..bbd3d05 100644
--- a/core/java/android/hardware/fingerprint/Fingerprint.java
+++ b/core/java/android/hardware/fingerprint/Fingerprint.java
@@ -23,62 +23,36 @@
* Container for fingerprint metadata.
* @hide
*/
-public final class Fingerprint extends BiometricAuthenticator.BiometricIdentifier {
- private CharSequence mName;
+public final class Fingerprint extends BiometricAuthenticator.Identifier {
private int mGroupId;
- private int mFingerId;
- private long mDeviceId; // physical device this is associated with
public Fingerprint(CharSequence name, int groupId, int fingerId, long deviceId) {
- mName = name;
+ super(name, fingerId, deviceId);
mGroupId = groupId;
- mFingerId = fingerId;
- mDeviceId = deviceId;
}
private Fingerprint(Parcel in) {
- mName = in.readString();
+ super(in.readString(), in.readInt(), in.readLong());
mGroupId = in.readInt();
- mFingerId = in.readInt();
- mDeviceId = in.readLong();
}
/**
- * Gets the human-readable name for the given fingerprint.
- * @return name given to finger
- */
- public CharSequence getName() { return mName; }
-
- /**
- * Gets the device-specific finger id. Used by Settings to map a name to a specific
- * fingerprint template.
- * @return device-specific id for this finger
- * @hide
- */
- public int getFingerId() { return mFingerId; }
-
- /**
* Gets the group id specified when the fingerprint was enrolled.
* @return group id for the set of fingerprints this one belongs to.
- * @hide
*/
- public int getGroupId() { return mGroupId; }
-
- /**
- * Device this fingerprint belongs to.
- * @hide
- */
- public long getDeviceId() { return mDeviceId; }
+ public int getGroupId() {
+ return mGroupId;
+ }
public int describeContents() {
return 0;
}
public void writeToParcel(Parcel out, int flags) {
- out.writeString(mName.toString());
+ out.writeString(getName().toString());
+ out.writeInt(getBiometricId());
+ out.writeLong(getDeviceId());
out.writeInt(mGroupId);
- out.writeInt(mFingerId);
- out.writeLong(mDeviceId);
}
public static final Parcelable.Creator<Fingerprint> CREATOR
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index ebbfe1c..9192652 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -34,6 +34,7 @@
import android.hardware.biometrics.BiometricFingerprintConstants;
import android.hardware.biometrics.BiometricPrompt;
import android.hardware.biometrics.IBiometricPromptReceiver;
+import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
import android.os.Binder;
import android.os.Bundle;
import android.os.CancellationSignal;
@@ -403,7 +404,8 @@
/**
* Per-user version, see {@link FingerprintManager#authenticate(CryptoObject,
- * CancellationSignal, int, AuthenticationCallback, Handler)}
+ * CancellationSignal, int, AuthenticationCallback, Handler)}. This version does not
+ * display the BiometricPrompt.
* @param userId the user ID that the fingerprint hardware will authenticate for.
* @hide
*/
@@ -442,9 +444,7 @@
}
/**
- * Per-user version, see {@link FingerprintManager#authenticate(CryptoObject,
- * CancellationSignal, Bundle, Executor, IBiometricPromptReceiver, AuthenticationCallback)}
- * @param userId the user ID that the fingerprint hardware will authenticate for.
+ * Per-user version. This method invokes the BiometricPrompt.
*/
private void authenticate(int userId,
@Nullable android.hardware.biometrics.CryptoObject crypto,
@@ -657,7 +657,7 @@
if (mService != null) try {
mRemovalCallback = callback;
mRemovalFingerprint = fp;
- mService.remove(mToken, fp.getFingerId(), fp.getGroupId(), userId, mServiceReceiver);
+ mService.remove(mToken, fp.getBiometricId(), fp.getGroupId(), userId, mServiceReceiver);
} catch (RemoteException e) {
Slog.w(TAG, "Remote exception in remove: ", e);
if (callback != null) {
@@ -841,7 +841,7 @@
try {
final PowerManager powerManager = mContext.getSystemService(PowerManager.class);
mService.addLockoutResetCallback(
- new IFingerprintServiceLockoutResetCallback.Stub() {
+ new IBiometricServiceLockoutResetCallback.Stub() {
@Override
public void onLockoutReset(long deviceId, IRemoteCallback serverCallback)
@@ -881,7 +881,7 @@
@Override
public void handleMessage(android.os.Message msg) {
- switch(msg.what) {
+ switch (msg.what) {
case MSG_ENROLL_RESULT:
sendEnrollResult((Fingerprint) msg.obj, msg.arg1 /* remaining */);
break;
@@ -908,45 +908,45 @@
break;
}
}
-
- private void sendRemovedResult(Fingerprint fingerprint, int remaining) {
- if (mRemovalCallback == null) {
- return;
- }
- if (fingerprint == null) {
- Slog.e(TAG, "Received MSG_REMOVED, but fingerprint is null");
- return;
- }
-
- int fingerId = fingerprint.getFingerId();
- int reqFingerId = mRemovalFingerprint.getFingerId();
- if (reqFingerId != 0 && fingerId != 0 && fingerId != reqFingerId) {
- Slog.w(TAG, "Finger id didn't match: " + fingerId + " != " + reqFingerId);
- return;
- }
- int groupId = fingerprint.getGroupId();
- int reqGroupId = mRemovalFingerprint.getGroupId();
- if (groupId != reqGroupId) {
- Slog.w(TAG, "Group id didn't match: " + groupId + " != " + reqGroupId);
- return;
- }
-
- mRemovalCallback.onRemovalSucceeded(fingerprint, remaining);
- }
-
- private void sendEnumeratedResult(long deviceId, int fingerId, int groupId) {
- if (mEnumerateCallback != null) {
- mEnumerateCallback.onEnumerate(new Fingerprint(null, groupId, fingerId, deviceId));
- }
- }
-
- private void sendEnrollResult(Fingerprint fp, int remaining) {
- if (mEnrollmentCallback != null) {
- mEnrollmentCallback.onEnrollmentProgress(remaining);
- }
- }
};
+ private void sendRemovedResult(Fingerprint fingerprint, int remaining) {
+ if (mRemovalCallback == null) {
+ return;
+ }
+ if (fingerprint == null) {
+ Slog.e(TAG, "Received MSG_REMOVED, but fingerprint is null");
+ return;
+ }
+
+ int fingerId = fingerprint.getBiometricId();
+ int reqFingerId = mRemovalFingerprint.getBiometricId();
+ if (reqFingerId != 0 && fingerId != 0 && fingerId != reqFingerId) {
+ Slog.w(TAG, "Finger id didn't match: " + fingerId + " != " + reqFingerId);
+ return;
+ }
+ int groupId = fingerprint.getGroupId();
+ int reqGroupId = mRemovalFingerprint.getGroupId();
+ if (groupId != reqGroupId) {
+ Slog.w(TAG, "Group id didn't match: " + groupId + " != " + reqGroupId);
+ return;
+ }
+
+ mRemovalCallback.onRemovalSucceeded(fingerprint, remaining);
+ }
+
+ private void sendEnumeratedResult(long deviceId, int fingerId, int groupId) {
+ if (mEnumerateCallback != null) {
+ mEnumerateCallback.onEnumerate(new Fingerprint(null, groupId, fingerId, deviceId));
+ }
+ }
+
+ private void sendEnrollResult(Fingerprint fp, int remaining) {
+ if (mEnrollmentCallback != null) {
+ mEnrollmentCallback.onEnrollmentProgress(remaining);
+ }
+ }
+
private void sendAuthenticatedSucceeded(Fingerprint fp, int userId) {
if (mAuthenticationCallback != null) {
final BiometricAuthenticator.AuthenticationResult result =
diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
index 8aa2183..71a6420 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
@@ -17,9 +17,9 @@
import android.os.Bundle;
import android.hardware.biometrics.IBiometricPromptReceiver;
+import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
import android.hardware.fingerprint.IFingerprintClientActiveCallback;
import android.hardware.fingerprint.IFingerprintServiceReceiver;
-import android.hardware.fingerprint.IFingerprintServiceLockoutResetCallback;
import android.hardware.fingerprint.Fingerprint;
import java.util.List;
@@ -78,7 +78,7 @@
void resetTimeout(in byte [] cryptoToken);
// Add a callback which gets notified when the fingerprint lockout period expired.
- void addLockoutResetCallback(IFingerprintServiceLockoutResetCallback callback);
+ void addLockoutResetCallback(IBiometricServiceLockoutResetCallback callback);
// Explicitly set the active user (for enrolling work profile)
void setActiveUser(int uid);
diff --git a/core/java/android/hardware/hdmi/HdmiAudioSystemClient.java b/core/java/android/hardware/hdmi/HdmiAudioSystemClient.java
new file mode 100644
index 0000000..9bebbd2
--- /dev/null
+++ b/core/java/android/hardware/hdmi/HdmiAudioSystemClient.java
@@ -0,0 +1,119 @@
+/*
+ * 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.hardware.hdmi;
+
+import android.annotation.Nullable;
+import android.os.Handler;
+import android.os.RemoteException;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.annotations.VisibleForTesting.Visibility;
+
+/**
+ * HdmiAudioSystemClient represents HDMI-CEC logical device of type Audio System in the Android
+ * system which acts as an audio system device such as sound bar.
+ *
+ * <p>HdmiAudioSystemClient provides methods that control, get information from TV/Display device
+ * connected through HDMI bus.
+ *
+ * @hide
+ */
+public final class HdmiAudioSystemClient extends HdmiClient {
+ private static final String TAG = "HdmiAudioSystemClient";
+
+ private static final int REPORT_AUDIO_STATUS_INTERVAL_MS = 500;
+
+ private final Handler mHandler;
+ private boolean mCanSendAudioStatus = true;
+ private boolean mPendingReportAudioStatus;
+
+ private int mLastVolume;
+ private int mLastMaxVolume;
+ private boolean mLastIsMute;
+
+ @VisibleForTesting(visibility = Visibility.PACKAGE)
+ public HdmiAudioSystemClient(IHdmiControlService service) {
+ this(service, null);
+ }
+
+ @VisibleForTesting(visibility = Visibility.PACKAGE)
+ public HdmiAudioSystemClient(IHdmiControlService service, @Nullable Handler handler) {
+ super(service);
+ mHandler = handler == null ? new Handler() : handler;
+ }
+
+ /** @hide */
+ // TODO(b/110094868): unhide and add @SystemApi for Q
+ @Override
+ public int getDeviceType() {
+ return HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM;
+ }
+
+ /**
+ * Sends a Report Audio Status HDMI CEC command to TV devices when necessary.
+ *
+ * According to HDMI CEC specification, an audio system can report its audio status when System
+ * Audio Mode is on, so that the TV can display the audio status of external amplifier.
+ *
+ * @hide
+ */
+ public void sendReportAudioStatusCecCommand(boolean isMuteAdjust, int volume, int maxVolume,
+ boolean isMute) {
+ if (isMuteAdjust) {
+ // always report audio status when it's muted/unmuted
+ try {
+ mService.reportAudioStatus(getDeviceType(), volume, maxVolume, isMute);
+ } catch (RemoteException e) {
+ // do nothing. Reporting audio status is optional.
+ }
+ return;
+ }
+
+ mLastVolume = volume;
+ mLastMaxVolume = maxVolume;
+ mLastIsMute = isMute;
+ if (mCanSendAudioStatus) {
+ try {
+ mService.reportAudioStatus(getDeviceType(), volume, maxVolume, isMute);
+ mCanSendAudioStatus = false;
+ mHandler.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ if (mPendingReportAudioStatus) {
+ // report audio status if there is any pending message
+ try {
+ mService.reportAudioStatus(getDeviceType(), mLastVolume,
+ mLastMaxVolume, mLastIsMute);
+ mHandler.postDelayed(this, REPORT_AUDIO_STATUS_INTERVAL_MS);
+ } catch (RemoteException e) {
+ mCanSendAudioStatus = true;
+ } finally {
+ mPendingReportAudioStatus = false;
+ }
+ } else {
+ mCanSendAudioStatus = true;
+ }
+ }
+ }, REPORT_AUDIO_STATUS_INTERVAL_MS);
+ } catch (RemoteException e) {
+ // do nothing. Reporting audio status is optional.
+ }
+ } else {
+ // if audio status cannot be sent, send it latter
+ mPendingReportAudioStatus = true;
+ }
+ }
+}
diff --git a/core/java/android/hardware/hdmi/HdmiControlManager.java b/core/java/android/hardware/hdmi/HdmiControlManager.java
index e34423c..72a6ffe 100644
--- a/core/java/android/hardware/hdmi/HdmiControlManager.java
+++ b/core/java/android/hardware/hdmi/HdmiControlManager.java
@@ -22,10 +22,10 @@
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.SuppressLint;
-import android.content.Context;
-import android.content.pm.PackageManager;
import android.annotation.SystemApi;
import android.annotation.SystemService;
+import android.content.Context;
+import android.content.pm.PackageManager;
import android.os.RemoteException;
import android.util.ArrayMap;
import android.util.Log;
@@ -262,6 +262,8 @@
private final boolean mHasPlaybackDevice;
// True if we have a logical device of type TV hosted in the system.
private final boolean mHasTvDevice;
+ // True if we have a logical device of type audio system hosted in the system.
+ private final boolean mHasAudioSystemDevice;
/**
* {@hide} - hide this constructor because it has a parameter of type IHdmiControlService,
@@ -280,6 +282,7 @@
}
mHasTvDevice = hasDeviceType(types, HdmiDeviceInfo.DEVICE_TV);
mHasPlaybackDevice = hasDeviceType(types, HdmiDeviceInfo.DEVICE_PLAYBACK);
+ mHasAudioSystemDevice = hasDeviceType(types, HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM);
}
private static boolean hasDeviceType(int[] types, int type) {
@@ -301,6 +304,7 @@
* @return {@link HdmiClient} instance. {@code null} on failure.
* See {@link HdmiDeviceInfo#DEVICE_PLAYBACK}
* See {@link HdmiDeviceInfo#DEVICE_TV}
+ * See {@link HdmiDeviceInfo#DEVICE_AUDIO_SYSTEM}
*/
@Nullable
@SuppressLint("Doclava125")
@@ -313,6 +317,8 @@
return mHasTvDevice ? new HdmiTvClient(mService) : null;
case HdmiDeviceInfo.DEVICE_PLAYBACK:
return mHasPlaybackDevice ? new HdmiPlaybackClient(mService) : null;
+ case HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM:
+ return mHasAudioSystemDevice ? new HdmiAudioSystemClient(mService) : null;
default:
return null;
}
@@ -349,6 +355,24 @@
}
/**
+ * Gets an object that represents an HDMI-CEC logical device of type audio system on the system.
+ *
+ * <p>Used to send HDMI control messages to other devices like TV through HDMI bus. It is also
+ * possible to communicate with other logical devices hosted in the same system if the system is
+ * configured to host more than one type of HDMI-CEC logical devices.
+ *
+ * @return {@link HdmiAudioSystemClient} instance. {@code null} on failure.
+ *
+ * TODO(b/110094868): unhide for Q
+ * @hide
+ */
+ @Nullable
+ @SuppressLint("Doclava125")
+ public HdmiAudioSystemClient getAudioSystemClient() {
+ return (HdmiAudioSystemClient) getClient(HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM);
+ }
+
+ /**
* Controls standby mode of the system. It will also try to turn on/off the connected devices if
* necessary.
*
diff --git a/core/java/android/hardware/hdmi/HdmiPlaybackClient.java b/core/java/android/hardware/hdmi/HdmiPlaybackClient.java
index 874b0c6..df231e6 100644
--- a/core/java/android/hardware/hdmi/HdmiPlaybackClient.java
+++ b/core/java/android/hardware/hdmi/HdmiPlaybackClient.java
@@ -23,7 +23,8 @@
/**
* HdmiPlaybackClient represents HDMI-CEC logical device of type Playback
* in the Android system which acts as a playback device such as set-top box.
- * It provides with methods that control, get information from TV/Display device
+ *
+ * <p>HdmiPlaybackClient provides methods that control, get information from TV/Display device
* connected through HDMI bus.
*
* @hide
diff --git a/core/java/android/hardware/hdmi/HdmiTvClient.java b/core/java/android/hardware/hdmi/HdmiTvClient.java
index a336e5c..f33a137 100644
--- a/core/java/android/hardware/hdmi/HdmiTvClient.java
+++ b/core/java/android/hardware/hdmi/HdmiTvClient.java
@@ -29,8 +29,9 @@
/**
* HdmiTvClient represents HDMI-CEC logical device of type TV in the Android system
- * which acts as TV/Display. It provides with methods that manage, interact with other
- * devices on the CEC bus.
+ * which acts as TV/Display.
+ *
+ * <p>HdmiTvClient provides methods that manage, interact with other devices on the CEC bus.
*
* @hide
*/
diff --git a/core/java/android/hardware/hdmi/IHdmiControlService.aidl b/core/java/android/hardware/hdmi/IHdmiControlService.aidl
index 67e2d18..2b8d00b 100644
--- a/core/java/android/hardware/hdmi/IHdmiControlService.aidl
+++ b/core/java/android/hardware/hdmi/IHdmiControlService.aidl
@@ -72,4 +72,5 @@
void sendMhlVendorCommand(int portId, int offset, int length, in byte[] data);
void addHdmiMhlVendorCommandListener(IHdmiMhlVendorCommandListener listener);
void setStandbyMode(boolean isStandbyModeOn);
+ void reportAudioStatus(int deviceType, int volume, int maxVolume, boolean isMute);
}
diff --git a/core/java/android/hardware/location/NanoAppFilter.java b/core/java/android/hardware/location/NanoAppFilter.java
index 4d8e734..562065e 100644
--- a/core/java/android/hardware/location/NanoAppFilter.java
+++ b/core/java/android/hardware/location/NanoAppFilter.java
@@ -85,7 +85,7 @@
mAppId = in.readLong();
mAppVersion = in.readInt();
mVersionRestrictionMask = in.readInt();
- mAppIdVendorMask = in.readInt();
+ mAppIdVendorMask = in.readLong();
}
public int describeContents() {
@@ -93,7 +93,6 @@
}
public void writeToParcel(Parcel out, int flags) {
-
out.writeLong(mAppId);
out.writeInt(mAppVersion);
out.writeInt(mVersionRestrictionMask);
diff --git a/core/java/android/hardware/radio/RadioManager.aidl b/core/java/android/hardware/radio/RadioManager.aidl
index 8a39388..34c05d8 100644
--- a/core/java/android/hardware/radio/RadioManager.aidl
+++ b/core/java/android/hardware/radio/RadioManager.aidl
@@ -20,6 +20,9 @@
parcelable RadioManager.BandConfig;
/** @hide */
+parcelable RadioManager.BandDescriptor;
+
+/** @hide */
parcelable RadioManager.ModuleProperties;
/** @hide */
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index b4c8a5e..431c651 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -370,19 +370,6 @@
InputConnection mStartedInputConnection;
EditorInfo mInputEditorInfo;
- /**
- * A token to keep tracking the last IPC that triggered
- * {@link #doStartInput(InputConnection, EditorInfo, boolean)}. If
- * {@link #doStartInput(InputConnection, EditorInfo, boolean)} was not caused by IPCs from
- * {@link com.android.server.InputMethodManagerService}, this needs to remain unchanged.
- *
- * <p>Some IPCs to {@link com.android.server.InputMethodManagerService} require this token to
- * disentangle event flows for various purposes such as better window animation and providing
- * fine-grained debugging information.</p>
- */
- @Nullable
- private IBinder mStartInputToken;
-
int mShowInputFlags;
boolean mShowInputRequested;
boolean mLastShowInputRequested;
@@ -528,7 +515,7 @@
public void dispatchStartInputWithToken(@Nullable InputConnection inputConnection,
@NonNull EditorInfo editorInfo, boolean restarting,
@NonNull IBinder startInputToken) {
- mStartInputToken = startInputToken;
+ mImm.reportStartInput(mToken, startInputToken);
// This needs to be dispatched to interface methods rather than doStartInput().
// Otherwise IME developers who have overridden those interface methods will lose
@@ -579,8 +566,8 @@
}
clearInsetOfPreviousIme();
// If user uses hard keyboard, IME button should always be shown.
- mImm.setImeWindowStatus(mToken, mStartInputToken,
- mapToImeWindowStatus(isInputViewShown()), mBackDisposition);
+ mImm.setImeWindowStatus(mToken, mapToImeWindowStatus(isInputViewShown()),
+ mBackDisposition);
if (resultReceiver != null) {
resultReceiver.send(wasVis != isInputViewShown()
? InputMethodManager.RESULT_SHOWN
@@ -1059,8 +1046,8 @@
}
// If user uses hard keyboard, IME button should always be shown.
boolean showing = onEvaluateInputViewShown();
- mImm.setImeWindowStatus(mToken, mStartInputToken,
- IME_ACTIVE | (showing ? IME_VISIBLE : 0), mBackDisposition);
+ mImm.setImeWindowStatus(mToken, IME_ACTIVE | (showing ? IME_VISIBLE : 0),
+ mBackDisposition);
}
}
@@ -1110,8 +1097,7 @@
return;
}
mBackDisposition = disposition;
- mImm.setImeWindowStatus(mToken, mStartInputToken, mapToImeWindowStatus(isInputViewShown()),
- mBackDisposition);
+ mImm.setImeWindowStatus(mToken, mapToImeWindowStatus(isInputViewShown()), mBackDisposition);
}
/**
@@ -1861,8 +1847,7 @@
final int nextImeWindowStatus = mapToImeWindowStatus(isInputViewShown());
if (previousImeWindowStatus != nextImeWindowStatus) {
- mImm.setImeWindowStatus(mToken, mStartInputToken, nextImeWindowStatus,
- mBackDisposition);
+ mImm.setImeWindowStatus(mToken, nextImeWindowStatus, mBackDisposition);
}
if ((previousImeWindowStatus & IME_ACTIVE) == 0) {
if (DEBUG) Log.v(TAG, "showWindow: showing!");
@@ -1887,7 +1872,7 @@
}
private void doHideWindow() {
- mImm.setImeWindowStatus(mToken, mStartInputToken, 0, mBackDisposition);
+ mImm.setImeWindowStatus(mToken, 0, mBackDisposition);
hideWindow();
}
@@ -2869,7 +2854,6 @@
p.println(" mInputStarted=" + mInputStarted
+ " mInputViewStarted=" + mInputViewStarted
+ " mCandidatesViewStarted=" + mCandidatesViewStarted);
- p.println(" mStartInputToken=" + mStartInputToken);
if (mInputEditorInfo != null) {
p.println(" mInputEditorInfo:");
diff --git a/core/java/android/net/http/HttpResponseCache.java b/core/java/android/net/http/HttpResponseCache.java
index 729aff0..7da76d1 100644
--- a/core/java/android/net/http/HttpResponseCache.java
+++ b/core/java/android/net/http/HttpResponseCache.java
@@ -16,9 +16,8 @@
package android.net.http;
-import com.android.okhttp.Cache;
-import com.android.okhttp.AndroidShimResponseCache;
-import com.android.okhttp.OkCacheContainer;
+import com.android.okhttp.internalandroidapi.AndroidResponseCacheAdapter;
+import com.android.okhttp.internalandroidapi.HasCacheHolder;
import java.io.Closeable;
import java.io.File;
@@ -149,12 +148,12 @@
* } catch (Exception httpResponseCacheNotAvailable) {
* }}</pre>
*/
-public final class HttpResponseCache extends ResponseCache implements Closeable, OkCacheContainer {
+public final class HttpResponseCache extends ResponseCache implements HasCacheHolder, Closeable {
- private final AndroidShimResponseCache delegate;
+ private final AndroidResponseCacheAdapter mDelegate;
- private HttpResponseCache(AndroidShimResponseCache delegate) {
- this.delegate = delegate;
+ private HttpResponseCache(AndroidResponseCacheAdapter delegate) {
+ mDelegate = delegate;
}
/**
@@ -184,30 +183,33 @@
ResponseCache installed = ResponseCache.getDefault();
if (installed instanceof HttpResponseCache) {
HttpResponseCache installedResponseCache = (HttpResponseCache) installed;
+ CacheHolder cacheHolder = installedResponseCache.getCacheHolder();
// don't close and reopen if an equivalent cache is already installed
- AndroidShimResponseCache trueResponseCache = installedResponseCache.delegate;
- if (trueResponseCache.isEquivalent(directory, maxSize)) {
+ if (cacheHolder.isEquivalent(directory, maxSize)) {
return installedResponseCache;
} else {
// The HttpResponseCache that owns this object is about to be replaced.
- trueResponseCache.close();
+ installedResponseCache.close();
}
}
- AndroidShimResponseCache trueResponseCache =
- AndroidShimResponseCache.create(directory, maxSize);
- HttpResponseCache newResponseCache = new HttpResponseCache(trueResponseCache);
- ResponseCache.setDefault(newResponseCache);
- return newResponseCache;
+ CacheHolder cacheHolder = CacheHolder.create(directory, maxSize);
+ AndroidResponseCacheAdapter androidResponseCacheAdapter =
+ new AndroidResponseCacheAdapter(cacheHolder);
+ HttpResponseCache responseCache = new HttpResponseCache(androidResponseCacheAdapter);
+ ResponseCache.setDefault(responseCache);
+ return responseCache;
}
- @Override public CacheResponse get(URI uri, String requestMethod,
+ @Override
+ public CacheResponse get(URI uri, String requestMethod,
Map<String, List<String>> requestHeaders) throws IOException {
- return delegate.get(uri, requestMethod, requestHeaders);
+ return mDelegate.get(uri, requestMethod, requestHeaders);
}
- @Override public CacheRequest put(URI uri, URLConnection urlConnection) throws IOException {
- return delegate.put(uri, urlConnection);
+ @Override
+ public CacheRequest put(URI uri, URLConnection urlConnection) throws IOException {
+ return mDelegate.put(uri, urlConnection);
}
/**
@@ -217,7 +219,7 @@
*/
public long size() {
try {
- return delegate.size();
+ return mDelegate.getSize();
} catch (IOException e) {
// This can occur if the cache failed to lazily initialize.
return -1;
@@ -229,7 +231,7 @@
* its data.
*/
public long maxSize() {
- return delegate.maxSize();
+ return mDelegate.getMaxSize();
}
/**
@@ -239,7 +241,7 @@
*/
public void flush() {
try {
- delegate.flush();
+ mDelegate.flush();
} catch (IOException ignored) {
}
}
@@ -249,7 +251,7 @@
* supply a response or validate a locally cached response.
*/
public int getNetworkCount() {
- return delegate.getNetworkCount();
+ return mDelegate.getNetworkCount();
}
/**
@@ -258,7 +260,7 @@
* validated over the network.
*/
public int getHitCount() {
- return delegate.getHitCount();
+ return mDelegate.getHitCount();
}
/**
@@ -267,18 +269,19 @@
* to handle a redirects and retries.
*/
public int getRequestCount() {
- return delegate.getRequestCount();
+ return mDelegate.getRequestCount();
}
/**
* Uninstalls the cache and releases any active resources. Stored contents
* will remain on the filesystem.
*/
- @Override public void close() throws IOException {
+ @Override
+ public void close() throws IOException {
if (ResponseCache.getDefault() == this) {
ResponseCache.setDefault(null);
}
- delegate.close();
+ mDelegate.close();
}
/**
@@ -288,13 +291,12 @@
if (ResponseCache.getDefault() == this) {
ResponseCache.setDefault(null);
}
- delegate.delete();
+ mDelegate.delete();
}
/** @hide Needed for OkHttp integration. */
@Override
- public Cache getCache() {
- return delegate.getCache();
+ public CacheHolder getCacheHolder() {
+ return mDelegate.getCacheHolder();
}
-
}
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 221abed..9cf7de5 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -247,8 +247,10 @@
* - Deferred job metrics.
* New in version 32:
* - Ambient display properly output in data dump.
+ * New in version 33:
+ * - Fixed bug in min learned capacity updating process.
*/
- static final int CHECKIN_VERSION = 32;
+ static final int CHECKIN_VERSION = 33;
/**
* Old version, we hit 9 and ran out of room, need to remove.
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index ac87105..175b405 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -21,7 +21,6 @@
import android.util.ExceptionUtils;
import android.util.Log;
import android.util.Slog;
-import android.util.SparseIntArray;
import com.android.internal.os.BinderCallsStats;
import com.android.internal.os.BinderInternal;
@@ -35,12 +34,7 @@
import java.io.FileDescriptor;
import java.io.FileOutputStream;
import java.io.PrintWriter;
-import java.lang.ref.WeakReference;
import java.lang.reflect.Modifier;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.Map;
/**
* Base class for a remotable object, the core part of a lightweight
@@ -551,6 +545,20 @@
}
/**
+ * Resolves a transaction code to a human readable name.
+ *
+ * <p>Default implementation is a stub that returns null.
+ * <p>AIDL generated code will return the original method name.
+ *
+ * @param transactionCode The code to resolve.
+ * @return A human readable name.
+ * @hide
+ */
+ public @Nullable String getTransactionName(int transactionCode) {
+ return null;
+ }
+
+ /**
* Implemented to call the more convenient version
* {@link #dump(FileDescriptor, PrintWriter, String[])}.
*/
@@ -730,7 +738,7 @@
}
res = onTransact(code, data, reply, flags);
} catch (RemoteException|RuntimeException e) {
- binderCallsStats.callThrewException(callSession);
+ binderCallsStats.callThrewException(callSession, e);
if (LOG_RUNTIME_EXCEPTION) {
Log.w(TAG, "Caught a RuntimeException from the binder stub implementation.", e);
}
@@ -768,442 +776,3 @@
}
}
-/**
- * Java proxy for a native IBinder object.
- * Allocated and constructed by the native javaObjectforIBinder function. Never allocated
- * directly from Java code.
- */
-final class BinderProxy implements IBinder {
- // See android_util_Binder.cpp for the native half of this.
-
- // Assume the process-wide default value when created
- volatile boolean mWarnOnBlocking = Binder.sWarnOnBlocking;
-
- /*
- * Map from longs to BinderProxy, retaining only a WeakReference to the BinderProxies.
- * We roll our own only because we need to lazily remove WeakReferences during accesses
- * to avoid accumulating junk WeakReference objects. WeakHashMap isn't easily usable
- * because we want weak values, not keys.
- * Our hash table is never resized, but the number of entries is unlimited;
- * performance degrades as occupancy increases significantly past MAIN_INDEX_SIZE.
- * Not thread-safe. Client ensures there's a single access at a time.
- */
- private static final class ProxyMap {
- private static final int LOG_MAIN_INDEX_SIZE = 8;
- private static final int MAIN_INDEX_SIZE = 1 << LOG_MAIN_INDEX_SIZE;
- private static final int MAIN_INDEX_MASK = MAIN_INDEX_SIZE - 1;
- // Debuggable builds will throw an AssertionError if the number of map entries exceeds:
- private static final int CRASH_AT_SIZE = 20_000;
-
- /**
- * We next warn when we exceed this bucket size.
- */
- private int mWarnBucketSize = 20;
-
- /**
- * Increment mWarnBucketSize by WARN_INCREMENT each time we warn.
- */
- private static final int WARN_INCREMENT = 10;
-
- /**
- * Hash function tailored to native pointers.
- * Returns a value < MAIN_INDEX_SIZE.
- */
- private static int hash(long arg) {
- return ((int) ((arg >> 2) ^ (arg >> (2 + LOG_MAIN_INDEX_SIZE)))) & MAIN_INDEX_MASK;
- }
-
- /**
- * Return the total number of pairs in the map.
- */
- private int size() {
- int size = 0;
- for (ArrayList<WeakReference<BinderProxy>> a : mMainIndexValues) {
- if (a != null) {
- size += a.size();
- }
- }
- return size;
- }
-
- /**
- * Return the total number of pairs in the map containing values that have
- * not been cleared. More expensive than the above size function.
- */
- private int unclearedSize() {
- int size = 0;
- for (ArrayList<WeakReference<BinderProxy>> a : mMainIndexValues) {
- if (a != null) {
- for (WeakReference<BinderProxy> ref : a) {
- if (ref.get() != null) {
- ++size;
- }
- }
- }
- }
- return size;
- }
-
- /**
- * Remove ith entry from the hash bucket indicated by hash.
- */
- private void remove(int hash, int index) {
- Long[] keyArray = mMainIndexKeys[hash];
- ArrayList<WeakReference<BinderProxy>> valueArray = mMainIndexValues[hash];
- int size = valueArray.size(); // KeyArray may have extra elements.
- // Move last entry into empty slot, and truncate at end.
- if (index != size - 1) {
- keyArray[index] = keyArray[size - 1];
- valueArray.set(index, valueArray.get(size - 1));
- }
- valueArray.remove(size - 1);
- // Just leave key array entry; it's unused. We only trust the valueArray size.
- }
-
- /**
- * Look up the supplied key. If we have a non-cleared entry for it, return it.
- */
- BinderProxy get(long key) {
- int myHash = hash(key);
- Long[] keyArray = mMainIndexKeys[myHash];
- if (keyArray == null) {
- return null;
- }
- ArrayList<WeakReference<BinderProxy>> valueArray = mMainIndexValues[myHash];
- int bucketSize = valueArray.size();
- for (int i = 0; i < bucketSize; ++i) {
- long foundKey = keyArray[i];
- if (key == foundKey) {
- WeakReference<BinderProxy> wr = valueArray.get(i);
- BinderProxy bp = wr.get();
- if (bp != null) {
- return bp;
- } else {
- remove(myHash, i);
- return null;
- }
- }
- }
- return null;
- }
-
- private int mRandom; // A counter used to generate a "random" index. World's 2nd worst RNG.
-
- /**
- * Add the key-value pair to the map.
- * Requires that the indicated key is not already in the map.
- */
- void set(long key, @NonNull BinderProxy value) {
- int myHash = hash(key);
- ArrayList<WeakReference<BinderProxy>> valueArray = mMainIndexValues[myHash];
- if (valueArray == null) {
- valueArray = mMainIndexValues[myHash] = new ArrayList<>();
- mMainIndexKeys[myHash] = new Long[1];
- }
- int size = valueArray.size();
- WeakReference<BinderProxy> newWr = new WeakReference<>(value);
- // First look for a cleared reference.
- // This ensures that ArrayList size is bounded by the maximum occupancy of
- // that bucket.
- for (int i = 0; i < size; ++i) {
- if (valueArray.get(i).get() == null) {
- valueArray.set(i, newWr);
- Long[] keyArray = mMainIndexKeys[myHash];
- keyArray[i] = key;
- if (i < size - 1) {
- // "Randomly" check one of the remaining entries in [i+1, size), so that
- // needlessly long buckets are eventually pruned.
- int rnd = Math.floorMod(++mRandom, size - (i + 1));
- if (valueArray.get(i + 1 + rnd).get() == null) {
- remove(myHash, i + 1 + rnd);
- }
- }
- return;
- }
- }
- valueArray.add(size, newWr);
- Long[] keyArray = mMainIndexKeys[myHash];
- if (keyArray.length == size) {
- // size >= 1, since we initially allocated one element
- Long[] newArray = new Long[size + size / 2 + 2];
- System.arraycopy(keyArray, 0, newArray, 0, size);
- newArray[size] = key;
- mMainIndexKeys[myHash] = newArray;
- } else {
- keyArray[size] = key;
- }
- if (size >= mWarnBucketSize) {
- final int totalSize = size();
- Log.v(Binder.TAG, "BinderProxy map growth! bucket size = " + size
- + " total = " + totalSize);
- mWarnBucketSize += WARN_INCREMENT;
- if (Build.IS_DEBUGGABLE && totalSize >= CRASH_AT_SIZE) {
- // Use the number of uncleared entries to determine whether we should
- // really report a histogram and crash. We don't want to fundamentally
- // change behavior for a debuggable process, so we GC only if we are
- // about to crash.
- final int totalUnclearedSize = unclearedSize();
- if (totalUnclearedSize >= CRASH_AT_SIZE) {
- dumpProxyInterfaceCounts();
- dumpPerUidProxyCounts();
- Runtime.getRuntime().gc();
- throw new AssertionError("Binder ProxyMap has too many entries: "
- + totalSize + " (total), " + totalUnclearedSize + " (uncleared), "
- + unclearedSize() + " (uncleared after GC). BinderProxy leak?");
- } else if (totalSize > 3 * totalUnclearedSize / 2) {
- Log.v(Binder.TAG, "BinderProxy map has many cleared entries: "
- + (totalSize - totalUnclearedSize) + " of " + totalSize
- + " are cleared");
- }
- }
- }
- }
-
- /**
- * Dump a histogram to the logcat. Used to diagnose abnormally large proxy maps.
- */
- private void dumpProxyInterfaceCounts() {
- Map<String, Integer> counts = new HashMap<>();
- for (ArrayList<WeakReference<BinderProxy>> a : mMainIndexValues) {
- if (a != null) {
- for (WeakReference<BinderProxy> weakRef : a) {
- BinderProxy bp = weakRef.get();
- String key;
- if (bp == null) {
- key = "<cleared weak-ref>";
- } else {
- try {
- key = bp.getInterfaceDescriptor();
- } catch (Throwable t) {
- key = "<exception during getDescriptor>";
- }
- }
- Integer i = counts.get(key);
- if (i == null) {
- counts.put(key, 1);
- } else {
- counts.put(key, i + 1);
- }
- }
- }
- }
- Map.Entry<String, Integer>[] sorted = counts.entrySet().toArray(
- new Map.Entry[counts.size()]);
- Arrays.sort(sorted, (Map.Entry<String, Integer> a, Map.Entry<String, Integer> b)
- -> b.getValue().compareTo(a.getValue()));
- Log.v(Binder.TAG, "BinderProxy descriptor histogram (top ten):");
- int printLength = Math.min(10, sorted.length);
- for (int i = 0; i < printLength; i++) {
- Log.v(Binder.TAG, " #" + (i + 1) + ": " + sorted[i].getKey() + " x"
- + sorted[i].getValue());
- }
- }
-
- /**
- * Dump per uid binder proxy counts to the logcat.
- */
- private void dumpPerUidProxyCounts() {
- SparseIntArray counts = BinderInternal.nGetBinderProxyPerUidCounts();
- if (counts.size() == 0) return;
- Log.d(Binder.TAG, "Per Uid Binder Proxy Counts:");
- for (int i = 0; i < counts.size(); i++) {
- final int uid = counts.keyAt(i);
- final int binderCount = counts.valueAt(i);
- Log.d(Binder.TAG, "UID : " + uid + " count = " + binderCount);
- }
- }
-
- // Corresponding ArrayLists in the following two arrays always have the same size.
- // They contain no empty entries. However WeakReferences in the values ArrayLists
- // may have been cleared.
-
- // mMainIndexKeys[i][j] corresponds to mMainIndexValues[i].get(j) .
- // The values ArrayList has the proper size(), the corresponding keys array
- // is always at least the same size, but may be larger.
- // If either a particular keys array, or the corresponding values ArrayList
- // are null, then they both are.
- private final Long[][] mMainIndexKeys = new Long[MAIN_INDEX_SIZE][];
- private final ArrayList<WeakReference<BinderProxy>>[] mMainIndexValues =
- new ArrayList[MAIN_INDEX_SIZE];
- }
-
- private static ProxyMap sProxyMap = new ProxyMap();
-
- /**
- * Dump proxy debug information.
- *
- * Note: this method is not thread-safe; callers must serialize with other
- * accesses to sProxyMap, in particular {@link #getInstance(long, long)}.
- *
- * @hide
- */
- private static void dumpProxyDebugInfo() {
- if (Build.IS_DEBUGGABLE) {
- sProxyMap.dumpProxyInterfaceCounts();
- // Note that we don't call dumpPerUidProxyCounts(); this is because this
- // method may be called as part of the uid limit being hit, and calling
- // back into the UID tracking code would cause us to try to acquire a mutex
- // that is held during that callback.
- }
- }
-
- /**
- * Return a BinderProxy for IBinder.
- * This method is thread-hostile! The (native) caller serializes getInstance() calls using
- * gProxyLock.
- * If we previously returned a BinderProxy bp for the same iBinder, and bp is still
- * in use, then we return the same bp.
- *
- * @param nativeData C++ pointer to (possibly still empty) BinderProxyNativeData.
- * Takes ownership of nativeData iff <result>.mNativeData == nativeData, or if
- * we exit via an exception. If neither applies, it's the callers responsibility to
- * recycle nativeData.
- * @param iBinder C++ pointer to IBinder. Does not take ownership of referenced object.
- */
- private static BinderProxy getInstance(long nativeData, long iBinder) {
- BinderProxy result;
- try {
- result = sProxyMap.get(iBinder);
- if (result != null) {
- return result;
- }
- result = new BinderProxy(nativeData);
- } catch (Throwable e) {
- // We're throwing an exception (probably OOME); don't drop nativeData.
- NativeAllocationRegistry.applyFreeFunction(NoImagePreloadHolder.sNativeFinalizer,
- nativeData);
- throw e;
- }
- NoImagePreloadHolder.sRegistry.registerNativeAllocation(result, nativeData);
- // The registry now owns nativeData, even if registration threw an exception.
- sProxyMap.set(iBinder, result);
- return result;
- }
-
- private BinderProxy(long nativeData) {
- mNativeData = nativeData;
- }
-
- /**
- * Guestimate of native memory associated with a BinderProxy.
- * This includes the underlying IBinder, associated DeathRecipientList, and KeyedVector
- * that points back to us. We guess high since it includes a GlobalRef, which
- * may be in short supply.
- */
- private static final int NATIVE_ALLOCATION_SIZE = 1000;
-
- // Use a Holder to allow static initialization of BinderProxy in the boot image, and
- // to avoid some initialization ordering issues.
- private static class NoImagePreloadHolder {
- public static final long sNativeFinalizer = getNativeFinalizer();
- public static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry(
- BinderProxy.class.getClassLoader(), sNativeFinalizer, NATIVE_ALLOCATION_SIZE);
- }
-
- public native boolean pingBinder();
- public native boolean isBinderAlive();
-
- public IInterface queryLocalInterface(String descriptor) {
- return null;
- }
-
- public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
- Binder.checkParcel(this, code, data, "Unreasonably large binder buffer");
-
- if (mWarnOnBlocking && ((flags & FLAG_ONEWAY) == 0)) {
- // For now, avoid spamming the log by disabling after we've logged
- // about this interface at least once
- mWarnOnBlocking = false;
- Log.w(Binder.TAG, "Outgoing transactions from this process must be FLAG_ONEWAY",
- new Throwable());
- }
-
- final boolean tracingEnabled = Binder.isTracingEnabled();
- if (tracingEnabled) {
- final Throwable tr = new Throwable();
- Binder.getTransactionTracker().addTrace(tr);
- StackTraceElement stackTraceElement = tr.getStackTrace()[1];
- Trace.traceBegin(Trace.TRACE_TAG_ALWAYS,
- stackTraceElement.getClassName() + "." + stackTraceElement.getMethodName());
- }
- try {
- return transactNative(code, data, reply, flags);
- } finally {
- if (tracingEnabled) {
- Trace.traceEnd(Trace.TRACE_TAG_ALWAYS);
- }
- }
- }
-
- private static native long getNativeFinalizer();
- public native String getInterfaceDescriptor() throws RemoteException;
- public native boolean transactNative(int code, Parcel data, Parcel reply,
- int flags) throws RemoteException;
- public native void linkToDeath(DeathRecipient recipient, int flags)
- throws RemoteException;
- public native boolean unlinkToDeath(DeathRecipient recipient, int flags);
-
- public void dump(FileDescriptor fd, String[] args) throws RemoteException {
- Parcel data = Parcel.obtain();
- Parcel reply = Parcel.obtain();
- data.writeFileDescriptor(fd);
- data.writeStringArray(args);
- try {
- transact(DUMP_TRANSACTION, data, reply, 0);
- reply.readException();
- } finally {
- data.recycle();
- reply.recycle();
- }
- }
-
- public void dumpAsync(FileDescriptor fd, String[] args) throws RemoteException {
- Parcel data = Parcel.obtain();
- Parcel reply = Parcel.obtain();
- data.writeFileDescriptor(fd);
- data.writeStringArray(args);
- try {
- transact(DUMP_TRANSACTION, data, reply, FLAG_ONEWAY);
- } finally {
- data.recycle();
- reply.recycle();
- }
- }
-
- public void shellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
- String[] args, ShellCallback callback,
- ResultReceiver resultReceiver) throws RemoteException {
- Parcel data = Parcel.obtain();
- Parcel reply = Parcel.obtain();
- data.writeFileDescriptor(in);
- data.writeFileDescriptor(out);
- data.writeFileDescriptor(err);
- data.writeStringArray(args);
- ShellCallback.writeToParcel(callback, data);
- resultReceiver.writeToParcel(data, 0);
- try {
- transact(SHELL_COMMAND_TRANSACTION, data, reply, 0);
- reply.readException();
- } finally {
- data.recycle();
- reply.recycle();
- }
- }
-
- private static final void sendDeathNotice(DeathRecipient recipient) {
- if (false) Log.v("JavaBinder", "sendDeathNotice to " + recipient);
- try {
- recipient.binderDied();
- }
- catch (RuntimeException exc) {
- Log.w("BinderNative", "Uncaught exception from death notification",
- exc);
- }
- }
-
- /**
- * C++ pointer to BinderProxyNativeData. That consists of strong pointers to the
- * native IBinder object, and a DeathRecipientList.
- */
- private final long mNativeData;
-}
diff --git a/core/java/android/os/BinderProxy.java b/core/java/android/os/BinderProxy.java
new file mode 100644
index 0000000..5752b6f
--- /dev/null
+++ b/core/java/android/os/BinderProxy.java
@@ -0,0 +1,588 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.annotation.NonNull;
+import android.util.Log;
+import android.util.SparseIntArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.os.BinderInternal;
+
+import libcore.util.NativeAllocationRegistry;
+
+import java.io.FileDescriptor;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Java proxy for a native IBinder object.
+ * Allocated and constructed by the native javaObjectforIBinder function. Never allocated
+ * directly from Java code.
+ *
+ * @hide
+ */
+public final class BinderProxy implements IBinder {
+ // See android_util_Binder.cpp for the native half of this.
+
+ // Assume the process-wide default value when created
+ volatile boolean mWarnOnBlocking = Binder.sWarnOnBlocking;
+
+ /*
+ * Map from longs to BinderProxy, retaining only a WeakReference to the BinderProxies.
+ * We roll our own only because we need to lazily remove WeakReferences during accesses
+ * to avoid accumulating junk WeakReference objects. WeakHashMap isn't easily usable
+ * because we want weak values, not keys.
+ * Our hash table is never resized, but the number of entries is unlimited;
+ * performance degrades as occupancy increases significantly past MAIN_INDEX_SIZE.
+ * Not thread-safe. Client ensures there's a single access at a time.
+ */
+ private static final class ProxyMap {
+ private static final int LOG_MAIN_INDEX_SIZE = 8;
+ private static final int MAIN_INDEX_SIZE = 1 << LOG_MAIN_INDEX_SIZE;
+ private static final int MAIN_INDEX_MASK = MAIN_INDEX_SIZE - 1;
+ // Debuggable builds will throw an AssertionError if the number of map entries exceeds:
+ private static final int CRASH_AT_SIZE = 20_000;
+
+ /**
+ * We next warn when we exceed this bucket size.
+ */
+ private int mWarnBucketSize = 20;
+
+ /**
+ * Increment mWarnBucketSize by WARN_INCREMENT each time we warn.
+ */
+ private static final int WARN_INCREMENT = 10;
+
+ /**
+ * Hash function tailored to native pointers.
+ * Returns a value < MAIN_INDEX_SIZE.
+ */
+ private static int hash(long arg) {
+ return ((int) ((arg >> 2) ^ (arg >> (2 + LOG_MAIN_INDEX_SIZE)))) & MAIN_INDEX_MASK;
+ }
+
+ /**
+ * Return the total number of pairs in the map.
+ */
+ private int size() {
+ int size = 0;
+ for (ArrayList<WeakReference<BinderProxy>> a : mMainIndexValues) {
+ if (a != null) {
+ size += a.size();
+ }
+ }
+ return size;
+ }
+
+ /**
+ * Return the total number of pairs in the map containing values that have
+ * not been cleared. More expensive than the above size function.
+ */
+ private int unclearedSize() {
+ int size = 0;
+ for (ArrayList<WeakReference<BinderProxy>> a : mMainIndexValues) {
+ if (a != null) {
+ for (WeakReference<BinderProxy> ref : a) {
+ if (ref.get() != null) {
+ ++size;
+ }
+ }
+ }
+ }
+ return size;
+ }
+
+ /**
+ * Remove ith entry from the hash bucket indicated by hash.
+ */
+ private void remove(int hash, int index) {
+ Long[] keyArray = mMainIndexKeys[hash];
+ ArrayList<WeakReference<BinderProxy>> valueArray = mMainIndexValues[hash];
+ int size = valueArray.size(); // KeyArray may have extra elements.
+ // Move last entry into empty slot, and truncate at end.
+ if (index != size - 1) {
+ keyArray[index] = keyArray[size - 1];
+ valueArray.set(index, valueArray.get(size - 1));
+ }
+ valueArray.remove(size - 1);
+ // Just leave key array entry; it's unused. We only trust the valueArray size.
+ }
+
+ /**
+ * Look up the supplied key. If we have a non-cleared entry for it, return it.
+ */
+ BinderProxy get(long key) {
+ int myHash = hash(key);
+ Long[] keyArray = mMainIndexKeys[myHash];
+ if (keyArray == null) {
+ return null;
+ }
+ ArrayList<WeakReference<BinderProxy>> valueArray = mMainIndexValues[myHash];
+ int bucketSize = valueArray.size();
+ for (int i = 0; i < bucketSize; ++i) {
+ long foundKey = keyArray[i];
+ if (key == foundKey) {
+ WeakReference<BinderProxy> wr = valueArray.get(i);
+ BinderProxy bp = wr.get();
+ if (bp != null) {
+ return bp;
+ } else {
+ remove(myHash, i);
+ return null;
+ }
+ }
+ }
+ return null;
+ }
+
+ private int mRandom; // A counter used to generate a "random" index. World's 2nd worst RNG.
+
+ /**
+ * Add the key-value pair to the map.
+ * Requires that the indicated key is not already in the map.
+ */
+ void set(long key, @NonNull BinderProxy value) {
+ int myHash = hash(key);
+ ArrayList<WeakReference<BinderProxy>> valueArray = mMainIndexValues[myHash];
+ if (valueArray == null) {
+ valueArray = mMainIndexValues[myHash] = new ArrayList<>();
+ mMainIndexKeys[myHash] = new Long[1];
+ }
+ int size = valueArray.size();
+ WeakReference<BinderProxy> newWr = new WeakReference<>(value);
+ // First look for a cleared reference.
+ // This ensures that ArrayList size is bounded by the maximum occupancy of
+ // that bucket.
+ for (int i = 0; i < size; ++i) {
+ if (valueArray.get(i).get() == null) {
+ valueArray.set(i, newWr);
+ Long[] keyArray = mMainIndexKeys[myHash];
+ keyArray[i] = key;
+ if (i < size - 1) {
+ // "Randomly" check one of the remaining entries in [i+1, size), so that
+ // needlessly long buckets are eventually pruned.
+ int rnd = Math.floorMod(++mRandom, size - (i + 1));
+ if (valueArray.get(i + 1 + rnd).get() == null) {
+ remove(myHash, i + 1 + rnd);
+ }
+ }
+ return;
+ }
+ }
+ valueArray.add(size, newWr);
+ Long[] keyArray = mMainIndexKeys[myHash];
+ if (keyArray.length == size) {
+ // size >= 1, since we initially allocated one element
+ Long[] newArray = new Long[size + size / 2 + 2];
+ System.arraycopy(keyArray, 0, newArray, 0, size);
+ newArray[size] = key;
+ mMainIndexKeys[myHash] = newArray;
+ } else {
+ keyArray[size] = key;
+ }
+ if (size >= mWarnBucketSize) {
+ final int totalSize = size();
+ Log.v(Binder.TAG, "BinderProxy map growth! bucket size = " + size
+ + " total = " + totalSize);
+ mWarnBucketSize += WARN_INCREMENT;
+ if (Build.IS_DEBUGGABLE && totalSize >= CRASH_AT_SIZE) {
+ // Use the number of uncleared entries to determine whether we should
+ // really report a histogram and crash. We don't want to fundamentally
+ // change behavior for a debuggable process, so we GC only if we are
+ // about to crash.
+ final int totalUnclearedSize = unclearedSize();
+ if (totalUnclearedSize >= CRASH_AT_SIZE) {
+ dumpProxyInterfaceCounts();
+ dumpPerUidProxyCounts();
+ Runtime.getRuntime().gc();
+ throw new AssertionError("Binder ProxyMap has too many entries: "
+ + totalSize + " (total), " + totalUnclearedSize + " (uncleared), "
+ + unclearedSize() + " (uncleared after GC). BinderProxy leak?");
+ } else if (totalSize > 3 * totalUnclearedSize / 2) {
+ Log.v(Binder.TAG, "BinderProxy map has many cleared entries: "
+ + (totalSize - totalUnclearedSize) + " of " + totalSize
+ + " are cleared");
+ }
+ }
+ }
+ }
+
+ private InterfaceCount[] getSortedInterfaceCounts(int maxToReturn) {
+ if (maxToReturn < 0) {
+ throw new IllegalArgumentException("negative interface count");
+ }
+
+ Map<String, Integer> counts = new HashMap<>();
+ for (ArrayList<WeakReference<BinderProxy>> a : mMainIndexValues) {
+ if (a != null) {
+ for (WeakReference<BinderProxy> weakRef : a) {
+ BinderProxy bp = weakRef.get();
+ String key;
+ if (bp == null) {
+ key = "<cleared weak-ref>";
+ } else {
+ try {
+ key = bp.getInterfaceDescriptor();
+ } catch (Throwable t) {
+ key = "<exception during getDescriptor>";
+ }
+ }
+ Integer i = counts.get(key);
+ if (i == null) {
+ counts.put(key, 1);
+ } else {
+ counts.put(key, i + 1);
+ }
+ }
+ }
+ }
+ Map.Entry<String, Integer>[] sorted = counts.entrySet().toArray(
+ new Map.Entry[counts.size()]);
+
+ Arrays.sort(sorted, (Map.Entry<String, Integer> a, Map.Entry<String, Integer> b)
+ -> b.getValue().compareTo(a.getValue()));
+
+ int returnCount = Math.min(maxToReturn, sorted.length);
+ InterfaceCount[] ifaceCounts = new InterfaceCount[returnCount];
+ for (int i = 0; i < returnCount; i++) {
+ ifaceCounts[i] = new InterfaceCount(sorted[i].getKey(), sorted[i].getValue());
+ }
+ return ifaceCounts;
+ }
+
+ static final int MAX_NUM_INTERFACES_TO_DUMP = 10;
+
+ /**
+ * Dump a histogram to the logcat. Used to diagnose abnormally large proxy maps.
+ */
+ private void dumpProxyInterfaceCounts() {
+ final InterfaceCount[] sorted = getSortedInterfaceCounts(MAX_NUM_INTERFACES_TO_DUMP);
+
+ Log.v(Binder.TAG, "BinderProxy descriptor histogram "
+ + "(top " + Integer.toString(MAX_NUM_INTERFACES_TO_DUMP) + "):");
+ for (int i = 0; i < sorted.length; i++) {
+ Log.v(Binder.TAG, " #" + (i + 1) + ": " + sorted[i]);
+ }
+ }
+
+ /**
+ * Dump per uid binder proxy counts to the logcat.
+ */
+ private void dumpPerUidProxyCounts() {
+ SparseIntArray counts = BinderInternal.nGetBinderProxyPerUidCounts();
+ if (counts.size() == 0) return;
+ Log.d(Binder.TAG, "Per Uid Binder Proxy Counts:");
+ for (int i = 0; i < counts.size(); i++) {
+ final int uid = counts.keyAt(i);
+ final int binderCount = counts.valueAt(i);
+ Log.d(Binder.TAG, "UID : " + uid + " count = " + binderCount);
+ }
+ }
+
+ // Corresponding ArrayLists in the following two arrays always have the same size.
+ // They contain no empty entries. However WeakReferences in the values ArrayLists
+ // may have been cleared.
+
+ // mMainIndexKeys[i][j] corresponds to mMainIndexValues[i].get(j) .
+ // The values ArrayList has the proper size(), the corresponding keys array
+ // is always at least the same size, but may be larger.
+ // If either a particular keys array, or the corresponding values ArrayList
+ // are null, then they both are.
+ private final Long[][] mMainIndexKeys = new Long[MAIN_INDEX_SIZE][];
+ private final ArrayList<WeakReference<BinderProxy>>[] mMainIndexValues =
+ new ArrayList[MAIN_INDEX_SIZE];
+ }
+
+ @GuardedBy("sProxyMap")
+ private static final ProxyMap sProxyMap = new ProxyMap();
+
+ /**
+ * Simple pair-value class to store number of binder proxy interfaces live in this process.
+ */
+ public static final class InterfaceCount {
+ private final String mInterfaceName;
+ private final int mCount;
+
+ InterfaceCount(String interfaceName, int count) {
+ mInterfaceName = interfaceName;
+ mCount = count;
+ }
+
+ @Override
+ public String toString() {
+ return mInterfaceName + " x" + Integer.toString(mCount);
+ }
+ }
+
+ /**
+ * Get a sorted array with entries mapping proxy interface names to the number
+ * of live proxies with those names.
+ *
+ * @param num maximum number of proxy interface counts to return. Use
+ * Integer.MAX_VALUE to retrieve all
+ * @hide
+ */
+ public static InterfaceCount[] getSortedInterfaceCounts(int num) {
+ synchronized (sProxyMap) {
+ return sProxyMap.getSortedInterfaceCounts(num);
+ }
+ }
+
+ /**
+ * Dump proxy debug information.
+ *
+ * @hide
+ */
+ public static void dumpProxyDebugInfo() {
+ if (Build.IS_DEBUGGABLE) {
+ synchronized (sProxyMap) {
+ sProxyMap.dumpProxyInterfaceCounts();
+ sProxyMap.dumpPerUidProxyCounts();
+ }
+ }
+ }
+
+ /**
+ * Return a BinderProxy for IBinder.
+ * If we previously returned a BinderProxy bp for the same iBinder, and bp is still
+ * in use, then we return the same bp.
+ *
+ * @param nativeData C++ pointer to (possibly still empty) BinderProxyNativeData.
+ * Takes ownership of nativeData iff <result>.mNativeData == nativeData, or if
+ * we exit via an exception. If neither applies, it's the callers responsibility to
+ * recycle nativeData.
+ * @param iBinder C++ pointer to IBinder. Does not take ownership of referenced object.
+ */
+ private static BinderProxy getInstance(long nativeData, long iBinder) {
+ BinderProxy result;
+ synchronized (sProxyMap) {
+ try {
+ result = sProxyMap.get(iBinder);
+ if (result != null) {
+ return result;
+ }
+ result = new BinderProxy(nativeData);
+ } catch (Throwable e) {
+ // We're throwing an exception (probably OOME); don't drop nativeData.
+ NativeAllocationRegistry.applyFreeFunction(NoImagePreloadHolder.sNativeFinalizer,
+ nativeData);
+ throw e;
+ }
+ NoImagePreloadHolder.sRegistry.registerNativeAllocation(result, nativeData);
+ // The registry now owns nativeData, even if registration threw an exception.
+ sProxyMap.set(iBinder, result);
+ }
+ return result;
+ }
+
+ private BinderProxy(long nativeData) {
+ mNativeData = nativeData;
+ }
+
+ /**
+ * Guestimate of native memory associated with a BinderProxy.
+ * This includes the underlying IBinder, associated DeathRecipientList, and KeyedVector
+ * that points back to us. We guess high since it includes a GlobalRef, which
+ * may be in short supply.
+ */
+ private static final int NATIVE_ALLOCATION_SIZE = 1000;
+
+ // Use a Holder to allow static initialization of BinderProxy in the boot image, and
+ // to avoid some initialization ordering issues.
+ private static class NoImagePreloadHolder {
+ public static final long sNativeFinalizer = getNativeFinalizer();
+ public static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry(
+ BinderProxy.class.getClassLoader(), sNativeFinalizer, NATIVE_ALLOCATION_SIZE);
+ }
+
+ /**
+ * @return false if the hosting process is gone, otherwise whatever the remote returns
+ */
+ public native boolean pingBinder();
+
+ /**
+ * @return false if the hosting process is gone
+ */
+ public native boolean isBinderAlive();
+
+ /**
+ * Retrieve a local interface - always null in case of a proxy
+ */
+ public IInterface queryLocalInterface(String descriptor) {
+ return null;
+ }
+
+ /**
+ * Perform a binder transaction on a proxy.
+ *
+ * @param code The action to perform. This should
+ * be a number between {@link #FIRST_CALL_TRANSACTION} and
+ * {@link #LAST_CALL_TRANSACTION}.
+ * @param data Marshalled data to send to the target. Must not be null.
+ * If you are not sending any data, you must create an empty Parcel
+ * that is given here.
+ * @param reply Marshalled data to be received from the target. May be
+ * null if you are not interested in the return value.
+ * @param flags Additional operation flags. Either 0 for a normal
+ * RPC, or {@link #FLAG_ONEWAY} for a one-way RPC.
+ *
+ * @return
+ * @throws RemoteException
+ */
+ public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
+ Binder.checkParcel(this, code, data, "Unreasonably large binder buffer");
+
+ if (mWarnOnBlocking && ((flags & FLAG_ONEWAY) == 0)) {
+ // For now, avoid spamming the log by disabling after we've logged
+ // about this interface at least once
+ mWarnOnBlocking = false;
+ Log.w(Binder.TAG, "Outgoing transactions from this process must be FLAG_ONEWAY",
+ new Throwable());
+ }
+
+ final boolean tracingEnabled = Binder.isTracingEnabled();
+ if (tracingEnabled) {
+ final Throwable tr = new Throwable();
+ Binder.getTransactionTracker().addTrace(tr);
+ StackTraceElement stackTraceElement = tr.getStackTrace()[1];
+ Trace.traceBegin(Trace.TRACE_TAG_ALWAYS,
+ stackTraceElement.getClassName() + "." + stackTraceElement.getMethodName());
+ }
+ try {
+ return transactNative(code, data, reply, flags);
+ } finally {
+ if (tracingEnabled) {
+ Trace.traceEnd(Trace.TRACE_TAG_ALWAYS);
+ }
+ }
+ }
+
+ /* Returns the native free function */
+ private static native long getNativeFinalizer();
+ /**
+ * See {@link IBinder#getInterfaceDescriptor()}
+ */
+ public native String getInterfaceDescriptor() throws RemoteException;
+
+ /**
+ * Native implementation of transact() for proxies
+ */
+ public native boolean transactNative(int code, Parcel data, Parcel reply,
+ int flags) throws RemoteException;
+ /**
+ * See {@link IBinder#linkToDeath(DeathRecipient, int)}
+ */
+ public native void linkToDeath(DeathRecipient recipient, int flags)
+ throws RemoteException;
+ /**
+ * See {@link IBinder#unlinkToDeath}
+ */
+ public native boolean unlinkToDeath(DeathRecipient recipient, int flags);
+
+ /**
+ * Perform a dump on the remote object
+ *
+ * @param fd The raw file descriptor that the dump is being sent to.
+ * @param args additional arguments to the dump request.
+ * @throws RemoteException
+ */
+ public void dump(FileDescriptor fd, String[] args) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeFileDescriptor(fd);
+ data.writeStringArray(args);
+ try {
+ transact(DUMP_TRANSACTION, data, reply, 0);
+ reply.readException();
+ } finally {
+ data.recycle();
+ reply.recycle();
+ }
+ }
+
+ /**
+ * Perform an asynchronous dump on the remote object
+ *
+ * @param fd The raw file descriptor that the dump is being sent to.
+ * @param args additional arguments to the dump request.
+ * @throws RemoteException
+ */
+ public void dumpAsync(FileDescriptor fd, String[] args) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeFileDescriptor(fd);
+ data.writeStringArray(args);
+ try {
+ transact(DUMP_TRANSACTION, data, reply, FLAG_ONEWAY);
+ } finally {
+ data.recycle();
+ reply.recycle();
+ }
+ }
+
+ /**
+ * See {@link IBinder#shellCommand(FileDescriptor, FileDescriptor, FileDescriptor,
+ * String[], ShellCallback, ResultReceiver)}
+ *
+ * @param in The raw file descriptor that an input data stream can be read from.
+ * @param out The raw file descriptor that normal command messages should be written to.
+ * @param err The raw file descriptor that command error messages should be written to.
+ * @param args Command-line arguments.
+ * @param callback Optional callback to the caller's shell to perform operations in it.
+ * @param resultReceiver Called when the command has finished executing, with the result code.
+ * @throws RemoteException
+ */
+ public void shellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
+ String[] args, ShellCallback callback,
+ ResultReceiver resultReceiver) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeFileDescriptor(in);
+ data.writeFileDescriptor(out);
+ data.writeFileDescriptor(err);
+ data.writeStringArray(args);
+ ShellCallback.writeToParcel(callback, data);
+ resultReceiver.writeToParcel(data, 0);
+ try {
+ transact(SHELL_COMMAND_TRANSACTION, data, reply, 0);
+ reply.readException();
+ } finally {
+ data.recycle();
+ reply.recycle();
+ }
+ }
+
+ private static void sendDeathNotice(DeathRecipient recipient) {
+ if (false) Log.v("JavaBinder", "sendDeathNotice to " + recipient);
+ try {
+ recipient.binderDied();
+ } catch (RuntimeException exc) {
+ Log.w("BinderNative", "Uncaught exception from death notification",
+ exc);
+ }
+ }
+
+ /**
+ * C++ pointer to BinderProxyNativeData. That consists of strong pointers to the
+ * native IBinder object, and a DeathRecipientList.
+ */
+ private final long mNativeData;
+}
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 80c534c..e71f4e9 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -165,6 +165,11 @@
public static final String[] SUPPORTED_64_BIT_ABIS =
getStringList("ro.product.cpu.abilist64", ",");
+ /** {@hide} */
+ @TestApi
+ public static boolean is64BitAbi(String abi) {
+ return VMRuntime.is64BitAbi(abi);
+ }
static {
/*
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index 213260f..e32ed9d 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -33,6 +33,8 @@
public class Environment {
private static final String TAG = "Environment";
+ // NOTE: keep credential-protected paths in sync with StrictMode.java
+
private static final String ENV_EXTERNAL_STORAGE = "EXTERNAL_STORAGE";
private static final String ENV_ANDROID_ROOT = "ANDROID_ROOT";
private static final String ENV_ANDROID_DATA = "ANDROID_DATA";
@@ -149,6 +151,7 @@
}
/** {@hide} */
+ @TestApi
public static File getStorageDirectory() {
return DIR_ANDROID_STORAGE;
}
diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java
index 88d6e84..3a3d9ea 100644
--- a/core/java/android/os/FileUtils.java
+++ b/core/java/android/os/FileUtils.java
@@ -53,32 +53,35 @@
import java.util.Arrays;
import java.util.Comparator;
import java.util.Objects;
+import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;
import java.util.zip.CRC32;
import java.util.zip.CheckedInputStream;
/**
- * Tools for managing files. Not for public consumption.
- * @hide
+ * Utility methods useful for working with files.
*/
public class FileUtils {
private static final String TAG = "FileUtils";
- public static final int S_IRWXU = 00700;
- public static final int S_IRUSR = 00400;
- public static final int S_IWUSR = 00200;
- public static final int S_IXUSR = 00100;
+ /** {@hide} */ public static final int S_IRWXU = 00700;
+ /** {@hide} */ public static final int S_IRUSR = 00400;
+ /** {@hide} */ public static final int S_IWUSR = 00200;
+ /** {@hide} */ public static final int S_IXUSR = 00100;
- public static final int S_IRWXG = 00070;
- public static final int S_IRGRP = 00040;
- public static final int S_IWGRP = 00020;
- public static final int S_IXGRP = 00010;
+ /** {@hide} */ public static final int S_IRWXG = 00070;
+ /** {@hide} */ public static final int S_IRGRP = 00040;
+ /** {@hide} */ public static final int S_IWGRP = 00020;
+ /** {@hide} */ public static final int S_IXGRP = 00010;
- public static final int S_IRWXO = 00007;
- public static final int S_IROTH = 00004;
- public static final int S_IWOTH = 00002;
- public static final int S_IXOTH = 00001;
+ /** {@hide} */ public static final int S_IRWXO = 00007;
+ /** {@hide} */ public static final int S_IROTH = 00004;
+ /** {@hide} */ public static final int S_IWOTH = 00002;
+ /** {@hide} */ public static final int S_IXOTH = 00001;
+
+ private FileUtils() {
+ }
/** Regular expression for safe filenames: no spaces or metacharacters.
*
@@ -90,10 +93,14 @@
private static final File[] EMPTY = new File[0];
- private static final boolean ENABLE_COPY_OPTIMIZATIONS = true;
+ // non-final so it can be toggled by Robolectric's ShadowFileUtils
+ private static boolean sEnableCopyOptimizations = true;
private static final long COPY_CHECKPOINT_BYTES = 524288;
+ /**
+ * Listener that is called periodically as progress is made.
+ */
public interface ProgressListener {
public void onProgress(long progress);
}
@@ -105,6 +112,7 @@
* @param uid to apply through {@code chown}, or -1 to leave unchanged
* @param gid to apply through {@code chown}, or -1 to leave unchanged
* @return 0 on success, otherwise errno.
+ * @hide
*/
public static int setPermissions(File path, int mode, int uid, int gid) {
return setPermissions(path.getAbsolutePath(), mode, uid, gid);
@@ -117,6 +125,7 @@
* @param uid to apply through {@code chown}, or -1 to leave unchanged
* @param gid to apply through {@code chown}, or -1 to leave unchanged
* @return 0 on success, otherwise errno.
+ * @hide
*/
public static int setPermissions(String path, int mode, int uid, int gid) {
try {
@@ -145,6 +154,7 @@
* @param uid to apply through {@code chown}, or -1 to leave unchanged
* @param gid to apply through {@code chown}, or -1 to leave unchanged
* @return 0 on success, otherwise errno.
+ * @hide
*/
public static int setPermissions(FileDescriptor fd, int mode, int uid, int gid) {
try {
@@ -166,7 +176,14 @@
return 0;
}
- public static void copyPermissions(File from, File to) throws IOException {
+ /**
+ * Copy the owner UID, owner GID, and mode bits from one file to another.
+ *
+ * @param from File where attributes should be copied from.
+ * @param to File where attributes should be copied to.
+ * @hide
+ */
+ public static void copyPermissions(@NonNull File from, @NonNull File to) throws IOException {
try {
final StructStat stat = Os.stat(from.getAbsolutePath());
Os.chmod(to.getAbsolutePath(), stat.st_mode);
@@ -177,8 +194,10 @@
}
/**
- * Return owning UID of given path, otherwise -1.
+ * @deprecated use {@link Os#stat(String)} instead.
+ * @hide
*/
+ @Deprecated
public static int getUid(String path) {
try {
return Os.stat(path).st_uid;
@@ -190,6 +209,8 @@
/**
* Perform an fsync on the given FileOutputStream. The stream at this
* point must be flushed but not yet closed.
+ *
+ * @hide
*/
public static boolean sync(FileOutputStream stream) {
try {
@@ -204,6 +225,7 @@
/**
* @deprecated use {@link #copy(File, File)} instead.
+ * @hide
*/
@Deprecated
public static boolean copyFile(File srcFile, File destFile) {
@@ -217,6 +239,7 @@
/**
* @deprecated use {@link #copy(File, File)} instead.
+ * @hide
*/
@Deprecated
public static void copyFileOrThrow(File srcFile, File destFile) throws IOException {
@@ -227,6 +250,7 @@
/**
* @deprecated use {@link #copy(InputStream, OutputStream)} instead.
+ * @hide
*/
@Deprecated
public static boolean copyToFile(InputStream inputStream, File destFile) {
@@ -240,6 +264,7 @@
/**
* @deprecated use {@link #copy(InputStream, OutputStream)} instead.
+ * @hide
*/
@Deprecated
public static void copyToFileOrThrow(InputStream in, File destFile) throws IOException {
@@ -265,7 +290,7 @@
* @return number of bytes copied.
*/
public static long copy(@NonNull File from, @NonNull File to) throws IOException {
- return copy(from, to, null, null);
+ return copy(from, to, null, null, null);
}
/**
@@ -274,16 +299,17 @@
* Attempts to use several optimization strategies to copy the data in the
* kernel before falling back to a userspace copy as a last resort.
*
- * @param listener to be periodically notified as the copy progresses.
* @param signal to signal if the copy should be cancelled early.
+ * @param executor that listener events should be delivered via.
+ * @param listener to be periodically notified as the copy progresses.
* @return number of bytes copied.
*/
public static long copy(@NonNull File from, @NonNull File to,
- @Nullable ProgressListener listener, @Nullable CancellationSignal signal)
- throws IOException {
+ @Nullable CancellationSignal signal, @Nullable Executor executor,
+ @Nullable ProgressListener listener) throws IOException {
try (FileInputStream in = new FileInputStream(from);
FileOutputStream out = new FileOutputStream(to)) {
- return copy(in, out, listener, signal);
+ return copy(in, out, signal, executor, listener);
}
}
@@ -296,7 +322,7 @@
* @return number of bytes copied.
*/
public static long copy(@NonNull InputStream in, @NonNull OutputStream out) throws IOException {
- return copy(in, out, null, null);
+ return copy(in, out, null, null, null);
}
/**
@@ -305,22 +331,23 @@
* Attempts to use several optimization strategies to copy the data in the
* kernel before falling back to a userspace copy as a last resort.
*
- * @param listener to be periodically notified as the copy progresses.
* @param signal to signal if the copy should be cancelled early.
+ * @param executor that listener events should be delivered via.
+ * @param listener to be periodically notified as the copy progresses.
* @return number of bytes copied.
*/
public static long copy(@NonNull InputStream in, @NonNull OutputStream out,
- @Nullable ProgressListener listener, @Nullable CancellationSignal signal)
- throws IOException {
- if (ENABLE_COPY_OPTIMIZATIONS) {
+ @Nullable CancellationSignal signal, @Nullable Executor executor,
+ @Nullable ProgressListener listener) throws IOException {
+ if (sEnableCopyOptimizations) {
if (in instanceof FileInputStream && out instanceof FileOutputStream) {
return copy(((FileInputStream) in).getFD(), ((FileOutputStream) out).getFD(),
- listener, signal);
+ signal, executor, listener);
}
}
// Worse case fallback to userspace
- return copyInternalUserspace(in, out, listener, signal);
+ return copyInternalUserspace(in, out, signal, executor, listener);
}
/**
@@ -333,7 +360,7 @@
*/
public static long copy(@NonNull FileDescriptor in, @NonNull FileDescriptor out)
throws IOException {
- return copy(in, out, null, null);
+ return copy(in, out, null, null, null);
}
/**
@@ -342,14 +369,15 @@
* Attempts to use several optimization strategies to copy the data in the
* kernel before falling back to a userspace copy as a last resort.
*
- * @param listener to be periodically notified as the copy progresses.
* @param signal to signal if the copy should be cancelled early.
+ * @param executor that listener events should be delivered via.
+ * @param listener to be periodically notified as the copy progresses.
* @return number of bytes copied.
*/
public static long copy(@NonNull FileDescriptor in, @NonNull FileDescriptor out,
- @Nullable ProgressListener listener, @Nullable CancellationSignal signal)
- throws IOException {
- return copy(in, out, listener, signal, Long.MAX_VALUE);
+ @Nullable CancellationSignal signal, @Nullable Executor executor,
+ @Nullable ProgressListener listener) throws IOException {
+ return copy(in, out, Long.MAX_VALUE, signal, executor, listener);
}
/**
@@ -358,22 +386,24 @@
* Attempts to use several optimization strategies to copy the data in the
* kernel before falling back to a userspace copy as a last resort.
*
- * @param listener to be periodically notified as the copy progresses.
- * @param signal to signal if the copy should be cancelled early.
* @param count the number of bytes to copy.
+ * @param signal to signal if the copy should be cancelled early.
+ * @param executor that listener events should be delivered via.
+ * @param listener to be periodically notified as the copy progresses.
* @return number of bytes copied.
+ * @hide
*/
- public static long copy(@NonNull FileDescriptor in, @NonNull FileDescriptor out,
- @Nullable ProgressListener listener, @Nullable CancellationSignal signal, long count)
- throws IOException {
- if (ENABLE_COPY_OPTIMIZATIONS) {
+ public static long copy(@NonNull FileDescriptor in, @NonNull FileDescriptor out, long count,
+ @Nullable CancellationSignal signal, @Nullable Executor executor,
+ @Nullable ProgressListener listener) throws IOException {
+ if (sEnableCopyOptimizations) {
try {
final StructStat st_in = Os.fstat(in);
final StructStat st_out = Os.fstat(out);
if (S_ISREG(st_in.st_mode) && S_ISREG(st_out.st_mode)) {
- return copyInternalSendfile(in, out, listener, signal, count);
+ return copyInternalSendfile(in, out, count, signal, executor, listener);
} else if (S_ISFIFO(st_in.st_mode) || S_ISFIFO(st_out.st_mode)) {
- return copyInternalSplice(in, out, listener, signal, count);
+ return copyInternalSplice(in, out, count, signal, executor, listener);
}
} catch (ErrnoException e) {
throw e.rethrowAsIOException();
@@ -381,15 +411,17 @@
}
// Worse case fallback to userspace
- return copyInternalUserspace(in, out, listener, signal, count);
+ return copyInternalUserspace(in, out, count, signal, executor, listener);
}
/**
* Requires one of input or output to be a pipe.
+ *
+ * @hide
*/
@VisibleForTesting
- public static long copyInternalSplice(FileDescriptor in, FileDescriptor out,
- ProgressListener listener, CancellationSignal signal, long count)
+ public static long copyInternalSplice(FileDescriptor in, FileDescriptor out, long count,
+ CancellationSignal signal, Executor executor, ProgressListener listener)
throws ErrnoException {
long progress = 0;
long checkpoint = 0;
@@ -405,24 +437,32 @@
if (signal != null) {
signal.throwIfCanceled();
}
- if (listener != null) {
- listener.onProgress(progress);
+ if (executor != null && listener != null) {
+ final long progressSnapshot = progress;
+ executor.execute(() -> {
+ listener.onProgress(progressSnapshot);
+ });
}
checkpoint = 0;
}
}
- if (listener != null) {
- listener.onProgress(progress);
+ if (executor != null && listener != null) {
+ final long progressSnapshot = progress;
+ executor.execute(() -> {
+ listener.onProgress(progressSnapshot);
+ });
}
return progress;
}
/**
* Requires both input and output to be a regular file.
+ *
+ * @hide
*/
@VisibleForTesting
- public static long copyInternalSendfile(FileDescriptor in, FileDescriptor out,
- ProgressListener listener, CancellationSignal signal, long count)
+ public static long copyInternalSendfile(FileDescriptor in, FileDescriptor out, long count,
+ CancellationSignal signal, Executor executor, ProgressListener listener)
throws ErrnoException {
long progress = 0;
long checkpoint = 0;
@@ -437,33 +477,52 @@
if (signal != null) {
signal.throwIfCanceled();
}
- if (listener != null) {
- listener.onProgress(progress);
+ if (executor != null && listener != null) {
+ final long progressSnapshot = progress;
+ executor.execute(() -> {
+ listener.onProgress(progressSnapshot);
+ });
}
checkpoint = 0;
}
}
- if (listener != null) {
- listener.onProgress(progress);
+ if (executor != null && listener != null) {
+ final long progressSnapshot = progress;
+ executor.execute(() -> {
+ listener.onProgress(progressSnapshot);
+ });
}
return progress;
}
+ /** {@hide} */
+ @Deprecated
@VisibleForTesting
public static long copyInternalUserspace(FileDescriptor in, FileDescriptor out,
- ProgressListener listener, CancellationSignal signal, long count) throws IOException {
+ ProgressListener listener, CancellationSignal signal, long count)
+ throws IOException {
+ return copyInternalUserspace(in, out, count, signal, Runnable::run, listener);
+ }
+
+ /** {@hide} */
+ @VisibleForTesting
+ public static long copyInternalUserspace(FileDescriptor in, FileDescriptor out, long count,
+ CancellationSignal signal, Executor executor, ProgressListener listener)
+ throws IOException {
if (count != Long.MAX_VALUE) {
return copyInternalUserspace(new SizedInputStream(new FileInputStream(in), count),
- new FileOutputStream(out), listener, signal);
+ new FileOutputStream(out), signal, executor, listener);
} else {
return copyInternalUserspace(new FileInputStream(in),
- new FileOutputStream(out), listener, signal);
+ new FileOutputStream(out), signal, executor, listener);
}
}
+ /** {@hide} */
@VisibleForTesting
public static long copyInternalUserspace(InputStream in, OutputStream out,
- ProgressListener listener, CancellationSignal signal) throws IOException {
+ CancellationSignal signal, Executor executor, ProgressListener listener)
+ throws IOException {
long progress = 0;
long checkpoint = 0;
byte[] buffer = new byte[8192];
@@ -479,14 +538,20 @@
if (signal != null) {
signal.throwIfCanceled();
}
- if (listener != null) {
- listener.onProgress(progress);
+ if (executor != null && listener != null) {
+ final long progressSnapshot = progress;
+ executor.execute(() -> {
+ listener.onProgress(progressSnapshot);
+ });
}
checkpoint = 0;
}
}
- if (listener != null) {
- listener.onProgress(progress);
+ if (executor != null && listener != null) {
+ final long progressSnapshot = progress;
+ executor.execute(() -> {
+ listener.onProgress(progressSnapshot);
+ });
}
return progress;
}
@@ -494,6 +559,7 @@
/**
* Check if a filename is "safe" (no metacharacters or spaces).
* @param file The file to check
+ * @hide
*/
public static boolean isFilenameSafe(File file) {
// Note, we check whether it matches what's known to be safe,
@@ -509,6 +575,7 @@
* @param ellipsis to add of the file was truncated (can be null)
* @return the contents of the file, possibly truncated
* @throws IOException if something goes wrong reading the file
+ * @hide
*/
public static String readTextFile(File file, int max, String ellipsis) throws IOException {
InputStream input = new FileInputStream(file);
@@ -563,13 +630,16 @@
}
}
+ /** {@hide} */
public static void stringToFile(File file, String string) throws IOException {
stringToFile(file.getAbsolutePath(), string);
}
- /*
+ /**
* Writes the bytes given in {@code content} to the file whose absolute path
* is {@code filename}.
+ *
+ * @hide
*/
public static void bytesToFile(String filename, byte[] content) throws IOException {
if (filename.startsWith("/proc/")) {
@@ -592,18 +662,23 @@
* @param filename
* @param string
* @throws IOException
+ * @hide
*/
public static void stringToFile(String filename, String string) throws IOException {
bytesToFile(filename, string.getBytes(StandardCharsets.UTF_8));
}
/**
- * Computes the checksum of a file using the CRC32 checksum routine.
- * The value of the checksum is returned.
+ * Computes the checksum of a file using the CRC32 checksum routine. The
+ * value of the checksum is returned.
*
- * @param file the file to checksum, must not be null
+ * @param file the file to checksum, must not be null
* @return the checksum value or an exception is thrown.
+ * @deprecated this is a weak hashing algorithm, and should not be used due
+ * to its potential for collision.
+ * @hide
*/
+ @Deprecated
public static long checksumCrc32(File file) throws FileNotFoundException, IOException {
CRC32 checkSummer = new CRC32();
CheckedInputStream cis = null;
@@ -632,6 +707,7 @@
* @param minCount Always keep at least this many files.
* @param minAgeMs Always keep files younger than this age, in milliseconds.
* @return if any files were deleted.
+ * @hide
*/
public static boolean deleteOlderFiles(File dir, int minCount, long minAgeMs) {
if (minCount < 0 || minAgeMs < 0) {
@@ -673,6 +749,8 @@
* Both files <em>must</em> have been resolved using
* {@link File#getCanonicalFile()} to avoid symlink or path traversal
* attacks.
+ *
+ * @hide
*/
public static boolean contains(File[] dirs, File file) {
for (File dir : dirs) {
@@ -690,12 +768,15 @@
* Both files <em>must</em> have been resolved using
* {@link File#getCanonicalFile()} to avoid symlink or path traversal
* attacks.
+ *
+ * @hide
*/
public static boolean contains(File dir, File file) {
if (dir == null || file == null) return false;
return contains(dir.getAbsolutePath(), file.getAbsolutePath());
}
+ /** {@hide} */
public static boolean contains(String dirPath, String filePath) {
if (dirPath.equals(filePath)) {
return true;
@@ -706,6 +787,7 @@
return filePath.startsWith(dirPath);
}
+ /** {@hide} */
public static boolean deleteContentsAndDir(File dir) {
if (deleteContents(dir)) {
return dir.delete();
@@ -714,6 +796,7 @@
}
}
+ /** {@hide} */
public static boolean deleteContents(File dir) {
File[] files = dir.listFiles();
boolean success = true;
@@ -743,6 +826,8 @@
/**
* Check if given filename is valid for an ext4 filesystem.
+ *
+ * @hide
*/
public static boolean isValidExtFilename(String name) {
return (name != null) && name.equals(buildValidExtFilename(name));
@@ -751,6 +836,8 @@
/**
* Mutate the given filename to make it valid for an ext4 filesystem,
* replacing any invalid characters with "_".
+ *
+ * @hide
*/
public static String buildValidExtFilename(String name) {
if (TextUtils.isEmpty(name) || ".".equals(name) || "..".equals(name)) {
@@ -792,6 +879,8 @@
/**
* Check if given filename is valid for a FAT filesystem.
+ *
+ * @hide
*/
public static boolean isValidFatFilename(String name) {
return (name != null) && name.equals(buildValidFatFilename(name));
@@ -800,6 +889,8 @@
/**
* Mutate the given filename to make it valid for a FAT filesystem,
* replacing any invalid characters with "_".
+ *
+ * @hide
*/
public static String buildValidFatFilename(String name) {
if (TextUtils.isEmpty(name) || ".".equals(name) || "..".equals(name)) {
@@ -820,6 +911,7 @@
return res.toString();
}
+ /** {@hide} */
@VisibleForTesting
public static String trimFilename(String str, int maxBytes) {
final StringBuilder res = new StringBuilder(str);
@@ -827,6 +919,7 @@
return res.toString();
}
+ /** {@hide} */
private static void trimFilename(StringBuilder res, int maxBytes) {
byte[] raw = res.toString().getBytes(StandardCharsets.UTF_8);
if (raw.length > maxBytes) {
@@ -839,12 +932,14 @@
}
}
+ /** {@hide} */
public static String rewriteAfterRename(File beforeDir, File afterDir, String path) {
if (path == null) return null;
final File result = rewriteAfterRename(beforeDir, afterDir, new File(path));
return (result != null) ? result.getAbsolutePath() : null;
}
+ /** {@hide} */
public static String[] rewriteAfterRename(File beforeDir, File afterDir, String[] paths) {
if (paths == null) return null;
final String[] result = new String[paths.length];
@@ -858,6 +953,8 @@
* Given a path under the "before" directory, rewrite it to live under the
* "after" directory. For example, {@code /before/foo/bar.txt} would become
* {@code /after/foo/bar.txt}.
+ *
+ * @hide
*/
public static File rewriteAfterRename(File beforeDir, File afterDir, File file) {
if (file == null || beforeDir == null || afterDir == null) return null;
@@ -869,6 +966,7 @@
return null;
}
+ /** {@hide} */
private static File buildUniqueFileWithExtension(File parent, String name, String ext)
throws FileNotFoundException {
File file = buildFile(parent, name, ext);
@@ -895,6 +993,7 @@
* 'example.txt' or 'example (1).txt', etc.
*
* @throws FileNotFoundException
+ * @hide
*/
public static File buildUniqueFile(File parent, String mimeType, String displayName)
throws FileNotFoundException {
@@ -905,6 +1004,8 @@
/**
* Generates a unique file name under the given parent directory, keeping
* any extension intact.
+ *
+ * @hide
*/
public static File buildUniqueFile(File parent, String displayName)
throws FileNotFoundException {
@@ -929,6 +1030,8 @@
* If the display name doesn't have an extension that matches the requested MIME type, the
* extension is regarded as a part of filename and default extension for that MIME type is
* appended.
+ *
+ * @hide
*/
public static String[] splitFileName(String mimeType, String displayName) {
String name;
@@ -975,6 +1078,7 @@
return new String[] { name, ext };
}
+ /** {@hide} */
private static File buildFile(File parent, String name, String ext) {
if (TextUtils.isEmpty(ext)) {
return new File(parent, name);
@@ -983,6 +1087,7 @@
}
}
+ /** {@hide} */
public static @NonNull String[] listOrEmpty(@Nullable File dir) {
if (dir == null) return EmptyArray.STRING;
final String[] res = dir.list();
@@ -993,6 +1098,7 @@
}
}
+ /** {@hide} */
public static @NonNull File[] listFilesOrEmpty(@Nullable File dir) {
if (dir == null) return EMPTY;
final File[] res = dir.listFiles();
@@ -1003,6 +1109,7 @@
}
}
+ /** {@hide} */
public static @NonNull File[] listFilesOrEmpty(@Nullable File dir, FilenameFilter filter) {
if (dir == null) return EMPTY;
final File[] res = dir.listFiles(filter);
@@ -1013,6 +1120,7 @@
}
}
+ /** {@hide} */
public static @Nullable File newFileOrNull(@Nullable String path) {
return (path != null) ? new File(path) : null;
}
@@ -1021,6 +1129,8 @@
* Creates a directory with name {@code name} under an existing directory {@code baseDir}.
* Returns a {@code File} object representing the directory on success, {@code null} on
* failure.
+ *
+ * @hide
*/
public static @Nullable File createDir(File baseDir, String name) {
final File dir = new File(baseDir, name);
@@ -1036,6 +1146,8 @@
* Round the given size of a storage device to a nice round power-of-two
* value, such as 256MB or 32GB. This avoids showing weird values like
* "29.5GB" in UI.
+ *
+ * @hide
*/
public static long roundStorageSize(long size) {
long val = 1;
@@ -1050,6 +1162,23 @@
return val * pow;
}
+ /**
+ * Closes the given object quietly, ignoring any checked exceptions. Does
+ * nothing if the given object is {@code null}.
+ */
+ public static void closeQuietly(@Nullable AutoCloseable closeable) {
+ IoUtils.closeQuietly(closeable);
+ }
+
+ /**
+ * Closes the given object quietly, ignoring any checked exceptions. Does
+ * nothing if the given object is {@code null}.
+ */
+ public static void closeQuietly(@Nullable FileDescriptor fd) {
+ IoUtils.closeQuietly(fd);
+ }
+
+ /** {@hide} */
@VisibleForTesting
public static class MemoryPipe extends Thread implements AutoCloseable {
private final FileDescriptor[] pipe;
diff --git a/core/java/android/os/IncidentReportArgs.java b/core/java/android/os/IncidentReportArgs.java
index 1aeac5f..3ca7f77 100644
--- a/core/java/android/os/IncidentReportArgs.java
+++ b/core/java/android/os/IncidentReportArgs.java
@@ -144,7 +144,6 @@
/**
* Set this incident report privacy policy spec.
- * @hide
*/
public void setPrivacyPolicy(int dest) {
switch (dest) {
diff --git a/core/java/android/os/MessageQueue.java b/core/java/android/os/MessageQueue.java
index b1c33c2..7c2ecc5 100644
--- a/core/java/android/os/MessageQueue.java
+++ b/core/java/android/os/MessageQueue.java
@@ -18,6 +18,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.TestApi;
import android.os.MessageQueueProto;
import android.util.Log;
import android.util.Printer;
@@ -458,6 +459,7 @@
*
* @hide
*/
+ @TestApi
public int postSyncBarrier() {
return postSyncBarrier(SystemClock.uptimeMillis());
}
@@ -501,6 +503,7 @@
*
* @hide
*/
+ @TestApi
public void removeSyncBarrier(int token) {
// Remove a sync barrier token from the queue.
// If the queue is no longer stalled by a barrier then wake it.
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 3d4ce61..8878260 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -1465,7 +1465,6 @@
* {@link #readParcelableList(List, ClassLoader)} if required.
*
* @see #readParcelableList(List, ClassLoader)
- * @hide
*/
public final <T extends Parcelable> void writeParcelableList(@Nullable List<T> val, int flags) {
if (val == null) {
@@ -2570,7 +2569,6 @@
* list was {@code null}, {@code list} is cleared.
*
* @see #writeParcelableList(List, int)
- * @hide
*/
@NonNull
public final <T extends Parcelable> List<T> readParcelableList(@NonNull List<T> list,
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 9c25848..463a6aa 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -961,7 +961,6 @@
*
* @hide Requires signature permission.
*/
- @TestApi
public void nap(long time) {
try {
mService.nap(time);
diff --git a/core/java/android/os/RedactingFileDescriptor.java b/core/java/android/os/RedactingFileDescriptor.java
new file mode 100644
index 0000000..60eb5c3
--- /dev/null
+++ b/core/java/android/os/RedactingFileDescriptor.java
@@ -0,0 +1,143 @@
+/*
+ * 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.content.Context;
+import android.os.storage.StorageManager;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.OsConstants;
+import android.util.Slog;
+
+import libcore.io.IoUtils;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.InterruptedIOException;
+
+/**
+ * Variant of {@link FileDescriptor} that allows its creator to specify regions
+ * that should be redacted (appearing as zeros to the reader).
+ *
+ * @hide
+ */
+public class RedactingFileDescriptor {
+ private static final String TAG = "RedactingFileDescriptor";
+ private static final boolean DEBUG = true;
+
+ private final long[] mRedactRanges;
+
+ private FileDescriptor mInner = null;
+ private ParcelFileDescriptor mOuter = null;
+
+ private RedactingFileDescriptor(Context context, File file, long[] redactRanges)
+ throws IOException {
+ mRedactRanges = checkRangesArgument(redactRanges);
+
+ try {
+ try {
+ mInner = Os.open(file.getAbsolutePath(), OsConstants.O_RDONLY, 0);
+ mOuter = context.getSystemService(StorageManager.class)
+ .openProxyFileDescriptor(ParcelFileDescriptor.MODE_READ_ONLY, mCallback);
+ } catch (ErrnoException e) {
+ throw e.rethrowAsIOException();
+ }
+ } catch (IOException e) {
+ IoUtils.closeQuietly(mInner);
+ IoUtils.closeQuietly(mOuter);
+ throw e;
+ }
+ }
+
+ private static long[] checkRangesArgument(long[] ranges) {
+ if (ranges.length % 2 != 0) {
+ throw new IllegalArgumentException();
+ }
+ for (int i = 0; i < ranges.length - 1; i += 2) {
+ if (ranges[i] > ranges[i + 1]) {
+ throw new IllegalArgumentException();
+ }
+ }
+ return ranges;
+ }
+
+ /**
+ * Open the given {@link File} and returns a {@link ParcelFileDescriptor}
+ * that offers a redacted, read-only view of the underlying data.
+ *
+ * @param file The underlying file to open.
+ * @param redactRanges List of file offsets that should be redacted, stored
+ * as {@code [start1, end1, start2, end2, ...]}. Start values are
+ * inclusive and end values are exclusive.
+ */
+ public static ParcelFileDescriptor open(Context context, File file, long[] redactRanges)
+ throws IOException {
+ return new RedactingFileDescriptor(context, file, redactRanges).mOuter;
+ }
+
+ private final ProxyFileDescriptorCallback mCallback = new ProxyFileDescriptorCallback() {
+ @Override
+ public long onGetSize() throws ErrnoException {
+ return Os.fstat(mInner).st_size;
+ }
+
+ @Override
+ public int onRead(long offset, int size, byte[] data) throws ErrnoException {
+ int n = 0;
+ while (n < size) {
+ try {
+ final int res = Os.pread(mInner, data, n, size - n, offset + n);
+ if (res == 0) {
+ break;
+ } else {
+ n += res;
+ }
+ } catch (InterruptedIOException e) {
+ n += e.bytesTransferred;
+ }
+ }
+
+ // Redact any relevant ranges before returning
+ final long[] ranges = mRedactRanges;
+ for (int i = 0; i < ranges.length; i += 2) {
+ final long start = Math.max(offset, ranges[i]);
+ final long end = Math.min(offset + size, ranges[i + 1]);
+ for (long j = start; j < end; j++) {
+ data[(int) (j - offset)] = 0;
+ }
+ }
+ return n;
+ }
+
+ @Override
+ public int onWrite(long offset, int size, byte[] data) throws ErrnoException {
+ throw new ErrnoException(TAG, OsConstants.EBADF);
+ }
+
+ @Override
+ public void onFsync() throws ErrnoException {
+ Os.fsync(mInner);
+ }
+
+ @Override
+ public void onRelease() {
+ if (DEBUG) Slog.v(TAG, "onRelease()");
+ IoUtils.closeQuietly(mInner);
+ }
+ };
+}
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index f224550..5ff6e55 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -16,6 +16,7 @@
package android.os;
import android.animation.ValueAnimator;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.TestApi;
@@ -27,15 +28,19 @@
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
import android.net.TrafficStats;
import android.net.Uri;
+import android.os.storage.IStorageManager;
import android.os.strictmode.CleartextNetworkViolation;
import android.os.strictmode.ContentUriWithoutPermissionViolation;
+import android.os.strictmode.CredentialProtectedWhileLockedViolation;
import android.os.strictmode.CustomViolation;
import android.os.strictmode.DiskReadViolation;
import android.os.strictmode.DiskWriteViolation;
import android.os.strictmode.ExplicitGcViolation;
import android.os.strictmode.FileUriExposedViolation;
+import android.os.strictmode.ImplicitDirectBootViolation;
import android.os.strictmode.InstanceCountViolation;
import android.os.strictmode.IntentReceiverLeakedViolation;
import android.os.strictmode.LeakedClosableViolation;
@@ -68,6 +73,8 @@
import java.io.PrintWriter;
import java.io.StringWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayDeque;
@@ -187,116 +194,105 @@
// of the Looper.
private static final int MAX_OFFENSES_PER_LOOP = 10;
- // Byte 1: Thread-policy
+ /** @hide */
+ @IntDef(flag = true, prefix = { "DETECT_THREAD_", "PENALTY_" }, value = {
+ DETECT_THREAD_DISK_WRITE,
+ DETECT_THREAD_DISK_READ,
+ DETECT_THREAD_NETWORK,
+ DETECT_THREAD_CUSTOM,
+ DETECT_THREAD_RESOURCE_MISMATCH,
+ DETECT_THREAD_UNBUFFERED_IO,
+ DETECT_THREAD_EXPLICIT_GC,
+ PENALTY_GATHER,
+ PENALTY_LOG,
+ PENALTY_DIALOG,
+ PENALTY_DEATH,
+ PENALTY_FLASH,
+ PENALTY_DROPBOX,
+ PENALTY_DEATH_ON_NETWORK,
+ PENALTY_DEATH_ON_CLEARTEXT_NETWORK,
+ PENALTY_DEATH_ON_FILE_URI_EXPOSURE,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ThreadPolicyMask {}
+
+ // Thread policy: bits 0-15
/** @hide */
- @TestApi public static final int DETECT_DISK_WRITE = 0x01; // for ThreadPolicy
-
+ private static final int DETECT_THREAD_DISK_WRITE = 1 << 0;
/** @hide */
- @TestApi public static final int DETECT_DISK_READ = 0x02; // for ThreadPolicy
-
+ private static final int DETECT_THREAD_DISK_READ = 1 << 1;
/** @hide */
- @TestApi public static final int DETECT_NETWORK = 0x04; // for ThreadPolicy
-
- /**
- * For StrictMode.noteSlowCall()
- *
- * @hide
- */
- @TestApi public static final int DETECT_CUSTOM = 0x08; // for ThreadPolicy
-
- /**
- * For StrictMode.noteResourceMismatch()
- *
- * @hide
- */
- @TestApi public static final int DETECT_RESOURCE_MISMATCH = 0x10; // for ThreadPolicy
-
+ private static final int DETECT_THREAD_NETWORK = 1 << 2;
/** @hide */
- @TestApi public static final int DETECT_UNBUFFERED_IO = 0x20; // for ThreadPolicy
-
+ private static final int DETECT_THREAD_CUSTOM = 1 << 3;
+ /** @hide */
+ private static final int DETECT_THREAD_RESOURCE_MISMATCH = 1 << 4;
+ /** @hide */
+ private static final int DETECT_THREAD_UNBUFFERED_IO = 1 << 5;
/** @hide */
- public static final int DETECT_EXPLICIT_GC = 0x40; // for ThreadPolicy
-
- private static final int ALL_THREAD_DETECT_BITS =
- DETECT_DISK_WRITE
- | DETECT_DISK_READ
- | DETECT_NETWORK
- | DETECT_CUSTOM
- | DETECT_RESOURCE_MISMATCH
- | DETECT_UNBUFFERED_IO
- | DETECT_EXPLICIT_GC;
-
- // Byte 2: Process-policy
-
- /**
- * Note, a "VM_" bit, not thread.
- *
- * @hide
- */
- @TestApi public static final int DETECT_VM_CURSOR_LEAKS = 0x01 << 8; // for VmPolicy
-
- /**
- * Note, a "VM_" bit, not thread.
- *
- * @hide
- */
- @TestApi public static final int DETECT_VM_CLOSABLE_LEAKS = 0x02 << 8; // for VmPolicy
-
- /**
- * Note, a "VM_" bit, not thread.
- *
- * @hide
- */
- @TestApi public static final int DETECT_VM_ACTIVITY_LEAKS = 0x04 << 8; // for VmPolicy
+ private static final int DETECT_THREAD_EXPLICIT_GC = 1 << 6;
/** @hide */
- @TestApi public static final int DETECT_VM_INSTANCE_LEAKS = 0x08 << 8; // for VmPolicy
+ private static final int DETECT_THREAD_ALL = 0x0000ffff;
/** @hide */
- @TestApi public static final int DETECT_VM_REGISTRATION_LEAKS = 0x10 << 8; // for VmPolicy
+ @IntDef(flag = true, prefix = { "DETECT_THREAD_", "PENALTY_" }, value = {
+ DETECT_VM_CURSOR_LEAKS,
+ DETECT_VM_CLOSABLE_LEAKS,
+ DETECT_VM_ACTIVITY_LEAKS,
+ DETECT_VM_INSTANCE_LEAKS,
+ DETECT_VM_REGISTRATION_LEAKS,
+ DETECT_VM_FILE_URI_EXPOSURE,
+ DETECT_VM_CLEARTEXT_NETWORK,
+ DETECT_VM_CONTENT_URI_WITHOUT_PERMISSION,
+ DETECT_VM_UNTAGGED_SOCKET,
+ DETECT_VM_NON_SDK_API_USAGE,
+ DETECT_VM_IMPLICIT_DIRECT_BOOT,
+ PENALTY_GATHER,
+ PENALTY_LOG,
+ PENALTY_DIALOG,
+ PENALTY_DEATH,
+ PENALTY_FLASH,
+ PENALTY_DROPBOX,
+ PENALTY_DEATH_ON_NETWORK,
+ PENALTY_DEATH_ON_CLEARTEXT_NETWORK,
+ PENALTY_DEATH_ON_FILE_URI_EXPOSURE,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface VmPolicyMask {}
+
+ // VM policy: bits 0-15
/** @hide */
- @TestApi public static final int DETECT_VM_FILE_URI_EXPOSURE = 0x20 << 8; // for VmPolicy
+ private static final int DETECT_VM_CURSOR_LEAKS = 1 << 0;
+ /** @hide */
+ private static final int DETECT_VM_CLOSABLE_LEAKS = 1 << 1;
+ /** @hide */
+ private static final int DETECT_VM_ACTIVITY_LEAKS = 1 << 2;
+ /** @hide */
+ private static final int DETECT_VM_INSTANCE_LEAKS = 1 << 3;
+ /** @hide */
+ private static final int DETECT_VM_REGISTRATION_LEAKS = 1 << 4;
+ /** @hide */
+ private static final int DETECT_VM_FILE_URI_EXPOSURE = 1 << 5;
+ /** @hide */
+ private static final int DETECT_VM_CLEARTEXT_NETWORK = 1 << 6;
+ /** @hide */
+ private static final int DETECT_VM_CONTENT_URI_WITHOUT_PERMISSION = 1 << 7;
+ /** @hide */
+ private static final int DETECT_VM_UNTAGGED_SOCKET = 1 << 8;
+ /** @hide */
+ private static final int DETECT_VM_NON_SDK_API_USAGE = 1 << 9;
+ /** @hide */
+ private static final int DETECT_VM_IMPLICIT_DIRECT_BOOT = 1 << 10;
+ /** @hide */
+ private static final int DETECT_VM_CREDENTIAL_PROTECTED_WHILE_LOCKED = 1 << 11;
/** @hide */
- @TestApi public static final int DETECT_VM_CLEARTEXT_NETWORK = 0x40 << 8; // for VmPolicy
+ private static final int DETECT_VM_ALL = 0x0000ffff;
- /** @hide */
- @TestApi
- public static final int DETECT_VM_CONTENT_URI_WITHOUT_PERMISSION = 0x80 << 8; // for VmPolicy
-
- /** @hide */
- @TestApi public static final int DETECT_VM_UNTAGGED_SOCKET = 0x80 << 24; // for VmPolicy
-
- /** @hide */
- @TestApi public static final int DETECT_VM_NON_SDK_API_USAGE = 0x40 << 24; // for VmPolicy
-
- private static final int ALL_VM_DETECT_BITS =
- DETECT_VM_CURSOR_LEAKS
- | DETECT_VM_CLOSABLE_LEAKS
- | DETECT_VM_ACTIVITY_LEAKS
- | DETECT_VM_INSTANCE_LEAKS
- | DETECT_VM_REGISTRATION_LEAKS
- | DETECT_VM_FILE_URI_EXPOSURE
- | DETECT_VM_CLEARTEXT_NETWORK
- | DETECT_VM_CONTENT_URI_WITHOUT_PERMISSION
- | DETECT_VM_UNTAGGED_SOCKET
- | DETECT_VM_NON_SDK_API_USAGE;
-
-
- // Byte 3: Penalty
-
- /** {@hide} */
- public static final int PENALTY_LOG = 0x01 << 16; // normal android.util.Log
- /** {@hide} */
- public static final int PENALTY_DIALOG = 0x02 << 16;
- /** {@hide} */
- public static final int PENALTY_DEATH = 0x04 << 16;
- /** {@hide} */
- public static final int PENALTY_FLASH = 0x10 << 16;
- /** {@hide} */
- public static final int PENALTY_DROPBOX = 0x20 << 16;
+ // Penalty policy: bits 16-31
/**
* Non-public penalty mode which overrides all the other penalty bits and signals that we're in
@@ -307,50 +303,27 @@
*
* @hide
*/
- public static final int PENALTY_GATHER = 0x40 << 16;
+ public static final int PENALTY_GATHER = 1 << 31;
- // Byte 4: Special cases
+ /** {@hide} */
+ public static final int PENALTY_LOG = 1 << 30;
+ /** {@hide} */
+ public static final int PENALTY_DIALOG = 1 << 29;
+ /** {@hide} */
+ public static final int PENALTY_DEATH = 1 << 28;
+ /** {@hide} */
+ public static final int PENALTY_FLASH = 1 << 27;
+ /** {@hide} */
+ public static final int PENALTY_DROPBOX = 1 << 26;
+ /** {@hide} */
+ public static final int PENALTY_DEATH_ON_NETWORK = 1 << 25;
+ /** {@hide} */
+ public static final int PENALTY_DEATH_ON_CLEARTEXT_NETWORK = 1 << 24;
+ /** {@hide} */
+ public static final int PENALTY_DEATH_ON_FILE_URI_EXPOSURE = 1 << 23;
- /**
- * Death when network traffic is detected on main thread.
- *
- * @hide
- */
- public static final int PENALTY_DEATH_ON_NETWORK = 0x01 << 24;
-
- /**
- * Death when cleartext network traffic is detected.
- *
- * @hide
- */
- public static final int PENALTY_DEATH_ON_CLEARTEXT_NETWORK = 0x02 << 24;
-
- /**
- * Death when file exposure is detected.
- *
- * @hide
- */
- public static final int PENALTY_DEATH_ON_FILE_URI_EXPOSURE = 0x04 << 24;
-
- // CAUTION: we started stealing the top bits of Byte 4 for VM above
-
- /** Mask of all the penalty bits valid for thread policies. */
- private static final int THREAD_PENALTY_MASK =
- PENALTY_LOG
- | PENALTY_DIALOG
- | PENALTY_DEATH
- | PENALTY_DROPBOX
- | PENALTY_GATHER
- | PENALTY_DEATH_ON_NETWORK
- | PENALTY_FLASH;
-
- /** Mask of all the penalty bits valid for VM policies. */
- private static final int VM_PENALTY_MASK =
- PENALTY_LOG
- | PENALTY_DEATH
- | PENALTY_DROPBOX
- | PENALTY_DEATH_ON_CLEARTEXT_NETWORK
- | PENALTY_DEATH_ON_FILE_URI_EXPOSURE;
+ /** @hide */
+ public static final int PENALTY_ALL = 0xffff0000;
/** {@hide} */
public static final int NETWORK_POLICY_ACCEPT = 0;
@@ -448,11 +421,12 @@
/** The default, lax policy which doesn't catch anything. */
public static final ThreadPolicy LAX = new ThreadPolicy(0, null, null);
- final int mask;
+ final @ThreadPolicyMask int mask;
final OnThreadViolationListener mListener;
final Executor mCallbackExecutor;
- private ThreadPolicy(int mask, OnThreadViolationListener listener, Executor executor) {
+ private ThreadPolicy(@ThreadPolicyMask int mask, OnThreadViolationListener listener,
+ Executor executor) {
this.mask = mask;
mListener = listener;
mCallbackExecutor = executor;
@@ -482,7 +456,7 @@
* </pre>
*/
public static final class Builder {
- private int mMask = 0;
+ private @ThreadPolicyMask int mMask = 0;
private OnThreadViolationListener mListener;
private Executor mExecutor;
@@ -528,52 +502,52 @@
/** Disable the detection of everything. */
public Builder permitAll() {
- return disable(ALL_THREAD_DETECT_BITS);
+ return disable(DETECT_THREAD_ALL);
}
/** Enable detection of network operations. */
public Builder detectNetwork() {
- return enable(DETECT_NETWORK);
+ return enable(DETECT_THREAD_NETWORK);
}
/** Disable detection of network operations. */
public Builder permitNetwork() {
- return disable(DETECT_NETWORK);
+ return disable(DETECT_THREAD_NETWORK);
}
/** Enable detection of disk reads. */
public Builder detectDiskReads() {
- return enable(DETECT_DISK_READ);
+ return enable(DETECT_THREAD_DISK_READ);
}
/** Disable detection of disk reads. */
public Builder permitDiskReads() {
- return disable(DETECT_DISK_READ);
+ return disable(DETECT_THREAD_DISK_READ);
}
/** Enable detection of slow calls. */
public Builder detectCustomSlowCalls() {
- return enable(DETECT_CUSTOM);
+ return enable(DETECT_THREAD_CUSTOM);
}
/** Disable detection of slow calls. */
public Builder permitCustomSlowCalls() {
- return disable(DETECT_CUSTOM);
+ return disable(DETECT_THREAD_CUSTOM);
}
/** Disable detection of mismatches between defined resource types and getter calls. */
public Builder permitResourceMismatches() {
- return disable(DETECT_RESOURCE_MISMATCH);
+ return disable(DETECT_THREAD_RESOURCE_MISMATCH);
}
/** Detect unbuffered input/output operations. */
public Builder detectUnbufferedIo() {
- return enable(DETECT_UNBUFFERED_IO);
+ return enable(DETECT_THREAD_UNBUFFERED_IO);
}
/** Disable detection of unbuffered input/output operations. */
public Builder permitUnbufferedIo() {
- return disable(DETECT_UNBUFFERED_IO);
+ return disable(DETECT_THREAD_UNBUFFERED_IO);
}
/**
@@ -589,17 +563,17 @@
* resource as an integer to avoid unnecessary type conversion.
*/
public Builder detectResourceMismatches() {
- return enable(DETECT_RESOURCE_MISMATCH);
+ return enable(DETECT_THREAD_RESOURCE_MISMATCH);
}
/** Enable detection of disk writes. */
public Builder detectDiskWrites() {
- return enable(DETECT_DISK_WRITE);
+ return enable(DETECT_THREAD_DISK_WRITE);
}
/** Disable detection of disk writes. */
public Builder permitDiskWrites() {
- return disable(DETECT_DISK_WRITE);
+ return disable(DETECT_THREAD_DISK_WRITE);
}
/**
@@ -607,12 +581,13 @@
*
* @hide
*/
+ @TestApi
public Builder detectExplicitGc() {
// TODO(b/3400644): Un-hide this for next API update
// TODO(b/3400644): Un-hide ExplicitGcViolation for next API update
// TODO(b/3400644): Make DETECT_EXPLICIT_GC a @TestApi for next API update
// TODO(b/3400644): Call this from detectAll in next API update
- return enable(DETECT_EXPLICIT_GC);
+ return enable(DETECT_THREAD_EXPLICIT_GC);
}
/**
@@ -622,7 +597,7 @@
*/
public Builder permitExplicitGc() {
// TODO(b/3400644): Un-hide this for next API update
- return disable(DETECT_EXPLICIT_GC);
+ return disable(DETECT_THREAD_EXPLICIT_GC);
}
/**
@@ -695,13 +670,13 @@
return penaltyListener(executor, listener);
}
- private Builder enable(int bit) {
- mMask |= bit;
+ private Builder enable(@ThreadPolicyMask int mask) {
+ mMask |= mask;
return this;
}
- private Builder disable(int bit) {
- mMask &= ~bit;
+ private Builder disable(@ThreadPolicyMask int mask) {
+ mMask &= ~mask;
return this;
}
@@ -738,7 +713,7 @@
/** The default, lax policy which doesn't catch anything. */
public static final VmPolicy LAX = new VmPolicy(0, EMPTY_CLASS_LIMIT_MAP, null, null);
- final int mask;
+ final @VmPolicyMask int mask;
final OnVmViolationListener mListener;
final Executor mCallbackExecutor;
@@ -746,7 +721,7 @@
final HashMap<Class, Integer> classInstanceLimit;
private VmPolicy(
- int mask,
+ @VmPolicyMask int mask,
HashMap<Class, Integer> classInstanceLimit,
OnVmViolationListener listener,
Executor executor) {
@@ -783,7 +758,7 @@
* </pre>
*/
public static final class Builder {
- private int mMask;
+ private @VmPolicyMask int mMask;
private OnVmViolationListener mListener;
private Executor mExecutor;
@@ -889,8 +864,13 @@
detectContentUriWithoutPermission();
detectUntaggedSockets();
}
+ if (targetSdk >= Build.VERSION_CODES.Q) {
+ detectCredentialProtectedWhileLocked();
+ }
// TODO: Decide whether to detect non SDK API usage beyond a certain API level.
+ // TODO: enable detectImplicitDirectBoot() once system is less noisy
+
return this;
}
@@ -999,6 +979,51 @@
}
/**
+ * Detect any implicit reliance on Direct Boot automatic filtering
+ * of {@link PackageManager} values. Violations are only triggered
+ * when implicit calls are made while the user is locked.
+ * <p>
+ * Apps becoming Direct Boot aware need to carefully inspect each
+ * query site and explicitly decide which combination of flags they
+ * want to use:
+ * <ul>
+ * <li>{@link PackageManager#MATCH_DIRECT_BOOT_AWARE}
+ * <li>{@link PackageManager#MATCH_DIRECT_BOOT_UNAWARE}
+ * <li>{@link PackageManager#MATCH_DIRECT_BOOT_AUTO}
+ * </ul>
+ */
+ public Builder detectImplicitDirectBoot() {
+ return enable(DETECT_VM_IMPLICIT_DIRECT_BOOT);
+ }
+
+ /** @hide */
+ public Builder permitImplicitDirectBoot() {
+ return disable(DETECT_VM_IMPLICIT_DIRECT_BOOT);
+ }
+
+ /**
+ * Detect access to filesystem paths stored in credential protected
+ * storage areas while the user is locked.
+ * <p>
+ * When a user is locked, credential protected storage is
+ * unavailable, and files stored in these locations appear to not
+ * exist, which can result in subtle app bugs if they assume default
+ * behaviors or empty states. Instead, apps should store data needed
+ * while a user is locked under device protected storage areas.
+ *
+ * @see Context#createCredentialProtectedStorageContext()
+ * @see Context#createDeviceProtectedStorageContext()
+ */
+ public Builder detectCredentialProtectedWhileLocked() {
+ return enable(DETECT_VM_CREDENTIAL_PROTECTED_WHILE_LOCKED);
+ }
+
+ /** @hide */
+ public Builder permitCredentialProtectedWhileLocked() {
+ return disable(DETECT_VM_CREDENTIAL_PROTECTED_WHILE_LOCKED);
+ }
+
+ /**
* Crashes the whole process on violation. This penalty runs at the end of all enabled
* penalties so you'll still get your logging or other violations before the process
* dies.
@@ -1059,13 +1084,13 @@
return penaltyListener(executor, listener);
}
- private Builder enable(int bit) {
- mMask |= bit;
+ private Builder enable(@VmPolicyMask int mask) {
+ mMask |= mask;
return this;
}
- Builder disable(int bit) {
- mMask &= ~bit;
+ Builder disable(@VmPolicyMask int mask) {
+ mMask &= ~mask;
return this;
}
@@ -1130,21 +1155,21 @@
}
/** @hide */
- public static void setThreadPolicyMask(final int policyMask) {
+ public static void setThreadPolicyMask(@ThreadPolicyMask int threadPolicyMask) {
// In addition to the Java-level thread-local in Dalvik's
// BlockGuard, we also need to keep a native thread-local in
// Binder in order to propagate the value across Binder calls,
// even across native-only processes. The two are kept in
// sync via the callback to onStrictModePolicyChange, below.
- setBlockGuardPolicy(policyMask);
+ setBlockGuardPolicy(threadPolicyMask);
// And set the Android native version...
- Binder.setThreadStrictModePolicy(policyMask);
+ Binder.setThreadStrictModePolicy(threadPolicyMask);
}
// Sets the policy in Dalvik/libcore (BlockGuard)
- private static void setBlockGuardPolicy(final int policyMask) {
- if (policyMask == 0) {
+ private static void setBlockGuardPolicy(@ThreadPolicyMask int threadPolicyMask) {
+ if (threadPolicyMask == 0) {
BlockGuard.setThreadPolicy(BlockGuard.LAX_POLICY);
return;
}
@@ -1156,7 +1181,17 @@
androidPolicy = THREAD_ANDROID_POLICY.get();
BlockGuard.setThreadPolicy(androidPolicy);
}
- androidPolicy.setPolicyMask(policyMask);
+ androidPolicy.setThreadPolicyMask(threadPolicyMask);
+ }
+
+ private static void setBlockGuardVmPolicy(@VmPolicyMask int vmPolicyMask) {
+ // We only need to install BlockGuard for a small subset of VM policies
+ vmPolicyMask &= DETECT_VM_CREDENTIAL_PROTECTED_WHILE_LOCKED;
+ if (vmPolicyMask != 0) {
+ BlockGuard.setVmPolicy(VM_ANDROID_POLICY);
+ } else {
+ BlockGuard.setVmPolicy(BlockGuard.LAX_VM_POLICY);
+ }
}
// Sets up CloseGuard in Dalvik/libcore
@@ -1173,8 +1208,13 @@
* @return the bitmask of all the DETECT_* and PENALTY_* bits currently enabled
* @hide
*/
- public static int getThreadPolicyMask() {
- return BlockGuard.getThreadPolicy().getPolicyMask();
+ public static @ThreadPolicyMask int getThreadPolicyMask() {
+ final BlockGuard.Policy policy = BlockGuard.getThreadPolicy();
+ if (policy instanceof AndroidBlockGuardPolicy) {
+ return ((AndroidBlockGuardPolicy) policy).getThreadPolicyMask();
+ } else {
+ return 0;
+ }
}
/** Returns the current thread's policy. */
@@ -1206,9 +1246,9 @@
}
/** @hide */
- public static int allowThreadDiskWritesMask() {
+ public static @ThreadPolicyMask int allowThreadDiskWritesMask() {
int oldPolicyMask = getThreadPolicyMask();
- int newPolicyMask = oldPolicyMask & ~(DETECT_DISK_WRITE | DETECT_DISK_READ);
+ int newPolicyMask = oldPolicyMask & ~(DETECT_THREAD_DISK_WRITE | DETECT_THREAD_DISK_READ);
if (newPolicyMask != oldPolicyMask) {
setThreadPolicyMask(newPolicyMask);
}
@@ -1230,9 +1270,9 @@
}
/** @hide */
- public static int allowThreadDiskReadsMask() {
+ public static @ThreadPolicyMask int allowThreadDiskReadsMask() {
int oldPolicyMask = getThreadPolicyMask();
- int newPolicyMask = oldPolicyMask & ~(DETECT_DISK_READ);
+ int newPolicyMask = oldPolicyMask & ~(DETECT_THREAD_DISK_READ);
if (newPolicyMask != oldPolicyMask) {
setThreadPolicyMask(newPolicyMask);
}
@@ -1400,32 +1440,6 @@
sVmPolicy.mCallbackExecutor);
}
- /**
- * Parses the BlockGuard policy mask out from the Exception's getMessage() String value. Kinda
- * gross, but least invasive. :/
- *
- * <p>Input is of the following forms: "policy=137 violation=64" "policy=137 violation=64
- * msg=Arbitrary text"
- *
- * <p>Returns 0 on failure, which is a valid policy, but not a valid policy during a violation
- * (else there must've been some policy in effect to violate).
- */
- private static int parsePolicyFromMessage(String message) {
- if (message == null || !message.startsWith("policy=")) {
- return 0;
- }
- int spaceIndex = message.indexOf(' ');
- if (spaceIndex == -1) {
- return 0;
- }
- String policyString = message.substring(7, spaceIndex);
- try {
- return Integer.parseInt(policyString);
- } catch (NumberFormatException e) {
- return 0;
- }
- }
-
private static final ThreadLocal<ArrayList<ViolationInfo>> violationsBeingTimed =
new ThreadLocal<ArrayList<ViolationInfo>>() {
@Override
@@ -1456,30 +1470,30 @@
}
private static class AndroidBlockGuardPolicy implements BlockGuard.Policy {
- private int mPolicyMask;
+ private @ThreadPolicyMask int mThreadPolicyMask;
// Map from violation stacktrace hashcode -> uptimeMillis of
// last violation. No locking needed, as this is only
// accessed by the same thread.
private ArrayMap<Integer, Long> mLastViolationTime;
- public AndroidBlockGuardPolicy(final int policyMask) {
- mPolicyMask = policyMask;
+ public AndroidBlockGuardPolicy(@ThreadPolicyMask int threadPolicyMask) {
+ mThreadPolicyMask = threadPolicyMask;
}
@Override
public String toString() {
- return "AndroidBlockGuardPolicy; mPolicyMask=" + mPolicyMask;
+ return "AndroidBlockGuardPolicy; mPolicyMask=" + mThreadPolicyMask;
}
// Part of BlockGuard.Policy interface:
public int getPolicyMask() {
- return mPolicyMask;
+ return mThreadPolicyMask;
}
// Part of BlockGuard.Policy interface:
public void onWriteToDisk() {
- if ((mPolicyMask & DETECT_DISK_WRITE) == 0) {
+ if ((mThreadPolicyMask & DETECT_THREAD_DISK_WRITE) == 0) {
return;
}
if (tooManyViolationsThisLoop()) {
@@ -1490,7 +1504,7 @@
// Not part of BlockGuard.Policy; just part of StrictMode:
void onCustomSlowCall(String name) {
- if ((mPolicyMask & DETECT_CUSTOM) == 0) {
+ if ((mThreadPolicyMask & DETECT_THREAD_CUSTOM) == 0) {
return;
}
if (tooManyViolationsThisLoop()) {
@@ -1501,7 +1515,7 @@
// Not part of BlockGuard.Policy; just part of StrictMode:
void onResourceMismatch(Object tag) {
- if ((mPolicyMask & DETECT_RESOURCE_MISMATCH) == 0) {
+ if ((mThreadPolicyMask & DETECT_THREAD_RESOURCE_MISMATCH) == 0) {
return;
}
if (tooManyViolationsThisLoop()) {
@@ -1512,7 +1526,7 @@
// Not part of BlockGuard.Policy; just part of StrictMode:
public void onUnbufferedIO() {
- if ((mPolicyMask & DETECT_UNBUFFERED_IO) == 0) {
+ if ((mThreadPolicyMask & DETECT_THREAD_UNBUFFERED_IO) == 0) {
return;
}
if (tooManyViolationsThisLoop()) {
@@ -1523,7 +1537,7 @@
// Part of BlockGuard.Policy interface:
public void onReadFromDisk() {
- if ((mPolicyMask & DETECT_DISK_READ) == 0) {
+ if ((mThreadPolicyMask & DETECT_THREAD_DISK_READ) == 0) {
return;
}
if (tooManyViolationsThisLoop()) {
@@ -1534,10 +1548,10 @@
// Part of BlockGuard.Policy interface:
public void onNetwork() {
- if ((mPolicyMask & DETECT_NETWORK) == 0) {
+ if ((mThreadPolicyMask & DETECT_THREAD_NETWORK) == 0) {
return;
}
- if ((mPolicyMask & PENALTY_DEATH_ON_NETWORK) != 0) {
+ if ((mThreadPolicyMask & PENALTY_DEATH_ON_NETWORK) != 0) {
throw new NetworkOnMainThreadException();
}
if (tooManyViolationsThisLoop()) {
@@ -1548,7 +1562,7 @@
// Part of BlockGuard.Policy interface:
public void onExplicitGc() {
- if ((mPolicyMask & DETECT_EXPLICIT_GC) == 0) {
+ if ((mThreadPolicyMask & DETECT_THREAD_EXPLICIT_GC) == 0) {
return;
}
if (tooManyViolationsThisLoop()) {
@@ -1557,8 +1571,12 @@
startHandlingViolationException(new ExplicitGcViolation());
}
- public void setPolicyMask(int policyMask) {
- mPolicyMask = policyMask;
+ public @ThreadPolicyMask int getThreadPolicyMask() {
+ return mThreadPolicyMask;
+ }
+
+ public void setThreadPolicyMask(@ThreadPolicyMask int threadPolicyMask) {
+ mThreadPolicyMask = threadPolicyMask;
}
// Start handling a violation that just started and hasn't
@@ -1567,7 +1585,8 @@
// thread and, if so, uses it to roughly measure how long the
// violation took.
void startHandlingViolationException(Violation e) {
- final ViolationInfo info = new ViolationInfo(e, mPolicyMask);
+ final int penaltyMask = (mThreadPolicyMask & PENALTY_ALL);
+ final ViolationInfo info = new ViolationInfo(e, penaltyMask);
info.violationUptimeMillis = SystemClock.uptimeMillis();
handleViolationWithTimingAttempt(info);
}
@@ -1596,7 +1615,7 @@
//
// TODO: if in gather mode, ignore Looper.myLooper() and always
// go into this immediate mode?
- if (looper == null || (info.mPolicy & THREAD_PENALTY_MASK) == PENALTY_DEATH) {
+ if (looper == null || (info.mPenaltyMask == PENALTY_DEATH)) {
info.durationMillis = -1; // unknown (redundant, already set)
onThreadPolicyViolation(info);
return;
@@ -1667,7 +1686,7 @@
// to people who push/pop temporary policy in regions of code,
// hence the policy being passed around.
void onThreadPolicyViolation(final ViolationInfo info) {
- if (LOG_V) Log.d(TAG, "onThreadPolicyViolation; policy=" + info.mPolicy);
+ if (LOG_V) Log.d(TAG, "onThreadPolicyViolation; penalty=" + info.mPenaltyMask);
if (info.penaltyEnabled(PENALTY_GATHER)) {
ArrayList<ViolationInfo> violations = gatheredViolations.get();
@@ -1708,25 +1727,20 @@
final Violation violation = info.mViolation;
- // The violationMaskSubset, passed to ActivityManager, is a
- // subset of the original StrictMode policy bitmask, with
- // only the bit violated and penalty bits to be executed
- // by the ActivityManagerService remaining set.
- int violationMaskSubset = 0;
+ // Penalties that ActivityManager should execute on our behalf.
+ int penaltyMask = 0;
if (info.penaltyEnabled(PENALTY_DIALOG)
&& timeSinceLastViolationMillis > MIN_DIALOG_INTERVAL_MS) {
- violationMaskSubset |= PENALTY_DIALOG;
+ penaltyMask |= PENALTY_DIALOG;
}
if (info.penaltyEnabled(PENALTY_DROPBOX) && lastViolationTime == 0) {
- violationMaskSubset |= PENALTY_DROPBOX;
+ penaltyMask |= PENALTY_DROPBOX;
}
- if (violationMaskSubset != 0) {
- violationMaskSubset |= info.getViolationBit();
-
- final boolean justDropBox = (info.mPolicy & THREAD_PENALTY_MASK) == PENALTY_DROPBOX;
+ if (penaltyMask != 0) {
+ final boolean justDropBox = (info.mPenaltyMask == PENALTY_DROPBOX);
if (justDropBox) {
// If all we're going to ask the activity manager
// to do is dropbox it (the common case during
@@ -1734,13 +1748,13 @@
// call synchronously which Binder data suggests
// isn't always super fast, despite the implementation
// in the ActivityManager trying to be mostly async.
- dropboxViolationAsync(violationMaskSubset, info);
+ dropboxViolationAsync(penaltyMask, info);
} else {
- handleApplicationStrictModeViolation(violationMaskSubset, info);
+ handleApplicationStrictModeViolation(penaltyMask, info);
}
}
- if ((info.getPolicyMask() & PENALTY_DEATH) != 0) {
+ if (info.penaltyEnabled(PENALTY_DEATH)) {
throw new RuntimeException("StrictMode ThreadPolicy violation", violation);
}
@@ -1753,11 +1767,11 @@
executor.execute(
() -> {
// Lift violated policy to prevent infinite recursion.
- ThreadPolicy oldPolicy = allowThreadViolations();
+ ThreadPolicy oldPolicy = StrictMode.allowThreadViolations();
try {
listener.onThreadViolation(violation);
} finally {
- setThreadPolicy(oldPolicy);
+ StrictMode.setThreadPolicy(oldPolicy);
}
});
} catch (RejectedExecutionException e) {
@@ -1767,6 +1781,34 @@
}
}
+ private static final BlockGuard.VmPolicy VM_ANDROID_POLICY = new BlockGuard.VmPolicy() {
+ @Override
+ public void onPathAccess(String path) {
+ if (path == null) return;
+
+ // NOTE: keep credential-protected paths in sync with Environment.java
+ if (path.startsWith("/data/user/")
+ || path.startsWith("/data/media/")
+ || path.startsWith("/data/system_ce/")
+ || path.startsWith("/data/misc_ce/")
+ || path.startsWith("/data/vendor_ce/")
+ || path.startsWith("/storage/emulated/")) {
+ final int second = path.indexOf('/', 1);
+ final int third = path.indexOf('/', second + 1);
+ final int fourth = path.indexOf('/', third + 1);
+ if (fourth == -1) return;
+
+ try {
+ final int userId = Integer.parseInt(path.substring(third + 1, fourth));
+ onCredentialProtectedPathAccess(path, userId);
+ } catch (NumberFormatException ignored) {
+ }
+ } else if (path.startsWith("/data/data/")) {
+ onCredentialProtectedPathAccess(path, UserHandle.USER_SYSTEM);
+ }
+ }
+ };
+
/**
* In the common case, as set by conditionallyEnableDebugLogging, we're just dropboxing any
* violations but not showing a dialog, not loggging, and not killing the process. In these
@@ -1774,7 +1816,7 @@
* per-thread and vm-wide violations when applicable.
*/
private static void dropboxViolationAsync(
- final int violationMaskSubset, final ViolationInfo info) {
+ final int penaltyMask, final ViolationInfo info) {
int outstanding = sDropboxCallsInFlight.incrementAndGet();
if (outstanding > 20) {
// What's going on? Let's not make make the situation
@@ -1786,13 +1828,13 @@
if (LOG_V) Log.d(TAG, "Dropboxing async; in-flight=" + outstanding);
BackgroundThread.getHandler().post(() -> {
- handleApplicationStrictModeViolation(violationMaskSubset, info);
+ handleApplicationStrictModeViolation(penaltyMask, info);
int outstandingInner = sDropboxCallsInFlight.decrementAndGet();
if (LOG_V) Log.d(TAG, "Dropbox complete; in-flight=" + outstandingInner);
});
}
- private static void handleApplicationStrictModeViolation(int violationMaskSubset,
+ private static void handleApplicationStrictModeViolation(int penaltyMask,
ViolationInfo info) {
final int oldMask = getThreadPolicyMask();
try {
@@ -1807,7 +1849,7 @@
Log.w(TAG, "No activity manager; failed to Dropbox violation.");
} else {
am.handleApplicationStrictModeViolation(
- RuntimeInit.getApplicationObject(), violationMaskSubset, info);
+ RuntimeInit.getApplicationObject(), penaltyMask, info);
}
} catch (RemoteException e) {
if (e instanceof DeadObjectException) {
@@ -1840,6 +1882,7 @@
}
/** @hide */
+ @TestApi
public static void conditionallyCheckInstanceCounts() {
VmPolicy policy = getVmPolicy();
int policySize = policy.classInstanceLimit.size();
@@ -1894,7 +1937,7 @@
if (looper != null) {
MessageQueue mq = looper.mQueue;
if (policy.classInstanceLimit.size() == 0
- || (sVmPolicy.mask & VM_PENALTY_MASK) == 0) {
+ || (sVmPolicy.mask & PENALTY_ALL) == 0) {
mq.removeIdleHandler(sProcessIdleHandler);
sIsIdlerRegistered = false;
} else if (!sIsIdlerRegistered) {
@@ -1933,6 +1976,8 @@
VMRuntime.setNonSdkApiUsageConsumer(null);
VMRuntime.setDedupeHiddenApiWarnings(true);
}
+
+ setBlockGuardVmPolicy(sVmPolicy.mask);
}
}
@@ -1991,6 +2036,16 @@
}
/** @hide */
+ public static boolean vmImplicitDirectBootEnabled() {
+ return (sVmPolicy.mask & DETECT_VM_IMPLICIT_DIRECT_BOOT) != 0;
+ }
+
+ /** @hide */
+ public static boolean vmCredentialProtectedWhileLockedEnabled() {
+ return (sVmPolicy.mask & DETECT_VM_CREDENTIAL_PROTECTED_WHILE_LOCKED) != 0;
+ }
+
+ /** @hide */
public static void onSqliteObjectLeaked(String message, Throwable originStack) {
onVmPolicyViolation(new SqliteObjectLeakedViolation(message, originStack));
}
@@ -2026,10 +2081,6 @@
}
/** @hide */
- public static final String CLEARTEXT_DETECTED_MSG =
- "Detected cleartext network traffic from UID ";
-
- /** @hide */
public static void onCleartextNetworkDetected(byte[] firstPacket) {
byte[] rawAddr = null;
if (firstPacket != null) {
@@ -2045,7 +2096,7 @@
}
final int uid = android.os.Process.myUid();
- String msg = CLEARTEXT_DETECTED_MSG + uid;
+ String msg = "Detected cleartext network traffic from UID " + uid;
if (rawAddr != null) {
try {
msg += " to " + InetAddress.getByAddress(rawAddr);
@@ -2062,6 +2113,47 @@
onVmPolicyViolation(new UntaggedSocketViolation());
}
+ /** @hide */
+ public static void onImplicitDirectBoot() {
+ onVmPolicyViolation(new ImplicitDirectBootViolation());
+ }
+
+ /** Assume locked until we hear otherwise */
+ private static volatile boolean sUserKeyUnlocked = false;
+
+ private static boolean isUserKeyUnlocked(int userId) {
+ final IStorageManager storage = IStorageManager.Stub
+ .asInterface(ServiceManager.getService("mount"));
+ if (storage != null) {
+ try {
+ return storage.isUserKeyUnlocked(userId);
+ } catch (RemoteException ignored) {
+ }
+ }
+ return false;
+ }
+
+ /** @hide */
+ private static void onCredentialProtectedPathAccess(String path, int userId) {
+ // We can cache the unlocked state for the userId we're running as,
+ // since any relocking of that user will always result in our
+ // process being killed to release any CE FDs we're holding onto.
+ if (userId == UserHandle.myUserId()) {
+ if (sUserKeyUnlocked) {
+ return;
+ } else if (isUserKeyUnlocked(userId)) {
+ sUserKeyUnlocked = true;
+ return;
+ }
+ } else if (isUserKeyUnlocked(userId)) {
+ return;
+ }
+
+ onVmPolicyViolation(new CredentialProtectedWhileLockedViolation(
+ "Accessed credential protected path " + path + " while user " + userId
+ + " was locked"));
+ }
+
// Map from VM violation fingerprint to uptime millis.
private static final HashMap<Integer, Long> sLastVmViolationTime = new HashMap<>();
@@ -2075,7 +2167,9 @@
final boolean penaltyDropbox = (sVmPolicy.mask & PENALTY_DROPBOX) != 0;
final boolean penaltyDeath = ((sVmPolicy.mask & PENALTY_DEATH) != 0) || forceDeath;
final boolean penaltyLog = (sVmPolicy.mask & PENALTY_LOG) != 0;
- final ViolationInfo info = new ViolationInfo(violation, sVmPolicy.mask);
+
+ final int penaltyMask = (sVmPolicy.mask & PENALTY_ALL);
+ final ViolationInfo info = new ViolationInfo(violation, penaltyMask);
// Erase stuff not relevant for process-wide violations
info.numAnimationsRunning = 0;
@@ -2104,16 +2198,14 @@
sLogger.log(info);
}
- int violationMaskSubset = PENALTY_DROPBOX | (ALL_VM_DETECT_BITS & sVmPolicy.mask);
-
if (penaltyDropbox) {
if (penaltyDeath) {
- handleApplicationStrictModeViolation(violationMaskSubset, info);
+ handleApplicationStrictModeViolation(PENALTY_DROPBOX, info);
} else {
// Common case for userdebug/eng builds. If no death and
// just dropboxing, we can do the ActivityManager call
// asynchronously.
- dropboxViolationAsync(violationMaskSubset, info);
+ dropboxViolationAsync(PENALTY_DROPBOX, info);
}
}
@@ -2188,7 +2280,7 @@
* Binder for its current (native) thread-local policy value and synchronize it to libcore's
* (Java) thread-local policy value.
*/
- private static void onBinderStrictModePolicyChange(int newPolicy) {
+ private static void onBinderStrictModePolicyChange(@ThreadPolicyMask int newPolicy) {
setBlockGuardPolicy(newPolicy);
}
@@ -2506,8 +2598,8 @@
/** Memoized stack trace of full violation. */
@Nullable private String mStackTrace;
- /** The strict mode policy mask at the time of violation. */
- private final int mPolicy;
+ /** The strict mode penalty mask at the time of violation. */
+ private final int mPenaltyMask;
/** The wall time duration of the violation, when known. -1 when not known. */
public int durationMillis = -1;
@@ -2538,9 +2630,9 @@
public long numInstances = -1;
/** Create an instance of ViolationInfo initialized from an exception. */
- ViolationInfo(Violation tr, int policy) {
+ ViolationInfo(Violation tr, int penaltyMask) {
this.mViolation = tr;
- this.mPolicy = policy;
+ this.mPenaltyMask = penaltyMask;
violationUptimeMillis = SystemClock.uptimeMillis();
this.numAnimationsRunning = ValueAnimator.getCurrentAnimationsCount();
Intent broadcastIntent = ActivityThread.getIntentBeingBroadcast();
@@ -2569,7 +2661,10 @@
}
}
- /** Equivalent output to {@link ApplicationErrorReport.CrashInfo#stackTrace}. */
+ /**
+ * Equivalent output to
+ * {@link android.app.ApplicationErrorReport.CrashInfo#stackTrace}.
+ */
public String getStackTrace() {
if (mStackTrace == null) {
StringWriter sw = new StringWriter();
@@ -2590,6 +2685,10 @@
return mStackTrace;
}
+ public Class<? extends Violation> getViolationClass() {
+ return mViolation.getClass();
+ }
+
/**
* Optional message describing this violation.
*
@@ -2600,18 +2699,8 @@
return mViolation.getMessage();
}
- /**
- * Policy mask at time of violation.
- *
- * @hide
- */
- @TestApi
- public int getPolicyMask() {
- return mPolicy;
- }
-
boolean penaltyEnabled(int p) {
- return (mPolicy & p) != 0;
+ return (mPenaltyMask & p) != 0;
}
/**
@@ -2624,51 +2713,6 @@
mBinderStack.addFirst(t.getStackTrace());
}
- /**
- * Retrieve the type of StrictMode violation.
- *
- * @hide
- */
- @TestApi
- public int getViolationBit() {
- if (mViolation instanceof DiskWriteViolation) {
- return DETECT_DISK_WRITE;
- } else if (mViolation instanceof DiskReadViolation) {
- return DETECT_DISK_READ;
- } else if (mViolation instanceof NetworkViolation) {
- return DETECT_NETWORK;
- } else if (mViolation instanceof CustomViolation) {
- return DETECT_CUSTOM;
- } else if (mViolation instanceof ResourceMismatchViolation) {
- return DETECT_RESOURCE_MISMATCH;
- } else if (mViolation instanceof UnbufferedIoViolation) {
- return DETECT_UNBUFFERED_IO;
- } else if (mViolation instanceof SqliteObjectLeakedViolation) {
- return DETECT_VM_CURSOR_LEAKS;
- } else if (mViolation instanceof LeakedClosableViolation) {
- return DETECT_VM_CLOSABLE_LEAKS;
- } else if (mViolation instanceof InstanceCountViolation) {
- return DETECT_VM_INSTANCE_LEAKS;
- } else if (mViolation instanceof IntentReceiverLeakedViolation) {
- return DETECT_VM_REGISTRATION_LEAKS;
- } else if (mViolation instanceof ServiceConnectionLeakedViolation) {
- return DETECT_VM_REGISTRATION_LEAKS;
- } else if (mViolation instanceof FileUriExposedViolation) {
- return DETECT_VM_FILE_URI_EXPOSURE;
- } else if (mViolation instanceof CleartextNetworkViolation) {
- return DETECT_VM_CLEARTEXT_NETWORK;
- } else if (mViolation instanceof ContentUriWithoutPermissionViolation) {
- return DETECT_VM_CONTENT_URI_WITHOUT_PERMISSION;
- } else if (mViolation instanceof UntaggedSocketViolation) {
- return DETECT_VM_UNTAGGED_SOCKET;
- } else if (mViolation instanceof ExplicitGcViolation) {
- return DETECT_EXPLICIT_GC;
- } else if (mViolation instanceof NonSdkApiUsedViolation) {
- return DETECT_VM_NON_SDK_API_USAGE;
- }
- throw new IllegalStateException("missing violation bit");
- }
-
@Override
public int hashCode() {
int result = 17;
@@ -2716,11 +2760,11 @@
}
mBinderStack.add(traceElements);
}
- int rawPolicy = in.readInt();
+ int rawPenaltyMask = in.readInt();
if (unsetGatheringBit) {
- mPolicy = rawPolicy & ~PENALTY_GATHER;
+ mPenaltyMask = rawPenaltyMask & ~PENALTY_GATHER;
} else {
- mPolicy = rawPolicy;
+ mPenaltyMask = rawPenaltyMask;
}
durationMillis = in.readInt();
violationNumThisLoop = in.readInt();
@@ -2746,7 +2790,7 @@
}
}
int start = dest.dataPosition();
- dest.writeInt(mPolicy);
+ dest.writeInt(mPenaltyMask);
dest.writeInt(durationMillis);
dest.writeInt(violationNumThisLoop);
dest.writeInt(numAnimationsRunning);
@@ -2758,8 +2802,8 @@
if (Binder.CHECK_PARCEL_SIZE && total > 10 * 1024) {
Slog.d(
TAG,
- "VIO: policy="
- + mPolicy
+ "VIO: penalty="
+ + mPenaltyMask
+ " dur="
+ durationMillis
+ " numLoop="
@@ -2779,7 +2823,7 @@
/** Dump a ViolationInfo instance to a Printer. */
public void dump(Printer pw, String prefix) {
pw.println(prefix + "stackTrace: " + getStackTrace());
- pw.println(prefix + "policy: " + mPolicy);
+ pw.println(prefix + "penalty: " + mPenaltyMask);
if (durationMillis != -1) {
pw.println(prefix + "durationMillis: " + durationMillis);
}
diff --git a/core/java/android/os/SystemProperties.java b/core/java/android/os/SystemProperties.java
index 7d3ba6a..fb34a52 100644
--- a/core/java/android/os/SystemProperties.java
+++ b/core/java/android/os/SystemProperties.java
@@ -96,6 +96,7 @@
*/
@NonNull
@SystemApi
+ @TestApi
public static String get(@NonNull String key) {
if (TRACK_KEY_ACCESS) onKeyAccess(key);
return native_get(key);
diff --git a/core/java/android/os/Trace.java b/core/java/android/os/Trace.java
index 583f060..a967b3d 100644
--- a/core/java/android/os/Trace.java
+++ b/core/java/android/os/Trace.java
@@ -288,6 +288,19 @@
}
/**
+ * Checks whether or not tracing is currently enabled. This is useful to avoid intermediate
+ * string creation for trace sections that require formatting. It is not necessary
+ * to guard all Trace method calls as they internally already check this. However it is
+ * recommended to use this to prevent creating any temporary objects that would then be
+ * passed to those methods to reduce runtime cost when tracing isn't enabled.
+ *
+ * @return true if tracing is currently enabled, false otherwise
+ */
+ public static boolean isEnabled() {
+ return isTagEnabled(TRACE_TAG_APP);
+ }
+
+ /**
* Writes a trace message to indicate that a given section of code has begun. This call must
* be followed by a corresponding call to {@link #endSection()} on the same thread.
*
@@ -319,4 +332,42 @@
nativeTraceEnd(TRACE_TAG_APP);
}
}
+
+ /**
+ * Writes a trace message to indicate that a given section of code has
+ * begun. Must be followed by a call to {@link #endAsyncSection(String, int)} with the same
+ * methodName and cookie. Unlike {@link #beginSection(String)} and {@link #endSection()},
+ * asynchronous events do not need to be nested. The name and cookie used to
+ * begin an event must be used to end it.
+ *
+ * @param methodName The method name to appear in the trace.
+ * @param cookie Unique identifier for distinguishing simultaneous events
+ */
+ public static void beginAsyncSection(String methodName, int cookie) {
+ asyncTraceBegin(TRACE_TAG_APP, methodName, cookie);
+ }
+
+ /**
+ * Writes a trace message to indicate that the current method has ended.
+ * Must be called exactly once for each call to {@link #beginAsyncSection(String, int)}
+ * using the same name and cookie.
+ *
+ * @param methodName The method name to appear in the trace.
+ * @param cookie Unique identifier for distinguishing simultaneous events
+ */
+ public static void endAsyncSection(String methodName, int cookie) {
+ asyncTraceEnd(TRACE_TAG_APP, methodName, cookie);
+ }
+
+ /**
+ * Writes trace message to indicate the value of a given counter.
+ *
+ * @param counterName The counter name to appear in the trace.
+ * @param counterValue The counter value.
+ */
+ public static void setCounter(String counterName, int counterValue) {
+ if (isTagEnabled(TRACE_TAG_APP)) {
+ nativeTraceCounter(TRACE_TAG_APP, counterName, counterValue);
+ }
+ }
}
diff --git a/core/java/android/os/VibrationEffect.java b/core/java/android/os/VibrationEffect.java
index 91c69fb..01d85c6 100644
--- a/core/java/android/os/VibrationEffect.java
+++ b/core/java/android/os/VibrationEffect.java
@@ -54,6 +54,7 @@
* @see #get(int)
* @hide
*/
+ @TestApi
public static final int EFFECT_CLICK = Effect.CLICK;
/**
@@ -62,6 +63,7 @@
* @see #get(int)
* @hide
*/
+ @TestApi
public static final int EFFECT_DOUBLE_CLICK = Effect.DOUBLE_CLICK;
/**
@@ -69,6 +71,7 @@
* @see #get(int)
* @hide
*/
+ @TestApi
public static final int EFFECT_TICK = Effect.TICK;
/**
@@ -76,6 +79,7 @@
* @see #get(int)
* @hide
*/
+ @TestApi
public static final int EFFECT_THUD = Effect.THUD;
/**
@@ -83,6 +87,7 @@
* @see #get(int)
* @hide
*/
+ @TestApi
public static final int EFFECT_POP = Effect.POP;
/**
@@ -90,8 +95,20 @@
* @see #get(int)
* @hide
*/
+ @TestApi
public static final int EFFECT_HEAVY_CLICK = Effect.HEAVY_CLICK;
+ /** {@hide} */
+ @TestApi
+ public static final int EFFECT_STRENGTH_LIGHT = EffectStrength.LIGHT;
+
+ /** {@hide} */
+ @TestApi
+ public static final int EFFECT_STRENGTH_MEDIUM = EffectStrength.MEDIUM;
+
+ /** {@hide} */
+ @TestApi
+ public static final int EFFECT_STRENGTH_STRONG = EffectStrength.STRONG;
/**
* Ringtone patterns. They may correspond with the device's ringtone audio, or may just be a
@@ -307,6 +324,7 @@
*
* @hide
*/
+ @TestApi
public abstract long getDuration();
/**
diff --git a/core/java/android/os/WorkSource.java b/core/java/android/os/WorkSource.java
index 3270719..2299ab2 100644
--- a/core/java/android/os/WorkSource.java
+++ b/core/java/android/os/WorkSource.java
@@ -2,6 +2,7 @@
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.content.Context;
import android.os.WorkSourceProto;
import android.provider.Settings;
@@ -83,6 +84,7 @@
}
/** @hide */
+ @TestApi
public WorkSource(int uid) {
mNum = 1;
mUids = new int[] { uid, 0 };
@@ -127,16 +129,19 @@
}
/** @hide */
+ @TestApi
public int size() {
return mNum;
}
/** @hide */
+ @TestApi
public int get(int index) {
return mUids[index];
}
/** @hide */
+ @TestApi
public String getName(int index) {
return mNames != null ? mNames[index] : null;
}
@@ -328,6 +333,7 @@
* to be aware of internal differences.
*/
@Deprecated
+ @TestApi
public WorkSource[] setReturningDiffs(WorkSource other) {
synchronized (sTmpWorkSource) {
sNewbWork = null;
@@ -379,6 +385,7 @@
* @deprecated meant for unit testing use only. Will be removed in a future API revision.
*/
@Deprecated
+ @TestApi
public WorkSource addReturningNewbs(WorkSource other) {
synchronized (sTmpWorkSource) {
sNewbWork = null;
@@ -388,6 +395,7 @@
}
/** @hide */
+ @TestApi
public boolean add(int uid) {
if (mNum <= 0) {
mNames = null;
@@ -407,6 +415,7 @@
}
/** @hide */
+ @TestApi
public boolean add(int uid, String name) {
if (mNum <= 0) {
insert(0, uid, name);
diff --git a/core/java/android/os/health/HealthKeys.java b/core/java/android/os/health/HealthKeys.java
index 842def3..5d60411 100644
--- a/core/java/android/os/health/HealthKeys.java
+++ b/core/java/android/os/health/HealthKeys.java
@@ -16,10 +16,8 @@
package android.os.health;
-import android.os.Parcel;
-import android.os.Parcelable;
+import android.annotation.TestApi;
-import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -32,6 +30,7 @@
*
* @hide
*/
+@TestApi
public class HealthKeys {
/**
@@ -81,6 +80,7 @@
*
* @hide
*/
+ @TestApi
public static class Constants {
private final String mDataType;
private final int[][] mKeys = new int[TYPE_COUNT][];
diff --git a/core/java/android/os/health/HealthStats.java b/core/java/android/os/health/HealthStats.java
index 90d89c5..74ce515 100644
--- a/core/java/android/os/health/HealthStats.java
+++ b/core/java/android/os/health/HealthStats.java
@@ -16,6 +16,7 @@
package android.os.health;
+import android.annotation.TestApi;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.ArrayMap;
@@ -126,6 +127,7 @@
*
* @hide
*/
+ @TestApi
public HealthStats(Parcel in) {
int count;
diff --git a/core/java/android/os/health/HealthStatsParceler.java b/core/java/android/os/health/HealthStatsParceler.java
index 28b3694..d358a2e 100644
--- a/core/java/android/os/health/HealthStatsParceler.java
+++ b/core/java/android/os/health/HealthStatsParceler.java
@@ -16,6 +16,7 @@
package android.os.health;
+import android.annotation.TestApi;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.ArrayMap;
@@ -35,6 +36,7 @@
* reuse them.
* @hide
*/
+@TestApi
public class HealthStatsParceler implements Parcelable {
private HealthStatsWriter mWriter;
private HealthStats mHealthStats;
diff --git a/core/java/android/os/health/HealthStatsWriter.java b/core/java/android/os/health/HealthStatsWriter.java
index 351836b..d4d10b0 100644
--- a/core/java/android/os/health/HealthStatsWriter.java
+++ b/core/java/android/os/health/HealthStatsWriter.java
@@ -16,6 +16,7 @@
package android.os.health;
+import android.annotation.TestApi;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.ArrayMap;
@@ -31,6 +32,7 @@
*
* @hide
*/
+@TestApi
public class HealthStatsWriter {
private final HealthKeys.Constants mConstants;
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index aeced951..8b8ae1c 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -71,6 +71,8 @@
import com.android.internal.os.SomeArgs;
import com.android.internal.util.Preconditions;
+import dalvik.system.BlockGuard;
+
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileNotFoundException;
@@ -1131,6 +1133,7 @@
/** {@hide} */
public void mkdirs(File file) {
+ BlockGuard.getVmPolicy().onPathAccess(file.getAbsolutePath());
try {
mStorageManager.mkdirs(mContext.getOpPackageName(), file.getAbsolutePath());
} catch (RemoteException e) {
diff --git a/core/java/android/os/storage/StorageVolume.java b/core/java/android/os/storage/StorageVolume.java
index fd5a22a..9880142 100644
--- a/core/java/android/os/storage/StorageVolume.java
+++ b/core/java/android/os/storage/StorageVolume.java
@@ -17,6 +17,7 @@
package android.os.storage;
import android.annotation.Nullable;
+import android.annotation.TestApi;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
@@ -162,6 +163,7 @@
* @return the mount path
* @hide
*/
+ @TestApi
public String getPath() {
return mPath.toString();
}
diff --git a/core/java/android/os/strictmode/CredentialProtectedWhileLockedViolation.java b/core/java/android/os/strictmode/CredentialProtectedWhileLockedViolation.java
new file mode 100644
index 0000000..12503f6
--- /dev/null
+++ b/core/java/android/os/strictmode/CredentialProtectedWhileLockedViolation.java
@@ -0,0 +1,39 @@
+/*
+ * 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.strictmode;
+
+import android.content.Context;
+
+/**
+ * Subclass of {@code Violation} that is used when a process accesses filesystem
+ * paths stored in credential protected storage areas while the user is locked.
+ * <p>
+ * When a user is locked, credential protected storage is unavailable, and files
+ * stored in these locations appear to not exist, which can result in subtle app
+ * bugs if they assume default behaviors or empty states. Instead, apps should
+ * store data needed while a user is locked under device protected storage
+ * areas.
+ *
+ * @see Context#createCredentialProtectedStorageContext()
+ * @see Context#createDeviceProtectedStorageContext()
+ */
+public final class CredentialProtectedWhileLockedViolation extends Violation {
+ /** @hide */
+ public CredentialProtectedWhileLockedViolation(String message) {
+ super(message);
+ }
+}
diff --git a/core/java/android/os/strictmode/ExplicitGcViolation.java b/core/java/android/os/strictmode/ExplicitGcViolation.java
index c7480f9..583ed1a 100644
--- a/core/java/android/os/strictmode/ExplicitGcViolation.java
+++ b/core/java/android/os/strictmode/ExplicitGcViolation.java
@@ -15,11 +15,14 @@
*/
package android.os.strictmode;
+import android.annotation.TestApi;
+
/**
* See #{@link android.os.StrictMode.ThreadPolicy.Builder#detectExplicitGc()}.
*
* @hide
*/
+@TestApi
public final class ExplicitGcViolation extends Violation {
/** @hide */
public ExplicitGcViolation() {
diff --git a/core/java/android/os/strictmode/ImplicitDirectBootViolation.java b/core/java/android/os/strictmode/ImplicitDirectBootViolation.java
new file mode 100644
index 0000000..e52e212
--- /dev/null
+++ b/core/java/android/os/strictmode/ImplicitDirectBootViolation.java
@@ -0,0 +1,33 @@
+/*
+ * 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.strictmode;
+
+import android.content.pm.PackageManager;
+
+/**
+ * Subclass of {@code Violation} that is used when a process implicitly relies
+ * on automatic Direct Boot filtering.
+ *
+ * @see PackageManager#MATCH_DIRECT_BOOT_AUTO
+ */
+public final class ImplicitDirectBootViolation extends Violation {
+ /** @hide */
+ public ImplicitDirectBootViolation() {
+ super("Implicitly relying on automatic Direct Boot filtering; request explicit"
+ + " filtering with PackageManager.MATCH_DIRECT_BOOT flags");
+ }
+}
diff --git a/core/java/android/os/strictmode/UntaggedSocketViolation.java b/core/java/android/os/strictmode/UntaggedSocketViolation.java
index 836a8b9..3b1ef25 100644
--- a/core/java/android/os/strictmode/UntaggedSocketViolation.java
+++ b/core/java/android/os/strictmode/UntaggedSocketViolation.java
@@ -17,12 +17,8 @@
public final class UntaggedSocketViolation extends Violation {
/** @hide */
- public static final String MESSAGE =
- "Untagged socket detected; use"
- + " TrafficStats.setThreadSocketTag() to track all network usage";
-
- /** @hide */
public UntaggedSocketViolation() {
- super(MESSAGE);
+ super("Untagged socket detected; use TrafficStats.setThreadSocketTag() to "
+ + "track all network usage");
}
}
diff --git a/core/java/android/print/PrintFileDocumentAdapter.java b/core/java/android/print/PrintFileDocumentAdapter.java
index a5f9305..eb4b315 100644
--- a/core/java/android/print/PrintFileDocumentAdapter.java
+++ b/core/java/android/print/PrintFileDocumentAdapter.java
@@ -118,7 +118,7 @@
protected Void doInBackground(Void... params) {
try (InputStream in = new FileInputStream(mFile);
OutputStream out = new FileOutputStream(mDestination.getFileDescriptor())) {
- FileUtils.copy(in, out, null, mCancellationSignal);
+ FileUtils.copy(in, out, mCancellationSignal, null, null);
} catch (OperationCanceledException e) {
// Ignored; already handled below
} catch (IOException e) {
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index a80dced..f97c64c 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -100,7 +100,8 @@
public static final String PROVIDER_INTERFACE = "android.content.action.DOCUMENTS_PROVIDER";
/** {@hide} */
- public static final String EXTRA_PACKAGE_NAME = "android.content.extra.PACKAGE_NAME";
+ @Deprecated
+ public static final String EXTRA_PACKAGE_NAME = Intent.EXTRA_PACKAGE_NAME;
/** {@hide} */
public static final String EXTRA_SHOW_ADVANCED = "android.content.extra.SHOW_ADVANCED";
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 4534f48..ffa5733 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -4161,7 +4161,8 @@
NOTIFICATION_VIBRATION_INTENSITY,
HAPTIC_FEEDBACK_INTENSITY,
DISPLAY_COLOR_MODE,
- ALARM_ALERT
+ ALARM_ALERT,
+ NOTIFICATION_LIGHT_PULSE,
};
/**
@@ -4364,6 +4365,7 @@
VALIDATORS.put(WIFI_STATIC_DNS1, WIFI_STATIC_DNS1_VALIDATOR);
VALIDATORS.put(WIFI_STATIC_DNS2, WIFI_STATIC_DNS2_VALIDATOR);
VALIDATORS.put(SHOW_BATTERY_PERCENT, SHOW_BATTERY_PERCENT_VALIDATOR);
+ VALIDATORS.put(NOTIFICATION_LIGHT_PULSE, BOOLEAN_VALIDATOR);
}
/**
@@ -6029,6 +6031,15 @@
NON_NEGATIVE_INTEGER_VALIDATOR;
/**
+ * Whether the in call notification is enabled to play sound during calls. The value is
+ * boolean (1 or 0).
+ * @hide
+ */
+ public static final String IN_CALL_NOTIFICATION_ENABLED = "in_call_notification_enabled";
+
+ private static final Validator IN_CALL_NOTIFICATION_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR;
+
+ /**
* Uri of the slice that's presented on the keyguard.
* Defaults to a slice with the date and next alarm.
*
@@ -6982,20 +6993,16 @@
*/
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.
+ * {@link android.view.textservice.SpellCheckerSubtype#hashCode()} of the selected subtype
+ * of the selected spell checker service which is one of the services managed by the text
+ * service manager.
*
* @hide
*/
public static final String SELECTED_SPELL_CHECKER_SUBTYPE =
"selected_spell_checker_subtype";
- private static final Validator SELECTED_SPELL_CHECKER_SUBTYPE_VALIDATOR =
- COMPONENT_NAME_VALIDATOR;
-
/**
* Whether spell checker is enabled or not.
*
@@ -7003,8 +7010,6 @@
*/
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/>
@@ -7123,35 +7128,6 @@
public static final String UI_NIGHT_MODE = "ui_night_mode";
/**
- * The current device UI theme mode effect SystemUI and Launcher.<br/>
- * <b>Values:</b><br/>
- * 0 - The mode that theme will controlled by wallpaper color.<br/>
- * 1 - The mode that will always light theme.<br/>
- * 2 - The mode that will always dark theme.<br/>
- *
- * @hide
- */
- public static final String THEME_MODE = "theme_mode";
-
- /**
- * THEME_MODE value for wallpaper mode.
- * @hide
- */
- public static final int THEME_MODE_WALLPAPER = 0;
-
- /**
- * THEME_MODE value for light theme mode.
- * @hide
- */
- public static final int THEME_MODE_LIGHT = 1;
-
- /**
- * THEME_MODE value for dark theme mode.
- * @hide
- */
- public static final int THEME_MODE_DARK = 2;
-
- /**
* Whether screensavers are enabled.
* @hide
*/
@@ -7302,7 +7278,7 @@
* see and assist with all of the user's notifications.
*
* @deprecated Use
- * {@link NotificationManager#isNotificationListenerAccessGranted(ComponentName)}.
+ * {@link NotificationManager#isNotificationAssistantAccessGranted(ComponentName)}.
* @hide
*/
@Deprecated
@@ -7318,7 +7294,7 @@
*
* @hide
* @deprecated Use
- * {@link NotificationManager#isNotificationAssistantAccessGranted(ComponentName)}.
+ * {@link NotificationManager#isNotificationListenerAccessGranted(ComponentName)}.
*/
@Deprecated
public static final String ENABLED_NOTIFICATION_LISTENERS = "enabled_notification_listeners";
@@ -7442,9 +7418,6 @@
*/
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
@@ -8000,14 +7973,10 @@
WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, // moved to global
WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY, // moved to global
WIFI_NUM_OPEN_NETWORKS_KEPT, // moved to global
- SELECTED_SPELL_CHECKER,
- SELECTED_SPELL_CHECKER_SUBTYPE,
- SPELL_CHECKER_ENABLED,
MOUNT_PLAY_NOTIFICATION_SND,
MOUNT_UMS_AUTOSTART,
MOUNT_UMS_PROMPT,
MOUNT_UMS_NOTIFY_ENABLED,
- SLEEP_TIMEOUT,
DOUBLE_TAP_TO_WAKE,
WAKE_GESTURE_ENABLED,
LONG_PRESS_TIMEOUT,
@@ -8051,6 +8020,9 @@
VOLUME_HUSH_GESTURE,
MANUAL_RINGER_TOGGLE_COUNT,
HUSH_GESTURE_USED,
+ IN_CALL_NOTIFICATION_ENABLED,
+ LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS,
+ LOCK_SCREEN_SHOW_NOTIFICATIONS,
};
/**
@@ -8127,15 +8099,10 @@
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);
@@ -8197,6 +8164,9 @@
ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES_VALIDATOR); //legacy restore setting
VALIDATORS.put(HUSH_GESTURE_USED, HUSH_GESTURE_USED_VALIDATOR);
VALIDATORS.put(MANUAL_RINGER_TOGGLE_COUNT, MANUAL_RINGER_TOGGLE_COUNT_VALIDATOR);
+ VALIDATORS.put(IN_CALL_NOTIFICATION_ENABLED, IN_CALL_NOTIFICATION_ENABLED_VALIDATOR);
+ VALIDATORS.put(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(LOCK_SCREEN_SHOW_NOTIFICATIONS, BOOLEAN_VALIDATOR);
}
/**
@@ -9023,6 +8993,14 @@
"location_background_throttle_package_whitelist";
/**
+ * Maximum staleness allowed for last location when returned to clients with only foreground
+ * location permissions.
+ * @hide
+ */
+ public static final String LOCATION_LAST_LOCATION_MAX_AGE_MILLIS =
+ "location_last_location_max_age_millis";
+
+ /**
* Whether TV will switch to MHL port when a mobile device is plugged in.
* (0 = false, 1 = true)
* @hide
@@ -9910,6 +9888,39 @@
public static final String WIFI_SCORE_PARAMS =
"wifi_score_params";
+ /**
+ * Setting to enable logging WifiIsUnusableEvent in metrics
+ * which gets triggered when wifi becomes unusable.
+ * Disabled by default, and setting it to 1 will enable it.
+ * @hide
+ */
+ public static final String WIFI_IS_UNUSABLE_EVENT_METRICS_ENABLED =
+ "wifi_is_unusable_event_metrics_enabled";
+
+ /**
+ * The minimum number of txBad the framework has to observe
+ * to trigger a wifi data stall.
+ * @hide
+ */
+ public static final String WIFI_DATA_STALL_MIN_TX_BAD =
+ "wifi_data_stall_min_tx_bad";
+
+ /**
+ * The minimum number of txSuccess the framework has to observe
+ * to trigger a wifi data stall when rxSuccess is 0.
+ * @hide
+ */
+ public static final String WIFI_DATA_STALL_MIN_TX_SUCCESS_WITHOUT_RX =
+ "wifi_data_stall_min_tx_success_without_rx";
+
+ /**
+ * Setting to enable logging Wifi LinkSpeedCounts in metrics.
+ * Disabled by default, and setting it to 1 will enable it.
+ * @hide
+ */
+ public static final String WIFI_LINK_SPEED_METRICS_ENABLED =
+ "wifi_link_speed_metrics_enabled";
+
/**
* The maximum number of times we will retry a connection to an access
* point for which we have failed in acquiring an IP address from DHCP.
@@ -10952,6 +10963,8 @@
* proc_state_cpu_times_read_delay_ms (long)
* external_stats_collection_rate_limit_ms (long)
* battery_level_collection_delay_ms (long)
+ * max_history_files (int)
+ * max_history_buffer_kb (int)
* </pre>
*
* <p>
@@ -11061,6 +11074,7 @@
*
* <pre>
* enabled (boolean)
+ * disable_home (boolean)
* disable_tilt_to_wake (boolean)
* disable_touch_to_wake (boolean)
* </pre>
@@ -12973,6 +12987,21 @@
*/
public static final String GNSS_HAL_LOCATION_REQUEST_DURATION_MILLIS =
"gnss_hal_location_request_duration_millis";
+
+ /**
+ * Binder call stats settings.
+ *
+ * The following strings are supported as keys:
+ * <pre>
+ * enabled (boolean)
+ * detailed_tracking (boolean)
+ * upload_data (boolean)
+ * sampling_interval (int)
+ * </pre>
+ *
+ * @hide
+ */
+ public static final String BINDER_CALLS_STATS = "binder_calls_stats";
}
/**
diff --git a/core/java/android/service/notification/Adjustment.java b/core/java/android/service/notification/Adjustment.java
index 7348cf6..0d94af4 100644
--- a/core/java/android/service/notification/Adjustment.java
+++ b/core/java/android/service/notification/Adjustment.java
@@ -65,6 +65,12 @@
public static final String KEY_USER_SENTIMENT = "key_user_sentiment";
/**
+ * Data type: ArrayList of {@link android.app.Notification.Action}.
+ * Used to suggest extra actions for a notification.
+ */
+ public static final String KEY_SMART_ACTIONS = "key_smart_actions";
+
+ /**
* Create a notification adjustment.
*
* @param pkg The package of the notification.
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index a7d70d0..09425a9 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -1426,6 +1426,7 @@
private boolean mShowBadge;
private @UserSentiment int mUserSentiment = USER_SENTIMENT_NEUTRAL;
private boolean mHidden;
+ private ArrayList<Notification.Action> mSmartActions;
public Ranking() {}
@@ -1556,6 +1557,13 @@
}
/**
+ * @hide
+ */
+ public List<Notification.Action> getSmartActions() {
+ return mSmartActions;
+ }
+
+ /**
* Returns whether this notification can be displayed as a badge.
*
* @return true if the notification can be displayed as a badge, false otherwise.
@@ -1583,7 +1591,7 @@
CharSequence explanation, String overrideGroupKey,
NotificationChannel channel, ArrayList<String> overridePeople,
ArrayList<SnoozeCriterion> snoozeCriteria, boolean showBadge,
- int userSentiment, boolean hidden) {
+ int userSentiment, boolean hidden, ArrayList<Notification.Action> smartActions) {
mKey = key;
mRank = rank;
mIsAmbient = importance < NotificationManager.IMPORTANCE_LOW;
@@ -1599,6 +1607,7 @@
mShowBadge = showBadge;
mUserSentiment = userSentiment;
mHidden = hidden;
+ mSmartActions = smartActions;
}
/**
@@ -1648,6 +1657,7 @@
private ArrayMap<String, Boolean> mShowBadge;
private ArrayMap<String, Integer> mUserSentiment;
private ArrayMap<String, Boolean> mHidden;
+ private ArrayMap<String, ArrayList<Notification.Action>> mSmartActions;
private RankingMap(NotificationRankingUpdate rankingUpdate) {
mRankingUpdate = rankingUpdate;
@@ -1676,7 +1686,7 @@
getVisibilityOverride(key), getSuppressedVisualEffects(key),
getImportance(key), getImportanceExplanation(key), getOverrideGroupKey(key),
getChannel(key), getOverridePeople(key), getSnoozeCriteria(key),
- getShowBadge(key), getUserSentiment(key), getHidden(key));
+ getShowBadge(key), getUserSentiment(key), getHidden(key), getSmartActions(key));
return rank >= 0;
}
@@ -1814,6 +1824,15 @@
return hidden == null ? false : hidden.booleanValue();
}
+ private ArrayList<Notification.Action> getSmartActions(String key) {
+ synchronized (this) {
+ if (mSmartActions == null) {
+ buildSmartActions();
+ }
+ }
+ return mSmartActions.get(key);
+ }
+
// Locked by 'this'
private void buildRanksLocked() {
String[] orderedKeys = mRankingUpdate.getOrderedKeys();
@@ -1931,6 +1950,15 @@
}
}
+ // Locked by 'this'
+ private void buildSmartActions() {
+ Bundle smartActions = mRankingUpdate.getSmartActions();
+ mSmartActions = new ArrayMap<>(smartActions.size());
+ for (String key : smartActions.keySet()) {
+ mSmartActions.put(key, smartActions.getParcelableArrayList(key));
+ }
+ }
+
// ----------- Parcelable
@Override
diff --git a/core/java/android/service/notification/NotificationRankingUpdate.java b/core/java/android/service/notification/NotificationRankingUpdate.java
index 00c47ec..bed22149 100644
--- a/core/java/android/service/notification/NotificationRankingUpdate.java
+++ b/core/java/android/service/notification/NotificationRankingUpdate.java
@@ -37,12 +37,13 @@
private final Bundle mShowBadge;
private final Bundle mUserSentiment;
private final Bundle mHidden;
+ private final Bundle mSmartActions;
public NotificationRankingUpdate(String[] keys, String[] interceptedKeys,
Bundle visibilityOverrides, Bundle suppressedVisualEffects,
int[] importance, Bundle explanation, Bundle overrideGroupKeys,
Bundle channels, Bundle overridePeople, Bundle snoozeCriteria,
- Bundle showBadge, Bundle userSentiment, Bundle hidden) {
+ Bundle showBadge, Bundle userSentiment, Bundle hidden, Bundle smartActions) {
mKeys = keys;
mInterceptedKeys = interceptedKeys;
mVisibilityOverrides = visibilityOverrides;
@@ -56,6 +57,7 @@
mShowBadge = showBadge;
mUserSentiment = userSentiment;
mHidden = hidden;
+ mSmartActions = smartActions;
}
public NotificationRankingUpdate(Parcel in) {
@@ -73,6 +75,7 @@
mShowBadge = in.readBundle();
mUserSentiment = in.readBundle();
mHidden = in.readBundle();
+ mSmartActions = in.readBundle();
}
@Override
@@ -95,6 +98,7 @@
out.writeBundle(mShowBadge);
out.writeBundle(mUserSentiment);
out.writeBundle(mHidden);
+ out.writeBundle(mSmartActions);
}
public static final Parcelable.Creator<NotificationRankingUpdate> CREATOR
@@ -159,4 +163,8 @@
public Bundle getHidden() {
return mHidden;
}
+
+ public Bundle getSmartActions() {
+ return mSmartActions;
+ }
}
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index 5546e80..f52234d 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -498,7 +498,7 @@
Boolean allowWhenScreenOff = unsafeBoolean(parser, ALLOW_ATT_SCREEN_OFF);
if (allowWhenScreenOff != null) {
readSuppressedEffects = true;
- if (allowWhenScreenOff) {
+ if (!allowWhenScreenOff) {
rt.suppressedVisualEffects |= SUPPRESSED_EFFECT_LIGHTS
| SUPPRESSED_EFFECT_FULL_SCREEN_INTENT;
}
@@ -506,7 +506,7 @@
Boolean allowWhenScreenOn = unsafeBoolean(parser, ALLOW_ATT_SCREEN_ON);
if (allowWhenScreenOn != null) {
readSuppressedEffects = true;
- if (allowWhenScreenOn) {
+ if (!allowWhenScreenOn) {
rt.suppressedVisualEffects |= SUPPRESSED_EFFECT_PEEK;
}
}
@@ -1007,6 +1007,24 @@
return true;
}
+ /**
+ * Returns whether the conditionId is a valid ScheduleCondition.
+ * If allowNever is true, this will return true even if the ScheduleCondition never occurs.
+ */
+ public static boolean isValidScheduleConditionId(Uri conditionId, boolean allowNever) {
+ ScheduleInfo info;
+ try {
+ info = tryParseScheduleConditionId(conditionId);
+ } catch (NullPointerException | ArrayIndexOutOfBoundsException e) {
+ return false;
+ }
+
+ if (info == null || (!allowNever && (info.days == null || info.days.length == 0))) {
+ return false;
+ }
+ return true;
+ }
+
public static ScheduleInfo tryParseScheduleConditionId(Uri conditionId) {
final boolean isSchedule = conditionId != null
&& conditionId.getScheme().equals(Condition.SCHEME)
diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java
index cd177c4..26223f7 100644
--- a/core/java/android/service/voice/VoiceInteractionSession.java
+++ b/core/java/android/service/voice/VoiceInteractionSession.java
@@ -109,6 +109,12 @@
*/
public static final int SHOW_SOURCE_ACTIVITY = 1<<4;
+ /**
+ * Flag for use with {@link #onShow}: indicates that the voice interaction service was invoked
+ * from a physical button.
+ */
+ public static final int SHOW_SOURCE_PUSH_TO_TALK = 1 << 5;
+
final Context mContext;
final HandlerCaller mHandlerCaller;
diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java
index 01562b3..6f1bd78 100644
--- a/core/java/android/speech/tts/TextToSpeech.java
+++ b/core/java/android/speech/tts/TextToSpeech.java
@@ -33,7 +33,6 @@
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
-import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
@@ -487,8 +486,9 @@
* intent. The possible values for this extra are
* {@link TextToSpeech#SUCCESS} and {@link TextToSpeech#ERROR}.
*
- * @deprecated No longer in use. If client ise interested in information about what
- * changed, is should send ACTION_CHECK_TTS_DATA intent to discover available voices.
+ * @deprecated No longer in use. If client is interested in information about what
+ * changed, it should use the ACTION_CHECK_TTS_DATA
+ * intent to discover available voices.
*/
@Deprecated
public static final String EXTRA_TTS_DATA_INSTALLED = "dataInstalled";
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index 2367d63..c6c1e8a 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -1594,7 +1594,7 @@
}
float get(final int offset) {
- if (mHorizontals == null) {
+ if (mHorizontals == null || offset < 0 || offset >= mHorizontals.length) {
return getHorizontal(offset, mPrimary);
} else {
return mHorizontals[offset - mLineStartOffset];
diff --git a/core/java/android/text/PrecomputedText.java b/core/java/android/text/PrecomputedText.java
index 369f357..027ead3 100644
--- a/core/java/android/text/PrecomputedText.java
+++ b/core/java/android/text/PrecomputedText.java
@@ -437,7 +437,6 @@
public boolean canUseMeasuredResult(@IntRange(from = 0) int start, @IntRange(from = 0) int end,
@NonNull TextDirectionHeuristic textDir, @NonNull TextPaint paint,
@Layout.BreakStrategy int strategy, @Layout.HyphenationFrequency int frequency) {
- final TextPaint mtPaint = mParams.getTextPaint();
return mStart == start
&& mEnd == end
&& mParams.isSameTextMetricsInternal(paint, textDir, strategy, frequency);
diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java
index a0daa07..dde4c1d 100644
--- a/core/java/android/text/TextUtils.java
+++ b/core/java/android/text/TextUtils.java
@@ -351,8 +351,16 @@
}
/**
- * String.split() returns [''] when the string to be split is empty. This returns []. This does
- * not remove any empty strings from the result. For example split("a,", "," ) returns {"a", ""}.
+ *
+ * This method yields the same result as {@code text.split(expression, -1)} except that if
+ * {@code text.isEmpty()} then this method returns an empty array whereas
+ * {@code "".split(expression, -1)} would have returned an array with a single {@code ""}.
+ *
+ * The {@code -1} means that trailing empty Strings are not removed from the result; for
+ * example split("a,", "," ) returns {"a", ""}. Note that whether a leading zero-width match
+ * can result in a leading {@code ""} depends on whether your app
+ * {@link android.content.pm.ApplicationInfo#targetSdkVersion targets an SDK version}
+ * {@code <= 28}; see {@link Pattern#split(CharSequence, int)}.
*
* @param text the string to split
* @param expression the regular expression to match
@@ -369,8 +377,16 @@
}
/**
- * Splits a string on a pattern. String.split() returns [''] when the string to be
- * split is empty. This returns []. This does not remove any empty strings from the result.
+ * Splits a string on a pattern. This method yields the same result as
+ * {@code pattern.split(text, -1)} except that if {@code text.isEmpty()} then this method
+ * returns an empty array whereas {@code pattern.split("", -1)} would have returned an array
+ * with a single {@code ""}.
+ *
+ * The {@code -1} means that trailing empty Strings are not removed from the result;
+ * Note that whether a leading zero-width match can result in a leading {@code ""} depends
+ * on whether your app {@link android.content.pm.ApplicationInfo#targetSdkVersion targets
+ * an SDK version} {@code <= 28}; see {@link Pattern#split(CharSequence, int)}.
+ *
* @param text the string to split
* @param pattern the regular expression to match
* @return an array of strings. The array will be empty if text is empty
@@ -2075,6 +2091,25 @@
return (T) text.subSequence(0, size);
}
+ /**
+ * Trims the {@code text} to the first {@code size} characters and adds an ellipsis if the
+ * resulting string is shorter than the input. This will result in an output string which is
+ * longer than {@code size} for most inputs.
+ *
+ * @param size length of the result, should be greater than 0
+ *
+ * @hide
+ */
+ @Nullable
+ public static <T extends CharSequence> T trimToLengthWithEllipsis(@Nullable T text,
+ @IntRange(from = 1) int size) {
+ T trimmed = trimToSize(text, size);
+ if (trimmed.length() < text.length()) {
+ trimmed = (T) (trimmed.toString() + "...");
+ }
+ return trimmed;
+ }
+
private static Object sLock = new Object();
private static char[] sTemp = null;
diff --git a/core/java/android/text/style/BulletSpan.java b/core/java/android/text/style/BulletSpan.java
index 70175c8..c0ac70e 100644
--- a/core/java/android/text/style/BulletSpan.java
+++ b/core/java/android/text/style/BulletSpan.java
@@ -23,8 +23,6 @@
import android.annotation.Px;
import android.graphics.Canvas;
import android.graphics.Paint;
-import android.graphics.Path;
-import android.graphics.Path.Direction;
import android.os.Parcel;
import android.text.Layout;
import android.text.ParcelableSpan;
@@ -73,7 +71,6 @@
private final int mGapWidth;
@Px
private final int mBulletRadius;
- private Path mBulletPath = null;
@ColorInt
private final int mColor;
private final boolean mWantColor;
@@ -224,19 +221,7 @@
final float yPosition = (top + bottom) / 2f;
final float xPosition = x + dir * mBulletRadius;
- if (canvas.isHardwareAccelerated()) {
- if (mBulletPath == null) {
- mBulletPath = new Path();
- mBulletPath.addCircle(0.0f, 0.0f, mBulletRadius, Direction.CW);
- }
-
- canvas.save();
- canvas.translate(xPosition, yPosition);
- canvas.drawPath(mBulletPath, paint);
- canvas.restore();
- } else {
- canvas.drawCircle(xPosition, yPosition, mBulletRadius, paint);
- }
+ canvas.drawCircle(xPosition, yPosition, mBulletRadius, paint);
if (mWantColor) {
paint.setColor(oldcolor);
diff --git a/core/java/android/text/util/Linkify.java b/core/java/android/text/util/Linkify.java
index f3d39de..08cbbe6 100644
--- a/core/java/android/text/util/Linkify.java
+++ b/core/java/android/text/util/Linkify.java
@@ -35,6 +35,7 @@
import com.android.i18n.phonenumbers.PhoneNumberMatch;
import com.android.i18n.phonenumbers.PhoneNumberUtil;
import com.android.i18n.phonenumbers.PhoneNumberUtil.Leniency;
+import com.android.internal.annotations.GuardedBy;
import libcore.util.EmptyArray;
@@ -63,6 +64,10 @@
* does not have a URL scheme prefix, the supplied scheme will be prepended to
* create <code>http://example.com</code> when the clickable URL link is
* created.
+ *
+ * @see MatchFilter
+ * @see TransformFilter
+ * @see UrlSpanFactory
*/
public class Linkify {
@@ -218,6 +223,44 @@
}
/**
+ * Factory class to create {@link URLSpan}s. While adding spans to a {@link Spannable},
+ * {@link Linkify} will call {@link UrlSpanFactory#create(String)} function to create a
+ * {@link URLSpan}.
+ *
+ * @see #addLinks(Spannable, int, UrlSpanFactory)
+ * @see #addLinks(Spannable, Pattern, String, String[], MatchFilter, TransformFilter,
+ * UrlSpanFactory)
+ */
+ public static class UrlSpanFactory {
+ private static final Object sInstanceLock = new Object();
+
+ @GuardedBy("sInstanceLock")
+ private static volatile UrlSpanFactory sInstance = null;
+
+ private static synchronized UrlSpanFactory getInstance() {
+ if (sInstance == null) {
+ synchronized (sInstanceLock) {
+ if (sInstance == null) {
+ sInstance = new UrlSpanFactory();
+ }
+ }
+ }
+ return sInstance;
+ }
+
+ /**
+ * Factory function that will called by {@link Linkify} in order to create a
+ * {@link URLSpan}.
+ *
+ * @param url URL found
+ * @return a URLSpan instance
+ */
+ public URLSpan create(final String url) {
+ return new URLSpan(url);
+ }
+ }
+
+ /**
* Scans the text of the provided Spannable and turns all occurrences
* of the link types indicated in the mask into clickable links.
* If the mask is nonzero, it also removes any existing URLSpans
@@ -228,24 +271,55 @@
* @param mask Mask to define which kinds of links will be searched.
*
* @return True if at least one link is found and applied.
+ *
+ * @see #addLinks(Spannable, int, UrlSpanFactory)
*/
public static final boolean addLinks(@NonNull Spannable text, @LinkifyMask int mask) {
- return addLinks(text, mask, null);
+ return addLinks(text, mask, null, null);
}
+ /**
+ * Scans the text of the provided Spannable and turns all occurrences
+ * of the link types indicated in the mask into clickable links.
+ * If the mask is nonzero, it also removes any existing URLSpans
+ * attached to the Spannable, to avoid problems if you call it
+ * repeatedly on the same text.
+ *
+ * @param text Spannable whose text is to be marked-up with links
+ * @param mask mask to define which kinds of links will be searched
+ * @param urlSpanFactory factory class used to create {@link URLSpan}s
+ * @return True if at least one link is found and applied.
+ */
+ public static final boolean addLinks(@NonNull Spannable text, @LinkifyMask int mask,
+ @Nullable UrlSpanFactory urlSpanFactory) {
+ return addLinks(text, mask, null, urlSpanFactory);
+ }
+
+ /**
+ * Scans the text of the provided Spannable and turns all occurrences of the link types
+ * indicated in the mask into clickable links. If the mask is nonzero, it also removes any
+ * existing URLSpans attached to the Spannable, to avoid problems if you call it repeatedly
+ * on the same text.
+ *
+ * @param text Spannable whose text is to be marked-up with links
+ * @param mask mask to define which kinds of links will be searched
+ * @param context Context to be used while identifying phone numbers
+ * @param urlSpanFactory factory class used to create {@link URLSpan}s
+ * @return true if at least one link is found and applied.
+ */
private static boolean addLinks(@NonNull Spannable text, @LinkifyMask int mask,
- @Nullable Context context) {
+ @Nullable Context context, @Nullable UrlSpanFactory urlSpanFactory) {
if (mask == 0) {
return false;
}
- URLSpan[] old = text.getSpans(0, text.length(), URLSpan.class);
+ final URLSpan[] old = text.getSpans(0, text.length(), URLSpan.class);
for (int i = old.length - 1; i >= 0; i--) {
text.removeSpan(old[i]);
}
- ArrayList<LinkSpec> links = new ArrayList<LinkSpec>();
+ final ArrayList<LinkSpec> links = new ArrayList<LinkSpec>();
if ((mask & WEB_URLS) != 0) {
gatherLinks(links, text, Patterns.AUTOLINK_WEB_URL,
@@ -274,7 +348,7 @@
}
for (LinkSpec link: links) {
- applyLink(link.url, link.start, link.end, text);
+ applyLink(link.url, link.start, link.end, text, urlSpanFactory);
}
return true;
@@ -290,6 +364,8 @@
* @param mask Mask to define which kinds of links will be searched.
*
* @return True if at least one link is found and applied.
+ *
+ * @see #addLinks(Spannable, int, UrlSpanFactory)
*/
public static final boolean addLinks(@NonNull TextView text, @LinkifyMask int mask) {
if (mask == 0) {
@@ -299,7 +375,7 @@
final Context context = text.getContext();
final CharSequence t = text.getText();
if (t instanceof Spannable) {
- if (addLinks((Spannable) t, mask, context)) {
+ if (addLinks((Spannable) t, mask, context, null)) {
addLinkMovementMethod(text);
return true;
}
@@ -308,7 +384,7 @@
} else {
SpannableString s = SpannableString.valueOf(t);
- if (addLinks(s, mask, context)) {
+ if (addLinks(s, mask, context, null)) {
addLinkMovementMethod(text);
text.setText(s);
@@ -403,6 +479,8 @@
* @param pattern Regex pattern to be used for finding links
* @param scheme URL scheme string (eg <code>http://</code>) to be
* prepended to the links that do not start with this scheme.
+ * @see #addLinks(Spannable, Pattern, String, String[], MatchFilter, TransformFilter,
+ * UrlSpanFactory)
*/
public static final boolean addLinks(@NonNull Spannable text, @NonNull Pattern pattern,
@Nullable String scheme) {
@@ -423,6 +501,8 @@
* @param transformFilter Filter to allow the client code to update the link found.
*
* @return True if at least one link is found and applied.
+ * @see #addLinks(Spannable, Pattern, String, String[], MatchFilter, TransformFilter,
+ * UrlSpanFactory)
*/
public static final boolean addLinks(@NonNull Spannable spannable, @NonNull Pattern pattern,
@Nullable String scheme, @Nullable MatchFilter matchFilter,
@@ -446,10 +526,39 @@
* @param transformFilter Filter to allow the client code to update the link found.
*
* @return True if at least one link is found and applied.
+ *
+ * @see #addLinks(Spannable, Pattern, String, String[], MatchFilter, TransformFilter,
+ * UrlSpanFactory)
*/
public static final boolean addLinks(@NonNull Spannable spannable, @NonNull Pattern pattern,
- @Nullable String defaultScheme, @Nullable String[] schemes,
+ @Nullable String defaultScheme, @Nullable String[] schemes,
@Nullable MatchFilter matchFilter, @Nullable TransformFilter transformFilter) {
+ return addLinks(spannable, pattern, defaultScheme, schemes, matchFilter, transformFilter,
+ null);
+ }
+
+ /**
+ * Applies a regex to a Spannable turning the matches into links.
+ *
+ * @param spannable spannable whose text is to be marked-up with links.
+ * @param pattern regex pattern to be used for finding links.
+ * @param defaultScheme the default scheme to be prepended to links if the link does not
+ * start with one of the <code>schemes</code> given.
+ * @param schemes array of schemes (eg <code>http://</code>) to check if the link found
+ * contains a scheme. Passing a null or empty value means prepend
+ * defaultScheme
+ * to all links.
+ * @param matchFilter the filter that is used to allow the client code additional control
+ * over which pattern matches are to be converted into links.
+ * @param transformFilter filter to allow the client code to update the link found.
+ * @param urlSpanFactory factory class used to create {@link URLSpan}s
+ *
+ * @return True if at least one link is found and applied.
+ */
+ public static final boolean addLinks(@NonNull Spannable spannable, @NonNull Pattern pattern,
+ @Nullable String defaultScheme, @Nullable String[] schemes,
+ @Nullable MatchFilter matchFilter, @Nullable TransformFilter transformFilter,
+ @Nullable UrlSpanFactory urlSpanFactory) {
final String[] schemesCopy;
if (defaultScheme == null) defaultScheme = "";
if (schemes == null || schemes.length < 1) {
@@ -478,7 +587,7 @@
if (allowed) {
String url = makeUrl(m.group(0), schemesCopy, m, transformFilter);
- applyLink(url, start, end, spannable);
+ applyLink(url, start, end, spannable, urlSpanFactory);
hasMatches = true;
}
}
@@ -486,9 +595,12 @@
return hasMatches;
}
- private static final void applyLink(String url, int start, int end, Spannable text) {
- URLSpan span = new URLSpan(url);
-
+ private static void applyLink(String url, int start, int end, Spannable text,
+ @Nullable UrlSpanFactory urlSpanFactory) {
+ if (urlSpanFactory == null) {
+ urlSpanFactory = UrlSpanFactory.getInstance();
+ }
+ final URLSpan span = urlSpanFactory.create(url);
text.setSpan(span, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index ec045b1..c037cd0 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -33,6 +33,8 @@
public static final String FFLAG_PREFIX = "sys.fflag.";
public static final String FFLAG_OVERRIDE_PREFIX = FFLAG_PREFIX + "override.";
+ public static final String PERSIST_PREFIX = "persist." + FFLAG_OVERRIDE_PREFIX;
+ public static final String HEARING_AID_SETTINGS = "settings_bluetooth_hearing_aid";
private static final Map<String, String> DEFAULT_FLAGS;
static {
@@ -41,6 +43,8 @@
DEFAULT_FLAGS.put("settings_bluetooth_while_driving", "false");
DEFAULT_FLAGS.put("settings_audio_switcher", "true");
DEFAULT_FLAGS.put("settings_systemui_theme", "true");
+ DEFAULT_FLAGS.put("settings_dynamic_homepage", "false");
+ DEFAULT_FLAGS.put(HEARING_AID_SETTINGS, "true");
}
/**
diff --git a/core/java/android/util/Half.java b/core/java/android/util/Half.java
index 84c2e83..5718d99 100644
--- a/core/java/android/util/Half.java
+++ b/core/java/android/util/Half.java
@@ -20,8 +20,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import sun.misc.FloatingDecimal;
-
/**
* <p>The {@code Half} class is a wrapper and a utility class to manipulate half-precision 16-bit
* <a href="https://en.wikipedia.org/wiki/Half-precision_floating-point_format">IEEE 754</a>
@@ -1026,7 +1024,7 @@
* half-precision float value
*/
public static @HalfFloat short parseHalf(@NonNull String s) throws NumberFormatException {
- return toHalf(FloatingDecimal.parseFloat(s));
+ return toHalf(Float.parseFloat(s));
}
/**
diff --git a/core/java/android/util/IntArray.java b/core/java/android/util/IntArray.java
index 3617aa7..5a74ec0 100644
--- a/core/java/android/util/IntArray.java
+++ b/core/java/android/util/IntArray.java
@@ -18,9 +18,11 @@
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.Preconditions;
-import java.util.Arrays;
+
import libcore.util.EmptyArray;
+import java.util.Arrays;
+
/**
* Implements a growing array of int primitives.
*
@@ -102,7 +104,7 @@
ensureCapacity(1);
int rightSegment = mSize - index;
mSize++;
- checkBounds(index);
+ ArrayUtils.checkBounds(mSize, index);
if (rightSegment != 0) {
// Move by 1 all values from the right of 'index'
@@ -175,7 +177,7 @@
* Returns the value at the specified position in this array.
*/
public int get(int index) {
- checkBounds(index);
+ ArrayUtils.checkBounds(mSize, index);
return mValues[index];
}
@@ -183,7 +185,7 @@
* Sets the value at the specified position in this array.
*/
public void set(int index, int value) {
- checkBounds(index);
+ ArrayUtils.checkBounds(mSize, index);
mValues[index] = value;
}
@@ -205,7 +207,7 @@
* Removes the value at the specified index from this array.
*/
public void remove(int index) {
- checkBounds(index);
+ ArrayUtils.checkBounds(mSize, index);
System.arraycopy(mValues, index + 1, mValues, index, mSize - index - 1);
mSize--;
}
@@ -223,10 +225,4 @@
public int[] toArray() {
return Arrays.copyOf(mValues, mSize);
}
-
- private void checkBounds(int index) {
- if (index < 0 || mSize <= index) {
- throw new ArrayIndexOutOfBoundsException(mSize, index);
- }
- }
}
diff --git a/core/java/android/util/LongArray.java b/core/java/android/util/LongArray.java
index fa98096..5ed1c8c 100644
--- a/core/java/android/util/LongArray.java
+++ b/core/java/android/util/LongArray.java
@@ -106,7 +106,7 @@
ensureCapacity(1);
int rightSegment = mSize - index;
mSize++;
- checkBounds(index);
+ ArrayUtils.checkBounds(mSize, index);
if (rightSegment != 0) {
// Move by 1 all values from the right of 'index'
@@ -166,7 +166,7 @@
* Returns the value at the specified position in this array.
*/
public long get(int index) {
- checkBounds(index);
+ ArrayUtils.checkBounds(mSize, index);
return mValues[index];
}
@@ -174,7 +174,7 @@
* Sets the value at the specified position in this array.
*/
public void set(int index, long value) {
- checkBounds(index);
+ ArrayUtils.checkBounds(mSize, index);
mValues[index] = value;
}
@@ -196,7 +196,7 @@
* Removes the value at the specified index from this array.
*/
public void remove(int index) {
- checkBounds(index);
+ ArrayUtils.checkBounds(mSize, index);
System.arraycopy(mValues, index + 1, mValues, index, mSize - index - 1);
mSize--;
}
@@ -215,12 +215,6 @@
return Arrays.copyOf(mValues, mSize);
}
- private void checkBounds(int index) {
- if (index < 0 || mSize <= index) {
- throw new ArrayIndexOutOfBoundsException(mSize, index);
- }
- }
-
/**
* Test if each element of {@code a} equals corresponding element from {@code b}
*/
diff --git a/core/java/android/util/MathUtils.java b/core/java/android/util/MathUtils.java
index b2e24c3..72865cc 100644
--- a/core/java/android/util/MathUtils.java
+++ b/core/java/android/util/MathUtils.java
@@ -187,6 +187,21 @@
}
/**
+ * Perform Hermite interpolation between two values.
+ * Eg:
+ * smoothStep(0, 0.5f, 0.5f) = 1f
+ * smoothStep(0, 0.5f, 0.25f) = 0.5f
+ *
+ * @param start Left edge.
+ * @param end Right edge.
+ * @param x A value between {@code start} and {@code end}.
+ * @return A number between 0 and 1 representing where {@code x} is in the interpolation.
+ */
+ public static float smoothStep(float start, float end, float x) {
+ return constrain((x - start) / (end - start), 0f, 1f);
+ }
+
+ /**
* Returns the sum of the two parameters, or throws an exception if the resulting sum would
* cause an overflow or underflow.
* @throws IllegalArgumentException when overflow or underflow would occur.
diff --git a/core/java/android/util/TimestampedValue.java b/core/java/android/util/TimestampedValue.java
index 75fa18d..1289e4d 100644
--- a/core/java/android/util/TimestampedValue.java
+++ b/core/java/android/util/TimestampedValue.java
@@ -126,4 +126,12 @@
dest.writeLong(timestampedValue.mReferenceTimeMillis);
dest.writeValue(timestampedValue.mValue);
}
+
+ /**
+ * Returns the difference in milliseconds between two instance's reference times.
+ */
+ public static long referenceTimeDifference(
+ @NonNull TimestampedValue<?> one, @NonNull TimestampedValue<?> two) {
+ return one.mReferenceTimeMillis - two.mReferenceTimeMillis;
+ }
}
diff --git a/core/java/android/util/Xml.java b/core/java/android/util/Xml.java
index 041e8a8..e3b8fec 100644
--- a/core/java/android/util/Xml.java
+++ b/core/java/android/util/Xml.java
@@ -16,27 +16,27 @@
package android.util;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.Reader;
-import java.io.StringReader;
-import java.io.UnsupportedEncodingException;
-import org.apache.harmony.xml.ExpatReader;
-import org.kxml2.io.KXmlParser;
+import libcore.util.XmlObjectFactory;
+
import org.xml.sax.ContentHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlPullParserFactory;
import org.xmlpull.v1.XmlSerializer;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Reader;
+import java.io.StringReader;
+import java.io.UnsupportedEncodingException;
+
/**
* XML utility methods.
*/
public class Xml {
- /** @hide */ public Xml() {}
+ private Xml() {}
/**
* {@link org.xmlpull.v1.XmlPullParser} "relaxed" feature name.
@@ -52,7 +52,7 @@
public static void parse(String xml, ContentHandler contentHandler)
throws SAXException {
try {
- XMLReader reader = new ExpatReader();
+ XMLReader reader = XmlObjectFactory.newXMLReader();
reader.setContentHandler(contentHandler);
reader.parse(new InputSource(new StringReader(xml)));
} catch (IOException e) {
@@ -66,7 +66,7 @@
*/
public static void parse(Reader in, ContentHandler contentHandler)
throws IOException, SAXException {
- XMLReader reader = new ExpatReader();
+ XMLReader reader = XmlObjectFactory.newXMLReader();
reader.setContentHandler(contentHandler);
reader.parse(new InputSource(in));
}
@@ -77,7 +77,7 @@
*/
public static void parse(InputStream in, Encoding encoding,
ContentHandler contentHandler) throws IOException, SAXException {
- XMLReader reader = new ExpatReader();
+ XMLReader reader = XmlObjectFactory.newXMLReader();
reader.setContentHandler(contentHandler);
InputSource source = new InputSource(in);
source.setEncoding(encoding.expatName);
@@ -89,7 +89,7 @@
*/
public static XmlPullParser newPullParser() {
try {
- KXmlParser parser = new KXmlParser();
+ XmlPullParser parser = XmlObjectFactory.newXmlPullParser();
parser.setFeature(XmlPullParser.FEATURE_PROCESS_DOCDECL, true);
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
return parser;
@@ -102,25 +102,7 @@
* Creates a new xml serializer.
*/
public static XmlSerializer newSerializer() {
- try {
- return XmlSerializerFactory.instance.newSerializer();
- } catch (XmlPullParserException e) {
- throw new AssertionError(e);
- }
- }
-
- /** Factory for xml serializers. Initialized on demand. */
- static class XmlSerializerFactory {
- static final String TYPE
- = "org.kxml2.io.KXmlParser,org.kxml2.io.KXmlSerializer";
- static final XmlPullParserFactory instance;
- static {
- try {
- instance = XmlPullParserFactory.newInstance(TYPE, null);
- } catch (XmlPullParserException e) {
- throw new AssertionError(e);
- }
- }
+ return XmlObjectFactory.newXmlSerializer();
}
/**
diff --git a/core/java/android/util/jar/StrictJarManifest.java b/core/java/android/util/jar/StrictJarManifest.java
index dbb466c..faec099 100644
--- a/core/java/android/util/jar/StrictJarManifest.java
+++ b/core/java/android/util/jar/StrictJarManifest.java
@@ -44,6 +44,9 @@
private static final byte[] VALUE_SEPARATOR = new byte[] { ':', ' ' };
+ /** The attribute name "Name". */
+ static final Attributes.Name ATTRIBUTE_NAME_NAME = new Attributes.Name("Name");
+
private final Attributes mainAttributes;
private final HashMap<String, Attributes> entries;
@@ -276,7 +279,7 @@
Iterator<String> i = manifest.getEntries().keySet().iterator();
while (i.hasNext()) {
String key = i.next();
- writeEntry(out, Attributes.Name.NAME, key, encoder, buffer);
+ writeEntry(out, ATTRIBUTE_NAME_NAME, key, encoder, buffer);
Attributes attributes = manifest.entries.get(key);
Iterator<?> entries = attributes.keySet().iterator();
while (entries.hasNext()) {
diff --git a/core/java/android/util/jar/StrictJarManifestReader.java b/core/java/android/util/jar/StrictJarManifestReader.java
index 9881bb0..b17abc8 100644
--- a/core/java/android/util/jar/StrictJarManifestReader.java
+++ b/core/java/android/util/jar/StrictJarManifestReader.java
@@ -58,7 +58,7 @@
public void readEntries(Map<String, Attributes> entries, Map<String, StrictJarManifest.Chunk> chunks) throws IOException {
int mark = pos;
while (readHeader()) {
- if (!Attributes.Name.NAME.equals(name)) {
+ if (!StrictJarManifest.ATTRIBUTE_NAME_NAME.equals(name)) {
throw new IOException("Entry is not named");
}
String entryNameValue = value;
diff --git a/core/java/android/view/DisplayCutout.java b/core/java/android/view/DisplayCutout.java
index 47bda53..496bc9f 100644
--- a/core/java/android/view/DisplayCutout.java
+++ b/core/java/android/view/DisplayCutout.java
@@ -29,6 +29,7 @@
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Region;
+import android.graphics.Region.Op;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
@@ -325,14 +326,9 @@
* @hide
*/
public static DisplayCutout fromBoundingRect(int left, int top, int right, int bottom) {
- Path path = new Path();
- path.reset();
- path.moveTo(left, top);
- path.lineTo(left, bottom);
- path.lineTo(right, bottom);
- path.lineTo(right, top);
- path.close();
- return fromBounds(path);
+ Region r = Region.obtain();
+ r.set(left, top, right, bottom);
+ return fromBounds(r);
}
/**
@@ -340,26 +336,19 @@
*
* @hide
*/
- public static DisplayCutout fromBounds(Path path) {
- RectF clipRect = new RectF();
- path.computeBounds(clipRect, false /* unused */);
- Region clipRegion = Region.obtain();
- clipRegion.set((int) clipRect.left, (int) clipRect.top,
- (int) clipRect.right, (int) clipRect.bottom);
-
- Region bounds = new Region();
- bounds.setPath(path, clipRegion);
- clipRegion.recycle();
- return new DisplayCutout(ZERO_RECT, bounds, false /* copyArguments */);
+ public static DisplayCutout fromBounds(Region region) {
+ return new DisplayCutout(ZERO_RECT, region, false /* copyArguments */);
}
/**
- * Creates the bounding path according to @android:string/config_mainBuiltInDisplayCutout.
+ * Creates the display cutout according to
+ * @android:string/config_mainBuiltInDisplayCutoutRectApproximation, which is the closest
+ * rectangle-base approximation of the cutout.
*
* @hide
*/
- public static DisplayCutout fromResources(Resources res, int displayWidth, int displayHeight) {
- return fromSpec(res.getString(R.string.config_mainBuiltInDisplayCutout),
+ public static DisplayCutout fromResourcesRectApproximation(Resources res, int displayWidth, int displayHeight) {
+ return fromSpec(res.getString(R.string.config_mainBuiltInDisplayCutoutRectApproximation),
displayWidth, displayHeight, DENSITY_DEVICE_STABLE / (float) DENSITY_DEFAULT);
}
@@ -369,7 +358,8 @@
* @hide
*/
public static Path pathFromResources(Resources res, int displayWidth, int displayHeight) {
- return pathAndDisplayCutoutFromSpec(res.getString(R.string.config_mainBuiltInDisplayCutout),
+ return pathAndDisplayCutoutFromSpec(
+ res.getString(R.string.config_mainBuiltInDisplayCutout),
displayWidth, displayHeight, DENSITY_DEVICE_STABLE / (float) DENSITY_DEFAULT).first;
}
@@ -417,6 +407,7 @@
}
final Path p;
+ final Region r = Region.obtain();
try {
p = PathParser.createPathFromPathData(spec);
} catch (Throwable e) {
@@ -431,6 +422,8 @@
m.postTranslate(offsetX, 0);
p.transform(m);
+ addToRegion(p, r);
+
if (bottomSpec != null) {
final Path bottomPath;
try {
@@ -443,9 +436,10 @@
m.postTranslate(0, displayHeight);
bottomPath.transform(m);
p.addPath(bottomPath);
+ addToRegion(bottomPath, r);
}
- final Pair<Path, DisplayCutout> result = new Pair<>(p, fromBounds(p));
+ final Pair<Path, DisplayCutout> result = new Pair<>(p, fromBounds(r));
synchronized (CACHE_LOCK) {
sCachedSpec = spec;
sCachedDisplayWidth = displayWidth;
@@ -456,6 +450,14 @@
return result;
}
+ private static void addToRegion(Path p, Region r) {
+ final RectF rectF = new RectF();
+ final Rect rect = new Rect();
+ p.computeBounds(rectF, false /* unused */);
+ rectF.round(rect);
+ r.op(rect, Op.UNION);
+ }
+
private static Region boundingRectsToRegion(List<Rect> rects) {
Region result = Region.obtain();
if (rects != null) {
diff --git a/core/java/android/view/DisplayListCanvas.java b/core/java/android/view/DisplayListCanvas.java
index 671532c..df4d5c4 100644
--- a/core/java/android/view/DisplayListCanvas.java
+++ b/core/java/android/view/DisplayListCanvas.java
@@ -180,13 +180,12 @@
///////////////////////////////////////////////////////////////////////////
/**
- * Draws the specified display list onto this canvas. The display list can only
- * be drawn if {@link android.view.RenderNode#isValid()} returns true.
+ * Draws the specified display list onto this canvas.
*
* @param renderNode The RenderNode to draw.
*/
public void drawRenderNode(RenderNode renderNode) {
- nDrawRenderNode(mNativeCanvasWrapper, renderNode.getNativeDisplayList());
+ nDrawRenderNode(mNativeCanvasWrapper, renderNode.mNativeRenderNode);
}
///////////////////////////////////////////////////////////////////////////
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index f868a00..bedfa9f 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -37,15 +37,10 @@
* {@hide}
*/
interface IWindowSession {
- int add(IWindow window, int seq, in WindowManager.LayoutParams attrs,
- in int viewVisibility, out Rect outContentInsets, out Rect outStableInsets,
- out InputChannel outInputChannel);
int addToDisplay(IWindow window, int seq, in WindowManager.LayoutParams attrs,
in int viewVisibility, in int layerStackId, out Rect outFrame,
out Rect outContentInsets, out Rect outStableInsets, out Rect outOutsets,
out DisplayCutout.ParcelableWrapper displayCutout, out InputChannel outInputChannel);
- int addWithoutInputChannel(IWindow window, int seq, in WindowManager.LayoutParams attrs,
- in int viewVisibility, out Rect outContentInsets, out Rect outStableInsets);
int addToDisplayWithoutInputChannel(IWindow window, int seq, in WindowManager.LayoutParams attrs,
in int viewVisibility, in int layerStackId, out Rect outContentInsets,
out Rect outStableInsets);
diff --git a/core/java/android/view/InputEvent.java b/core/java/android/view/InputEvent.java
index 1f2aab9..c257364 100644
--- a/core/java/android/view/InputEvent.java
+++ b/core/java/android/view/InputEvent.java
@@ -95,6 +95,19 @@
}
/**
+ * Gets the display id of the event.
+ * @return The display id associated with the event.
+ * @hide
+ */
+ public abstract int getDisplayId();
+
+ /**
+ * Modifies the display id associated with the event
+ * @param displayId
+ * @hide
+ */
+ public abstract void setDisplayId(int displayId);
+ /**
* Copies the event.
*
* @return A deep copy of the event.
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index 35546f8..2c00391 100644
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -16,6 +16,8 @@
package android.view;
+import static android.view.Display.INVALID_DISPLAY;
+
import android.annotation.NonNull;
import android.annotation.TestApi;
import android.os.Parcel;
@@ -1246,6 +1248,7 @@
private int mDeviceId;
private int mSource;
+ private int mDisplayId;
private int mMetaState;
private int mAction;
private int mKeyCode;
@@ -1473,6 +1476,7 @@
mScanCode = scancode;
mFlags = flags;
mSource = source;
+ mDisplayId = INVALID_DISPLAY;
}
/**
@@ -1497,6 +1501,7 @@
mDeviceId = deviceId;
mFlags = flags;
mSource = InputDevice.SOURCE_KEYBOARD;
+ mDisplayId = INVALID_DISPLAY;
}
/**
@@ -1511,6 +1516,7 @@
mMetaState = origEvent.mMetaState;
mDeviceId = origEvent.mDeviceId;
mSource = origEvent.mSource;
+ mDisplayId = origEvent.mDisplayId;
mScanCode = origEvent.mScanCode;
mFlags = origEvent.mFlags;
mCharacters = origEvent.mCharacters;
@@ -1537,6 +1543,7 @@
mMetaState = origEvent.mMetaState;
mDeviceId = origEvent.mDeviceId;
mSource = origEvent.mSource;
+ mDisplayId = origEvent.mDisplayId;
mScanCode = origEvent.mScanCode;
mFlags = origEvent.mFlags;
mCharacters = origEvent.mCharacters;
@@ -1564,7 +1571,7 @@
*/
public static KeyEvent obtain(long downTime, long eventTime, int action,
int code, int repeat, int metaState,
- int deviceId, int scancode, int flags, int source, String characters) {
+ int deviceId, int scancode, int flags, int source, int displayId, String characters) {
KeyEvent ev = obtain();
ev.mDownTime = downTime;
ev.mEventTime = eventTime;
@@ -1576,11 +1583,26 @@
ev.mScanCode = scancode;
ev.mFlags = flags;
ev.mSource = source;
+ ev.mDisplayId = displayId;
ev.mCharacters = characters;
return ev;
}
/**
+ * Obtains a (potentially recycled) key event.
+ *
+ * @hide
+ */
+ public static KeyEvent obtain(long downTime, long eventTime, int action,
+ int code, int repeat, int metaState,
+ int deviceId, int scancode, int flags, int source, String characters) {
+ return obtain(downTime, eventTime, action, code, repeat, metaState, deviceId, scancode,
+ flags, source, INVALID_DISPLAY, characters);
+ }
+
+ /**
+
+ /**
* Obtains a (potentially recycled) copy of another key event.
*
* @hide
@@ -1597,6 +1619,7 @@
ev.mScanCode = other.mScanCode;
ev.mFlags = other.mFlags;
ev.mSource = other.mSource;
+ ev.mDisplayId = other.mDisplayId;
ev.mCharacters = other.mCharacters;
return ev;
}
@@ -1683,6 +1706,7 @@
mMetaState = origEvent.mMetaState;
mDeviceId = origEvent.mDeviceId;
mSource = origEvent.mSource;
+ mDisplayId = origEvent.mDisplayId;
mScanCode = origEvent.mScanCode;
mFlags = origEvent.mFlags;
// Don't copy mCharacters, since one way or the other we'll lose it
@@ -1917,6 +1941,18 @@
mSource = source;
}
+ /** @hide */
+ @Override
+ public final int getDisplayId() {
+ return mDisplayId;
+ }
+
+ /** @hide */
+ @Override
+ public final void setDisplayId(int displayId) {
+ mDisplayId = displayId;
+ }
+
/**
* <p>Returns the state of the meta keys.</p>
*
@@ -2852,6 +2888,7 @@
msg.append(", downTime=").append(mDownTime);
msg.append(", deviceId=").append(mDeviceId);
msg.append(", source=0x").append(Integer.toHexString(mSource));
+ msg.append(", displayId=").append(mDisplayId);
msg.append(" }");
return msg.toString();
}
@@ -2983,6 +3020,7 @@
private KeyEvent(Parcel in) {
mDeviceId = in.readInt();
mSource = in.readInt();
+ mDisplayId = in.readInt();
mAction = in.readInt();
mKeyCode = in.readInt();
mRepeatCount = in.readInt();
@@ -2999,6 +3037,7 @@
out.writeInt(mDeviceId);
out.writeInt(mSource);
+ out.writeInt(mDisplayId);
out.writeInt(mAction);
out.writeInt(mKeyCode);
out.writeInt(mRepeatCount);
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index 9148c27..344806a 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -1943,11 +1943,13 @@
}
/** @hide */
+ @Override
public int getDisplayId() {
return nativeGetDisplayId(mNativePtr);
}
/** @hide */
+ @Override
public void setDisplayId(int displayId) {
nativeSetDisplayId(mNativePtr, displayId);
}
diff --git a/core/java/android/view/RenderNode.java b/core/java/android/view/RenderNode.java
index 7c25fac..e0df59f 100644
--- a/core/java/android/view/RenderNode.java
+++ b/core/java/android/view/RenderNode.java
@@ -16,6 +16,7 @@
package android.view;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.Matrix;
@@ -29,6 +30,9 @@
import libcore.util.NativeAllocationRegistry;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* <p>A display list records a series of graphics related operations and can replay
* them later. Display lists are usually built by recording operations on a
@@ -139,7 +143,9 @@
RenderNode.class.getClassLoader(), nGetNativeFinalizer(), 1024);
}
- // Do not access directly unless you are ThreadedRenderer
+ /** Not for general use; use only if you are ThreadedRenderer or DisplayListCanvas.
+ * @hide
+ */
final long mNativeRenderNode;
private final View mOwningView;
@@ -159,15 +165,6 @@
}
/**
- * Immediately destroys the RenderNode
- * Only suitable for testing/benchmarking where waiting for the GC/finalizer
- * is not feasible.
- */
- public void destroy() {
- // TODO: Removed temporarily
- }
-
- /**
* Creates a new RenderNode that can be used to record batches of
* drawing operations, and store / apply render properties when drawn.
*
@@ -219,6 +216,14 @@
}
/**
+ * Same as {@link #start(int, int)} but with the RenderNode's width & height
+ */
+ public DisplayListCanvas start() {
+ return DisplayListCanvas.obtain(this,
+ nGetWidth(mNativeRenderNode), nGetHeight(mNativeRenderNode));
+ }
+
+ /**
* Ends the recording for this display list. A display list cannot be
* replayed if recording is not finished. Calling this method marks
* the display list valid and {@link #isValid()} will return true.
@@ -251,13 +256,6 @@
return nIsValid(mNativeRenderNode);
}
- long getNativeDisplayList() {
- if (!isValid()) {
- throw new IllegalStateException("The display list is not valid.");
- }
- return mNativeRenderNode;
- }
-
///////////////////////////////////////////////////////////////////////////
// Matrix manipulation
///////////////////////////////////////////////////////////////////////////
@@ -455,6 +453,25 @@
return nSetHasOverlappingRendering(mNativeRenderNode, hasOverlappingRendering);
}
+ /** @hide */
+ @IntDef({USAGE_BACKGROUND})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface UsageHint {}
+
+ /** The default usage hint */
+ public static final int USAGE_UNKNOWN = 0;
+
+ /** Usage is background content */
+ public static final int USAGE_BACKGROUND = 1;
+
+ /**
+ * Provides a hint on what this RenderNode's display list content contains. This hint is used
+ * for automatic content transforms to improve accessibility or similar.
+ */
+ public void setUsageHint(@UsageHint int usageHint) {
+ nSetUsageHint(mNativeRenderNode, usageHint);
+ }
+
/**
* Indicates whether the content of this display list overlaps.
*
@@ -463,7 +480,6 @@
* @see #setHasOverlappingRendering(boolean)
*/
public boolean hasOverlappingRendering() {
- //noinspection SimplifiableIfStatement
return nHasOverlappingRendering(mNativeRenderNode);
}
@@ -955,6 +971,8 @@
private static native boolean nSetHasOverlappingRendering(long renderNode,
boolean hasOverlappingRendering);
@CriticalNative
+ private static native void nSetUsageHint(long renderNode, int usageHint);
+ @CriticalNative
private static native boolean nSetElevation(long renderNode, float lift);
@CriticalNative
private static native boolean nSetTranslationX(long renderNode, float translationX);
@@ -1009,4 +1027,8 @@
private static native float nGetPivotX(long renderNode);
@CriticalNative
private static native float nGetPivotY(long renderNode);
+ @CriticalNative
+ private static native int nGetWidth(long renderNode);
+ @CriticalNative
+ private static native int nGetHeight(long renderNode);
}
diff --git a/core/java/android/view/RoundScrollbarRenderer.java b/core/java/android/view/RoundScrollbarRenderer.java
index 1348510..df9e23e 100644
--- a/core/java/android/view/RoundScrollbarRenderer.java
+++ b/core/java/android/view/RoundScrollbarRenderer.java
@@ -31,13 +31,14 @@
private static final int MAX_SCROLLBAR_ANGLE_SWIPE = 16;
private static final int MIN_SCROLLBAR_ANGLE_SWIPE = 6;
private static final float WIDTH_PERCENTAGE = 0.02f;
- private static final int DEFAULT_THUMB_COLOR = 0x4CFFFFFF;
- private static final int DEFAULT_TRACK_COLOR = 0x26FFFFFF;
+ private static final int DEFAULT_THUMB_COLOR = 0xFFE8EAED;
+ private static final int DEFAULT_TRACK_COLOR = 0x4CFFFFFF;
private final Paint mThumbPaint = new Paint();
private final Paint mTrackPaint = new Paint();
private final RectF mRect = new RectF();
private final View mParent;
+ private final int mMaskThickness;
public RoundScrollbarRenderer(View parent) {
// Paints for the round scrollbar.
@@ -52,6 +53,12 @@
mTrackPaint.setStyle(Paint.Style.STROKE);
mParent = parent;
+
+ // Fetch the resource indicating the thickness of CircularDisplayMask, rounding in the same
+ // way WindowManagerService.showCircularMask does. The scroll bar is inset by this amount so
+ // that it doesn't get clipped.
+ mMaskThickness = parent.getContext().getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.circular_display_mask_thickness);
}
public void drawRoundScrollbars(Canvas canvas, float alpha, Rect bounds) {
@@ -82,13 +89,13 @@
startAngle = clamp(startAngle, -SCROLLBAR_ANGLE_RANGE / 2,
SCROLLBAR_ANGLE_RANGE / 2 - sweepAngle);
- // Draw the track and the scroll bar.
+ // Draw the track and the thumb.
+ float inset = thumbWidth / 2 + mMaskThickness;
mRect.set(
- bounds.left - thumbWidth / 2,
- bounds.top,
- bounds.right - thumbWidth / 2,
- bounds.bottom);
-
+ bounds.left + inset,
+ bounds.top + inset,
+ bounds.right - inset,
+ bounds.bottom - inset);
canvas.drawArc(mRect, -SCROLLBAR_ANGLE_RANGE / 2, SCROLLBAR_ANGLE_RANGE, false,
mTrackPaint);
canvas.drawArc(mRect, startAngle, sweepAngle, false, mThumbPaint);
diff --git a/core/java/android/view/SurfaceSession.java b/core/java/android/view/SurfaceSession.java
index b5912bc..ee08bf7 100644
--- a/core/java/android/view/SurfaceSession.java
+++ b/core/java/android/view/SurfaceSession.java
@@ -37,7 +37,12 @@
}
public SurfaceSession(Surface root) {
- mNativeClient = nativeCreateScoped(root.mNativeObject);
+ synchronized (root.mLock) {
+ if (root.mNativeObject == 0) {
+ throw new IllegalStateException("Surface is not initialized or has been released");
+ }
+ mNativeClient = nativeCreateScoped(root.mNativeObject);
+ }
}
/* no user serviceable parts here ... */
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index aa1e407..2930699 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -850,7 +850,9 @@
void buildLayer(RenderNode node) {
- nBuildLayer(mNativeProxy, node.getNativeDisplayList());
+ if (node.isValid()) {
+ nBuildLayer(mNativeProxy, node.mNativeRenderNode);
+ }
}
@@ -928,7 +930,7 @@
* not the RenderNode from a View.
**/
public static Bitmap createHardwareBitmap(RenderNode node, int width, int height) {
- return nCreateHardwareBitmap(node.getNativeDisplayList(), width, height);
+ return nCreateHardwareBitmap(node.mNativeRenderNode, width, height);
}
/**
@@ -953,6 +955,10 @@
nSetDebuggingEnabled(enable);
}
+ void allocateBuffers(Surface surface) {
+ nAllocateBuffers(mNativeProxy, surface);
+ }
+
@Override
protected void finalize() throws Throwable {
try {
@@ -1241,4 +1247,5 @@
private static native void nSetDebuggingEnabled(boolean enabled);
private static native void nSetIsolatedProcess(boolean enabled);
private static native void nSetContextPriority(int priority);
+ private static native void nAllocateBuffers(long nativeProxy, Surface window);
}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 964ee5e..e2e2b00 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -6119,14 +6119,18 @@
}
x += getScrollX();
y += getScrollY();
- if (isVerticalScrollBarEnabled() && !isVerticalScrollBarHidden()) {
+ final boolean canScrollVertically =
+ computeVerticalScrollRange() > computeVerticalScrollExtent();
+ if (isVerticalScrollBarEnabled() && !isVerticalScrollBarHidden() && canScrollVertically) {
final Rect touchBounds = mScrollCache.mScrollBarTouchBounds;
getVerticalScrollBarBounds(null, touchBounds);
if (touchBounds.contains((int) x, (int) y)) {
return true;
}
}
- if (isHorizontalScrollBarEnabled()) {
+ final boolean canScrollHorizontally =
+ computeHorizontalScrollRange() > computeHorizontalScrollExtent();
+ if (isHorizontalScrollBarEnabled() && canScrollHorizontally) {
final Rect touchBounds = mScrollCache.mScrollBarTouchBounds;
getHorizontalScrollBarBounds(null, touchBounds);
if (touchBounds.contains((int) x, (int) y)) {
@@ -6141,18 +6145,18 @@
}
private boolean isOnVerticalScrollbarThumb(float x, float y) {
- if (mScrollCache == null) {
+ if (mScrollCache == null || !isVerticalScrollBarEnabled() || isVerticalScrollBarHidden()) {
return false;
}
- if (isVerticalScrollBarEnabled() && !isVerticalScrollBarHidden()) {
+ final int range = computeVerticalScrollRange();
+ final int extent = computeVerticalScrollExtent();
+ if (range > extent) {
x += getScrollX();
y += getScrollY();
final Rect bounds = mScrollCache.mScrollBarBounds;
final Rect touchBounds = mScrollCache.mScrollBarTouchBounds;
getVerticalScrollBarBounds(bounds, touchBounds);
- final int range = computeVerticalScrollRange();
final int offset = computeVerticalScrollOffset();
- final int extent = computeVerticalScrollExtent();
final int thumbLength = ScrollBarUtils.getThumbLength(bounds.height(), bounds.width(),
extent, range);
final int thumbOffset = ScrollBarUtils.getThumbOffset(bounds.height(), thumbLength,
@@ -6168,18 +6172,19 @@
}
private boolean isOnHorizontalScrollbarThumb(float x, float y) {
- if (mScrollCache == null) {
+ if (mScrollCache == null || !isHorizontalScrollBarEnabled()) {
return false;
}
- if (isHorizontalScrollBarEnabled()) {
+ final int range = computeHorizontalScrollRange();
+ final int extent = computeHorizontalScrollExtent();
+ if (range > extent) {
x += getScrollX();
y += getScrollY();
final Rect bounds = mScrollCache.mScrollBarBounds;
final Rect touchBounds = mScrollCache.mScrollBarTouchBounds;
getHorizontalScrollBarBounds(bounds, touchBounds);
- final int range = computeHorizontalScrollRange();
final int offset = computeHorizontalScrollOffset();
- final int extent = computeHorizontalScrollExtent();
+
final int thumbLength = ScrollBarUtils.getThumbLength(bounds.width(), bounds.height(),
extent, range);
final int thumbOffset = ScrollBarUtils.getThumbOffset(bounds.width(), thumbLength,
@@ -7542,7 +7547,7 @@
*/
public void onPopulateAccessibilityEventInternal(AccessibilityEvent event) {
if ((event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED)
- && !TextUtils.isEmpty(getAccessibilityPaneTitle())) {
+ && isAccessibilityPane()) {
event.getText().add(getAccessibilityPaneTitle());
}
}
@@ -12958,7 +12963,7 @@
}
}
}
- if (!TextUtils.isEmpty(getAccessibilityPaneTitle())) {
+ if (isAccessibilityPane()) {
if (isVisible != oldVisible) {
notifyViewAccessibilityStateChangedIfNeeded(isVisible
? AccessibilityEvent.CONTENT_CHANGE_TYPE_PANE_APPEARED
@@ -20437,6 +20442,7 @@
private RenderNode getDrawableRenderNode(Drawable drawable, RenderNode renderNode) {
if (renderNode == null) {
renderNode = RenderNode.create(drawable.getClass().getName(), this);
+ renderNode.setUsageHint(RenderNode.USAGE_BACKGROUND);
}
final Rect bounds = drawable.getBounds();
@@ -23993,6 +23999,10 @@
Log.w(VIEW_LOG_TAG, "startDragAndDrop called on a detached view.");
return false;
}
+ if (!mAttachInfo.mViewRootImpl.mSurface.isValid()) {
+ Log.w(VIEW_LOG_TAG, "startDragAndDrop called with an invalid surface.");
+ return false;
+ }
if (data != null) {
data.prepareToLeaveProcess((flags & View.DRAG_FLAG_GLOBAL) != 0);
diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java
index 276f50a..e4c595b 100644
--- a/core/java/android/view/ViewDebug.java
+++ b/core/java/android/view/ViewDebug.java
@@ -531,7 +531,6 @@
throws IOException {
RenderNode node = RenderNode.create("ViewDebug", null);
profileViewAndChildren(view, node, out, true);
- node.destroy();
}
private static void profileViewAndChildren(View view, RenderNode node, BufferedWriter out,
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 7944319..d0539ae 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -4652,7 +4652,7 @@
* which is added in order to fade it out in its old location should be removed
* once the animation is complete.</p>
*
- * @param view The view to be added
+ * @param view The view to be added. The view must not have a parent.
* @param index The index at which this view should be drawn, must be >= 0.
* This value is relative to the {@link #getChildAt(int) index} values in the normal
* child list of this container, where any transient view at a particular index will
@@ -4661,9 +4661,14 @@
* @hide
*/
public void addTransientView(View view, int index) {
- if (index < 0) {
+ if (index < 0 || view == null) {
return;
}
+ if (view.mParent != null) {
+ throw new IllegalStateException("The specified view already has a parent "
+ + view.mParent);
+ }
+
if (mTransientIndices == null) {
mTransientIndices = new ArrayList<Integer>();
mTransientViews = new ArrayList<View>();
@@ -4683,7 +4688,9 @@
mTransientViews.add(view);
}
view.mParent = this;
- view.dispatchAttachedToWindow(mAttachInfo, (mViewFlags&VISIBILITY_MASK));
+ if (mAttachInfo != null) {
+ view.dispatchAttachedToWindow(mAttachInfo, (mViewFlags & VISIBILITY_MASK));
+ }
invalidate(true);
}
@@ -4705,7 +4712,9 @@
mTransientViews.remove(i);
mTransientIndices.remove(i);
view.mParent = null;
- view.dispatchDetachedFromWindow();
+ if (view.mAttachInfo != null) {
+ view.dispatchDetachedFromWindow();
+ }
invalidate(true);
return;
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index dd24185..240f3c0 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1338,6 +1338,7 @@
renderer.setStopped(mStopped);
}
if (!mStopped) {
+ mNewSurfaceNeeded = true;
scheduleTraversals();
} else {
if (renderer != null) {
@@ -2100,7 +2101,7 @@
& View.PFLAG_REQUEST_TRANSPARENT_REGIONS) == 0) {
// Don't pre-allocate if transparent regions
// are requested as they may not be needed
- mSurface.allocateBuffers();
+ mAttachInfo.mThreadedRenderer.allocateBuffers(mSurface);
}
} catch (OutOfResourcesException e) {
handleOutOfResourcesException(e);
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 4d7b73c..8c7ac73 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -1692,6 +1692,15 @@
public static final int PRIVATE_FLAG_IS_SCREEN_DECOR = 0x00400000;
/**
+ * Flag to indicate that the status bar window is now in an explicit expanded state, meaning
+ * that status bar will not be hidden by any window with flag {@link #FLAG_FULLSCREEN} or
+ * {@link View#SYSTEM_UI_FLAG_FULLSCREEN} set.
+ * This can only be set by {@link LayoutParams#TYPE_STATUS_BAR}.
+ * @hide
+ */
+ public static final int PRIVATE_FLAG_STATUS_BAR_EXPANDED = 0x00800000;
+
+ /**
* Control flags that are private to the platform.
* @hide
*/
@@ -1779,7 +1788,11 @@
@ViewDebug.FlagToString(
mask = PRIVATE_FLAG_IS_SCREEN_DECOR,
equals = PRIVATE_FLAG_IS_SCREEN_DECOR,
- name = "IS_SCREEN_DECOR")
+ name = "IS_SCREEN_DECOR"),
+ @ViewDebug.FlagToString(
+ mask = PRIVATE_FLAG_STATUS_BAR_EXPANDED,
+ equals = PRIVATE_FLAG_STATUS_BAR_EXPANDED,
+ name = "STATUS_BAR_EXPANDED")
})
@TestApi
public int privateFlags;
diff --git a/core/java/android/view/accessibility/AccessibilityCache.java b/core/java/android/view/accessibility/AccessibilityCache.java
index da5a1cd..0e1e379 100644
--- a/core/java/android/view/accessibility/AccessibilityCache.java
+++ b/core/java/android/view/accessibility/AccessibilityCache.java
@@ -418,20 +418,28 @@
*
* @param nodes The nodes in the hosting window.
* @param rootNodeId The id of the root to evict.
+ *
+ * @return {@code true} if the cache was cleared
*/
- private void clearSubTreeRecursiveLocked(LongSparseArray<AccessibilityNodeInfo> nodes,
+ private boolean clearSubTreeRecursiveLocked(LongSparseArray<AccessibilityNodeInfo> nodes,
long rootNodeId) {
AccessibilityNodeInfo current = nodes.get(rootNodeId);
if (current == null) {
- return;
+ // The node isn't in the cache, but its descendents might be.
+ clear();
+ return true;
}
nodes.remove(rootNodeId);
final int childCount = current.getChildCount();
for (int i = 0; i < childCount; i++) {
final long childNodeId = current.getChildId(i);
- clearSubTreeRecursiveLocked(nodes, childNodeId);
+ if (clearSubTreeRecursiveLocked(nodes, childNodeId)) {
+ current.recycle();
+ return true;
+ }
}
current.recycle();
+ return false;
}
/**
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index e8e6537..eee3630 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -601,22 +601,14 @@
private static final int BOOLEAN_PROPERTY_CHECKED = 0x00000002;
- private static final int BOOLEAN_PROPERTY_FOCUSABLE = 0x00000004;
-
private static final int BOOLEAN_PROPERTY_FOCUSED = 0x00000008;
private static final int BOOLEAN_PROPERTY_SELECTED = 0x00000010;
- private static final int BOOLEAN_PROPERTY_CLICKABLE = 0x00000020;
-
- private static final int BOOLEAN_PROPERTY_LONG_CLICKABLE = 0x00000040;
-
private static final int BOOLEAN_PROPERTY_ENABLED = 0x00000080;
private static final int BOOLEAN_PROPERTY_PASSWORD = 0x00000100;
- private static final int BOOLEAN_PROPERTY_SCROLLABLE = 0x00000200;
-
private static final int BOOLEAN_PROPERTY_ACCESSIBILITY_FOCUSED = 0x00000400;
private static final int BOOLEAN_PROPERTY_VISIBLE_TO_USER = 0x00000800;
@@ -631,8 +623,6 @@
private static final int BOOLEAN_PROPERTY_CONTENT_INVALID = 0x00010000;
- private static final int BOOLEAN_PROPERTY_CONTEXT_CLICKABLE = 0x00020000;
-
private static final int BOOLEAN_PROPERTY_IMPORTANCE = 0x0040000;
private static final int BOOLEAN_PROPERTY_SCREEN_READER_FOCUSABLE = 0x0080000;
@@ -1168,6 +1158,10 @@
mActions.add(action);
}
+ private boolean hasActionWithId(int actionId) {
+ return getActionList().stream().anyMatch(action -> action.getId() == actionId);
+ }
+
/**
* Adds an action that can be performed on the node.
* <p>
@@ -1767,7 +1761,7 @@
* @return True if the node is focusable.
*/
public boolean isFocusable() {
- return getBooleanProperty(BOOLEAN_PROPERTY_FOCUSABLE);
+ return hasActionWithId(ACTION_FOCUS);
}
/**
@@ -1781,10 +1775,11 @@
* @param focusable True if the node is focusable.
*
* @throws IllegalStateException If called from an AccessibilityService.
+ * @deprecated Use {@link #addAction(AccessibilityAction)}
+ * with {@link AccessibilityAction#ACTION_FOCUS}
*/
- public void setFocusable(boolean focusable) {
- setBooleanProperty(BOOLEAN_PROPERTY_FOCUSABLE, focusable);
- }
+ @Deprecated
+ public void setFocusable(boolean focusable) { }
/**
* Gets whether this node is focused.
@@ -1892,7 +1887,7 @@
* @return True if the node is clickable.
*/
public boolean isClickable() {
- return getBooleanProperty(BOOLEAN_PROPERTY_CLICKABLE);
+ return hasActionWithId(ACTION_CLICK);
}
/**
@@ -1906,10 +1901,11 @@
* @param clickable True if the node is clickable.
*
* @throws IllegalStateException If called from an AccessibilityService.
+ * @deprecated Use {@link #addAction(AccessibilityAction)}
+ * with {@link AccessibilityAction#ACTION_CLICK}
*/
- public void setClickable(boolean clickable) {
- setBooleanProperty(BOOLEAN_PROPERTY_CLICKABLE, clickable);
- }
+ @Deprecated
+ public void setClickable(boolean clickable) { }
/**
* Gets whether this node is long clickable.
@@ -1917,7 +1913,7 @@
* @return True if the node is long clickable.
*/
public boolean isLongClickable() {
- return getBooleanProperty(BOOLEAN_PROPERTY_LONG_CLICKABLE);
+ return hasActionWithId(ACTION_LONG_CLICK);
}
/**
@@ -1931,10 +1927,11 @@
* @param longClickable True if the node is long clickable.
*
* @throws IllegalStateException If called from an AccessibilityService.
+ * @deprecated Use {@link #addAction(AccessibilityAction)}
+ * with {@link AccessibilityAction#ACTION_LONG_CLICK}
*/
- public void setLongClickable(boolean longClickable) {
- setBooleanProperty(BOOLEAN_PROPERTY_LONG_CLICKABLE, longClickable);
- }
+ @Deprecated
+ public void setLongClickable(boolean longClickable) { }
/**
* Gets whether this node is enabled.
@@ -1992,7 +1989,13 @@
* @return True if the node is scrollable, false otherwise.
*/
public boolean isScrollable() {
- return getBooleanProperty(BOOLEAN_PROPERTY_SCROLLABLE);
+ return hasActionWithId(ACTION_SCROLL_BACKWARD)
+ || hasActionWithId(ACTION_SCROLL_FORWARD)
+ || hasActionWithId(R.id.accessibilityActionScrollToPosition)
+ || hasActionWithId(R.id.accessibilityActionScrollUp)
+ || hasActionWithId(R.id.accessibilityActionScrollDown)
+ || hasActionWithId(R.id.accessibilityActionScrollLeft)
+ || hasActionWithId(R.id.accessibilityActionScrollRight);
}
/**
@@ -2006,9 +2009,11 @@
* @param scrollable True if the node is scrollable, false otherwise.
*
* @throws IllegalStateException If called from an AccessibilityService.
+ * @deprecated Use {@link #addAction(AccessibilityAction)}
*/
+ @Deprecated
+
public void setScrollable(boolean scrollable) {
- setBooleanProperty(BOOLEAN_PROPERTY_SCROLLABLE, scrollable);
}
/**
@@ -2199,7 +2204,7 @@
* @return True if the node is context clickable.
*/
public boolean isContextClickable() {
- return getBooleanProperty(BOOLEAN_PROPERTY_CONTEXT_CLICKABLE);
+ return hasActionWithId(R.id.accessibilityActionContextClick);
}
/**
@@ -2212,10 +2217,11 @@
*
* @param contextClickable True if the node is context clickable.
* @throws IllegalStateException If called from an AccessibilityService.
+ * @deprecated Use {@link #addAction(AccessibilityAction)}
+ * with {@link AccessibilityAction#ACTION_CONTEXT_CLICK}
*/
- public void setContextClickable(boolean contextClickable) {
- setBooleanProperty(BOOLEAN_PROPERTY_CONTEXT_CLICKABLE, contextClickable);
- }
+ @Deprecated
+ public void setContextClickable(boolean contextClickable) { }
/**
* Gets the node's live region mode.
@@ -2309,7 +2315,7 @@
* @return If the node can be dismissed.
*/
public boolean isDismissable() {
- return getBooleanProperty(BOOLEAN_PROPERTY_DISMISSABLE);
+ return hasActionWithId(ACTION_DISMISS);
}
/**
@@ -2321,10 +2327,11 @@
* </p>
*
* @param dismissable If the node can be dismissed.
+ * @deprecated Use {@link #addAction(AccessibilityAction)}
+ * with {@link AccessibilityAction#ACTION_DISMISS}
*/
- public void setDismissable(boolean dismissable) {
- setBooleanProperty(BOOLEAN_PROPERTY_DISMISSABLE, dismissable);
- }
+ @Deprecated
+ public void setDismissable(boolean dismissable) { }
/**
* Returns whether the node originates from a view considered important for accessibility.
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 8f28102..41daf9e 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -57,6 +57,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.internal.os.IResultReceiver;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.Preconditions;
@@ -72,6 +73,8 @@
import java.util.Collections;
import java.util.List;
import java.util.Objects;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
//TODO: use java.lang.ref.Cleaner once Android supports Java 9
import sun.misc.Cleaner;
@@ -572,10 +575,11 @@
final AutofillClient client = getClient();
if (client != null) {
+ final SyncResultReceiver receiver = new SyncResultReceiver();
try {
- final boolean sessionWasRestored = mService.restoreSession(mSessionId,
- client.autofillClientGetActivityToken(),
- mServiceClient.asBinder());
+ mService.restoreSession(mSessionId, client.autofillClientGetActivityToken(),
+ mServiceClient.asBinder(), receiver);
+ final boolean sessionWasRestored = receiver.getIntResult() == 1;
if (!sessionWasRestored) {
Log.w(TAG, "Session " + mSessionId + " could not be restored");
@@ -691,7 +695,9 @@
*/
@Nullable public FillEventHistory getFillEventHistory() {
try {
- return mService.getFillEventHistory();
+ final SyncResultReceiver receiver = new SyncResultReceiver();
+ mService.getFillEventHistory(receiver);
+ return receiver.getObjectResult(SyncResultReceiver.TYPE_PARCELABLE);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
return null;
@@ -1242,8 +1248,10 @@
public boolean hasEnabledAutofillServices() {
if (mService == null) return false;
+ final SyncResultReceiver receiver = new SyncResultReceiver();
try {
- return mService.isServiceEnabled(mContext.getUserId(), mContext.getPackageName());
+ mService.isServiceEnabled(mContext.getUserId(), mContext.getPackageName(), receiver);
+ return receiver.getIntResult() == 1;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1257,8 +1265,10 @@
public ComponentName getAutofillServiceComponentName() {
if (mService == null) return null;
+ final SyncResultReceiver receiver = new SyncResultReceiver();
try {
- return mService.getAutofillServiceComponentName();
+ mService.getAutofillServiceComponentName(receiver);
+ return receiver.getObjectResult(SyncResultReceiver.TYPE_PARCELABLE);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1281,7 +1291,9 @@
*/
@Nullable public String getUserDataId() {
try {
- return mService.getUserDataId();
+ final SyncResultReceiver receiver = new SyncResultReceiver();
+ mService.getUserDataId(receiver);
+ return receiver.getObjectResult(SyncResultReceiver.TYPE_STRING);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
return null;
@@ -1301,7 +1313,9 @@
*/
@Nullable public UserData getUserData() {
try {
- return mService.getUserData();
+ final SyncResultReceiver receiver = new SyncResultReceiver();
+ mService.getUserData(receiver);
+ return receiver.getObjectResult(SyncResultReceiver.TYPE_PARCELABLE);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
return null;
@@ -1337,8 +1351,10 @@
* the user.
*/
public boolean isFieldClassificationEnabled() {
+ final SyncResultReceiver receiver = new SyncResultReceiver();
try {
- return mService.isFieldClassificationEnabled();
+ mService.isFieldClassificationEnabled(receiver);
+ return receiver.getIntResult() == 1;
} catch (RemoteException e) {
e.rethrowFromSystemServer();
return false;
@@ -1358,8 +1374,10 @@
*/
@Nullable
public String getDefaultFieldClassificationAlgorithm() {
+ final SyncResultReceiver receiver = new SyncResultReceiver();
try {
- return mService.getDefaultFieldClassificationAlgorithm();
+ mService.getDefaultFieldClassificationAlgorithm(receiver);
+ return receiver.getObjectResult(SyncResultReceiver.TYPE_STRING);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
return null;
@@ -1376,9 +1394,10 @@
*/
@NonNull
public List<String> getAvailableFieldClassificationAlgorithms() {
- final String[] algorithms;
+ final SyncResultReceiver receiver = new SyncResultReceiver();
try {
- algorithms = mService.getAvailableFieldClassificationAlgorithms();
+ mService.getAvailableFieldClassificationAlgorithms(receiver);
+ final String[] algorithms = receiver.getObjectResult(SyncResultReceiver.TYPE_STRING);
return algorithms != null ? Arrays.asList(algorithms) : Collections.emptyList();
} catch (RemoteException e) {
e.rethrowFromSystemServer();
@@ -1399,8 +1418,10 @@
public boolean isAutofillSupported() {
if (mService == null) return false;
+ final SyncResultReceiver receiver = new SyncResultReceiver();
try {
- return mService.isServiceSupported(mContext.getUserId());
+ mService.isServiceSupported(mContext.getUserId(), receiver);
+ return receiver.getIntResult() == 1;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1521,10 +1542,12 @@
final AutofillClient client = getClient();
if (client == null) return; // NOTE: getClient() already logged it..
- mSessionId = mService.startSession(client.autofillClientGetActivityToken(),
+ final SyncResultReceiver receiver = new SyncResultReceiver();
+ mService.startSession(client.autofillClientGetActivityToken(),
mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(),
mCallback != null, flags, client.autofillClientGetComponentName(),
- isCompatibilityModeEnabledLocked());
+ isCompatibilityModeEnabledLocked(), receiver);
+ mSessionId = receiver.getIntResult();
if (mSessionId != NO_SESSION) {
mState = STATE_ACTIVE;
}
@@ -1602,7 +1625,9 @@
mServiceClient = new AutofillManagerClient(this);
try {
final int userId = mContext.getUserId();
- final int flags = mService.addClient(mServiceClient, userId);
+ final SyncResultReceiver receiver = new SyncResultReceiver();
+ mService.addClient(mServiceClient, userId, receiver);
+ final int flags = receiver.getIntResult();
mEnabled = (flags & FLAG_ADD_CLIENT_ENABLED) != 0;
sDebug = (flags & FLAG_ADD_CLIENT_DEBUG) != 0;
sVerbose = (flags & FLAG_ADD_CLIENT_VERBOSE) != 0;
@@ -1923,7 +1948,7 @@
mFillableIds.add(id);
}
if (sVerbose) {
- Log.v(TAG, "setTrackedViews(): fillableIds=" + fillableIds
+ Log.v(TAG, "setTrackedViews(): fillableIds=" + Arrays.toString(fillableIds)
+ ", mFillableIds" + mFillableIds);
}
}
@@ -2818,4 +2843,104 @@
}
}
}
+
+ /**
+ * @hide
+ */
+ public static final class SyncResultReceiver extends IResultReceiver.Stub {
+
+ private static final String EXTRA = "EXTRA";
+
+ /**
+ * How long to block waiting for {@link IResultReceiver} callbacks when calling server.
+ */
+ private static final long BINDER_TIMEOUT_MS = 5000;
+
+ private static final int TYPE_STRING = 0;
+ private static final int TYPE_STRING_ARRAY = 1;
+ private static final int TYPE_PARCELABLE = 2;
+
+ private final CountDownLatch mLatch = new CountDownLatch(1);
+ private int mResult;
+ private Bundle mBundle;
+
+ private void waitResult() {
+ try {
+ if (!mLatch.await(BINDER_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+ throw new IllegalStateException("Not called in " + BINDER_TIMEOUT_MS + "ms");
+ }
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+ }
+
+ /**
+ * Gets the result from an operation that returns an {@code int}.
+ */
+ int getIntResult() {
+ waitResult();
+ return mResult;
+ }
+
+ /**
+ * Gets the result from an operation that returns an {@code Object}.
+ *
+ * @param type type of expected object.
+ */
+ @Nullable
+ @SuppressWarnings("unchecked")
+ <T> T getObjectResult(int type) {
+ waitResult();
+ if (mBundle == null) {
+ return null;
+ }
+ switch (type) {
+ case TYPE_STRING:
+ return (T) mBundle.getString(EXTRA);
+ case TYPE_STRING_ARRAY:
+ return (T) mBundle.getString(EXTRA);
+ case TYPE_PARCELABLE:
+ return (T) mBundle.getParcelable(EXTRA);
+ default:
+ throw new IllegalArgumentException("unsupported type: " + type);
+ }
+ }
+
+ @Override
+ public void send(int resultCode, Bundle resultData) {
+ mResult = resultCode;
+ mBundle = resultData;
+ mLatch.countDown();
+ }
+
+ /**
+ * Creates a bundle for a {@code String} value.
+ */
+ @NonNull
+ public static Bundle bundleFor(@Nullable String value) {
+ final Bundle bundle = new Bundle();
+ bundle.putString(EXTRA, value);
+ return bundle;
+ }
+
+ /**
+ * Creates a bundle for a {@code String[]} value.
+ */
+ @NonNull
+ public static Bundle bundleFor(@Nullable String[] value) {
+ final Bundle bundle = new Bundle();
+ bundle.putStringArray(EXTRA, value);
+ return bundle;
+ }
+
+ /**
+ * Creates a bundle for a {@code Parcelable} value.
+ */
+ @NonNull
+ public static Bundle bundleFor(@Nullable Parcelable value) {
+ final Bundle bundle = new Bundle();
+ bundle.putParcelable(EXTRA, value);
+ return bundle;
+ }
+ }
}
diff --git a/core/java/android/view/autofill/IAutoFillManager.aidl b/core/java/android/view/autofill/IAutoFillManager.aidl
index 6b26f23..26aeba5 100644
--- a/core/java/android/view/autofill/IAutoFillManager.aidl
+++ b/core/java/android/view/autofill/IAutoFillManager.aidl
@@ -28,41 +28,39 @@
import android.view.autofill.AutofillId;
import android.view.autofill.AutofillValue;
import android.view.autofill.IAutoFillManagerClient;
+import com.android.internal.os.IResultReceiver;
/**
* Mediator between apps being auto-filled and auto-fill service implementations.
*
* {@hide}
*/
- // TODO(b/73536867) STOPSHIP : this whole interface should be either oneway or not, and we're
- // gradually converting the methods (as some of them return a value form the server and must be
- // refactored).
-interface IAutoFillManager {
+oneway interface IAutoFillManager {
// Returns flags: FLAG_ADD_CLIENT_ENABLED | FLAG_ADD_CLIENT_DEBUG | FLAG_ADD_CLIENT_VERBOSE
- int addClient(in IAutoFillManagerClient client, int userId);
+ void addClient(in IAutoFillManagerClient client, int userId, in IResultReceiver result);
void removeClient(in IAutoFillManagerClient client, int userId);
- int startSession(IBinder activityToken, in IBinder appCallback, in AutofillId autoFillId,
- in Rect bounds, in AutofillValue value, int userId, boolean hasCallback, int flags,
- in ComponentName componentName, boolean compatMode);
- FillEventHistory getFillEventHistory();
- boolean restoreSession(int sessionId, in IBinder activityToken, in IBinder appCallback);
- oneway void updateSession(int sessionId, in AutofillId id, in Rect bounds,
- in AutofillValue value, int action, int flags, int userId);
- oneway void setAutofillFailure(int sessionId, in List<AutofillId> ids, int userId);
- oneway void finishSession(int sessionId, int userId);
- oneway void cancelSession(int sessionId, int userId);
- oneway void setAuthenticationResult(in Bundle data, int sessionId, int authenticationId,
- int userId);
- oneway void setHasCallback(int sessionId, int userId, boolean hasIt);
+ void startSession(IBinder activityToken, in IBinder appCallback, in AutofillId autoFillId,
+ in Rect bounds, in AutofillValue value, int userId, boolean hasCallback, int flags,
+ in ComponentName componentName, boolean compatMode, in IResultReceiver result);
+ void getFillEventHistory(in IResultReceiver result);
+ void restoreSession(int sessionId, in IBinder activityToken, in IBinder appCallback,
+ in IResultReceiver result);
+ void updateSession(int sessionId, in AutofillId id, in Rect bounds,
+ in AutofillValue value, int action, int flags, int userId);
+ void setAutofillFailure(int sessionId, in List<AutofillId> ids, int userId);
+ void finishSession(int sessionId, int userId);
+ void cancelSession(int sessionId, int userId);
+ void setAuthenticationResult(in Bundle data, int sessionId, int authenticationId, int userId);
+ void setHasCallback(int sessionId, int userId, boolean hasIt);
void disableOwnedAutofillServices(int userId);
- boolean isServiceSupported(int userId);
- boolean isServiceEnabled(int userId, String packageName);
+ void isServiceSupported(int userId, in IResultReceiver result);
+ void isServiceEnabled(int userId, String packageName, in IResultReceiver result);
void onPendingSaveUi(int operation, IBinder token);
- UserData getUserData();
- String getUserDataId();
+ void getUserData(in IResultReceiver result);
+ void getUserDataId(in IResultReceiver result);
void setUserData(in UserData userData);
- boolean isFieldClassificationEnabled();
- ComponentName getAutofillServiceComponentName();
- String[] getAvailableFieldClassificationAlgorithms();
- String getDefaultFieldClassificationAlgorithm();
+ void isFieldClassificationEnabled(in IResultReceiver result);
+ void getAutofillServiceComponentName(in IResultReceiver result);
+ void getAvailableFieldClassificationAlgorithms(in IResultReceiver result);
+ void getDefaultFieldClassificationAlgorithm(in IResultReceiver result);
}
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 7e6af49..bb93af5 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -798,10 +798,18 @@
}
/** @hide */
- public void setImeWindowStatus(IBinder imeToken, IBinder startInputToken, int vis,
- int backDisposition) {
+ public void setImeWindowStatus(IBinder imeToken, int vis, int backDisposition) {
try {
- mService.setImeWindowStatus(imeToken, startInputToken, vis, backDisposition);
+ mService.setImeWindowStatus(imeToken, vis, backDisposition);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /** @hide */
+ public void reportStartInput(IBinder imeToken, IBinder startInputToken) {
+ try {
+ mService.reportStartInput(imeToken, startInputToken);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1239,7 +1247,7 @@
}
boolean startInputInner(@InputMethodClient.StartInputReason final int startInputReason,
- IBinder windowGainingFocus, int controlFlags, int softInputMode,
+ @Nullable IBinder windowGainingFocus, int controlFlags, int softInputMode,
int windowFlags) {
final View view;
synchronized (mH) {
@@ -1256,6 +1264,20 @@
}
}
+ if (windowGainingFocus == null) {
+ windowGainingFocus = view.getWindowToken();
+ if (windowGainingFocus == null) {
+ Log.e(TAG, "ABORT input: ServedView must be attached to a Window");
+ return false;
+ }
+ controlFlags |= CONTROL_WINDOW_VIEW_HAS_FOCUS;
+ if (view.onCheckIsTextEditor()) {
+ controlFlags |= CONTROL_WINDOW_IS_TEXT_EDITOR;
+ }
+ softInputMode = view.getViewRootImpl().mWindowAttributes.softInputMode;
+ windowFlags = view.getViewRootImpl().mWindowAttributes.flags;
+ }
+
// Now we need to get an input connection from the served view.
// This is complicated in a couple ways: we can't be holding our lock
// when calling out to the view, and we need to make sure we call into
diff --git a/core/java/android/view/textclassifier/SystemTextClassifier.java b/core/java/android/view/textclassifier/SystemTextClassifier.java
index 10191e0..16eb5af 100644
--- a/core/java/android/view/textclassifier/SystemTextClassifier.java
+++ b/core/java/android/view/textclassifier/SystemTextClassifier.java
@@ -30,6 +30,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting.Visibility;
+import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
import java.util.concurrent.CountDownLatch;
@@ -162,6 +163,17 @@
}
}
+ @Override
+ public void dump(@NonNull IndentingPrintWriter printWriter) {
+ printWriter.println("SystemTextClassifier:");
+ printWriter.increaseIndent();
+ printWriter.printPair("mFallback", mFallback);
+ printWriter.printPair("mPackageName", mPackageName);
+ printWriter.printPair("mSessionId", mSessionId);
+ printWriter.decreaseIndent();
+ printWriter.println();
+ }
+
/**
* Attempts to initialize a new classification session.
*
diff --git a/core/java/android/view/textclassifier/TextClassificationConstants.java b/core/java/android/view/textclassifier/TextClassificationConstants.java
index 21b5603..2fc7422 100644
--- a/core/java/android/view/textclassifier/TextClassificationConstants.java
+++ b/core/java/android/view/textclassifier/TextClassificationConstants.java
@@ -20,6 +20,8 @@
import android.util.KeyValueListParser;
import android.util.Slog;
+import com.android.internal.util.IndentingPrintWriter;
+
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@@ -241,4 +243,25 @@
private static List<String> parseEntityList(String listStr) {
return Collections.unmodifiableList(Arrays.asList(listStr.split(ENTITY_LIST_DELIMITER)));
}
+
+ void dump(IndentingPrintWriter pw) {
+ pw.println("TextClassificationConstants:");
+ pw.increaseIndent();
+ pw.printPair("isLocalTextClassifierEnabled", mLocalTextClassifierEnabled);
+ pw.printPair("isSystemTextClassifierEnabled", mSystemTextClassifierEnabled);
+ pw.printPair("isModelDarkLaunchEnabled", mModelDarkLaunchEnabled);
+ pw.printPair("isSmartSelectionEnabled", mSmartSelectionEnabled);
+ pw.printPair("isSmartTextShareEnabled", mSmartTextShareEnabled);
+ pw.printPair("isSmartLinkifyEnabled", mSmartLinkifyEnabled);
+ pw.printPair("isSmartSelectionAnimationEnabled", mSmartSelectionAnimationEnabled);
+ pw.printPair("getSuggestSelectionMaxRangeLength", mSuggestSelectionMaxRangeLength);
+ pw.printPair("getClassifyTextMaxRangeLength", mClassifyTextMaxRangeLength);
+ pw.printPair("getGenerateLinksMaxTextLength", mGenerateLinksMaxTextLength);
+ pw.printPair("getGenerateLinksLogSampleRate", mGenerateLinksLogSampleRate);
+ pw.printPair("getEntityListDefault", mEntityListDefault);
+ pw.printPair("getEntityListNotEditable", mEntityListNotEditable);
+ pw.printPair("getEntityListEditable", mEntityListEditable);
+ pw.decreaseIndent();
+ pw.println();
+ }
}
diff --git a/core/java/android/view/textclassifier/TextClassificationManager.java b/core/java/android/view/textclassifier/TextClassificationManager.java
index dc1194b..201218ba 100644
--- a/core/java/android/view/textclassifier/TextClassificationManager.java
+++ b/core/java/android/view/textclassifier/TextClassificationManager.java
@@ -27,6 +27,7 @@
import android.view.textclassifier.TextClassifier.TextClassifierType;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
import java.lang.ref.WeakReference;
@@ -246,6 +247,13 @@
: mContext;
}
+ /** @hide **/
+ public void dump(IndentingPrintWriter pw) {
+ getLocalTextClassifier().dump(pw);
+ getSystemTextClassifier().dump(pw);
+ getSettings().dump(pw);
+ }
+
/** @hide */
public static TextClassificationConstants getSettings(Context context) {
Preconditions.checkNotNull(context);
diff --git a/core/java/android/view/textclassifier/TextClassifier.java b/core/java/android/view/textclassifier/TextClassifier.java
index 24f531d..1505863 100644
--- a/core/java/android/view/textclassifier/TextClassifier.java
+++ b/core/java/android/view/textclassifier/TextClassifier.java
@@ -34,6 +34,7 @@
import android.util.ArrayMap;
import android.util.ArraySet;
+import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
import java.lang.annotation.Retention;
@@ -390,6 +391,11 @@
return false;
}
+ /** @hide **/
+ default void dump(@NonNull IndentingPrintWriter printWriter) {
+
+ }
+
/**
* Configuration object for specifying what entities to identify.
*
diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java
index 6e5751a..29339e9 100644
--- a/core/java/android/view/textclassifier/TextClassifierImpl.java
+++ b/core/java/android/view/textclassifier/TextClassifierImpl.java
@@ -41,6 +41,7 @@
import android.provider.ContactsContract;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
import java.io.File;
@@ -439,6 +440,24 @@
return builder.setId(createId(text, start, end)).build();
}
+ @Override
+ public void dump(@NonNull IndentingPrintWriter printWriter) {
+ synchronized (mLock) {
+ listAllModelsLocked();
+ printWriter.println("TextClassifierImpl:");
+ printWriter.increaseIndent();
+ printWriter.println("Model file(s):");
+ printWriter.increaseIndent();
+ for (ModelFile modelFile : mAllModelFiles) {
+ printWriter.println(modelFile.toString());
+ }
+ printWriter.decreaseIndent();
+ printWriter.printPair("mFallback", mFallback);
+ printWriter.decreaseIndent();
+ printWriter.println();
+ }
+ }
+
/**
* Closes the ParcelFileDescriptor and logs any errors that occur.
*/
diff --git a/core/java/android/webkit/WebBackForwardList.java b/core/java/android/webkit/WebBackForwardList.java
index 0c34e3c..4d3bbe4 100644
--- a/core/java/android/webkit/WebBackForwardList.java
+++ b/core/java/android/webkit/WebBackForwardList.java
@@ -57,7 +57,8 @@
/**
* Clone the entire object to be used in the UI thread by clients of
* WebView. This creates a copy that should never be modified by any of the
- * webkit package classes.
+ * webkit package classes. On Android 4.4 and later there is no need to use
+ * this, as the object is already a read-only copy of the internal state.
*/
protected abstract WebBackForwardList clone();
}
diff --git a/core/java/android/webkit/WebHistoryItem.java b/core/java/android/webkit/WebHistoryItem.java
index 74db039..b9e7042 100644
--- a/core/java/android/webkit/WebHistoryItem.java
+++ b/core/java/android/webkit/WebHistoryItem.java
@@ -23,7 +23,7 @@
/**
* A convenience class for accessing fields in an entry in the back/forward list
* of a WebView. Each WebHistoryItem is a snapshot of the requested history
- * item. Each history item may be updated during the load of a page.
+ * item.
* @see WebBackForwardList
*/
public abstract class WebHistoryItem implements Cloneable {
@@ -44,8 +44,6 @@
* history item. See getTargetUrl() for the url that is the actual target of
* this history item.
* @return The base url of this history item.
- * Note: The VM ensures 32-bit atomic read/write operations so we don't have
- * to synchronize this method.
*/
public abstract String getUrl();
@@ -60,22 +58,20 @@
/**
* Return the document title of this history item.
* @return The document title of this history item.
- * Note: The VM ensures 32-bit atomic read/write operations so we don't have
- * to synchronize this method.
*/
public abstract String getTitle();
/**
* Return the favicon of this history item or {@code null} if no favicon was found.
* @return A Bitmap containing the favicon for this history item or {@code null}.
- * Note: The VM ensures 32-bit atomic read/write operations so we don't have
- * to synchronize this method.
*/
@Nullable
public abstract Bitmap getFavicon();
/**
- * Clone the history item for use by clients of WebView.
+ * Clone the history item for use by clients of WebView. On Android 4.4 and later
+ * there is no need to use this, as the object is already a read-only copy of the
+ * internal state.
*/
protected abstract WebHistoryItem clone();
}
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index cba11a8..a085395 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -987,7 +987,9 @@
* {@link PluginState#OFF}.
*
* @param state a PluginState value
- * @deprecated Plugins will not be supported in future, and should not be used.
+ * @deprecated Plugins are not supported in API level
+ * {@link android.os.Build.VERSION_CODES#KITKAT} or later;
+ * enabling plugins is a no-op.
*/
@Deprecated
public abstract void setPluginState(PluginState state);
@@ -1182,7 +1184,9 @@
*
* @return the plugin state as a {@link PluginState} value
* @see #setPluginState
- * @deprecated Plugins will not be supported in future, and should not be used.
+ * @deprecated Plugins are not supported in API level
+ * {@link android.os.Build.VERSION_CODES#KITKAT} or later;
+ * enabling plugins is a no-op.
*/
@Deprecated
public abstract PluginState getPluginState();
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 4eb85ac..9573f48 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -41,7 +41,6 @@
import android.os.RemoteException;
import android.os.StrictMode;
import android.print.PrintDocumentAdapter;
-import android.security.KeyChain;
import android.util.AttributeSet;
import android.util.Log;
import android.util.SparseArray;
@@ -71,280 +70,25 @@
import java.util.Map;
/**
- * <p>A View that displays web pages. This class is the basis upon which you
- * can roll your own web browser or simply display some online content within your Activity.
- * It uses the WebKit rendering engine to display
- * web pages and includes methods to navigate forward and backward
- * through a history, zoom in and out, perform text searches and more.
- *
- * <p>Note that, in order for your Activity to access the Internet and load web pages
- * in a WebView, you must add the {@code INTERNET} permissions to your
- * Android Manifest file:
- *
- * <pre>
- * {@code <uses-permission android:name="android.permission.INTERNET" />}
- * </pre>
- *
- * <p>This must be a child of the <a
- * href="{@docRoot}guide/topics/manifest/manifest-element.html">{@code <manifest>}</a>
- * element.
- *
- * <p>For more information, read
- * <a href="{@docRoot}guide/webapps/webview.html">Building Web Apps in WebView</a>.
+ * A View that displays web pages.
*
* <h3>Basic usage</h3>
*
- * <p>By default, a WebView provides no browser-like widgets, does not
- * enable JavaScript and web page errors are ignored. If your goal is only
- * to display some HTML as a part of your UI, this is probably fine;
- * the user won't need to interact with the web page beyond reading
- * it, and the web page won't need to interact with the user. If you
- * actually want a full-blown web browser, then you probably want to
- * invoke the Browser application with a URL Intent rather than show it
- * with a WebView. For example:
- * <pre>
- * Uri uri = Uri.parse("https://www.example.com");
- * Intent intent = new Intent(Intent.ACTION_VIEW, uri);
- * startActivity(intent);
- * </pre>
- * <p>See {@link android.content.Intent} for more information.
*
- * <p>To provide a WebView in your own Activity, include a {@code <WebView>} in your layout,
- * or set the entire Activity window as a WebView during {@link
- * android.app.Activity#onCreate(Bundle) onCreate()}:
+ * <p>In most cases, we recommend using a standard web browser, like Chrome, to deliver
+ * content to the user. To learn more about web browsers, read the guide on
+ * <a href="/guide/components/intents-common#Browser">
+ * invoking a browser with an intent</a>.
*
- * <pre class="prettyprint">
- * WebView webview = new WebView(this);
- * setContentView(webview);
- * </pre>
+ * <p>WebView objects allow you to display web content as part of your activity layout, but
+ * lack some of the features of fully-developed browsers. A WebView is useful when
+ * you need increased control over the UI and advanced configuration options that will allow
+ * you to embed web pages in a specially-designed environment for your app.
*
- * <p>Then load the desired web page:
- *
- * <pre>
- * // Simplest usage: note that an exception will NOT be thrown
- * // if there is an error loading this page (see below).
- * webview.loadUrl("https://example.com/");
- *
- * // OR, you can also load from an HTML string:
- * String summary = "<html><body>You scored <b>192</b> points.</body></html>";
- * webview.loadData(summary, "text/html", null);
- * // ... although note that there are restrictions on what this HTML can do.
- * // See {@link #loadData(String,String,String)} and {@link
- * #loadDataWithBaseURL(String,String,String,String,String)} for more info.
- * // Also see {@link #loadData(String,String,String)} for information on encoding special
- * // characters.
- * </pre>
- *
- * <p>A WebView has several customization points where you can add your
- * own behavior. These are:
- *
- * <ul>
- * <li>Creating and setting a {@link android.webkit.WebChromeClient} subclass.
- * This class is called when something that might impact a
- * browser UI happens, for instance, progress updates and
- * JavaScript alerts are sent here (see <a
- * href="{@docRoot}guide/developing/debug-tasks.html#DebuggingWebPages">Debugging Tasks</a>).
- * </li>
- * <li>Creating and setting a {@link android.webkit.WebViewClient} subclass.
- * It will be called when things happen that impact the
- * rendering of the content, eg, errors or form submissions. You
- * can also intercept URL loading here (via {@link
- * android.webkit.WebViewClient#shouldOverrideUrlLoading(WebView,String)
- * shouldOverrideUrlLoading()}).</li>
- * <li>Modifying the {@link android.webkit.WebSettings}, such as
- * enabling JavaScript with {@link android.webkit.WebSettings#setJavaScriptEnabled(boolean)
- * setJavaScriptEnabled()}. </li>
- * <li>Injecting Java objects into the WebView using the
- * {@link android.webkit.WebView#addJavascriptInterface} method. This
- * method allows you to inject Java objects into a page's JavaScript
- * context, so that they can be accessed by JavaScript in the page.</li>
- * </ul>
- *
- * <p>Here's a more complicated example, showing error handling,
- * settings, and progress notification:
- *
- * <pre class="prettyprint">
- * // Let's display the progress in the activity title bar, like the
- * // browser app does.
- * getWindow().requestFeature(Window.FEATURE_PROGRESS);
- *
- * webview.getSettings().setJavaScriptEnabled(true);
- *
- * final Activity activity = this;
- * webview.setWebChromeClient(new WebChromeClient() {
- * public void onProgressChanged(WebView view, int progress) {
- * // Activities and WebViews measure progress with different scales.
- * // The progress meter will automatically disappear when we reach 100%
- * activity.setProgress(progress * 1000);
- * }
- * });
- * webview.setWebViewClient(new WebViewClient() {
- * public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
- * Toast.makeText(activity, "Oh no! " + description, Toast.LENGTH_SHORT).show();
- * }
- * });
- *
- * webview.loadUrl("https://developer.android.com/");
- * </pre>
- *
- * <h3>Zoom</h3>
- *
- * <p>To enable the built-in zoom, set
- * {@link #getSettings() WebSettings}.{@link WebSettings#setBuiltInZoomControls(boolean)}
- * (introduced in API level {@link android.os.Build.VERSION_CODES#CUPCAKE}).
- *
- * <p class="note"><b>Note:</b> Using zoom if either the height or width is set to
- * {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT} may lead to undefined behavior
- * and should be avoided.
- *
- * <h3>Cookie and window management</h3>
- *
- * <p>For obvious security reasons, your application has its own
- * cache, cookie store etc.—it does not share the Browser
- * application's data.
- *
- * <p>By default, requests by the HTML to open new windows are
- * ignored. This is {@code true} whether they be opened by JavaScript or by
- * the target attribute on a link. You can customize your
- * {@link WebChromeClient} to provide your own behavior for opening multiple windows,
- * and render them in whatever manner you want.
- *
- * <p>The standard behavior for an Activity is to be destroyed and
- * recreated when the device orientation or any other configuration changes. This will cause
- * the WebView to reload the current page. If you don't want that, you
- * can set your Activity to handle the {@code orientation} and {@code keyboardHidden}
- * changes, and then just leave the WebView alone. It'll automatically
- * re-orient itself as appropriate. Read <a
- * href="{@docRoot}guide/topics/resources/runtime-changes.html">Handling Runtime Changes</a> for
- * more information about how to handle configuration changes during runtime.
- *
- *
- * <h3>Building web pages to support different screen densities</h3>
- *
- * <p>The screen density of a device is based on the screen resolution. A screen with low density
- * has fewer available pixels per inch, where a screen with high density
- * has more — sometimes significantly more — pixels per inch. The density of a
- * screen is important because, other things being equal, a UI element (such as a button) whose
- * height and width are defined in terms of screen pixels will appear larger on the lower density
- * screen and smaller on the higher density screen.
- * For simplicity, Android collapses all actual screen densities into three generalized densities:
- * high, medium, and low.
- * <p>By default, WebView scales a web page so that it is drawn at a size that matches the default
- * appearance on a medium density screen. So, it applies 1.5x scaling on a high density screen
- * (because its pixels are smaller) and 0.75x scaling on a low density screen (because its pixels
- * are bigger).
- * Starting with API level {@link android.os.Build.VERSION_CODES#ECLAIR}, WebView supports DOM, CSS,
- * and meta tag features to help you (as a web developer) target screens with different screen
- * densities.
- * <p>Here's a summary of the features you can use to handle different screen densities:
- * <ul>
- * <li>The {@code window.devicePixelRatio} DOM property. The value of this property specifies the
- * default scaling factor used for the current device. For example, if the value of {@code
- * window.devicePixelRatio} is "1.0", then the device is considered a medium density (mdpi) device
- * and default scaling is not applied to the web page; if the value is "1.5", then the device is
- * considered a high density device (hdpi) and the page content is scaled 1.5x; if the
- * value is "0.75", then the device is considered a low density device (ldpi) and the content is
- * scaled 0.75x.</li>
- * <li>The {@code -webkit-device-pixel-ratio} CSS media query. Use this to specify the screen
- * densities for which this style sheet is to be used. The corresponding value should be either
- * "0.75", "1", or "1.5", to indicate that the styles are for devices with low density, medium
- * density, or high density screens, respectively. For example:
- * <pre>
- * <link rel="stylesheet" media="screen and (-webkit-device-pixel-ratio:1.5)" href="hdpi.css" /></pre>
- * <p>The {@code hdpi.css} stylesheet is only used for devices with a screen pixel ratio of 1.5,
- * which is the high density pixel ratio.
- * </li>
- * </ul>
- *
- * <h3>HTML5 Video support</h3>
- *
- * <p>In order to support inline HTML5 video in your application you need to have hardware
- * acceleration turned on.
- *
- * <h3>Full screen support</h3>
- *
- * <p>In order to support full screen — for video or other HTML content — you need to set a
- * {@link android.webkit.WebChromeClient} and implement both
- * {@link WebChromeClient#onShowCustomView(View, WebChromeClient.CustomViewCallback)}
- * and {@link WebChromeClient#onHideCustomView()}. If the implementation of either of these two methods is
- * missing then the web contents will not be allowed to enter full screen. Optionally you can implement
- * {@link WebChromeClient#getVideoLoadingProgressView()} to customize the View displayed whilst a video
- * is loading.
- *
- * <h3>HTML5 Geolocation API support</h3>
- *
- * <p>For applications targeting Android N and later releases
- * (API level > {@link android.os.Build.VERSION_CODES#M}) the geolocation api is only supported on
- * secure origins such as https. For such applications requests to geolocation api on non-secure
- * origins are automatically denied without invoking the corresponding
- * {@link WebChromeClient#onGeolocationPermissionsShowPrompt(String, GeolocationPermissions.Callback)}
- * method.
- *
- * <h3>Layout size</h3>
- * <p>
- * It is recommended to set the WebView layout height to a fixed value or to
- * {@link android.view.ViewGroup.LayoutParams#MATCH_PARENT} instead of using
- * {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT}.
- * When using {@link android.view.ViewGroup.LayoutParams#MATCH_PARENT}
- * for the height none of the WebView's parents should use a
- * {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT} layout height since that could result in
- * incorrect sizing of the views.
- *
- * <p>Setting the WebView's height to {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT}
- * enables the following behaviors:
- * <ul>
- * <li>The HTML body layout height is set to a fixed value. This means that elements with a height
- * relative to the HTML body may not be sized correctly. </li>
- * <li>For applications targeting {@link android.os.Build.VERSION_CODES#KITKAT} and earlier SDKs the
- * HTML viewport meta tag will be ignored in order to preserve backwards compatibility. </li>
- * </ul>
- *
- * <p>
- * Using a layout width of {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT} is not
- * supported. If such a width is used the WebView will attempt to use the width of the parent
- * instead.
- *
- * <h3>Metrics</h3>
- *
- * <p>
- * WebView may upload anonymous diagnostic data to Google when the user has consented. This data
- * helps Google improve WebView. Data is collected on a per-app basis for each app which has
- * instantiated a WebView. An individual app can opt out of this feature by putting the following
- * tag in its manifest's {@code <application>} element:
- * <pre>
- * <manifest>
- * <application>
- * ...
- * <meta-data android:name="android.webkit.WebView.MetricsOptOut"
- * android:value="true" />
- * </application>
- * </manifest>
- * </pre>
- * <p>
- * Data will only be uploaded for a given app if the user has consented AND the app has not opted
- * out.
- *
- * <h3>Safe Browsing</h3>
- *
- * <p>
- * With Safe Browsing, WebView will block malicious URLs and present a warning UI to the user to
- * allow them to navigate back safely or proceed to the malicious page.
- * <p>
- * Safe Browsing is enabled by default on devices which support it. If your app needs to disable
- * Safe Browsing for all WebViews, it can do so in the manifest's {@code <application>} element:
- * <p>
- * <pre>
- * <manifest>
- * <application>
- * ...
- * <meta-data android:name="android.webkit.WebView.EnableSafeBrowsing"
- * android:value="false" />
- * </application>
- * </manifest>
- * </pre>
- *
- * <p>
- * Otherwise, see {@link WebSettings#setSafeBrowsingEnabled}.
+ * <p>To learn more about WebView and alternatives for serving web content, read the
+ * documentation on
+ * <a href="/guide/webapps/">
+ * Web-based content</a>.
*
*/
// Implementation notes.
@@ -1671,9 +1415,8 @@
/**
* Clears the client certificate preferences stored in response
* to proceeding/cancelling client cert requests. Note that WebView
- * automatically clears these preferences when it receives a
- * {@link KeyChain#ACTION_STORAGE_CHANGED} intent. The preferences are
- * shared by all the WebViews that are created by the embedder application.
+ * automatically clears these preferences when the system keychain is updated.
+ * The preferences are shared by all the WebViews that are created by the embedder application.
*
* @param onCleared A runnable to be invoked when client certs are cleared.
* The runnable will be called in UI thread.
diff --git a/core/java/android/widget/AdapterView.java b/core/java/android/widget/AdapterView.java
index 6c19256..4e77f0b 100644
--- a/core/java/android/widget/AdapterView.java
+++ b/core/java/android/widget/AdapterView.java
@@ -293,7 +293,7 @@
/**
* @return The callback to be invoked with an item in this AdapterView has
- * been clicked, or null id no callback has been set.
+ * been clicked, or null if no callback has been set.
*/
@Nullable
public final OnItemClickListener getOnItemClickListener() {
@@ -365,7 +365,7 @@
/**
* @return The callback to be invoked with an item in this AdapterView has
- * been clicked and held, or null id no callback as been set.
+ * been clicked and held, or null if no callback has been set.
*/
public final OnItemLongClickListener getOnItemLongClickListener() {
return mOnItemLongClickListener;
diff --git a/core/java/android/widget/DateTimeView.java b/core/java/android/widget/DateTimeView.java
index 621a745..2b1e900 100644
--- a/core/java/android/widget/DateTimeView.java
+++ b/core/java/android/widget/DateTimeView.java
@@ -30,7 +30,6 @@
import android.content.res.Configuration;
import android.content.res.TypedArray;
import android.database.ContentObserver;
-import android.icu.util.Calendar;
import android.os.Handler;
import android.text.format.Time;
import android.util.AttributeSet;
@@ -41,6 +40,7 @@
import java.text.DateFormat;
import java.util.ArrayList;
+import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;
@@ -301,7 +301,7 @@
*/
private long computeNextMidnight(TimeZone timeZone) {
Calendar c = Calendar.getInstance();
- c.setTimeZone(libcore.icu.DateUtilsBridge.icuTimeZone(timeZone));
+ c.setTimeZone(timeZone);
c.add(Calendar.DAY_OF_MONTH, 1);
c.set(Calendar.HOUR_OF_DAY, 0);
c.set(Calendar.MINUTE, 0);
diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java
index b12e854..51e481d 100644
--- a/core/java/android/widget/ImageView.java
+++ b/core/java/android/widget/ImageView.java
@@ -80,7 +80,7 @@
*
* <p>
* To learn more about Drawables, see: <a href="{@docRoot}guide/topics/resources/drawable-resource.html">Drawable Resources</a>.
- * To learn more about working with Bitmaps, see: <a href="{@docRoot}topic/performance/graphics/index.htm">Handling Bitmaps</a>.
+ * To learn more about working with Bitmaps, see: <a href="{@docRoot}topic/performance/graphics/index.html">Handling Bitmaps</a>.
* </p>
*
* @attr ref android.R.styleable#ImageView_adjustViewBounds
diff --git a/core/java/android/widget/Magnifier.java b/core/java/android/widget/Magnifier.java
index 929496f..11054c8 100644
--- a/core/java/android/widget/Magnifier.java
+++ b/core/java/android/widget/Magnifier.java
@@ -611,7 +611,6 @@
mRenderer.destroy();
mSurfaceControl.destroy();
mSurfaceSession.kill();
- mBitmapRenderNode.destroy();
mHandler.removeCallbacks(mMagnifierUpdater);
if (mBitmap != null) {
mBitmap.recycle();
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 4ea2ced..ea54696 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -247,6 +247,7 @@
* @attr ref android.R.styleable#TextView_textColorHint
* @attr ref android.R.styleable#TextView_textAppearance
* @attr ref android.R.styleable#TextView_textColorLink
+ * @attr ref android.R.styleable#TextView_textFontWeight
* @attr ref android.R.styleable#TextView_textSize
* @attr ref android.R.styleable#TextView_textScaleX
* @attr ref android.R.styleable#TextView_fontFamily
@@ -3450,7 +3451,7 @@
ColorStateList mTextColor = null;
ColorStateList mTextColorHint = null;
ColorStateList mTextColorLink = null;
- int mTextSize = 0;
+ int mTextSize = -1;
String mFontFamily = null;
Typeface mFontTypeface = null;
boolean mFontFamilyExplicit = false;
@@ -3661,7 +3662,7 @@
setHighlightColor(attributes.mTextColorHighlight);
}
- if (attributes.mTextSize != 0) {
+ if (attributes.mTextSize != -1) {
setRawTextSize(attributes.mTextSize, true /* shouldRequestLayout */);
}
@@ -6972,7 +6973,8 @@
public boolean hasOverlappingRendering() {
// horizontal fading edge causes SaveLayerAlpha, which doesn't support alpha modulation
return ((getBackground() != null && getBackground().getCurrent() != null)
- || mSpannable != null || hasSelection() || isHorizontalFadingEdgeEnabled());
+ || mSpannable != null || hasSelection() || isHorizontalFadingEdgeEnabled()
+ || mShadowColor != 0);
}
/**
diff --git a/core/java/com/android/internal/annotations/GuardedBy.java b/core/java/com/android/internal/annotations/GuardedBy.java
index fc61945..0e63214 100644
--- a/core/java/com/android/internal/annotations/GuardedBy.java
+++ b/core/java/com/android/internal/annotations/GuardedBy.java
@@ -23,10 +23,10 @@
/**
* Annotation type used to mark a method or field that can only be accessed when
- * holding the referenced lock.
+ * holding the referenced locks.
*/
@Target({ ElementType.FIELD, ElementType.METHOD })
@Retention(RetentionPolicy.CLASS)
public @interface GuardedBy {
- String value();
+ String[] value();
}
diff --git a/core/java/com/android/internal/app/IAppOpsService.aidl b/core/java/com/android/internal/app/IAppOpsService.aidl
index 0ed9724..768dddd 100644
--- a/core/java/com/android/internal/app/IAppOpsService.aidl
+++ b/core/java/com/android/internal/app/IAppOpsService.aidl
@@ -33,7 +33,7 @@
void stopWatchingMode(IAppOpsCallback callback);
IBinder getToken(IBinder clientToken);
int permissionToOpCode(String permission);
- int noteProxyOperation(int code, String proxyPackageName,
+ int noteProxyOperation(int code, int proxyUid, String proxyPackageName,
int callingUid, String callingPackageName);
// Remaining methods are only used in Java.
diff --git a/core/java/com/android/internal/app/LocaleStore.java b/core/java/com/android/internal/app/LocaleStore.java
index d247013..1d997f5 100644
--- a/core/java/com/android/internal/app/LocaleStore.java
+++ b/core/java/com/android/internal/app/LocaleStore.java
@@ -21,6 +21,7 @@
import android.provider.Settings;
import android.telephony.TelephonyManager;
+import java.io.Serializable;
import java.util.HashMap;
import java.util.HashSet;
import java.util.IllformedLocaleException;
@@ -31,7 +32,7 @@
private static final HashMap<String, LocaleInfo> sLocaleCache = new HashMap<>();
private static boolean sFullyInitialized = false;
- public static class LocaleInfo {
+ public static class LocaleInfo implements Serializable {
private static final int SUGGESTION_TYPE_NONE = 0;
private static final int SUGGESTION_TYPE_SIM = 1 << 0;
private static final int SUGGESTION_TYPE_CFG = 1 << 1;
diff --git a/core/java/com/android/internal/app/PlatLogoActivity.java b/core/java/com/android/internal/app/PlatLogoActivity.java
index f6a69d9..5778544 100644
--- a/core/java/com/android/internal/app/PlatLogoActivity.java
+++ b/core/java/com/android/internal/app/PlatLogoActivity.java
@@ -18,6 +18,9 @@
import android.animation.TimeAnimator;
import android.app.Activity;
+import android.content.ActivityNotFoundException;
+import android.content.ContentResolver;
+import android.content.Intent;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
@@ -25,12 +28,15 @@
import android.graphics.Path;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
+import android.provider.Settings;
import android.util.Log;
import android.view.MotionEvent;
import android.view.MotionEvent.PointerCoords;
import android.view.View;
import android.widget.FrameLayout;
+import org.json.JSONObject;
+
public class PlatLogoActivity extends Activity {
FrameLayout layout;
TimeAnimator anim;
@@ -87,7 +93,7 @@
darkest = 0;
for (int i=0; i<slots; i++) {
palette[i] = Color.HSVToColor(color);
- color[0] += 360f/slots;
+ color[0] = (color[0] + 360f/slots) % 360f;
if (lum(palette[i]) < lum(palette[darkest])) darkest = i;
}
@@ -178,27 +184,97 @@
bg = new PBackground();
layout.setBackground(bg);
+ final ContentResolver cr = getContentResolver();
+
layout.setOnTouchListener(new View.OnTouchListener() {
+ final String TOUCH_STATS = "touch.stats";
+
final PointerCoords pc0 = new PointerCoords();
final PointerCoords pc1 = new PointerCoords();
+ double pressure_min, pressure_max;
+ int maxPointers;
+ int tapCount;
+
@Override
public boolean onTouch(View v, MotionEvent event) {
+ final float pressure = event.getPressure();
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
+ pressure_min = pressure_max = pressure;
+ // fall through
case MotionEvent.ACTION_MOVE:
- if (event.getPointerCount() > 1) {
+ if (pressure < pressure_min) pressure_min = pressure;
+ if (pressure > pressure_max) pressure_max = pressure;
+ final int pc = event.getPointerCount();
+ if (pc > maxPointers) maxPointers = pc;
+ if (pc > 1) {
event.getPointerCoords(0, pc0);
event.getPointerCoords(1, pc1);
bg.setRadius((float) Math.hypot(pc0.x - pc1.x, pc0.y - pc1.y) / 2f);
}
break;
+ case MotionEvent.ACTION_CANCEL:
+ case MotionEvent.ACTION_UP:
+ try {
+ final String touchDataJson = Settings.System.getString(cr, TOUCH_STATS);
+ final JSONObject touchData = new JSONObject(
+ touchDataJson != null ? touchDataJson : "{}");
+ if (touchData.has("min")) {
+ pressure_min = Math.min(pressure_min, touchData.getDouble("min"));
+ }
+ if (touchData.has("max")) {
+ pressure_max = Math.max(pressure_max, touchData.getDouble("max"));
+ }
+ touchData.put("min", pressure_min);
+ touchData.put("max", pressure_max);
+ Settings.System.putString(cr, TOUCH_STATS, touchData.toString());
+ } catch (Exception e) {
+ Log.e("PlatLogoActivity", "Can't write touch settings", e);
+ }
+
+ if (maxPointers == 1) {
+ tapCount ++;
+ if (tapCount < 7) {
+ bg.randomizePalette();
+ } else {
+ launchNextStage();
+ }
+ } else {
+ tapCount = 0;
+ }
+ maxPointers = 0;
+ break;
}
return true;
}
});
}
+ private void launchNextStage() {
+ final ContentResolver cr = getContentResolver();
+
+ if (Settings.System.getLong(cr, Settings.System.EGG_MODE, 0) == 0) {
+ // For posterity: the moment this user unlocked the easter egg
+ try {
+ Settings.System.putLong(cr,
+ Settings.System.EGG_MODE,
+ System.currentTimeMillis());
+ } catch (RuntimeException e) {
+ Log.e("PlatLogoActivity", "Can't write settings", e);
+ }
+ }
+ try {
+ startActivity(new Intent(Intent.ACTION_MAIN)
+ .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_CLEAR_TASK)
+ .addCategory("com.android.internal.category.PLATLOGO"));
+ } catch (ActivityNotFoundException ex) {
+ Log.e("PlatLogoActivity", "No more eggs.");
+ }
+ finish();
+ }
+
@Override
public void onStart() {
super.onStart();
diff --git a/core/java/com/android/internal/app/procstats/AssociationState.java b/core/java/com/android/internal/app/procstats/AssociationState.java
new file mode 100644
index 0000000..e5d6556
--- /dev/null
+++ b/core/java/com/android/internal/app/procstats/AssociationState.java
@@ -0,0 +1,472 @@
+/*
+ * 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.app.procstats;
+
+
+import android.os.Parcel;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.util.ArrayMap;
+import android.util.Slog;
+import android.util.TimeUtils;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Objects;
+
+public final class AssociationState {
+ private static final String TAG = "ProcessStats";
+ private static final boolean DEBUG = false;
+
+ private final ProcessStats mProcessStats;
+ private final ProcessStats.PackageState mPackageState;
+ private final String mProcessName;
+ private final String mName;
+ private final DurationsTable mDurations;
+
+ public final class SourceState {
+ final SourceKey mKey;
+ int mProcStateSeq = -1;
+ int mProcState = ProcessStats.STATE_NOTHING;
+ boolean mInTrackingList;
+ int mNesting;
+ int mCount;
+ long mStartUptime;
+ long mDuration;
+ long mTrackingUptime;
+ int mActiveCount;
+ long mActiveStartUptime;
+ long mActiveDuration;
+
+ SourceState(SourceKey key) {
+ mKey = key;
+ }
+
+ public AssociationState getAssociationState() {
+ return AssociationState.this;
+ }
+
+ public String getProcessName() {
+ return mKey.mProcess;
+ }
+
+ public int getUid() {
+ return mKey.mUid;
+ }
+
+ public void trackProcState(int procState, int seq, long now) {
+ procState = ProcessState.PROCESS_STATE_TO_STATE[procState];
+ if (seq != mProcStateSeq) {
+ mProcStateSeq = seq;
+ mProcState = procState;
+ } else if (procState < mProcState) {
+ mProcState = procState;
+ }
+ if (procState < ProcessStats.STATE_HOME) {
+ if (!mInTrackingList) {
+ mInTrackingList = true;
+ mTrackingUptime = now;
+ mProcessStats.mTrackingAssociations.add(this);
+ }
+ } else {
+ stopTracking(now);
+ }
+ }
+
+ public void stop() {
+ mNesting--;
+ if (mNesting == 0) {
+ mDuration += SystemClock.uptimeMillis() - mStartUptime;
+ mNumActive--;
+ stopTracking(SystemClock.uptimeMillis());
+ }
+ }
+
+ void startActive(long now) {
+ if (mInTrackingList) {
+ if (mActiveStartUptime == 0) {
+ mActiveStartUptime = now;
+ mActiveCount++;
+ }
+ } else {
+ Slog.wtf(TAG, "startActive while not tracking: " + this);
+ }
+ }
+
+ void stopActive(long now) {
+ if (mActiveStartUptime != 0) {
+ if (!mInTrackingList) {
+ Slog.wtf(TAG, "stopActive while not tracking: " + this);
+ }
+ mActiveDuration += now - mActiveStartUptime;
+ mActiveStartUptime = 0;
+ }
+ }
+
+ void stopTracking(long now) {
+ stopActive(now);
+ if (mInTrackingList) {
+ mInTrackingList = false;
+ // Do a manual search for where to remove, since these objects will typically
+ // be towards the end of the array.
+ final ArrayList<SourceState> list = mProcessStats.mTrackingAssociations;
+ for (int i = list.size() - 1; i >= 0; i--) {
+ if (list.get(i) == this) {
+ list.remove(i);
+ return;
+ }
+ }
+ Slog.wtf(TAG, "Stop tracking didn't find in tracking list: " + this);
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder(64);
+ sb.append("SourceState{").append(Integer.toHexString(System.identityHashCode(this)))
+ .append(" ").append(mKey.mProcess).append("/").append(mKey.mUid);
+ if (mProcState != ProcessStats.STATE_NOTHING) {
+ sb.append(" ").append(DumpUtils.STATE_NAMES[mProcState]).append(" #")
+ .append(mProcStateSeq);
+ }
+ sb.append("}");
+ return sb.toString();
+ }
+ }
+
+ private final static class SourceKey {
+ /**
+ * UID, consider this final. Not final just to avoid a temporary object during lookup.
+ */
+ int mUid;
+
+ /**
+ * Process name, consider this final. Not final just to avoid a temporary object during
+ * lookup.
+ */
+ String mProcess;
+
+ SourceKey(int uid, String process) {
+ mUid = uid;
+ mProcess = process;
+ }
+
+ public boolean equals(Object o) {
+ if (!(o instanceof SourceKey)) {
+ return false;
+ }
+ SourceKey s = (SourceKey) o;
+ return s.mUid == mUid && Objects.equals(s.mProcess, mProcess);
+ }
+
+ @Override
+ public int hashCode() {
+ return Integer.hashCode(mUid) ^ (mProcess == null ? 0 : mProcess.hashCode());
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder(64);
+ sb.append("SourceKey{");
+ UserHandle.formatUid(sb, mUid);
+ sb.append(' ');
+ sb.append(mProcess);
+ sb.append('}');
+ return sb.toString();
+ }
+ }
+
+ /**
+ * All known sources for this target component... uid -> process name -> source state.
+ */
+ private final ArrayMap<SourceKey, SourceState> mSources = new ArrayMap<>();
+
+ private final SourceKey mTmpSourceKey = new SourceKey(0, null);
+
+ private ProcessState mProc;
+
+ private int mNumActive;
+
+ public AssociationState(ProcessStats processStats, ProcessStats.PackageState packageState,
+ String name, String processName, ProcessState proc) {
+ mProcessStats = processStats;
+ mPackageState = packageState;
+ mName = name;
+ mProcessName = processName;
+ mDurations = new DurationsTable(processStats.mTableData);
+ mProc = proc;
+ }
+
+ public int getUid() {
+ return mPackageState.mUid;
+ }
+
+ public String getPackage() {
+ return mPackageState.mPackageName;
+ }
+
+ public String getProcessName() {
+ return mProcessName;
+ }
+
+ public String getName() {
+ return mName;
+ }
+
+ public ProcessState getProcess() {
+ return mProc;
+ }
+
+ public void setProcess(ProcessState proc) {
+ mProc = proc;
+ }
+
+ public SourceState startSource(int uid, String processName) {
+ mTmpSourceKey.mUid = uid;
+ mTmpSourceKey.mProcess = processName;
+ SourceState src = mSources.get(mTmpSourceKey);
+ if (src == null) {
+ SourceKey key = new SourceKey(uid, processName);
+ src = new SourceState(key);
+ mSources.put(key, src);
+ }
+ src.mNesting++;
+ if (src.mNesting == 1) {
+ src.mCount++;
+ src.mStartUptime = SystemClock.uptimeMillis();
+ mNumActive++;
+ }
+ return src;
+ }
+
+ public void add(AssociationState other) {
+ mDurations.addDurations(other.mDurations);
+ for (int isrc = other.mSources.size() - 1; isrc >= 0; isrc--) {
+ final SourceKey key = other.mSources.keyAt(isrc);
+ final SourceState otherSrc = other.mSources.valueAt(isrc);
+ SourceState mySrc = mSources.get(key);
+ if (mySrc == null) {
+ mySrc = new SourceState(key);
+ mSources.put(key, mySrc);
+ }
+ mySrc.mCount += otherSrc.mCount;
+ mySrc.mDuration += otherSrc.mDuration;
+ mySrc.mActiveCount += otherSrc.mActiveCount;
+ mySrc.mActiveDuration += otherSrc.mActiveDuration;
+ }
+ }
+
+ public boolean isInUse() {
+ return mNumActive > 0;
+ }
+
+ public void resetSafely(long now) {
+ mDurations.resetTable();
+ if (!isInUse()) {
+ mSources.clear();
+ } else {
+ // We have some active sources... clear out everything but those.
+ for (int isrc = mSources.size() - 1; isrc >= 0; isrc--) {
+ SourceState src = mSources.valueAt(isrc);
+ if (src.mNesting > 0) {
+ src.mCount = 1;
+ src.mStartUptime = now;
+ src.mDuration = 0;
+ if (src.mActiveStartUptime > 0) {
+ src.mActiveCount = 1;
+ src.mActiveStartUptime = now;
+ } else {
+ src.mActiveCount = 0;
+ }
+ src.mActiveDuration = 0;
+ } else {
+ mSources.removeAt(isrc);
+ }
+ }
+ }
+ }
+
+ public void writeToParcel(ProcessStats stats, Parcel out, long nowUptime) {
+ mDurations.writeToParcel(out);
+ final int NSRC = mSources.size();
+ out.writeInt(NSRC);
+ for (int isrc = 0; isrc < NSRC; isrc++) {
+ final SourceKey key = mSources.keyAt(isrc);
+ final SourceState src = mSources.valueAt(isrc);
+ out.writeInt(key.mUid);
+ stats.writeCommonString(out, key.mProcess);
+ out.writeInt(src.mCount);
+ out.writeLong(src.mDuration);
+ out.writeInt(src.mActiveCount);
+ out.writeLong(src.mActiveDuration);
+ }
+ }
+
+ /**
+ * Returns non-null if all else fine, else a String that describes the error that
+ * caused it to fail.
+ */
+ public String readFromParcel(ProcessStats stats, Parcel in, int parcelVersion) {
+ if (!mDurations.readFromParcel(in)) {
+ return "Duration table corrupt";
+ }
+ final int NSRC = in.readInt();
+ if (NSRC < 0 || NSRC > 100000) {
+ return "Association with bad src count: " + NSRC;
+ }
+ for (int isrc = 0; isrc < NSRC; isrc++) {
+ final int uid = in.readInt();
+ final String procName = stats.readCommonString(in, parcelVersion);
+ final SourceKey key = new SourceKey(uid, procName);
+ final SourceState src = new SourceState(key);
+ src.mCount = in.readInt();
+ src.mDuration = in.readLong();
+ src.mActiveCount = in.readInt();
+ src.mActiveDuration = in.readLong();
+ mSources.put(key, src);
+ }
+ return null;
+ }
+
+ public void commitStateTime(long nowUptime) {
+ if (isInUse()) {
+ for (int isrc = mSources.size() - 1; isrc >= 0; isrc--) {
+ SourceState src = mSources.valueAt(isrc);
+ if (src.mNesting > 0) {
+ src.mDuration += nowUptime - src.mStartUptime;
+ src.mStartUptime = nowUptime;
+ }
+ if (src.mActiveStartUptime > 0) {
+ src.mActiveDuration += nowUptime - src.mActiveStartUptime;
+ src.mActiveStartUptime = nowUptime;
+ }
+ }
+ }
+ }
+
+ public void dumpStats(PrintWriter pw, String prefix, String prefixInner, String headerPrefix,
+ long now, long totalTime, boolean dumpSummary, boolean dumpAll) {
+ if (dumpAll) {
+ pw.print(prefix);
+ pw.print("mNumActive=");
+ pw.println(mNumActive);
+ }
+ final int NSRC = mSources.size();
+ for (int isrc = 0; isrc < NSRC; isrc++) {
+ final SourceKey key = mSources.keyAt(isrc);
+ final SourceState src = mSources.valueAt(isrc);
+ pw.print(prefixInner);
+ pw.print("<- ");
+ pw.print(key.mProcess);
+ pw.print(" / ");
+ UserHandle.formatUid(pw, key.mUid);
+ pw.println(":");
+ pw.print(prefixInner);
+ pw.print(" Count ");
+ pw.print(src.mCount);
+ long duration = src.mDuration;
+ if (src.mNesting > 0) {
+ duration += now - src.mStartUptime;
+ }
+ if (dumpAll) {
+ pw.print(" / Duration ");
+ TimeUtils.formatDuration(duration, pw);
+ pw.print(" / ");
+ } else {
+ pw.print(" / time ");
+ }
+ DumpUtils.printPercent(pw, (double)duration/(double)totalTime);
+ if (src.mNesting > 0) {
+ pw.print(" (running");
+ if (src.mProcState != ProcessStats.STATE_NOTHING) {
+ pw.print(" / ");
+ pw.print(DumpUtils.STATE_NAMES[src.mProcState]);
+ pw.print(" #");
+ pw.print(src.mProcStateSeq);
+ }
+ pw.print(")");
+ }
+ pw.println();
+ if (src.mActiveCount > 0) {
+ pw.print(prefixInner);
+ pw.print(" Active count ");
+ pw.print(src.mActiveCount);
+ duration = src.mActiveDuration;
+ if (src.mActiveStartUptime > 0) {
+ duration += now - src.mActiveStartUptime;
+ }
+ if (dumpAll) {
+ pw.print(" / Duration ");
+ TimeUtils.formatDuration(duration, pw);
+ pw.print(" / ");
+ } else {
+ pw.print(" / time ");
+ }
+ DumpUtils.printPercent(pw, (double)duration/(double)totalTime);
+ if (src.mActiveStartUptime > 0) {
+ pw.print(" (running)");
+ }
+ pw.println();
+ }
+ }
+ }
+
+ public void dumpTimesCheckin(PrintWriter pw, String pkgName, int uid, long vers,
+ String associationName, long now) {
+ final int NSRC = mSources.size();
+ for (int isrc = 0; isrc < NSRC; isrc++) {
+ final SourceKey key = mSources.keyAt(isrc);
+ final SourceState src = mSources.valueAt(isrc);
+ pw.print("pkgasc");
+ pw.print(",");
+ pw.print(pkgName);
+ pw.print(",");
+ pw.print(uid);
+ pw.print(",");
+ pw.print(vers);
+ pw.print(",");
+ pw.print(associationName);
+ pw.print(",");
+ pw.print(key.mProcess);
+ pw.print(",");
+ pw.print(key.mUid);
+ pw.print(",");
+ pw.print(src.mCount);
+ long duration = src.mDuration;
+ if (src.mNesting > 0) {
+ duration += now - src.mStartUptime;
+ }
+ pw.print(",");
+ pw.print(duration);
+ pw.print(",");
+ pw.print(src.mActiveCount);
+ duration = src.mActiveDuration;
+ if (src.mActiveStartUptime > 0) {
+ duration += now - src.mActiveStartUptime;
+ }
+ pw.print(",");
+ pw.print(duration);
+ pw.println();
+ }
+ }
+
+ public String toString() {
+ return "AssociationState{" + Integer.toHexString(System.identityHashCode(this))
+ + " " + mName + " pkg=" + mPackageState.mPackageName + " proc="
+ + Integer.toHexString(System.identityHashCode(mProc)) + "}";
+ }
+}
diff --git a/core/java/com/android/internal/app/procstats/DumpUtils.java b/core/java/com/android/internal/app/procstats/DumpUtils.java
index e38a844..06b6552 100644
--- a/core/java/com/android/internal/app/procstats/DumpUtils.java
+++ b/core/java/com/android/internal/app/procstats/DumpUtils.java
@@ -362,12 +362,13 @@
}
}
- public static void dumpProcessSummaryLocked(PrintWriter pw, String prefix,
+ public static void dumpProcessSummaryLocked(PrintWriter pw, String prefix, String header,
ArrayList<ProcessState> procs, int[] screenStates, int[] memStates, int[] procStates,
long now, long totalTime) {
for (int i=procs.size()-1; i>=0; i--) {
final ProcessState proc = procs.get(i);
- proc.dumpSummary(pw, prefix, screenStates, memStates, procStates, now, totalTime);
+ proc.dumpSummary(pw, prefix, header, screenStates, memStates, procStates, now,
+ totalTime);
}
}
diff --git a/core/java/com/android/internal/app/procstats/ProcessState.java b/core/java/com/android/internal/app/procstats/ProcessState.java
index 65bd48f..ad42288 100644
--- a/core/java/com/android/internal/app/procstats/ProcessState.java
+++ b/core/java/com/android/internal/app/procstats/ProcessState.java
@@ -71,7 +71,7 @@
private static final boolean DEBUG_PARCEL = false;
// Map from process states to the states we track.
- private static final int[] PROCESS_STATE_TO_STATE = new int[] {
+ static final int[] PROCESS_STATE_TO_STATE = new int[] {
STATE_PERSISTENT, // ActivityManager.PROCESS_STATE_PERSISTENT
STATE_PERSISTENT, // ActivityManager.PROCESS_STATE_PERSISTENT_UI
STATE_TOP, // ActivityManager.PROCESS_STATE_TOP
@@ -129,7 +129,7 @@
private final PssTable mPssTable;
private ProcessState mCommonProcess;
- private int mCurState = STATE_NOTHING;
+ private int mCurCombinedState = STATE_NOTHING;
private long mStartTime;
private int mLastPssState = STATE_NOTHING;
@@ -180,7 +180,7 @@
mPackage = pkg;
mUid = uid;
mVersion = vers;
- mCurState = commonProcess.mCurState;
+ mCurCombinedState = commonProcess.mCurCombinedState;
mStartTime = now;
mDurations = new DurationsTable(commonProcess.mStats.mTableData);
mPssTable = new PssTable(commonProcess.mStats.mTableData);
@@ -324,7 +324,7 @@
public boolean isInUse() {
return mActive || mNumActiveServices > 0 || mNumStartedServices > 0
- || mCurState != STATE_NOTHING;
+ || mCurCombinedState != STATE_NOTHING;
}
public boolean isActive() {
@@ -333,7 +333,7 @@
public boolean hasAnyData() {
return !(mDurations.getKeyCount() == 0
- && mCurState == STATE_NOTHING
+ && mCurCombinedState == STATE_NOTHING
&& mPssTable.getKeyCount() == 0);
}
@@ -355,7 +355,7 @@
}
// First update the common process.
- mCommonProcess.setState(state, now);
+ mCommonProcess.setCombinedState(state, now);
// If the common process is not multi-package, there is nothing else to do.
if (!mCommonProcess.mMultiPackage) {
@@ -364,25 +364,29 @@
if (pkgList != null) {
for (int ip=pkgList.size()-1; ip>=0; ip--) {
- pullFixedProc(pkgList, ip).setState(state, now);
+ pullFixedProc(pkgList, ip).setCombinedState(state, now);
}
}
}
- public void setState(int state, long now) {
+ public void setCombinedState(int state, long now) {
ensureNotDead();
- if (!mDead && (mCurState != state)) {
+ if (!mDead && (mCurCombinedState != state)) {
//Slog.i(TAG, "Setting state in " + mName + "/" + mPackage + ": " + state);
commitStateTime(now);
- mCurState = state;
+ mCurCombinedState = state;
}
}
+ public int getCombinedState() {
+ return mCurCombinedState;
+ }
+
public void commitStateTime(long now) {
- if (mCurState != STATE_NOTHING) {
+ if (mCurCombinedState != STATE_NOTHING) {
long dur = now - mStartTime;
if (dur > 0) {
- mDurations.addDuration(mCurState, dur);
+ mDurations.addDuration(mCurCombinedState, dur);
}
}
mStartTime = now;
@@ -430,8 +434,8 @@
mCommonProcess.incStartedServices(memFactor, now, serviceName);
}
mNumStartedServices++;
- if (mNumStartedServices == 1 && mCurState == STATE_NOTHING) {
- setState(STATE_SERVICE_RESTARTING + (memFactor*STATE_COUNT), now);
+ if (mNumStartedServices == 1 && mCurCombinedState == STATE_NOTHING) {
+ setCombinedState(STATE_SERVICE_RESTARTING + (memFactor*STATE_COUNT), now);
}
}
@@ -446,8 +450,8 @@
mCommonProcess.decStartedServices(memFactor, now, serviceName);
}
mNumStartedServices--;
- if (mNumStartedServices == 0 && (mCurState%STATE_COUNT) == STATE_SERVICE_RESTARTING) {
- setState(STATE_NOTHING, now);
+ if (mNumStartedServices == 0 && (mCurCombinedState %STATE_COUNT) == STATE_SERVICE_RESTARTING) {
+ setCombinedState(STATE_NOTHING, now);
} else if (mNumStartedServices < 0) {
Slog.wtfStack(TAG, "Proc started services underrun: pkg="
+ mPackage + " uid=" + mUid + " name=" + mName);
@@ -481,16 +485,16 @@
break;
}
if (!always) {
- if (mLastPssState == mCurState && SystemClock.uptimeMillis()
+ if (mLastPssState == mCurCombinedState && SystemClock.uptimeMillis()
< (mLastPssTime+(30*1000))) {
return;
}
}
- mLastPssState = mCurState;
+ mLastPssState = mCurCombinedState;
mLastPssTime = SystemClock.uptimeMillis();
- if (mCurState != STATE_NOTHING) {
+ if (mCurCombinedState != STATE_NOTHING) {
// First update the common process.
- mCommonProcess.mPssTable.mergeStats(mCurState, 1, pss, pss, pss, uss, uss, uss,
+ mCommonProcess.mPssTable.mergeStats(mCurCombinedState, 1, pss, pss, pss, uss, uss, uss,
rss, rss, rss);
// If the common process is not multi-package, there is nothing else to do.
@@ -500,7 +504,7 @@
if (pkgList != null) {
for (int ip=pkgList.size()-1; ip>=0; ip--) {
- pullFixedProc(pkgList, ip).mPssTable.mergeStats(mCurState, 1,
+ pullFixedProc(pkgList, ip).mPssTable.mergeStats(mCurCombinedState, 1,
pss, pss, pss, uss, uss, uss, rss, rss, rss);
}
}
@@ -580,7 +584,7 @@
ProcessStateHolder holder = pkgList.valueAt(index);
ProcessState proc = holder.state;
if (mDead && proc.mCommonProcess != proc) {
- // Somehow we are contining to use a process state that is dead, because
+ // Somehow we are continuing to use a process state that is dead, because
// it was not being told it was active during the last commit. We can recover
// from this by generating a fresh new state, but this is bad because we
// are losing whatever data we had in the old process state.
@@ -600,17 +604,17 @@
+ pkgList.keyAt(index) + "/" + proc.mUid
+ " for multi-proc " + proc.mName);
}
- PackageState pkg = vpkg.get(proc.mVersion);
- if (pkg == null) {
+ PackageState expkg = vpkg.get(proc.mVersion);
+ if (expkg == null) {
throw new IllegalStateException("No existing package "
+ pkgList.keyAt(index) + "/" + proc.mUid
+ " for multi-proc " + proc.mName + " version " + proc.mVersion);
}
String savedName = proc.mName;
- proc = pkg.mProcesses.get(proc.mName);
+ proc = expkg.mProcesses.get(proc.mName);
if (proc == null) {
throw new IllegalStateException("Didn't create per-package process "
- + savedName + " in pkg " + pkg.mPackageName + "/" + pkg.mUid);
+ + savedName + " in pkg " + expkg.mPackageName + "/" + expkg.mUid);
}
holder.state = proc;
}
@@ -619,7 +623,7 @@
public long getDuration(int state, long now) {
long time = mDurations.getValueForId((byte)state);
- if (mCurState == state) {
+ if (mCurCombinedState == state) {
time += now - mStartTime;
}
return time;
@@ -724,7 +728,7 @@
final int key = mDurations.getKeyAt(i);
final int type = SparseMappingTable.getIdFromKey(key);
long time = mDurations.getValue(key);
- if (mCurState == type) {
+ if (mCurCombinedState == type) {
time += now - mStartTime;
}
final int procState = type % STATE_COUNT;
@@ -769,11 +773,14 @@
return totalTime;
}
- public void dumpSummary(PrintWriter pw, String prefix,
+ public void dumpSummary(PrintWriter pw, String prefix, String header,
int[] screenStates, int[] memStates, int[] procStates,
long now, long totalTime) {
pw.print(prefix);
pw.print("* ");
+ if (header != null) {
+ pw.print(header);
+ }
pw.print(mName);
pw.print(" / ");
UserHandle.formatUid(pw, mUid);
@@ -824,7 +831,7 @@
final int bucket = ((iscreen + imem) * STATE_COUNT) + procStates[ip];
long time = mDurations.getValueForId((byte)bucket);
String running = "";
- if (mCurState == bucket) {
+ if (mCurCombinedState == bucket) {
running = " (running)";
}
if (time != 0) {
@@ -1174,14 +1181,14 @@
final int key = mDurations.getKeyAt(i);
final int type = SparseMappingTable.getIdFromKey(key);
long time = mDurations.getValue(key);
- if (mCurState == type) {
+ if (mCurCombinedState == type) {
didCurState = true;
time += now - mStartTime;
}
DumpUtils.printProcStateTagAndValue(pw, type, time);
}
- if (!didCurState && mCurState != STATE_NOTHING) {
- DumpUtils.printProcStateTagAndValue(pw, mCurState, now - mStartTime);
+ if (!didCurState && mCurCombinedState != STATE_NOTHING) {
+ DumpUtils.printProcStateTagAndValue(pw, mCurCombinedState, now - mStartTime);
}
}
@@ -1248,14 +1255,14 @@
final int key = mDurations.getKeyAt(i);
final int type = SparseMappingTable.getIdFromKey(key);
long time = mDurations.getValue(key);
- if (mCurState == type) {
+ if (mCurCombinedState == type) {
didCurState = true;
time += now - mStartTime;
}
durationByState.put(type, time);
}
- if (!didCurState && mCurState != STATE_NOTHING) {
- durationByState.put(mCurState, now - mStartTime);
+ if (!didCurState && mCurCombinedState != STATE_NOTHING) {
+ durationByState.put(mCurCombinedState, now - mStartTime);
}
for (int i=0; i<mPssTable.getKeyCount(); i++) {
diff --git a/core/java/com/android/internal/app/procstats/ProcessStats.java b/core/java/com/android/internal/app/procstats/ProcessStats.java
index d35bddd..15f140e 100644
--- a/core/java/com/android/internal/app/procstats/ProcessStats.java
+++ b/core/java/com/android/internal/app/procstats/ProcessStats.java
@@ -16,6 +16,7 @@
package com.android.internal.app.procstats;
+import android.content.ComponentName;
import android.os.Debug;
import android.os.Parcel;
import android.os.Parcelable;
@@ -157,7 +158,7 @@
};
// Current version of the parcel format.
- private static final int PARCEL_VERSION = 27;
+ private static final int PARCEL_VERSION = 32;
// In-memory Parcel magic number, used to detect attempts to unmarshall bad data
private static final int MAGIC = 0x50535454;
@@ -168,6 +169,8 @@
public final ProcessMap<LongSparseArray<PackageState>> mPackages = new ProcessMap<>();
public final ProcessMap<ProcessState> mProcesses = new ProcessMap<>();
+ public final ArrayList<AssociationState.SourceState> mTrackingAssociations = new ArrayList<>();
+
public final long[] mMemFactorDurations = new long[ADJ_COUNT];
public int mMemFactor = STATE_NOTHING;
public long mStartTime;
@@ -250,6 +253,7 @@
final PackageState otherState = versions.valueAt(iv);
final int NPROCS = otherState.mProcesses.size();
final int NSRVS = otherState.mServices.size();
+ final int NASCS = otherState.mAssociations.size();
for (int iproc=0; iproc<NPROCS; iproc++) {
ProcessState otherProc = otherState.mProcesses.valueAt(iproc);
if (otherProc.getCommonProcess() != otherProc) {
@@ -277,6 +281,14 @@
otherSvc.getProcessName(), otherSvc.getName());
thisSvc.add(otherSvc);
}
+ for (int iasc=0; iasc<NASCS; iasc++) {
+ AssociationState otherAsc = otherState.mAssociations.valueAt(iasc);
+ if (DEBUG) Slog.d(TAG, "Adding pkg " + pkgName + " uid " + uid
+ + " association " + otherAsc.getName());
+ AssociationState thisAsc = getAssociationStateLocked(pkgName, uid, vers,
+ otherAsc.getProcessName(), otherAsc.getName());
+ thisAsc.add(otherAsc);
+ }
}
}
}
@@ -478,7 +490,16 @@
pkgState.mServices.removeAt(isvc);
}
}
- if (pkgState.mProcesses.size() <= 0 && pkgState.mServices.size() <= 0) {
+ for (int iasc=pkgState.mAssociations.size()-1; iasc>=0; iasc--) {
+ final AssociationState as = pkgState.mAssociations.valueAt(iasc);
+ if (as.isInUse()) {
+ as.resetSafely(now);
+ } else {
+ pkgState.mAssociations.removeAt(iasc);
+ }
+ }
+ if (pkgState.mProcesses.size() <= 0 && pkgState.mServices.size() <= 0
+ && pkgState.mAssociations.size() <= 0) {
vpkgs.removeAt(iv);
}
}
@@ -708,7 +729,7 @@
}
}
- private void writeCommonString(Parcel out, String name) {
+ void writeCommonString(Parcel out, String name) {
Integer index = mCommonStringToIndex.get(name);
if (index != null) {
out.writeInt(index);
@@ -720,7 +741,7 @@
out.writeString(name);
}
- private String readCommonString(Parcel in, int version) {
+ String readCommonString(Parcel in, int version) {
if (version <= 9) {
return in.readString();
}
@@ -791,6 +812,10 @@
for (int isvc=0; isvc<NSRVS; isvc++) {
pkgState.mServices.valueAt(isvc).commitStateTime(now);
}
+ final int NASCS = pkgState.mAssociations.size();
+ for (int iasc=0; iasc<NASCS; iasc++) {
+ pkgState.mAssociations.valueAt(iasc).commitStateTime(now);
+ }
}
}
}
@@ -874,6 +899,14 @@
writeCommonString(out, svc.getProcessName());
svc.writeToParcel(out, now);
}
+ final int NASCS = pkgState.mAssociations.size();
+ out.writeInt(NASCS);
+ for (int iasc=0; iasc<NASCS; iasc++) {
+ writeCommonString(out, pkgState.mAssociations.keyAt(iasc));
+ final AssociationState asc = pkgState.mAssociations.valueAt(iasc);
+ writeCommonString(out, asc.getProcessName());
+ asc.writeToParcel(this, out, now);
+ }
}
}
}
@@ -1078,7 +1111,7 @@
while (NVERS > 0) {
NVERS--;
final long vers = in.readLong();
- PackageState pkgState = new PackageState(pkgName, uid);
+ PackageState pkgState = new PackageState(this, pkgName, uid, vers);
LongSparseArray<PackageState> vpkg = mPackages.get(pkgName, uid);
if (vpkg == null) {
vpkg = new LongSparseArray<>();
@@ -1157,6 +1190,34 @@
+ serviceName + " " + uid + " " + serv);
pkgState.mServices.put(serviceName, serv);
}
+ int NASCS = in.readInt();
+ if (NASCS < 0) {
+ mReadError = "bad package association count: " + NASCS;
+ return;
+ }
+ while (NASCS > 0) {
+ NASCS--;
+ String associationName = readCommonString(in, version);
+ if (associationName == null) {
+ mReadError = "bad package association name";
+ return;
+ }
+ String processName = readCommonString(in, version);
+ AssociationState asc = hadData
+ ? pkgState.mAssociations.get(associationName) : null;
+ if (asc == null) {
+ asc = new AssociationState(this, pkgState, associationName,
+ processName, null);
+ }
+ String errorMsg = asc.readFromParcel(this, in, version);
+ if (errorMsg != null) {
+ mReadError = errorMsg;
+ return;
+ }
+ if (DEBUG_PARCEL) Slog.d(TAG, "Adding package " + pkgName + " association: "
+ + associationName + " " + uid + " " + asc);
+ pkgState.mAssociations.put(associationName, asc);
+ }
}
}
}
@@ -1183,33 +1244,38 @@
public PackageState getPackageStateLocked(String packageName, int uid, long vers) {
LongSparseArray<PackageState> vpkg = mPackages.get(packageName, uid);
if (vpkg == null) {
- vpkg = new LongSparseArray<PackageState>();
+ vpkg = new LongSparseArray<>();
mPackages.put(packageName, uid, vpkg);
}
PackageState as = vpkg.get(vers);
if (as != null) {
return as;
}
- as = new PackageState(packageName, uid);
+ as = new PackageState(this, packageName, uid, vers);
vpkg.put(vers, as);
return as;
}
public ProcessState getProcessStateLocked(String packageName, int uid, long vers,
String processName) {
- final PackageState pkgState = getPackageStateLocked(packageName, uid, vers);
+ return getProcessStateLocked(getPackageStateLocked(packageName, uid, vers), processName);
+ }
+
+ public ProcessState getProcessStateLocked(PackageState pkgState, String processName) {
ProcessState ps = pkgState.mProcesses.get(processName);
if (ps != null) {
return ps;
}
- ProcessState commonProc = mProcesses.get(processName, uid);
+ ProcessState commonProc = mProcesses.get(processName, pkgState.mUid);
if (commonProc == null) {
- commonProc = new ProcessState(this, packageName, uid, vers, processName);
- mProcesses.put(processName, uid, commonProc);
+ commonProc = new ProcessState(this, pkgState.mPackageName, pkgState.mUid,
+ pkgState.mVersionCode, processName);
+ mProcesses.put(processName, pkgState.mUid, commonProc);
if (DEBUG) Slog.d(TAG, "GETPROC created new common " + commonProc);
}
if (!commonProc.isMultiPackage()) {
- if (packageName.equals(commonProc.getPackage()) && vers == commonProc.getVersion()) {
+ if (pkgState.mPackageName.equals(commonProc.getPackage())
+ && pkgState.mVersionCode == commonProc.getVersion()) {
// This common process is not in use by multiple packages, and
// is for the calling package, so we can just use it directly.
ps = commonProc;
@@ -1228,7 +1294,7 @@
// First let's make a copy of the current process state and put
// that under the now unique state for its original package name.
final PackageState commonPkgState = getPackageStateLocked(commonProc.getPackage(),
- uid, commonProc.getVersion());
+ pkgState.mUid, commonProc.getVersion());
if (commonPkgState != null) {
ProcessState cloned = commonProc.clone(now);
if (DEBUG) Slog.d(TAG, "GETPROC setting clone to pkg " + commonProc.getPackage()
@@ -1245,18 +1311,31 @@
Slog.d(TAG, "GETPROC leaving proc of " + ss);
}
}
+ // Also update active associations.
+ for (int i=commonPkgState.mAssociations.size()-1; i>=0; i--) {
+ AssociationState as = commonPkgState.mAssociations.valueAt(i);
+ if (as.getProcess() == commonProc) {
+ if (DEBUG) Slog.d(TAG, "GETPROC switching association to cloned: "
+ + as);
+ as.setProcess(cloned);
+ } else if (DEBUG) {
+ Slog.d(TAG, "GETPROC leaving proc of " + as);
+ }
+ }
} else {
Slog.w(TAG, "Cloning proc state: no package state " + commonProc.getPackage()
- + "/" + uid + " for proc " + commonProc.getName());
+ + "/" + pkgState.mUid + " for proc " + commonProc.getName());
}
// And now make a fresh new process state for the new package name.
- ps = new ProcessState(commonProc, packageName, uid, vers, processName, now);
+ ps = new ProcessState(commonProc, pkgState.mPackageName, pkgState.mUid,
+ pkgState.mVersionCode, processName, now);
if (DEBUG) Slog.d(TAG, "GETPROC created new pkg " + ps);
}
} else {
// The common process is for multiple packages, we need to create a
// separate object for the per-package data.
- ps = new ProcessState(commonProc, packageName, uid, vers, processName,
+ ps = new ProcessState(commonProc, pkgState.mPackageName, pkgState.mUid,
+ pkgState.mVersionCode, processName,
SystemClock.uptimeMillis());
if (DEBUG) Slog.d(TAG, "GETPROC created new pkg " + ps);
}
@@ -1281,6 +1360,52 @@
return ss;
}
+ public AssociationState getAssociationStateLocked(String packageName, int uid, long vers,
+ String processName, String className) {
+ final ProcessStats.PackageState pkgs = getPackageStateLocked(packageName, uid, vers);
+ AssociationState as = pkgs.mAssociations.get(className);
+ if (as != null) {
+ if (DEBUG) Slog.d(TAG, "GETASC: returning existing " + as);
+ return as;
+ }
+ final ProcessState procs = processName != null
+ ? getProcessStateLocked(packageName, uid, vers, processName) : null;
+ as = new AssociationState(this, pkgs, className, processName, procs);
+ pkgs.mAssociations.put(className, as);
+ if (DEBUG) Slog.d(TAG, "GETASC: creating " + as + " in " + procs);
+ return as;
+ }
+
+ public void updateTrackingAssociationsLocked(int curSeq, long now) {
+ final int NUM = mTrackingAssociations.size();
+ for (int i = NUM - 1; i >= 0; i--) {
+ final AssociationState.SourceState act = mTrackingAssociations.get(i);
+ if (act.mProcStateSeq != curSeq) {
+ act.mInTrackingList = false;
+ act.mProcState = STATE_NOTHING;
+ mTrackingAssociations.remove(i);
+ } else {
+ final ProcessState proc = act.getAssociationState().getProcess();
+ if (proc != null) {
+ final int procState = proc.getCombinedState() % STATE_COUNT;
+ if (act.mProcState == procState) {
+ act.startActive(now);
+ } else {
+ act.stopActive(now);
+ if (act.mProcState < procState) {
+ Slog.w(TAG, "Tracking association " + act + " whose proc state "
+ + act.mProcState + " is better than process " + proc
+ + " proc state " + procState);
+ }
+ }
+ } else {
+ Slog.wtf(TAG, "Tracking association without process: " + act
+ + " in " + act.getAssociationState());
+ }
+ }
+ }
+ }
+
public void dumpLocked(PrintWriter pw, String reqPackage, long now, boolean dumpSummary,
boolean dumpAll, boolean activeOnly) {
long totalTime = DumpUtils.dumpSingleTime(null, null, mMemFactorDurations, mMemFactor,
@@ -1304,6 +1429,7 @@
final PackageState pkgState = vpkgs.valueAt(iv);
final int NPROCS = pkgState.mProcesses.size();
final int NSRVS = pkgState.mServices.size();
+ final int NASCS = pkgState.mAssociations.size();
final boolean pkgMatch = reqPackage == null || reqPackage.equals(pkgName);
if (!pkgMatch) {
boolean procMatch = false;
@@ -1318,7 +1444,7 @@
continue;
}
}
- if (NPROCS > 0 || NSRVS > 0) {
+ if (NPROCS > 0 || NSRVS > 0 || NASCS > 0) {
if (!printedHeader) {
if (sepNeeded) pw.println();
pw.println("Per-Package Stats:");
@@ -1368,7 +1494,7 @@
}
procs.add(proc);
}
- DumpUtils.dumpProcessSummaryLocked(pw, " ", procs,
+ DumpUtils.dumpProcessSummaryLocked(pw, " ", "Prc ", procs,
ALL_SCREEN_ADJ, ALL_MEM_ADJ, NON_CACHED_PROC_STATES,
now, totalTime);
}
@@ -1378,14 +1504,14 @@
continue;
}
if (activeOnly && !svc.isInUse()) {
- pw.print(" (Not active: ");
+ pw.print(" (Not active service: ");
pw.print(pkgState.mServices.keyAt(isvc)); pw.println(")");
continue;
}
if (dumpAll) {
pw.print(" Service ");
} else {
- pw.print(" * ");
+ pw.print(" * Svc ");
}
pw.print(pkgState.mServices.keyAt(isvc));
pw.println(":");
@@ -1393,6 +1519,27 @@
svc.dumpStats(pw, " ", " ", " ",
now, totalTime, dumpSummary, dumpAll);
}
+ for (int iasc=0; iasc<NASCS; iasc++) {
+ AssociationState asc = pkgState.mAssociations.valueAt(iasc);
+ if (!pkgMatch && !reqPackage.equals(asc.getProcessName())) {
+ continue;
+ }
+ if (activeOnly && !asc.isInUse()) {
+ pw.print(" (Not active association: ");
+ pw.print(pkgState.mAssociations.keyAt(iasc)); pw.println(")");
+ continue;
+ }
+ if (dumpAll) {
+ pw.print(" Association ");
+ } else {
+ pw.print(" * Asc ");
+ }
+ pw.print(pkgState.mAssociations.keyAt(iasc));
+ pw.println(":");
+ pw.print(" Process: "); pw.println(asc.getProcessName());
+ asc.dumpStats(pw, " ", " ", " ",
+ now, totalTime, dumpSummary, dumpAll);
+ }
}
}
}
@@ -1440,17 +1587,77 @@
proc.dumpInternalLocked(pw, " ", dumpAll);
}
}
+
if (dumpAll) {
- pw.println();
+ if (sepNeeded) {
+ pw.println();
+ }
+ sepNeeded = true;
pw.print(" Total procs: "); pw.print(numShownProcs);
pw.print(" shown of "); pw.print(numTotalProcs); pw.println(" total");
+ if (mTrackingAssociations.size() > 0) {
+ pw.println();
+ pw.println("Tracking associations:");
+ for (int i = 0; i < mTrackingAssociations.size(); i++) {
+ final AssociationState.SourceState src = mTrackingAssociations.get(i);
+ final AssociationState asc = src.getAssociationState();
+ pw.print(" #");
+ pw.print(i);
+ pw.print(": ");
+ pw.print(asc.getProcessName());
+ pw.print("/");
+ UserHandle.formatUid(pw, asc.getUid());
+ pw.print(" <- ");
+ pw.print(src.getProcessName());
+ pw.print("/");
+ UserHandle.formatUid(pw, src.getUid());
+ pw.println(":");
+ pw.print(" Tracking for: ");
+ TimeUtils.formatDuration(now - src.mTrackingUptime, pw);
+ pw.println();
+ pw.print(" Component: ");
+ pw.print(new ComponentName(asc.getPackage(), asc.getName())
+ .flattenToShortString());
+ pw.println();
+ pw.print(" Proc state: ");
+ if (src.mProcState != ProcessStats.STATE_NOTHING) {
+ pw.print(DumpUtils.STATE_NAMES[src.mProcState]);
+ } else {
+ pw.print("--");
+ }
+ pw.print(" #");
+ pw.println(src.mProcStateSeq);
+ pw.print(" Process: ");
+ pw.println(asc.getProcess());
+ if (src.mActiveCount > 0) {
+ pw.print(" Active count ");
+ pw.print(src.mActiveCount);
+ long duration = src.mActiveDuration;
+ if (src.mActiveStartUptime > 0) {
+ duration += now - src.mActiveStartUptime;
+ }
+ if (dumpAll) {
+ pw.print(" / Duration ");
+ TimeUtils.formatDuration(duration, pw);
+ pw.print(" / ");
+ } else {
+ pw.print(" / time ");
+ }
+ DumpUtils.printPercent(pw, (double)duration/(double)totalTime);
+ if (src.mActiveStartUptime > 0) {
+ pw.print(" (running)");
+ }
+ pw.println();
+ }
+ }
+ }
}
if (sepNeeded) {
pw.println();
}
if (dumpSummary) {
- pw.println("Summary:");
+ pw.println("Process summary:");
dumpSummaryLocked(pw, reqPackage, now, activeOnly);
} else {
dumpTotalsLocked(pw, now);
@@ -1466,13 +1673,15 @@
pw.print(" mRunning="); pw.println(mRunning);
}
- dumpFragmentationLocked(pw);
+ if (reqPackage == null) {
+ dumpFragmentationLocked(pw);
+ }
}
public void dumpSummaryLocked(PrintWriter pw, String reqPackage, long now, boolean activeOnly) {
long totalTime = DumpUtils.dumpSingleTime(null, null, mMemFactorDurations, mMemFactor,
mStartTime, now);
- dumpFilteredSummaryLocked(pw, null, " ", ALL_SCREEN_ADJ, ALL_MEM_ADJ,
+ dumpFilteredSummaryLocked(pw, null, " ", null, ALL_SCREEN_ADJ, ALL_MEM_ADJ,
ALL_PROC_STATES, NON_CACHED_PROC_STATES, now, totalTime, reqPackage, activeOnly);
pw.println();
dumpTotalsLocked(pw, now);
@@ -1607,7 +1816,7 @@
pw.println();
}
- void dumpFilteredSummaryLocked(PrintWriter pw, String header, String prefix,
+ void dumpFilteredSummaryLocked(PrintWriter pw, String header, String prefix, String prcLabel,
int[] screenStates, int[] memStates, int[] procStates,
int[] sortProcStates, long now, long totalTime, String reqPackage, boolean activeOnly) {
ArrayList<ProcessState> procs = collectProcessesLocked(screenStates, memStates,
@@ -1617,7 +1826,7 @@
pw.println();
pw.println(header);
}
- DumpUtils.dumpProcessSummaryLocked(pw, prefix, procs, screenStates, memStates,
+ DumpUtils.dumpProcessSummaryLocked(pw, prefix, prcLabel, procs, screenStates, memStates,
sortProcStates, now, totalTime);
}
}
@@ -1708,6 +1917,7 @@
final PackageState pkgState = vpkgs.valueAt(iv);
final int NPROCS = pkgState.mProcesses.size();
final int NSRVS = pkgState.mServices.size();
+ final int NASCS = pkgState.mAssociations.size();
for (int iproc=0; iproc<NPROCS; iproc++) {
ProcessState proc = pkgState.mProcesses.valueAt(iproc);
proc.dumpPackageProcCheckin(pw, pkgName, uid, vers,
@@ -1719,6 +1929,12 @@
final ServiceState svc = pkgState.mServices.valueAt(isvc);
svc.dumpTimesCheckin(pw, pkgName, uid, vers, serviceName, now);
}
+ for (int iasc=0; iasc<NASCS; iasc++) {
+ final String associationName = DumpUtils.collapseString(pkgName,
+ pkgState.mAssociations.keyAt(iasc));
+ final AssociationState asc = pkgState.mAssociations.valueAt(iasc);
+ asc.dumpTimesCheckin(pw, pkgName, uid, vers, associationName, now);
+ }
}
}
}
@@ -1850,6 +2066,7 @@
final public static class ProcessStateHolder {
public final long appVersion;
public ProcessState state;
+ public PackageState pkg;
public ProcessStateHolder(long _appVersion) {
appVersion = _appVersion;
@@ -1857,16 +2074,35 @@
}
public static final class PackageState {
- public final ArrayMap<String, ProcessState> mProcesses
- = new ArrayMap<String, ProcessState>();
- public final ArrayMap<String, ServiceState> mServices
- = new ArrayMap<String, ServiceState>();
+ public final ProcessStats mProcessStats;
+ public final ArrayMap<String, ProcessState> mProcesses = new ArrayMap<>();
+ public final ArrayMap<String, ServiceState> mServices = new ArrayMap<>();
+ public final ArrayMap<String, AssociationState> mAssociations = new ArrayMap<>();
public final String mPackageName;
public final int mUid;
+ public final long mVersionCode;
- public PackageState(String packageName, int uid) {
+ public PackageState(ProcessStats procStats, String packageName, int uid, long versionCode) {
+ mProcessStats = procStats;
mUid = uid;
mPackageName = packageName;
+ mVersionCode = versionCode;
+ }
+
+ public AssociationState getAssociationStateLocked(ProcessState proc, String className) {
+ AssociationState as = mAssociations.get(className);
+ if (as != null) {
+ if (DEBUG) Slog.d(TAG, "GETASC: returning existing " + as);
+ if (proc != null) {
+ as.setProcess(proc);
+ }
+ return as;
+ }
+ as = new AssociationState(mProcessStats, this, className, proc.getName(),
+ proc);
+ mAssociations.put(className, as);
+ if (DEBUG) Slog.d(TAG, "GETASC: creating " + as + " in " + proc.getName());
+ return as;
}
}
diff --git a/core/java/com/android/internal/app/procstats/ServiceState.java b/core/java/com/android/internal/app/procstats/ServiceState.java
index 650de2ea..04e61e0 100644
--- a/core/java/com/android/internal/app/procstats/ServiceState.java
+++ b/core/java/com/android/internal/app/procstats/ServiceState.java
@@ -18,30 +18,13 @@
import android.os.Parcel;
-import android.os.Parcelable;
import android.os.SystemClock;
-import android.os.SystemProperties;
-import android.os.UserHandle;
-import android.text.format.DateFormat;
-import android.util.ArrayMap;
-import android.util.ArraySet;
-import android.util.DebugUtils;
-import android.util.Log;
import android.util.Slog;
-import android.util.SparseArray;
import android.util.TimeUtils;
-import com.android.internal.app.procstats.ProcessStats;
import static com.android.internal.app.procstats.ProcessStats.STATE_NOTHING;
-import java.io.IOException;
-import java.io.InputStream;
import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.Objects;
public final class ServiceState {
private static final String TAG = "ProcessStats";
@@ -51,7 +34,8 @@
public static final int SERVICE_STARTED = 1;
public static final int SERVICE_BOUND = 2;
public static final int SERVICE_EXEC = 3;
- public static final int SERVICE_COUNT = 4;
+ public static final int SERVICE_FOREGROUND = 4;
+ public static final int SERVICE_COUNT = 5;
private final String mPackage;
private final String mProcessName;
@@ -79,6 +63,10 @@
private int mExecState = STATE_NOTHING;
private long mExecStartTime;
+ private int mForegroundCount;
+ private int mForegroundState = STATE_NOTHING;
+ private long mForegroundStartTime;
+
public ServiceState(ProcessStats processStats, String pkg, String name,
String processName, ProcessState proc) {
mPackage = pkg;
@@ -121,6 +109,9 @@
if (mExecState != ProcessStats.STATE_NOTHING) {
setExecuting(true, memFactor, now);
}
+ if (mForegroundState != ProcessStats.STATE_NOTHING) {
+ setForeground(true, memFactor, now);
+ }
}
}
@@ -133,7 +124,8 @@
// There was already an old owner, reset this object for its
// new owner.
mOwner = newOwner;
- if (mStarted || mBoundState != STATE_NOTHING || mExecState != STATE_NOTHING) {
+ if (mStarted || mBoundState != STATE_NOTHING || mExecState != STATE_NOTHING
+ || mForegroundState != STATE_NOTHING) {
long now = SystemClock.uptimeMillis();
if (mStarted) {
if (DEBUG) Slog.d(TAG, "Service has new owner " + newOwner
@@ -153,6 +145,12 @@
+ mPackage + " service=" + mName + " proc=" + mProc);
setExecuting(false, 0, now);
}
+ if (mForegroundState != STATE_NOTHING) {
+ if (DEBUG) Slog.d(TAG, "Service has new owner " + newOwner
+ + " from " + mOwner + " while foreground: pkg="
+ + mPackage + " service=" + mName + " proc=" + mProc);
+ setForeground(false, 0, now);
+ }
}
}
}
@@ -161,7 +159,8 @@
public void clearCurrentOwner(Object owner, boolean silently) {
if (mOwner == owner) {
mProc.decActiveServices(mName);
- if (mStarted || mBoundState != STATE_NOTHING || mExecState != STATE_NOTHING) {
+ if (mStarted || mBoundState != STATE_NOTHING || mExecState != STATE_NOTHING
+ || mForegroundState != STATE_NOTHING) {
long now = SystemClock.uptimeMillis();
if (mStarted) {
if (!silently) {
@@ -187,6 +186,14 @@
}
setExecuting(false, 0, now);
}
+ if (mForegroundState != STATE_NOTHING) {
+ if (!silently) {
+ Slog.wtfStack(TAG, "Service owner " + owner
+ + " cleared while foreground: pkg=" + mPackage + " service="
+ + mName + " proc=" + mProc);
+ }
+ setForeground(false, 0, now);
+ }
}
mOwner = null;
}
@@ -206,6 +213,7 @@
mStartedCount += other.mStartedCount;
mBoundCount += other.mBoundCount;
mExecCount += other.mExecCount;
+ mForegroundCount += other.mForegroundCount;
}
public void resetSafely(long now) {
@@ -214,7 +222,9 @@
mStartedCount = mStartedState != STATE_NOTHING ? 1 : 0;
mBoundCount = mBoundState != STATE_NOTHING ? 1 : 0;
mExecCount = mExecState != STATE_NOTHING ? 1 : 0;
- mRunStartTime = mStartedStartTime = mBoundStartTime = mExecStartTime = now;
+ mForegroundCount = mForegroundState != STATE_NOTHING ? 1 : 0;
+ mRunStartTime = mStartedStartTime = mBoundStartTime = mExecStartTime =
+ mForegroundStartTime = now;
}
public void writeToParcel(Parcel out, long now) {
@@ -223,6 +233,7 @@
out.writeInt(mStartedCount);
out.writeInt(mBoundCount);
out.writeInt(mExecCount);
+ out.writeInt(mForegroundCount);
}
public boolean readFromParcel(Parcel in) {
@@ -233,6 +244,7 @@
mStartedCount = in.readInt();
mBoundCount = in.readInt();
mExecCount = in.readInt();
+ mForegroundCount = in.readInt();
return true;
}
@@ -257,11 +269,17 @@
now - mExecStartTime);
mExecStartTime = now;
}
+ if (mForegroundState != STATE_NOTHING) {
+ mDurations.addDuration(SERVICE_FOREGROUND + (mForegroundState*SERVICE_COUNT),
+ now - mForegroundStartTime);
+ mForegroundStartTime = now;
+ }
}
private void updateRunning(int memFactor, long now) {
final int state = (mStartedState != STATE_NOTHING || mBoundState != STATE_NOTHING
- || mExecState != STATE_NOTHING) ? memFactor : STATE_NOTHING;
+ || mExecState != STATE_NOTHING || mForegroundState != STATE_NOTHING)
+ ? memFactor : STATE_NOTHING;
if (mRunState != state) {
if (mRunState != STATE_NOTHING) {
mDurations.addDuration(SERVICE_RUN + (mRunState*SERVICE_COUNT),
@@ -348,6 +366,24 @@
}
}
+ public void setForeground(boolean foreground, int memFactor, long now) {
+ if (mOwner == null) {
+ Slog.wtf(TAG, "Foregrounding service " + this + " without owner");
+ }
+ final int state = foreground ? memFactor : STATE_NOTHING;
+ if (mForegroundState != state) {
+ if (mForegroundState != STATE_NOTHING) {
+ mDurations.addDuration(SERVICE_FOREGROUND + (mForegroundState*SERVICE_COUNT),
+ now - mForegroundStartTime);
+ } else if (foreground) {
+ mForegroundCount++;
+ }
+ mForegroundState = state;
+ mForegroundStartTime = now;
+ updateRunning(memFactor, now);
+ }
+ }
+
public long getDuration(int opType, int curState, long startTime, int memFactor,
long now) {
int state = opType + (memFactor*SERVICE_COUNT);
@@ -366,6 +402,9 @@
dumpStats(pw, prefix, prefixInner, headerPrefix, "Started",
mStartedCount, ServiceState.SERVICE_STARTED, mStartedState,
mStartedStartTime, now, totalTime, !dumpSummary || dumpAll);
+ dumpStats(pw, prefix, prefixInner, headerPrefix, "Foreground",
+ mForegroundCount, ServiceState.SERVICE_FOREGROUND, mForegroundState,
+ mForegroundStartTime, now, totalTime, !dumpSummary || dumpAll);
dumpStats(pw, prefix, prefixInner, headerPrefix, "Bound",
mBoundCount, ServiceState.SERVICE_BOUND, mBoundState,
mBoundStartTime, now, totalTime, !dumpSummary || dumpAll);
@@ -393,11 +432,19 @@
pw.print(" op count "); pw.print(count); pw.println(":");
dumpTime(pw, prefixInner, serviceType, state, startTime, now);
} else {
- long myTime = dumpTime(null, null, serviceType, state, startTime, now);
+ long myTime = dumpTimeInternal(null, null, serviceType, state, startTime, now,
+ true);
pw.print(prefix); pw.print(headerPrefix); pw.print(header);
pw.print(" count "); pw.print(count);
pw.print(" / time ");
+ boolean isRunning = myTime < 0;
+ if (isRunning) {
+ myTime = -myTime;
+ }
DumpUtils.printPercent(pw, (double)myTime/(double)totalTime);
+ if (isRunning) {
+ pw.print(" (running)");
+ }
pw.println();
}
}
@@ -405,8 +452,14 @@
public long dumpTime(PrintWriter pw, String prefix,
int serviceType, int curState, long curStartTime, long now) {
+ return dumpTimeInternal(pw, prefix, serviceType, curState, curStartTime, now, false);
+ }
+
+ long dumpTimeInternal(PrintWriter pw, String prefix,
+ int serviceType, int curState, long curStartTime, long now, boolean negativeIfRunning) {
long totalTime = 0;
int printedScreen = -1;
+ boolean isRunning = false;
for (int iscreen=0; iscreen<ProcessStats.ADJ_COUNT; iscreen+=ProcessStats.ADJ_SCREEN_MOD) {
int printedMem = -1;
for (int imem=0; imem<ProcessStats.ADJ_MEM_FACTOR_COUNT; imem++) {
@@ -415,6 +468,7 @@
String running = "";
if (curState == state && pw != null) {
running = " (running)";
+ isRunning = true;
}
if (time != 0) {
if (pw != null) {
@@ -438,7 +492,7 @@
TimeUtils.formatDuration(totalTime, pw);
pw.println();
}
- return totalTime;
+ return (isRunning && negativeIfRunning) ? -totalTime : totalTime;
}
public void dumpTimesCheckin(PrintWriter pw, String pkgName, int uid, long vers,
@@ -447,6 +501,9 @@
ServiceState.SERVICE_RUN, mRunCount, mRunState, mRunStartTime, now);
dumpTimeCheckin(pw, "pkgsvc-start", pkgName, uid, vers, serviceName,
ServiceState.SERVICE_STARTED, mStartedCount, mStartedState, mStartedStartTime, now);
+ dumpTimeCheckin(pw, "pkgsvc-fg", pkgName, uid, vers, serviceName,
+ ServiceState.SERVICE_FOREGROUND, mForegroundCount, mForegroundState,
+ mForegroundStartTime, now);
dumpTimeCheckin(pw, "pkgsvc-bound", pkgName, uid, vers, serviceName,
ServiceState.SERVICE_BOUND, mBoundCount, mBoundState, mBoundStartTime, now);
dumpTimeCheckin(pw, "pkgsvc-exec", pkgName, uid, vers, serviceName,
@@ -497,6 +554,6 @@
public String toString() {
return "ServiceState{" + Integer.toHexString(System.identityHashCode(this))
+ " " + mName + " pkg=" + mPackage + " proc="
- + Integer.toHexString(System.identityHashCode(this)) + "}";
+ + Integer.toHexString(System.identityHashCode(mProc)) + "}";
}
}
diff --git a/core/java/com/android/internal/app/procstats/SparseMappingTable.java b/core/java/com/android/internal/app/procstats/SparseMappingTable.java
index 91b2054..6b8703d 100644
--- a/core/java/com/android/internal/app/procstats/SparseMappingTable.java
+++ b/core/java/com/android/internal/app/procstats/SparseMappingTable.java
@@ -175,7 +175,6 @@
* Get the value for the given key and offset from that key.
*
* @param key A key as obtained from getKey or getOrAddKey.
- * @param value The value to set.
*/
public long getValue(int key) {
return getValue(key, 0);
@@ -187,7 +186,6 @@
* @param key A key as obtained from getKey or getOrAddKey.
* @param index The offset from that key. Must be less than the count
* provided to getOrAddKey when the space was allocated.
- * @param value The value to set.
*
* @return the value, or 0 in case of an error
*/
diff --git a/core/java/com/android/internal/backup/LocalTransport.java b/core/java/com/android/internal/backup/LocalTransport.java
index f4b7032..d0f0272 100644
--- a/core/java/com/android/internal/backup/LocalTransport.java
+++ b/core/java/com/android/internal/backup/LocalTransport.java
@@ -32,10 +32,9 @@
import android.system.Os;
import android.system.StructStat;
import android.util.ArrayMap;
+import android.util.Base64;
import android.util.Log;
-import com.android.org.bouncycastle.util.encoders.Base64;
-
import libcore.io.IoUtils;
import java.io.BufferedOutputStream;
@@ -323,7 +322,7 @@
BackupDataInput changeSet = new BackupDataInput(data.getFileDescriptor());
while (changeSet.readNextHeader()) {
String key = changeSet.getKey();
- String base64Key = new String(Base64.encode(key.getBytes()));
+ String base64Key = new String(Base64.encode(key.getBytes(), Base64.NO_WRAP));
int dataSize = changeSet.getDataSize();
if (DEBUG) {
Log.v(TAG, " Delta operation key " + key + " size " + dataSize
@@ -705,7 +704,7 @@
public DecodedFilename(File f) {
file = f;
- key = new String(Base64.decode(f.getName()));
+ key = new String(Base64.decode(f.getName(), Base64.DEFAULT));
}
@Override
diff --git a/core/java/com/android/internal/content/FileSystemProvider.java b/core/java/com/android/internal/content/FileSystemProvider.java
index b591163..f89a9d9 100644
--- a/core/java/com/android/internal/content/FileSystemProvider.java
+++ b/core/java/com/android/internal/content/FileSystemProvider.java
@@ -136,7 +136,7 @@
return null;
}
- String mimeType = getTypeForFile(file);
+ String mimeType = getDocumentType(documentId);
if (!MetadataReader.isSupportedMimeType(mimeType)) {
return null;
}
@@ -418,7 +418,19 @@
@Override
public String getDocumentType(String documentId) throws FileNotFoundException {
final File file = getFileForDocId(documentId);
- return getTypeForFile(file);
+ if (file.isDirectory()) {
+ return Document.MIME_TYPE_DIR;
+ } else {
+ final int lastDot = documentId.lastIndexOf('.');
+ if (lastDot >= 0) {
+ final String extension = documentId.substring(lastDot + 1).toLowerCase();
+ final String mime = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
+ if (mime != null) {
+ return mime;
+ }
+ }
+ return MIMETYPE_OCTET_STREAM;
+ }
}
@Override
@@ -483,7 +495,7 @@
}
}
- final String mimeType = getTypeForFile(file);
+ final String mimeType = getDocumentType(docId);
final String displayName = file.getName();
if (mimeType.startsWith("image/")) {
flags |= Document.FLAG_SUPPORTS_THUMBNAIL;
@@ -510,31 +522,10 @@
return row;
}
- private static String getTypeForFile(File file) {
- if (file.isDirectory()) {
- return Document.MIME_TYPE_DIR;
- } else {
- return getTypeForName(file.getName());
- }
- }
-
protected boolean typeSupportsMetadata(String mimeType) {
return MetadataReader.isSupportedMimeType(mimeType);
}
- private static String getTypeForName(String name) {
- final int lastDot = name.lastIndexOf('.');
- if (lastDot >= 0) {
- final String extension = name.substring(lastDot + 1).toLowerCase();
- final String mime = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
- if (mime != null) {
- return mime;
- }
- }
-
- return MIMETYPE_OCTET_STREAM;
- }
-
protected final File getFileForDocId(String docId) throws FileNotFoundException {
return getFileForDocId(docId, false);
}
diff --git a/core/java/com/android/internal/net/NetworkStatsFactory.java b/core/java/com/android/internal/net/NetworkStatsFactory.java
index c4d08c7..d1c2799 100644
--- a/core/java/com/android/internal/net/NetworkStatsFactory.java
+++ b/core/java/com/android/internal/net/NetworkStatsFactory.java
@@ -194,7 +194,7 @@
reader.finishLine();
}
} catch (NullPointerException|NumberFormatException e) {
- throw new ProtocolException("problem parsing stats", e);
+ throw protocolExceptionWithCause("problem parsing stats", e);
} finally {
IoUtils.closeQuietly(reader);
StrictMode.setThreadPolicy(savedPolicy);
@@ -244,7 +244,7 @@
reader.finishLine();
}
} catch (NullPointerException|NumberFormatException e) {
- throw new ProtocolException("problem parsing stats", e);
+ throw protocolExceptionWithCause("problem parsing stats", e);
} finally {
IoUtils.closeQuietly(reader);
StrictMode.setThreadPolicy(savedPolicy);
@@ -341,7 +341,7 @@
reader.finishLine();
}
} catch (NullPointerException|NumberFormatException e) {
- throw new ProtocolException("problem parsing idx " + idx, e);
+ throw protocolExceptionWithCause("problem parsing idx " + idx, e);
} finally {
IoUtils.closeQuietly(reader);
StrictMode.setThreadPolicy(savedPolicy);
@@ -378,4 +378,10 @@
@VisibleForTesting
public static native int nativeReadNetworkStatsDev(NetworkStats stats);
+
+ private static ProtocolException protocolExceptionWithCause(String message, Throwable cause) {
+ ProtocolException pe = new ProtocolException(message);
+ pe.initCause(cause);
+ return pe;
+ }
}
diff --git a/core/java/com/android/internal/os/BatteryStatsHistory.java b/core/java/com/android/internal/os/BatteryStatsHistory.java
new file mode 100644
index 0000000..24ad751
--- /dev/null
+++ b/core/java/com/android/internal/os/BatteryStatsHistory.java
@@ -0,0 +1,455 @@
+/*
+ * 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.os;
+
+import android.os.BatteryStats;
+import android.os.Parcel;
+import android.os.StatFs;
+import android.os.SystemClock;
+import android.util.ArraySet;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ParseUtils;
+
+import java.io.File;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * BatteryStatsHistory encapsulates battery history files.
+ * Battery history record is appended into buffer {@link #mHistoryBuffer} and backed up into
+ * {@link #mActiveFile}.
+ * When {@link #mHistoryBuffer} size reaches {@link BatteryStatsImpl.Constants#MAX_HISTORY_BUFFER},
+ * current mActiveFile is closed and a new mActiveFile is open.
+ * History files are under directory /data/system/battery-history/.
+ * History files have name battery-history-<num>.bin. The file number <num> starts from zero and
+ * grows sequentially.
+ * The mActiveFile is always the highest numbered history file.
+ * The lowest number file is always the oldest file.
+ * The highest number file is always the newest file.
+ * The file number grows sequentially and we never skip number.
+ * When count of history files exceeds {@link BatteryStatsImpl.Constants#MAX_HISTORY_FILES},
+ * the lowest numbered file is deleted and a new file is open.
+ *
+ * All interfaces in BatteryStatsHistory should only be called by BatteryStatsImpl and protected by
+ * locks on BatteryStatsImpl object.
+ */
+@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+public class BatteryStatsHistory {
+ private static final boolean DEBUG = false;
+ private static final String TAG = "BatteryStatsHistory";
+ public static final String HISTORY_DIR = "battery-history";
+ public static final String FILE_SUFFIX = ".bin";
+ private static final int MIN_FREE_SPACE = 100 * 1024 * 1024;
+
+ private final BatteryStatsImpl mStats;
+ private final Parcel mHistoryBuffer;
+ private final File mHistoryDir;
+ /**
+ * The active history file that the history buffer is backed up into.
+ */
+ private AtomicFile mActiveFile;
+ /**
+ * A list of history files with incremental indexes.
+ */
+ private final List<Integer> mFileNumbers = new ArrayList<>();
+
+ /**
+ * A list of small history parcels, used when BatteryStatsImpl object is created from
+ * deserialization of a parcel, such as Settings app or checkin file.
+ */
+ private List<Parcel> mHistoryParcels = null;
+
+ /**
+ * When iterating history files, the current file index.
+ */
+ private int mCurrentFileIndex;
+ /**
+ * When iterating history files, the current file parcel.
+ */
+ private Parcel mCurrentParcel;
+ /**
+ * When iterating history file, the current parcel's Parcel.dataSize().
+ */
+ private int mCurrentParcelEnd;
+ /**
+ * When iterating history files, the current record count.
+ */
+ private int mRecordCount = 0;
+ /**
+ * Used when BatteryStatsImpl object is created from deserialization of a parcel,
+ * such as Settings app or checkin file, to iterate over history parcels.
+ */
+ private int mParcelIndex = 0;
+
+ /**
+ * Constructor
+ * @param stats BatteryStatsImpl object.
+ * @param systemDir typically /data/system
+ * @param historyBuffer The in-memory history buffer.
+ */
+ public BatteryStatsHistory(BatteryStatsImpl stats, File systemDir, Parcel historyBuffer) {
+ mStats = stats;
+ mHistoryBuffer = historyBuffer;
+ mHistoryDir = new File(systemDir, HISTORY_DIR);
+ mHistoryDir.mkdirs();
+ if (!mHistoryDir.exists()) {
+ Slog.wtf(TAG, "HistoryDir does not exist:" + mHistoryDir.getPath());
+ }
+
+ final Set<Integer> dedup = new ArraySet<>();
+ // scan directory, fill mFileNumbers and mActiveFile.
+ mHistoryDir.listFiles(new FilenameFilter() {
+ @Override
+ public boolean accept(File dir, String name) {
+ final int b = name.lastIndexOf(FILE_SUFFIX);
+ if (b <= 0) {
+ return false;
+ }
+ final Integer c =
+ ParseUtils.parseInt(name.substring(0, b), -1);
+ if (c != -1) {
+ dedup.add(c);
+ return true;
+ } else {
+ return false;
+ }
+ }
+ });
+ if (!dedup.isEmpty()) {
+ mFileNumbers.addAll(dedup);
+ Collections.sort(mFileNumbers);
+ } else {
+ // No file found, default to have file 0.
+ mFileNumbers.add(0);
+ }
+ createActiveFile();
+ }
+
+ /**
+ * Used when BatteryStatsImpl object is created from deserialization of a parcel,
+ * such as Settings app or checkin file.
+ * @param stats BatteryStatsImpl object.
+ * @param historyBuffer the history buffer inside BatteryStatsImpl
+ */
+ public BatteryStatsHistory(BatteryStatsImpl stats, Parcel historyBuffer) {
+ mStats = stats;
+ mHistoryDir = null;
+ mHistoryBuffer = historyBuffer;
+ }
+ /**
+ * The highest numbered history file is active file that mHistoryBuffer is backed up into.
+ * If file does not exists, truncate() creates a empty file.
+ */
+ private void createActiveFile() {
+ final AtomicFile file = getFile(mFileNumbers.get(mFileNumbers.size() - 1));
+ if (DEBUG) {
+ Slog.d(TAG, "activeHistoryFile:" + file.getBaseFile().getPath());
+ }
+ if (!file.exists()) {
+ try {
+ file.truncate();
+ } catch (IOException e) {
+ Slog.e(TAG, "Error creating history file "+ file.getBaseFile().getPath(), e);
+ }
+ }
+ mActiveFile = file;
+ }
+
+ /**
+ * Create history AtomicFile from file number.
+ * @param num file number.
+ * @return AtomicFile object.
+ */
+ private AtomicFile getFile(int num) {
+ return new AtomicFile(
+ new File(mHistoryDir, num + FILE_SUFFIX));
+ }
+
+ /**
+ * When {@link #mHistoryBuffer} reaches {@link BatteryStatsImpl.Constants#MAX_HISTORY_BUFFER},
+ * create next history file.
+ */
+ public void createNextFile() {
+ if (mFileNumbers.isEmpty()) {
+ Slog.wtf(TAG, "mFileNumbers should never be empty");
+ return;
+ }
+ // The last number in mFileNumbers is the highest number. The next file number is highest
+ // number plus one.
+ final int next = mFileNumbers.get(mFileNumbers.size() - 1) + 1;
+ mFileNumbers.add(next);
+ createActiveFile();
+
+ // if free disk space is less than 100MB, delete oldest history file.
+ if (!hasFreeDiskSpace()) {
+ int oldest = mFileNumbers.remove(0);
+ getFile(oldest).delete();
+ }
+
+ // if there are more history files than allowed, delete oldest history files.
+ // MAX_HISTORY_FILES can be updated by GService config at run time.
+ while (mFileNumbers.size() > mStats.mConstants.MAX_HISTORY_FILES) {
+ int oldest = mFileNumbers.get(0);
+ getFile(oldest).delete();
+ mFileNumbers.remove(0);
+ }
+ }
+
+ /**
+ * Delete all existing history files. Active history file start from number 0 again.
+ */
+ public void resetAllFiles() {
+ for (Integer i : mFileNumbers) {
+ getFile(i).delete();
+ }
+ mFileNumbers.clear();
+ mFileNumbers.add(0);
+ createActiveFile();
+ }
+
+ /**
+ * Start iterating history files and history buffer.
+ * @return always return true.
+ */
+ public boolean startIteratingHistory() {
+ mRecordCount = 0;
+ mCurrentFileIndex = 0;
+ mCurrentParcel = null;
+ mCurrentParcelEnd = 0;
+ mParcelIndex = 0;
+ return true;
+ }
+
+ /**
+ * Finish iterating history files and history buffer.
+ */
+ public void finishIteratingHistory() {
+ // setDataPosition so mHistoryBuffer Parcel can be written.
+ mHistoryBuffer.setDataPosition(mHistoryBuffer.dataSize());
+ if (DEBUG) {
+ Slog.d(TAG, "Battery history records iterated: " + mRecordCount);
+ }
+ }
+
+ /**
+ * When iterating history files and history buffer, always start from the lowest numbered
+ * history file, when reached the mActiveFile (highest numbered history file), do not read from
+ * mActiveFile, read from history buffer instead because the buffer has more updated data.
+ * @param out a history item.
+ * @return The parcel that has next record. null if finished all history files and history
+ * buffer
+ */
+ public Parcel getNextParcel(BatteryStats.HistoryItem out) {
+ if (mRecordCount == 0) {
+ // reset out if it is the first record.
+ out.clear();
+ }
+ ++mRecordCount;
+
+ // First iterate through all records in current parcel.
+ if (mCurrentParcel != null)
+ {
+ if (mCurrentParcel.dataPosition() < mCurrentParcelEnd) {
+ // There are more records in current parcel.
+ return mCurrentParcel;
+ } else if (mHistoryBuffer == mCurrentParcel) {
+ // finished iterate through all history files and history buffer.
+ return null;
+ } else if (mHistoryParcels == null
+ || !mHistoryParcels.contains(mCurrentParcel)) {
+ // current parcel is from history file.
+ mCurrentParcel.recycle();
+ }
+ }
+
+ // Try next available history file.
+ // skip the last file because its data is in history buffer.
+ while (mCurrentFileIndex < mFileNumbers.size() - 1) {
+ mCurrentParcel = null;
+ mCurrentParcelEnd = 0;
+ final Parcel p = Parcel.obtain();
+ AtomicFile file = getFile(mFileNumbers.get(mCurrentFileIndex++));
+ if (readFileToParcel(p, file)) {
+ int bufSize = p.readInt();
+ int curPos = p.dataPosition();
+ mCurrentParcelEnd = curPos + bufSize;
+ mCurrentParcel = p;
+ if (curPos < mCurrentParcelEnd) {
+ return mCurrentParcel;
+ }
+ } else {
+ p.recycle();
+ }
+ }
+
+ // mHistoryParcels is created when BatteryStatsImpl object is created from deserialization
+ // of a parcel, such as Settings app or checkin file.
+ if (mHistoryParcels != null) {
+ while (mParcelIndex < mHistoryParcels.size()) {
+ final Parcel p = mHistoryParcels.get(mParcelIndex++);
+ if (!skipHead(p)) {
+ continue;
+ }
+ final int bufSize = p.readInt();
+ final int curPos = p.dataPosition();
+ mCurrentParcelEnd = curPos + bufSize;
+ mCurrentParcel = p;
+ if (curPos < mCurrentParcelEnd) {
+ return mCurrentParcel;
+ }
+ }
+ }
+
+ // finished iterator through history files (except the last one), now history buffer.
+ if (mHistoryBuffer.dataSize() <= 0) {
+ // buffer is empty.
+ return null;
+ }
+ mHistoryBuffer.setDataPosition(0);
+ mCurrentParcel = mHistoryBuffer;
+ mCurrentParcelEnd = mCurrentParcel.dataSize();
+ return mCurrentParcel;
+ }
+
+ /**
+ * Read history file into a parcel.
+ * @param out the Parcel read into.
+ * @param file the File to read from.
+ * @return true if success, false otherwise.
+ */
+ public boolean readFileToParcel(Parcel out, AtomicFile file) {
+ byte[] raw = null;
+ try {
+ final long start = SystemClock.uptimeMillis();
+ raw = file.readFully();
+ if (DEBUG) {
+ Slog.d(TAG, "readFileToParcel:" + file.getBaseFile().getPath()
+ + " duration ms:" + (SystemClock.uptimeMillis() - start));
+ }
+ } catch(Exception e) {
+ Slog.e(TAG, "Error reading file "+ file.getBaseFile().getPath(), e);
+ return false;
+ }
+ out.unmarshall(raw, 0, raw.length);
+ out.setDataPosition(0);
+ return skipHead(out);
+ }
+
+ /**
+ * Skip the header part of history parcel.
+ * @param p history parcel to skip head.
+ * @return true if version match, false if not.
+ */
+ private boolean skipHead(Parcel p) {
+ p.setDataPosition(0);
+ final int version = p.readInt();
+ if (version != mStats.VERSION) {
+ return false;
+ }
+ // skip historyBaseTime field.
+ p.readLong();
+ return true;
+ }
+
+ /**
+ * Read all history files and serialize into a big Parcel. This is to send history files to
+ * Settings app since Settings app can not access /data/system directory.
+ * Checkin file also call this method.
+ * @param out the output parcel
+ */
+ public void writeToParcel(Parcel out) {
+ final long start = SystemClock.uptimeMillis();
+ out.writeInt(mFileNumbers.size() - 1);
+ for(int i = 0; i < mFileNumbers.size() - 1; i++) {
+ AtomicFile file = getFile(mFileNumbers.get(i));
+ byte[] raw = new byte[0];
+ try {
+ raw = file.readFully();
+ } catch(Exception e) {
+ Slog.e(TAG, "Error reading file "+ file.getBaseFile().getPath(), e);
+ }
+ out.writeByteArray(raw);
+ }
+ if (DEBUG) {
+ Slog.d(TAG, "writeToParcel duration ms:" + (SystemClock.uptimeMillis() - start));
+ }
+ }
+
+ /**
+ * This is for Settings app, when Settings app receives big history parcel, it call
+ * this method to parse it into list of parcels.
+ * Checkin file also call this method.
+ * @param in the input parcel.
+ */
+ public void readFromParcel(Parcel in) {
+ final long start = SystemClock.uptimeMillis();
+ mHistoryParcels = new ArrayList<>();
+ final int count = in.readInt();
+ for(int i = 0; i < count; i++) {
+ byte[] temp = in.createByteArray();
+ if (temp.length == 0) {
+ continue;
+ }
+ Parcel p = Parcel.obtain();
+ p.unmarshall(temp, 0, temp.length);
+ p.setDataPosition(0);
+ mHistoryParcels.add(p);
+ }
+ if (DEBUG) {
+ Slog.d(TAG, "readFromParcel duration ms:" + (SystemClock.uptimeMillis() - start));
+ }
+ }
+
+ /**
+ * @return true if there is more than 100MB free disk space left.
+ */
+ private boolean hasFreeDiskSpace() {
+ final StatFs stats = new StatFs(mHistoryDir.getAbsolutePath());
+ return stats.getAvailableBytes() > MIN_FREE_SPACE;
+ }
+
+ public List<Integer> getFilesNumbers() {
+ return mFileNumbers;
+ }
+
+ public AtomicFile getActiveFile() {
+ return mActiveFile;
+ }
+
+ /**
+ * @return the total size of all history files and history buffer.
+ */
+ public int getHistoryUsedSize() {
+ int ret = 0;
+ for(int i = 0; i < mFileNumbers.size() - 1; i++) {
+ ret += getFile(mFileNumbers.get(i)).getBaseFile().length();
+ }
+ ret += mHistoryBuffer.dataSize();
+ if (mHistoryParcels != null) {
+ for(int i = 0; i < mHistoryParcels.size(); i++) {
+ ret += mHistoryParcels.get(i).dataSize();
+ }
+ }
+ return ret;
+ }
+}
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index d7db2e2..e12b913 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -64,6 +64,7 @@
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.IntArray;
import android.util.KeyValueListParser;
import android.util.Log;
@@ -90,7 +91,6 @@
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FastPrintWriter;
import com.android.internal.util.FastXmlSerializer;
-import com.android.internal.util.JournaledFile;
import com.android.internal.util.XmlUtils;
import libcore.util.EmptyArray;
@@ -107,6 +107,7 @@
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
+import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
@@ -140,35 +141,18 @@
private static final int MAGIC = 0xBA757475; // 'BATSTATS'
// Current on-disk Parcel version
- private static final int VERSION = 177 + (USE_OLD_HISTORY ? 1000 : 0);
-
- // Maximum number of items we will record in the history.
- private static final int MAX_HISTORY_ITEMS;
-
- // No, really, THIS is the maximum number of items we will record in the history.
- private static final int MAX_MAX_HISTORY_ITEMS;
+ static final int VERSION = 185 + (USE_OLD_HISTORY ? 1000 : 0);
// The maximum number of names wakelocks we will keep track of
// per uid; once the limit is reached, we batch the remaining wakelocks
// in to one common name.
private static final int MAX_WAKELOCKS_PER_UID;
- static final int MAX_HISTORY_BUFFER; // 256KB
- static final int MAX_MAX_HISTORY_BUFFER; // 320KB
-
static {
if (ActivityManager.isLowRamDeviceStatic()) {
- MAX_HISTORY_ITEMS = 800;
- MAX_MAX_HISTORY_ITEMS = 1200;
MAX_WAKELOCKS_PER_UID = 40;
- MAX_HISTORY_BUFFER = 96*1024; // 96KB
- MAX_MAX_HISTORY_BUFFER = 128*1024; // 128KB
} else {
- 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
}
}
@@ -189,7 +173,7 @@
protected Clocks mClocks;
- private final JournaledFile mFile;
+ private final AtomicFile mStatsFile;
public final AtomicFile mCheckinFile;
public final AtomicFile mDailyFile;
@@ -229,6 +213,15 @@
public boolean mPerProcStateCpuTimesAvailable = true;
/**
+ * When per process state cpu times tracking is off, cpu times in KernelSingleUidTimeReader are
+ * not updated. So, when the setting is turned on later, we would end up with huge cpu time
+ * deltas. This flag tracks the case where tracking is turned on from off so that we won't
+ * end up attributing the huge deltas to wrong buckets.
+ */
+ @GuardedBy("this")
+ private boolean mIsPerProcessStateCpuDataStale;
+
+ /**
* Uids for which per-procstate cpu times need to be updated.
*
* Contains uid -> procState mappings.
@@ -401,7 +394,7 @@
}
// If the KernelSingleUidTimeReader has stale cpu times, then we shouldn't try to
// compute deltas since it might result in mis-attributing cpu times to wrong states.
- if (mKernelSingleUidTimeReader.hasStaleData()) {
+ if (mIsPerProcessStateCpuDataStale) {
mPendingUids.clear();
return;
}
@@ -484,9 +477,9 @@
mKernelUidCpuFreqTimeReader.getAllUidCpuFreqTimeMs();
// If the KernelSingleUidTimeReader has stale cpu times, then we shouldn't try to
// compute deltas since it might result in mis-attributing cpu times to wrong states.
- if (mKernelSingleUidTimeReader.hasStaleData()) {
+ if (mIsPerProcessStateCpuDataStale) {
mKernelSingleUidTimeReader.setAllUidsCpuTimesMs(allUidCpuFreqTimesMs);
- mKernelSingleUidTimeReader.markDataAsStale(false);
+ mIsPerProcessStateCpuDataStale = false;
mPendingUids.clear();
return;
}
@@ -663,13 +656,14 @@
int mNextHistoryTagIdx = 0;
int mNumHistoryTagChars = 0;
int mHistoryBufferLastPos = -1;
- boolean mHistoryOverflow = false;
int mActiveHistoryStates = 0xffffffff;
int mActiveHistoryStates2 = 0xffffffff;
long mLastHistoryElapsedRealtime = 0;
long mTrackRunningHistoryElapsedRealtime = 0;
long mTrackRunningHistoryUptime = 0;
+ final BatteryStatsHistory mBatteryStatsHistory;
+
final HistoryItem mHistoryCur = new HistoryItem();
HistoryItem mHistory;
@@ -963,7 +957,7 @@
protected PowerProfile mPowerProfile;
@GuardedBy("this")
- private final Constants mConstants;
+ final Constants mConstants;
/*
* Holds a SamplingTimer associated with each Resource Power Manager state and voter,
@@ -1047,9 +1041,10 @@
public BatteryStatsImpl(Clocks clocks) {
init(clocks);
- mFile = null;
+ mStatsFile = null;
mCheckinFile = null;
mDailyFile = null;
+ mBatteryStatsHistory = null;
mHandler = null;
mPlatformIdleStateCallback = null;
mUserInfoProvider = null;
@@ -1068,7 +1063,34 @@
// methods are protected not private to be VisibleForTesting
public static class TimeBase {
- protected final ArrayList<TimeBaseObs> mObservers = new ArrayList<>();
+ private static class ObsWeakReference extends WeakReference<TimeBaseObs> {
+ public ObsWeakReference(TimeBaseObs referent) {
+ super(referent);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!(obj instanceof ObsWeakReference)) {
+ return false;
+ }
+ return this.get() == ((ObsWeakReference)obj).get();
+ }
+
+ @Override
+ public int hashCode() {
+ TimeBaseObs obs = get();
+ if (obs != null) {
+ return obs.hashCode();
+ } else {
+ return 0;
+ }
+ }
+ }
+
+ protected final ArraySet<ObsWeakReference> mObservers = new ArraySet<>();
protected long mUptime;
protected long mRealtime;
@@ -1112,17 +1134,17 @@
}
public void add(TimeBaseObs observer) {
- mObservers.add(observer);
+ mObservers.add(new ObsWeakReference(observer));
}
public void remove(TimeBaseObs observer) {
- if (!mObservers.remove(observer)) {
+ if (!mObservers.remove(new ObsWeakReference(observer))) {
Slog.wtf(TAG, "Removed unknown observer: " + observer);
}
}
public boolean hasObserver(TimeBaseObs observer) {
- return mObservers.contains(observer);
+ return mObservers.contains(new ObsWeakReference(observer));
}
public void init(long uptime, long realtime) {
@@ -1204,6 +1226,8 @@
}
public boolean setRunning(boolean running, long uptime, long realtime) {
+ int total = mObservers.size();
+ int removed = 0;
if (mRunning != running) {
mRunning = running;
if (running) {
@@ -1211,21 +1235,34 @@
mRealtimeStart = realtime;
long batteryUptime = mUnpluggedUptime = getUptime(uptime);
long batteryRealtime = mUnpluggedRealtime = getRealtime(realtime);
-
- for (int i = mObservers.size() - 1; i >= 0; i--) {
- mObservers.get(i).onTimeStarted(realtime, batteryUptime, batteryRealtime);
+ for(int i = mObservers.size() - 1; i >= 0; i--) {
+ final TimeBaseObs obs = mObservers.valueAt(i).get();
+ if (obs != null) {
+ obs.onTimeStarted(realtime, batteryUptime, batteryRealtime);
+ } else {
+ mObservers.removeAt(i);
+ removed++;
+ }
}
} else {
mPastUptime += uptime - mUptimeStart;
mPastRealtime += realtime - mRealtimeStart;
-
long batteryUptime = getUptime(uptime);
long batteryRealtime = getRealtime(realtime);
-
- for (int i = mObservers.size() - 1; i >= 0; i--) {
- mObservers.get(i).onTimeStopped(realtime, batteryUptime, batteryRealtime);
+ for(int i = mObservers.size() - 1; i >= 0; i--) {
+ final TimeBaseObs obs = mObservers.valueAt(i).get();
+ if (obs != null) {
+ obs.onTimeStopped(realtime, batteryUptime, batteryRealtime);
+ } else {
+ mObservers.removeAt(i);
+ removed++;
+ }
}
}
+ if (DEBUG && removed != 0) {
+ Slog.d(TAG,
+ "TimeBase observer removed:" + removed + " from total:" + total);
+ }
return true;
}
return false;
@@ -3154,8 +3191,13 @@
}
private void readHistoryTag(int index, HistoryTag tag) {
- tag.string = mReadHistoryStrings[index];
- tag.uid = mReadHistoryUids[index];
+ if (index < mReadHistoryStrings.length) {
+ tag.string = mReadHistoryStrings[index];
+ tag.uid = mReadHistoryUids[index];
+ } else {
+ tag.string = null;
+ tag.uid = 0;
+ }
tag.poolIdx = index;
}
@@ -3674,6 +3716,13 @@
mHistoryLastWritten.cmd = HistoryItem.CMD_NULL;
}
+ public void createFakeHistoryEvents(long numEvents) {
+ for(long i = 0; i < numEvents; i++) {
+ noteWifiOnLocked();
+ noteWifiOffLocked();
+ }
+ }
+
void addHistoryBufferLocked(long elapsedRealtimeMs, HistoryItem cur) {
if (!mHaveBatteryLevel || !mRecordingHistory) {
return;
@@ -3736,74 +3785,32 @@
}
mHistoryLastWritten.setTo(mHistoryLastLastWritten);
}
-
- boolean recordResetDueToOverflow = false;
final int dataSize = mHistoryBuffer.dataSize();
- if (dataSize >= MAX_MAX_HISTORY_BUFFER*3) {
- // Clients can't deal with history buffers this large. This only
- // really happens when the device is on charger and interacted with
- // for long periods of time, like in retail mode. Since the device is
- // most likely charged, when unplugged, stats would have reset anyways.
- // Reset the stats and mark that we overflowed.
- // b/32540341
- resetAllStatsLocked();
- // Mark that we want to set *OVERFLOW* event and the RESET:START
- // events.
- recordResetDueToOverflow = true;
-
- } else if (dataSize >= MAX_HISTORY_BUFFER) {
- if (!mHistoryOverflow) {
- mHistoryOverflow = true;
- addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_UPDATE, cur);
- addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_OVERFLOW, cur);
- return;
+ if (dataSize >= mConstants.MAX_HISTORY_BUFFER) {
+ //open a new history file.
+ final long start = SystemClock.uptimeMillis();
+ writeHistoryLocked(true);
+ if (DEBUG) {
+ Slog.d(TAG, "addHistoryBufferLocked writeHistoryLocked takes ms:"
+ + (SystemClock.uptimeMillis() - start));
}
-
- // After overflow, we allow various bit-wise states to settle to 0.
- boolean writeAnyway = false;
- final int curStates = cur.states & HistoryItem.SETTLE_TO_ZERO_STATES
- & mActiveHistoryStates;
- if (mHistoryLastWritten.states != curStates) {
- // mActiveHistoryStates keeps track of which bits in .states are now being
- // forced to 0.
- int old = mActiveHistoryStates;
- mActiveHistoryStates &= curStates | ~HistoryItem.SETTLE_TO_ZERO_STATES;
- writeAnyway |= old != mActiveHistoryStates;
- }
- final int curStates2 = cur.states2 & HistoryItem.SETTLE_TO_ZERO_STATES2
- & mActiveHistoryStates2;
- if (mHistoryLastWritten.states2 != curStates2) {
- // mActiveHistoryStates2 keeps track of which bits in .states2 are now being
- // forced to 0.
- int old = mActiveHistoryStates2;
- mActiveHistoryStates2 &= curStates2 | ~HistoryItem.SETTLE_TO_ZERO_STATES2;
- writeAnyway |= old != mActiveHistoryStates2;
- }
-
- // Once we've reached the maximum number of items, we only
- // record changes to the battery level and the most interesting states.
- // Once we've reached the maximum maximum number of items, we only
- // record changes to the battery level.
- if (!writeAnyway && mHistoryLastWritten.batteryLevel == cur.batteryLevel &&
- (dataSize >= MAX_MAX_HISTORY_BUFFER
- || ((mHistoryLastWritten.states^cur.states)
- & HistoryItem.MOST_INTERESTING_STATES) == 0
- || ((mHistoryLastWritten.states2^cur.states2)
- & HistoryItem.MOST_INTERESTING_STATES2) == 0)) {
- return;
- }
+ mBatteryStatsHistory.createNextFile();
+ mHistoryBuffer.setDataSize(0);
+ mHistoryBuffer.setDataPosition(0);
+ mHistoryBuffer.setDataCapacity(mConstants.MAX_HISTORY_BUFFER / 2);
+ mHistoryBufferLastPos = -1;
+ final long elapsedRealtime = mClocks.elapsedRealtime();
+ final long uptime = mClocks.uptimeMillis();
+ startRecordingHistory(elapsedRealtime, uptime, false);
addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_UPDATE, cur);
return;
}
- if (dataSize == 0 || recordResetDueToOverflow) {
+ if (dataSize == 0) {
// The history is currently empty; we need it to start with a time stamp.
cur.currentTime = System.currentTimeMillis();
- if (recordResetDueToOverflow) {
- addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_OVERFLOW, cur);
- }
addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_RESET, cur);
}
addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_UPDATE, cur);
@@ -3891,26 +3898,6 @@
mChangedStates = 0;
mChangedStates2 = 0;
-
- if (mNumHistoryItems == MAX_HISTORY_ITEMS
- || mNumHistoryItems == MAX_MAX_HISTORY_ITEMS) {
- addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_OVERFLOW, cur);
- }
-
- if (mNumHistoryItems >= MAX_HISTORY_ITEMS) {
- // Once we've reached the maximum number of items, we only
- // record changes to the battery level and the most interesting states.
- // Once we've reached the maximum maximum number of items, we only
- // record changes to the battery level.
- if (mHistoryEnd != null && mHistoryEnd.batteryLevel
- == cur.batteryLevel &&
- (mNumHistoryItems >= MAX_MAX_HISTORY_ITEMS
- || ((mHistoryEnd.states^(cur.states&mActiveHistoryStates))
- & HistoryItem.MOST_INTERESTING_STATES) == 0)) {
- return;
- }
- }
-
addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_UPDATE, cur);
}
@@ -3965,14 +3952,13 @@
mHistoryBuffer.setDataSize(0);
mHistoryBuffer.setDataPosition(0);
- mHistoryBuffer.setDataCapacity(MAX_HISTORY_BUFFER / 2);
+ mHistoryBuffer.setDataCapacity(mConstants.MAX_HISTORY_BUFFER / 2);
mHistoryLastLastWritten.clear();
mHistoryLastWritten.clear();
mHistoryTagPool.clear();
mNextHistoryTagIdx = 0;
mNumHistoryTagChars = 0;
mHistoryBufferLastPos = -1;
- mHistoryOverflow = false;
mActiveHistoryStates = 0xffffffff;
mActiveHistoryStates2 = 0xffffffff;
}
@@ -4021,7 +4007,9 @@
try {
IBatteryPropertiesRegistrar registrar = IBatteryPropertiesRegistrar.Stub.asInterface(
ServiceManager.getService("batteryproperties"));
- registrar.scheduleUpdate();
+ if (registrar != null) {
+ registrar.scheduleUpdate();
+ }
} catch (RemoteException e) {
// Ignore.
}
@@ -10081,11 +10069,13 @@
UserInfoProvider userInfoProvider) {
init(clocks);
- if (systemDir != null) {
- mFile = new JournaledFile(new File(systemDir, "batterystats.bin"),
- new File(systemDir, "batterystats.bin.tmp"));
+
+ if (systemDir == null) {
+ mStatsFile = null;
+ mBatteryStatsHistory = new BatteryStatsHistory(this, mHistoryBuffer);
} else {
- mFile = null;
+ mStatsFile = new AtomicFile(new File(systemDir, "batterystats.bin"));
+ mBatteryStatsHistory = new BatteryStatsHistory(this, systemDir, mHistoryBuffer);
}
mCheckinFile = new AtomicFile(new File(systemDir, "batterystats-checkin.bin"));
mDailyFile = new AtomicFile(new File(systemDir, "batterystats-daily.xml"));
@@ -10186,13 +10176,14 @@
public BatteryStatsImpl(Clocks clocks, Parcel p) {
init(clocks);
- mFile = null;
+ mStatsFile = null;
mCheckinFile = null;
mDailyFile = null;
mHandler = null;
mExternalSync = null;
mConstants = new Constants(mHandler);
clearHistoryLocked();
+ mBatteryStatsHistory = new BatteryStatsHistory(this, mHistoryBuffer);
readFromParcel(p);
mPlatformIdleStateCallback = null;
}
@@ -10309,8 +10300,6 @@
stream = mDailyFile.startWrite();
memStream.writeTo(stream);
stream.flush();
- FileUtils.sync(stream);
- stream.close();
mDailyFile.finishWrite(stream);
com.android.internal.logging.EventLogTags.writeCommitSysConfigFile(
"batterystats-daily",
@@ -10614,21 +10603,16 @@
}
public int getHistoryTotalSize() {
- return MAX_HISTORY_BUFFER;
+ return mConstants.MAX_HISTORY_BUFFER * mConstants.MAX_HISTORY_FILES;
}
public int getHistoryUsedSize() {
- return mHistoryBuffer.dataSize();
+ return mBatteryStatsHistory.getHistoryUsedSize();
}
@Override
public boolean startIteratingHistoryLocked() {
- if (DEBUG_HISTORY) Slog.i(TAG, "ITERATING: buff size=" + mHistoryBuffer.dataSize()
- + " pos=" + mHistoryBuffer.dataPosition());
- if (mHistoryBuffer.dataSize() <= 0) {
- return false;
- }
- mHistoryBuffer.setDataPosition(0);
+ mBatteryStatsHistory.startIteratingHistory();
mReadOverflow = false;
mIteratingHistory = true;
mReadHistoryStrings = new String[mHistoryTagPool.size()];
@@ -10668,18 +10652,13 @@
@Override
public boolean getNextHistoryLocked(HistoryItem out) {
- final int pos = mHistoryBuffer.dataPosition();
- if (pos == 0) {
- out.clear();
- }
- boolean end = pos >= mHistoryBuffer.dataSize();
- if (end) {
+ Parcel p = mBatteryStatsHistory.getNextParcel(out);
+ if (p == null) {
return false;
}
-
final long lastRealtime = out.time;
final long lastWalltime = out.currentTime;
- readHistoryDelta(mHistoryBuffer, out);
+ readHistoryDelta(p, out);
if (out.cmd != HistoryItem.CMD_CURRENT_TIME
&& out.cmd != HistoryItem.CMD_RESET && lastWalltime != 0) {
out.currentTime = lastWalltime + (out.time - lastRealtime);
@@ -10689,9 +10668,10 @@
@Override
public void finishIteratingHistoryLocked() {
+ mBatteryStatsHistory.finishIteratingHistory();
mIteratingHistory = false;
- mHistoryBuffer.setDataPosition(mHistoryBuffer.dataSize());
mReadHistoryStrings = null;
+ mReadHistoryUids = null;
}
@Override
@@ -10916,6 +10896,8 @@
initDischarge();
clearHistoryLocked();
+ mBatteryStatsHistory.resetAllFiles();
+
mHandler.sendEmptyMessage(MSG_REPORT_RESET_STATS);
}
@@ -12340,9 +12322,7 @@
boolean reset = false;
if (!mNoAutoReset && (oldStatus == BatteryManager.BATTERY_STATUS_FULL
|| level >= 90
- || (mDischargeCurrentLevel < 20 && level >= 80)
- || (getHighDischargeAmountSinceCharge() >= 200
- && mHistoryBuffer.dataSize() >= MAX_HISTORY_BUFFER))) {
+ || (mDischargeCurrentLevel < 20 && level >= 80))) {
Slog.i(TAG, "Resetting battery stats: level=" + level + " status=" + oldStatus
+ " dischargeLevel=" + mDischargeCurrentLevel
+ " lowAmount=" + getLowDischargeAmountSinceCharge()
@@ -12364,8 +12344,6 @@
stream = mCheckinFile.startWrite();
stream.write(parcel.marshall());
stream.flush();
- FileUtils.sync(stream);
- stream.close();
mCheckinFile.finishWrite(stream);
com.android.internal.logging.EventLogTags.writeCommitSysConfigFile(
"batterystats-checkin",
@@ -12453,7 +12431,7 @@
mModStepMode = 0;
}
if (doWrite || (mLastWriteTime + (60 * 1000)) < mSecRealtime) {
- if (mFile != null) {
+ if (mStatsFile != null && mBatteryStatsHistory.getActiveFile() != null) {
writeAsyncLocked();
}
}
@@ -12694,7 +12672,7 @@
if (mMinLearnedBatteryCapacity == -1) {
mMinLearnedBatteryCapacity = chargeFullUAh;
} else {
- Math.min(mMinLearnedBatteryCapacity, chargeFullUAh);
+ mMinLearnedBatteryCapacity = Math.min(mMinLearnedBatteryCapacity, chargeFullUAh);
}
mMaxLearnedBatteryCapacity = Math.max(mMaxLearnedBatteryCapacity, chargeFullUAh);
}
@@ -13244,6 +13222,8 @@
= "external_stats_collection_rate_limit_ms";
public static final String KEY_BATTERY_LEVEL_COLLECTION_DELAY_MS
= "battery_level_collection_delay_ms";
+ public static final String KEY_MAX_HISTORY_FILES = "max_history_files";
+ public static final String KEY_MAX_HISTORY_BUFFER_KB = "max_history_buffer_kb";
private static final boolean DEFAULT_TRACK_CPU_TIMES_BY_PROC_STATE = true;
private static final boolean DEFAULT_TRACK_CPU_ACTIVE_CLUSTER_TIME = true;
@@ -13252,6 +13232,10 @@
private static final long DEFAULT_UID_REMOVE_DELAY_MS = 5L * 60L * 1000L;
private static final long DEFAULT_EXTERNAL_STATS_COLLECTION_RATE_LIMIT_MS = 600_000;
private static final long DEFAULT_BATTERY_LEVEL_COLLECTION_DELAY_MS = 300_000;
+ private static final int DEFAULT_MAX_HISTORY_FILES = 32;
+ private static final int DEFAULT_MAX_HISTORY_BUFFER_KB = 128; /*Kilo Bytes*/
+ private static final int DEFAULT_MAX_HISTORY_FILES_LOW_RAM_DEVICE = 64;
+ private static final int DEFAULT_MAX_HISTORY_BUFFER_LOW_RAM_DEVICE_KB = 64; /*Kilo Bytes*/
public boolean TRACK_CPU_TIMES_BY_PROC_STATE = DEFAULT_TRACK_CPU_TIMES_BY_PROC_STATE;
public boolean TRACK_CPU_ACTIVE_CLUSTER_TIME = DEFAULT_TRACK_CPU_ACTIVE_CLUSTER_TIME;
@@ -13262,12 +13246,21 @@
= DEFAULT_EXTERNAL_STATS_COLLECTION_RATE_LIMIT_MS;
public long BATTERY_LEVEL_COLLECTION_DELAY_MS
= DEFAULT_BATTERY_LEVEL_COLLECTION_DELAY_MS;
+ public int MAX_HISTORY_FILES;
+ public int MAX_HISTORY_BUFFER; /*Bytes*/
private ContentResolver mResolver;
private final KeyValueListParser mParser = new KeyValueListParser(',');
public Constants(Handler handler) {
super(handler);
+ if (ActivityManager.isLowRamDeviceStatic()) {
+ MAX_HISTORY_FILES = DEFAULT_MAX_HISTORY_FILES_LOW_RAM_DEVICE;
+ MAX_HISTORY_BUFFER = DEFAULT_MAX_HISTORY_BUFFER_LOW_RAM_DEVICE_KB * 1024;
+ } else {
+ MAX_HISTORY_FILES = DEFAULT_MAX_HISTORY_FILES;
+ MAX_HISTORY_BUFFER = DEFAULT_MAX_HISTORY_BUFFER_KB * 1024;
+ }
}
public void startObserving(ContentResolver resolver) {
@@ -13313,13 +13306,23 @@
BATTERY_LEVEL_COLLECTION_DELAY_MS = mParser.getLong(
KEY_BATTERY_LEVEL_COLLECTION_DELAY_MS,
DEFAULT_BATTERY_LEVEL_COLLECTION_DELAY_MS);
+
+ MAX_HISTORY_FILES = mParser.getInt(KEY_MAX_HISTORY_FILES,
+ ActivityManager.isLowRamDeviceStatic() ?
+ DEFAULT_MAX_HISTORY_FILES_LOW_RAM_DEVICE
+ : DEFAULT_MAX_HISTORY_FILES);
+ MAX_HISTORY_BUFFER = mParser.getInt(KEY_MAX_HISTORY_BUFFER_KB,
+ ActivityManager.isLowRamDeviceStatic() ?
+ DEFAULT_MAX_HISTORY_BUFFER_LOW_RAM_DEVICE_KB
+ : DEFAULT_MAX_HISTORY_BUFFER_KB)
+ * 1024;
}
}
private void updateTrackCpuTimesByProcStateLocked(boolean wasEnabled, boolean isEnabled) {
TRACK_CPU_TIMES_BY_PROC_STATE = isEnabled;
if (isEnabled && !wasEnabled) {
- mKernelSingleUidTimeReader.markDataAsStale(true);
+ mIsPerProcessStateCpuDataStale = true;
mExternalSync.scheduleCpuSyncDueToSettingChange();
mNumSingleUidCpuTimeReads = 0;
@@ -13366,6 +13369,10 @@
pw.println(EXTERNAL_STATS_COLLECTION_RATE_LIMIT_MS);
pw.print(KEY_BATTERY_LEVEL_COLLECTION_DELAY_MS); pw.print("=");
pw.println(BATTERY_LEVEL_COLLECTION_DELAY_MS);
+ pw.print(KEY_MAX_HISTORY_FILES); pw.print("=");
+ pw.println(MAX_HISTORY_FILES);
+ pw.print(KEY_MAX_HISTORY_BUFFER_KB); pw.print("=");
+ pw.println(MAX_HISTORY_BUFFER/1024);
}
}
@@ -13417,20 +13424,22 @@
}
}
- Parcel mPendingWrite = null;
final ReentrantLock mWriteLock = new ReentrantLock();
public void writeAsyncLocked() {
- writeLocked(false);
+ writeStatsLocked(false);
+ writeHistoryLocked(false);
}
public void writeSyncLocked() {
- writeLocked(true);
+ writeStatsLocked(true);
+ writeHistoryLocked(true);
}
- void writeLocked(boolean sync) {
- if (mFile == null) {
- Slog.w("BatteryStats", "writeLocked: no file associated with this instance");
+ void writeStatsLocked(boolean sync) {
+ if (mStatsFile == null) {
+ Slog.w(TAG,
+ "writeStatsLocked: no file associated with this instance");
return;
}
@@ -13438,52 +13447,71 @@
return;
}
- Parcel out = Parcel.obtain();
- writeSummaryToParcel(out, true);
- mLastWriteTime = mClocks.elapsedRealtime();
-
- if (mPendingWrite != null) {
- mPendingWrite.recycle();
+ final Parcel p = Parcel.obtain();
+ final long start = SystemClock.uptimeMillis();
+ writeSummaryToParcel(p, false/*history is in separate file*/);
+ if (DEBUG) {
+ Slog.d(TAG, "writeSummaryToParcel duration ms:"
+ + (SystemClock.uptimeMillis() - start) + " bytes:" + p.dataSize());
}
- mPendingWrite = out;
+ mLastWriteTime = mClocks.elapsedRealtime();
+ writeParcelToFileLocked(p, mStatsFile, sync);
+ }
+ void writeHistoryLocked(boolean sync) {
+ if (mBatteryStatsHistory.getActiveFile() == null) {
+ Slog.w(TAG,
+ "writeHistoryLocked: no history file associated with this instance");
+ return;
+ }
+
+ if (mShuttingDown) {
+ return;
+ }
+
+ Parcel p = Parcel.obtain();
+ final long start = SystemClock.uptimeMillis();
+ writeHistoryBuffer(p, true, true);
+ if (DEBUG) {
+ Slog.d(TAG, "writeHistoryBuffer duration ms:"
+ + (SystemClock.uptimeMillis() - start) + " bytes:" + p.dataSize());
+ }
+ writeParcelToFileLocked(p, mBatteryStatsHistory.getActiveFile(), sync);
+ }
+
+ void writeParcelToFileLocked(Parcel p, AtomicFile file, boolean sync) {
if (sync) {
- commitPendingDataToDisk();
+ commitPendingDataToDisk(p, file);
} else {
BackgroundThread.getHandler().post(new Runnable() {
@Override public void run() {
- commitPendingDataToDisk();
+ commitPendingDataToDisk(p, file);
}
});
}
}
- public void commitPendingDataToDisk() {
- final Parcel next;
- synchronized (this) {
- next = mPendingWrite;
- mPendingWrite = null;
- if (next == null) {
- return;
- }
- }
-
+ private void commitPendingDataToDisk(Parcel p, AtomicFile file) {
mWriteLock.lock();
+ FileOutputStream fos = null;
try {
final long startTime = SystemClock.uptimeMillis();
- FileOutputStream stream = new FileOutputStream(mFile.chooseForWrite());
- stream.write(next.marshall());
- stream.flush();
- FileUtils.sync(stream);
- stream.close();
- mFile.commit();
+ fos = file.startWrite();
+ fos.write(p.marshall());
+ fos.flush();
+ file.finishWrite(fos);
+ if (DEBUG) {
+ Slog.d(TAG, "commitPendingDataToDisk file:" + file.getBaseFile().getPath()
+ + " duration ms:" + (SystemClock.uptimeMillis() - startTime)
+ + " bytes:" + p.dataSize());
+ }
com.android.internal.logging.EventLogTags.writeCommitSysConfigFile(
"batterystats", SystemClock.uptimeMillis() - startTime);
} catch (IOException e) {
- Slog.w("BatteryStats", "Error writing battery statistics", e);
- mFile.rollback();
+ Slog.w(TAG, "Error writing battery statistics", e);
+ file.failWrite(fos);
} finally {
- next.recycle();
+ p.recycle();
mWriteLock.unlock();
}
}
@@ -13493,35 +13521,65 @@
readDailyStatsLocked();
}
- if (mFile == null) {
- Slog.w("BatteryStats", "readLocked: no file associated with this instance");
+ if (mStatsFile == null) {
+ Slog.w(TAG, "readLocked: no file associated with this instance");
+ return;
+ }
+
+ if (mBatteryStatsHistory.getActiveFile() == null) {
+ Slog.w(TAG,
+ "readLocked: no history file associated with this instance");
return;
}
mUidStats.clear();
+ Parcel stats = Parcel.obtain();
try {
- File file = mFile.chooseForRead();
- if (!file.exists()) {
- return;
+ final long start = SystemClock.uptimeMillis();
+ byte[] raw = mStatsFile.readFully();
+ stats.unmarshall(raw, 0, raw.length);
+ stats.setDataPosition(0);
+ readSummaryFromParcel(stats);
+ if (DEBUG) {
+ Slog.d(TAG, "readLocked stats file:" + mStatsFile.getBaseFile().getPath()
+ + " bytes:" + raw.length + " takes ms:" + (SystemClock.uptimeMillis()
+ - start));
}
- FileInputStream stream = new FileInputStream(file);
-
- byte[] raw = BatteryStatsHelper.readFully(stream);
- Parcel in = Parcel.obtain();
- in.unmarshall(raw, 0, raw.length);
- in.setDataPosition(0);
- stream.close();
-
- readSummaryFromParcel(in);
- } catch(Exception e) {
- Slog.e("BatteryStats", "Error reading battery statistics", e);
+ } catch (Exception e) {
+ Slog.e(TAG, "Error reading battery statistics", e);
resetAllStatsLocked();
+ } finally {
+ stats.recycle();
+ }
+
+ Parcel history = Parcel.obtain();
+ try {
+ final long start = SystemClock.uptimeMillis();
+ byte[] raw = mBatteryStatsHistory.getActiveFile().readFully();
+ if (raw.length > 0) {
+ history.unmarshall(raw, 0, raw.length);
+ history.setDataPosition(0);
+ readHistoryBuffer(history, true);
+ }
+ if (DEBUG) {
+ Slog.d(TAG, "readLocked history file::"
+ + mBatteryStatsHistory.getActiveFile().getBaseFile().getPath()
+ + " bytes:" + raw.length + " takes ms:" + (SystemClock.uptimeMillis()
+ - start));
+ }
+ } catch (Exception e) {
+ Slog.e(TAG, "Error reading battery history", e);
+ clearHistoryLocked();
+ mBatteryStatsHistory.resetAllFiles();
+ } finally {
+ history.recycle();
}
mEndPlatformVersion = Build.ID;
- if (mHistoryBuffer.dataPosition() > 0) {
+ if (mHistoryBuffer.dataPosition() > 0
+ || mBatteryStatsHistory.getFilesNumbers().size() > 1) {
mRecordingHistory = true;
final long elapsedRealtime = mClocks.elapsedRealtime();
final long uptime = mClocks.uptimeMillis();
@@ -13539,37 +13597,22 @@
return 0;
}
- void readHistory(Parcel in, boolean andOldHistory) throws ParcelFormatException {
+ void readHistoryBuffer(Parcel in, boolean andOldHistory) throws ParcelFormatException {
+ final int version = in.readInt();
+ if (version != VERSION) {
+ Slog.w("BatteryStats", "readHistoryBuffer: version got " + version
+ + ", expected " + VERSION + "; erasing old stats");
+ return;
+ }
+
final long historyBaseTime = in.readLong();
mHistoryBuffer.setDataSize(0);
mHistoryBuffer.setDataPosition(0);
- mHistoryTagPool.clear();
- mNextHistoryTagIdx = 0;
- mNumHistoryTagChars = 0;
-
- int numTags = in.readInt();
- for (int i=0; i<numTags; i++) {
- int idx = in.readInt();
- String str = in.readString();
- if (str == null) {
- throw new ParcelFormatException("null history tag string");
- }
- int uid = in.readInt();
- HistoryTag tag = new HistoryTag();
- tag.string = str;
- tag.uid = uid;
- tag.poolIdx = idx;
- mHistoryTagPool.put(tag, idx);
- if (idx >= mNextHistoryTagIdx) {
- mNextHistoryTagIdx = idx+1;
- }
- mNumHistoryTagChars += tag.string.length() + 1;
- }
int bufSize = in.readInt();
int curPos = in.dataPosition();
- if (bufSize >= (MAX_MAX_HISTORY_BUFFER*3)) {
+ if (bufSize >= (mConstants.MAX_HISTORY_BUFFER*100)) {
throw new ParcelFormatException("File corrupt: history data buffer too large " +
bufSize);
} else if ((bufSize&~3) != bufSize) {
@@ -13626,7 +13669,7 @@
}
}
- void writeHistory(Parcel out, boolean inclData, boolean andOldHistory) {
+ void writeHistoryBuffer(Parcel out, boolean inclData, boolean andOldHistory) {
if (DEBUG_HISTORY) {
StringBuilder sb = new StringBuilder(128);
sb.append("****************** WRITING mHistoryBaseTime: ");
@@ -13635,19 +13678,14 @@
TimeUtils.formatDuration(mLastHistoryElapsedRealtime, sb);
Slog.i(TAG, sb.toString());
}
+ out.writeInt(VERSION);
out.writeLong(mHistoryBaseTime + mLastHistoryElapsedRealtime);
if (!inclData) {
out.writeInt(0);
out.writeInt(0);
return;
}
- out.writeInt(mHistoryTagPool.size());
- for (HashMap.Entry<HistoryTag, Integer> ent : mHistoryTagPool.entrySet()) {
- HistoryTag tag = ent.getKey();
- out.writeInt(ent.getValue());
- out.writeString(tag.string);
- out.writeInt(tag.uid);
- }
+
out.writeInt(mHistoryBuffer.dataSize());
if (DEBUG_HISTORY) Slog.i(TAG, "***************** WRITING HISTORY: "
+ mHistoryBuffer.dataSize() + " bytes at " + out.dataPosition());
@@ -13678,7 +13716,34 @@
return;
}
- readHistory(in, true);
+ boolean inclHistory = in.readBoolean();
+ if (inclHistory) {
+ readHistoryBuffer(in, true);
+ mBatteryStatsHistory.readFromParcel(in);
+ }
+
+ mHistoryTagPool.clear();
+ mNextHistoryTagIdx = 0;
+ mNumHistoryTagChars = 0;
+
+ int numTags = in.readInt();
+ for (int i=0; i<numTags; i++) {
+ int idx = in.readInt();
+ String str = in.readString();
+ if (str == null) {
+ throw new ParcelFormatException("null history tag string");
+ }
+ int uid = in.readInt();
+ HistoryTag tag = new HistoryTag();
+ tag.string = str;
+ tag.uid = uid;
+ tag.poolIdx = idx;
+ mHistoryTagPool.put(tag, idx);
+ if (idx >= mNextHistoryTagIdx) {
+ mNextHistoryTagIdx = idx+1;
+ }
+ mNumHistoryTagChars += tag.string.length() + 1;
+ }
mStartCount = in.readInt();
mUptime = in.readLong();
@@ -14103,7 +14168,7 @@
String pkgName = in.readString();
Uid.Pkg p = u.getPackageStatsLocked(pkgName);
final int NWA = in.readInt();
- if (NWA > 1000) {
+ if (NWA > 10000) {
throw new ParcelFormatException("File corrupt: too many wakeup alarms " + NWA);
}
p.mWakeupAlarms.clear();
@@ -14114,7 +14179,7 @@
p.mWakeupAlarms.put(tag, c);
}
NS = in.readInt();
- if (NS > 1000) {
+ if (NS > 10000) {
throw new ParcelFormatException("File corrupt: too many services " + NS);
}
for (int is = 0; is < NS; is++) {
@@ -14146,7 +14211,19 @@
out.writeInt(VERSION);
- writeHistory(out, inclHistory, true);
+ out.writeBoolean(inclHistory);
+ if (inclHistory) {
+ writeHistoryBuffer(out, true, true);
+ mBatteryStatsHistory.writeToParcel(out);
+ }
+
+ out.writeInt(mHistoryTagPool.size());
+ for (HashMap.Entry<HistoryTag, Integer> ent : mHistoryTagPool.entrySet()) {
+ HistoryTag tag = ent.getKey();
+ out.writeInt(ent.getValue());
+ out.writeString(tag.string);
+ out.writeInt(tag.uid);
+ }
out.writeInt(mStartCount);
out.writeLong(computeUptime(NOW_SYS, STATS_SINCE_CHARGED));
@@ -14644,7 +14721,8 @@
throw new ParcelFormatException("Bad magic number: #" + Integer.toHexString(magic));
}
- readHistory(in, false);
+ readHistoryBuffer(in, false);
+ mBatteryStatsHistory.readFromParcel(in);
mStartCount = in.readInt();
mStartClockTime = in.readLong();
@@ -14873,7 +14951,8 @@
out.writeInt(MAGIC);
- writeHistory(out, true, false);
+ writeHistoryBuffer(out, true, false);
+ mBatteryStatsHistory.writeToParcel(out);
out.writeInt(mStartCount);
out.writeLong(startClockTime);
diff --git a/core/java/com/android/internal/os/BinderCallsStats.java b/core/java/com/android/internal/os/BinderCallsStats.java
index fbb99e4..f87c081 100644
--- a/core/java/com/android/internal/os/BinderCallsStats.java
+++ b/core/java/com/android/internal/os/BinderCallsStats.java
@@ -16,11 +16,15 @@
package com.android.internal.os;
+import android.annotation.Nullable;
import android.os.Binder;
import android.os.SystemClock;
import android.os.UserHandle;
import android.text.format.DateFormat;
import android.util.ArrayMap;
+import android.util.Log;
+import android.util.Pair;
+import android.util.Slog;
import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
@@ -29,10 +33,12 @@
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Queue;
+import java.util.Set;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.function.ToDoubleFunction;
@@ -41,38 +47,52 @@
* per thread, uid or call description.
*/
public class BinderCallsStats {
+ public static final boolean ENABLED_DEFAULT = true;
+ public static final boolean DETAILED_TRACKING_DEFAULT = true;
+ public static final int PERIODIC_SAMPLING_INTERVAL_DEFAULT = 10;
+
+ private static final String TAG = "BinderCallsStats";
private static final int CALL_SESSIONS_POOL_SIZE = 100;
private static final int PERIODIC_SAMPLING_INTERVAL = 10;
+ private static final int MAX_EXCEPTION_COUNT_SIZE = 50;
+ private static final String EXCEPTION_COUNT_OVERFLOW_NAME = "overflow";
+ private static final CallSession NOT_ENABLED = new CallSession();
private static final BinderCallsStats sInstance = new BinderCallsStats();
- private volatile boolean mDetailedTracking = false;
+ private volatile boolean mEnabled = ENABLED_DEFAULT;
+ private volatile boolean mDetailedTracking = DETAILED_TRACKING_DEFAULT;
+ private volatile int mPeriodicSamplingInterval = PERIODIC_SAMPLING_INTERVAL_DEFAULT;
@GuardedBy("mLock")
private final SparseArray<UidEntry> mUidEntries = new SparseArray<>();
+ @GuardedBy("mLock")
+ private final ArrayMap<String, Integer> mExceptionCounts = new ArrayMap<>();
private final Queue<CallSession> mCallSessionsPool = new ConcurrentLinkedQueue<>();
private final Object mLock = new Object();
private long mStartTime = System.currentTimeMillis();
@GuardedBy("mLock")
private UidEntry mSampledEntries = new UidEntry(-1);
- private BinderCallsStats() {
- }
-
- @VisibleForTesting
- public BinderCallsStats(boolean detailedTracking) {
- mDetailedTracking = detailedTracking;
+ @VisibleForTesting // Use getInstance() instead.
+ public BinderCallsStats() {
}
public CallSession callStarted(Binder binder, int code) {
- return callStarted(binder.getClass().getName(), code);
+ return callStarted(binder.getClass().getName(), code, binder.getTransactionName(code));
}
- private CallSession callStarted(String className, int code) {
+ private CallSession callStarted(String className, int code, @Nullable String methodName) {
+ if (!mEnabled) {
+ return NOT_ENABLED;
+ }
+
CallSession s = mCallSessionsPool.poll();
if (s == null) {
s = new CallSession();
}
+
s.callStat.className = className;
s.callStat.msg = code;
+ s.callStat.methodName = methodName;
s.exceptionThrown = false;
s.cpuTimeStarted = -1;
s.timeStarted = -1;
@@ -83,7 +103,7 @@
s.timeStarted = getElapsedRealtimeMicro();
} else {
s.sampledCallStat = mSampledEntries.getOrCreate(s.callStat);
- if (s.sampledCallStat.callCount % PERIODIC_SAMPLING_INTERVAL == 0) {
+ if (s.sampledCallStat.callCount % mPeriodicSamplingInterval == 0) {
s.cpuTimeStarted = getThreadTimeMicro();
s.timeStarted = getElapsedRealtimeMicro();
}
@@ -94,7 +114,23 @@
public void callEnded(CallSession s, int parcelRequestSize, int parcelReplySize) {
Preconditions.checkNotNull(s);
+ if (s == NOT_ENABLED) {
+ return;
+ }
+
+ processCallEnded(s, parcelRequestSize, parcelReplySize);
+
+ if (mCallSessionsPool.size() < CALL_SESSIONS_POOL_SIZE) {
+ mCallSessionsPool.add(s);
+ }
+ }
+
+ private void processCallEnded(CallSession s, int parcelRequestSize, int parcelReplySize) {
synchronized (mLock) {
+ if (!mEnabled) {
+ return;
+ }
+
long duration;
long latencyDuration;
if (mDetailedTracking) {
@@ -108,7 +144,7 @@
latencyDuration = getElapsedRealtimeMicro() - s.timeStarted;
} else {
// callCount is always incremented, but time only once per sampling interval
- long samplesCount = cs.callCount / PERIODIC_SAMPLING_INTERVAL + 1;
+ long samplesCount = cs.callCount / mPeriodicSamplingInterval + 1;
duration = cs.cpuTimeMicros / samplesCount;
latencyDuration = cs.latencyMicros / samplesCount;
}
@@ -122,33 +158,31 @@
mUidEntries.put(callingUid, uidEntry);
}
+ CallStat callStat;
if (mDetailedTracking) {
// Find CallStat entry and update its total time
- CallStat callStat = uidEntry.getOrCreate(s.callStat);
- callStat.callCount++;
- callStat.cpuTimeMicros += duration;
- callStat.latencyMicros += latencyDuration;
+ callStat = uidEntry.getOrCreate(s.callStat);
callStat.exceptionCount += s.exceptionThrown ? 1 : 0;
- callStat.maxLatencyMicros = Math.max(callStat.maxLatencyMicros, latencyDuration);
callStat.maxRequestSizeBytes =
Math.max(callStat.maxRequestSizeBytes, parcelRequestSize);
callStat.maxReplySizeBytes =
Math.max(callStat.maxReplySizeBytes, parcelReplySize);
} else {
// update sampled timings in the beginning of each interval
- if (s.cpuTimeStarted >= 0) {
- s.sampledCallStat.cpuTimeMicros += duration;
- s.sampledCallStat.latencyMicros += latencyDuration;
- }
- s.sampledCallStat.callCount++;
+ callStat = s.sampledCallStat;
+ }
+ callStat.callCount++;
+ callStat.methodName = s.callStat.methodName;
+ if (s.cpuTimeStarted >= 0) {
+ callStat.cpuTimeMicros += duration;
+ callStat.maxCpuTimeMicros = Math.max(callStat.maxCpuTimeMicros, duration);
+ callStat.latencyMicros += latencyDuration;
+ callStat.maxLatencyMicros = Math.max(callStat.maxLatencyMicros, latencyDuration);
}
uidEntry.cpuTimeMicros += duration;
uidEntry.callCount++;
}
- if (mCallSessionsPool.size() < CALL_SESSIONS_POOL_SIZE) {
- mCallSessionsPool.add(s);
- }
}
/**
@@ -158,9 +192,58 @@
* <li>Do not throw an exception in this method, it will swallow the original exception thrown
* by the binder transaction.
*/
- public void callThrewException(CallSession s) {
+ public void callThrewException(CallSession s, Exception exception) {
Preconditions.checkNotNull(s);
+ if (!mEnabled) {
+ return;
+ }
s.exceptionThrown = true;
+ try {
+ String className = exception.getClass().getName();
+ synchronized (mLock) {
+ if (mExceptionCounts.size() >= MAX_EXCEPTION_COUNT_SIZE) {
+ className = EXCEPTION_COUNT_OVERFLOW_NAME;
+ }
+ Integer count = mExceptionCounts.get(className);
+ mExceptionCounts.put(className, count == null ? 1 : count + 1);
+ }
+ } catch (RuntimeException e) {
+ // Do not propagate the exception. We do not want to swallow original exception.
+ Log.wtf(TAG, "Unexpected exception while updating mExceptionCounts", e);
+ }
+ }
+
+ public ArrayList<ExportedCallStat> getExportedCallStats() {
+ // We do not collect all the data if detailed tracking is off.
+ if (!mDetailedTracking) {
+ return new ArrayList<ExportedCallStat>();
+ }
+
+ ArrayList<ExportedCallStat> resultCallStats = new ArrayList<>();
+ synchronized (mLock) {
+ int uidEntriesSize = mUidEntries.size();
+ for (int entryIdx = 0; entryIdx < uidEntriesSize; entryIdx++){
+ UidEntry entry = mUidEntries.valueAt(entryIdx);
+ for (CallStat stat : entry.getCallStatsList()) {
+ ExportedCallStat exported = new ExportedCallStat();
+ exported.uid = entry.uid;
+ exported.className = stat.className;
+ exported.methodName = stat.methodName == null
+ ? String.valueOf(stat.msg) : stat.methodName;
+ exported.cpuTimeMicros = stat.cpuTimeMicros;
+ exported.maxCpuTimeMicros = stat.maxCpuTimeMicros;
+ exported.latencyMicros = stat.latencyMicros;
+ exported.maxLatencyMicros = stat.maxLatencyMicros;
+ exported.callCount = stat.callCount;
+ exported.maxRequestSizeBytes = stat.maxRequestSizeBytes;
+ exported.maxReplySizeBytes = stat.maxReplySizeBytes;
+ exported.exceptionCount = stat.exceptionCount;
+ resultCallStats.add(exported);
+ }
+ }
+ }
+
+ return resultCallStats;
}
public void dump(PrintWriter pw, Map<Integer,String> appIdToPkgNameMap, boolean verbose) {
@@ -170,6 +253,11 @@
}
private void dumpLocked(PrintWriter pw, Map<Integer,String> appIdToPkgNameMap, boolean verbose) {
+ if (!mEnabled) {
+ pw.println("Binder calls stats disabled.");
+ return;
+ }
+
long totalCallsCount = 0;
long totalCpuTime = 0;
pw.print("Start time: ");
@@ -189,17 +277,19 @@
StringBuilder sb = new StringBuilder();
if (mDetailedTracking) {
pw.println("Per-UID raw data " + datasetSizeDesc
- + "(uid, call_desc, cpu_time_micros, latency_time_micros, "
- + "max_latency_time_micros, exception_count, max_request_size_bytes, "
- + "max_reply_size_bytes, call_count):");
+ + "(package/uid, call_desc, cpu_time_micros, max_cpu_time_micros, "
+ + "latency_time_micros, max_latency_time_micros, exception_count, "
+ + "max_request_size_bytes, max_reply_size_bytes, call_count):");
List<UidEntry> topEntries = verbose ? entries
: getHighestValues(entries, value -> value.cpuTimeMicros, 0.9);
for (UidEntry uidEntry : topEntries) {
for (CallStat e : uidEntry.getCallStatsList()) {
sb.setLength(0);
sb.append(" ")
- .append(uidEntry.uid).append(",").append(e)
+ .append(uidToString(uidEntry.uid, appIdToPkgNameMap))
+ .append(",").append(e)
.append(',').append(e.cpuTimeMicros)
+ .append(',').append(e.maxCpuTimeMicros)
.append(',').append(e.latencyMicros)
.append(',').append(e.maxLatencyMicros)
.append(',').append(e.exceptionCount)
@@ -222,7 +312,7 @@
for (CallStat e : sampledStatsList) {
sb.setLength(0);
sb.append(" ").append(e)
- .append(',').append(e.cpuTimeMicros * PERIODIC_SAMPLING_INTERVAL)
+ .append(',').append(e.cpuTimeMicros * mPeriodicSamplingInterval)
.append(',').append(e.callCount)
.append(',').append(e.exceptionCount);
pw.println(sb);
@@ -243,6 +333,18 @@
pw.println(String.format(" Summary: total_cpu_time=%d, "
+ "calls_count=%d, avg_call_cpu_time=%.0f",
totalCpuTime, totalCallsCount, (double)totalCpuTime / totalCallsCount));
+ pw.println();
+
+ pw.println("Exceptions thrown (exception_count, class_name):");
+ List<Pair<String, Integer>> exceptionEntries = new ArrayList<>();
+ // We cannot use new ArrayList(Collection) constructor because MapCollections does not
+ // implement toArray method.
+ mExceptionCounts.entrySet().iterator().forEachRemaining(
+ (e) -> exceptionEntries.add(Pair.create(e.getKey(), e.getValue())));
+ exceptionEntries.sort((e1, e2) -> Integer.compare(e2.second, e1.second));
+ for (Pair<String, Integer> entry : exceptionEntries) {
+ pw.println(String.format(" %6d %s", entry.second, entry.first));
+ }
}
private static String uidToString(int uid, Map<Integer, String> pkgNameMap) {
@@ -260,7 +362,7 @@
return Binder.getCallingUid();
}
- private long getElapsedRealtimeMicro() {
+ protected long getElapsedRealtimeMicro() {
return SystemClock.elapsedRealtimeNanos() / 1000;
}
@@ -269,30 +371,73 @@
}
public void setDetailedTracking(boolean enabled) {
- if (enabled != mDetailedTracking) {
- reset();
- mDetailedTracking = enabled;
+ synchronized (mLock) {
+ if (enabled != mDetailedTracking) {
+ mDetailedTracking = enabled;
+ reset();
+ }
+ }
+ }
+
+ public void setEnabled(boolean enabled) {
+ synchronized (mLock) {
+ if (enabled != mEnabled) {
+ mEnabled = enabled;
+ reset();
+ }
+ }
+ }
+
+ public void setSamplingInterval(int samplingInterval) {
+ synchronized (mLock) {
+ if (samplingInterval != mPeriodicSamplingInterval) {
+ mPeriodicSamplingInterval = samplingInterval;
+ reset();
+ }
}
}
public void reset() {
synchronized (mLock) {
mUidEntries.clear();
+ mExceptionCounts.clear();
mSampledEntries.mCallStats.clear();
mStartTime = System.currentTimeMillis();
}
}
+ /**
+ * Aggregated data by uid/class/method to be sent through WestWorld.
+ */
+ public static class ExportedCallStat {
+ public int uid;
+ public String className;
+ public String methodName;
+ public long cpuTimeMicros;
+ public long maxCpuTimeMicros;
+ public long latencyMicros;
+ public long maxLatencyMicros;
+ public long callCount;
+ public long maxRequestSizeBytes;
+ public long maxReplySizeBytes;
+ public long exceptionCount;
+ }
+
@VisibleForTesting
public static class CallStat {
public String className;
public int msg;
+ // Method name might be null when we cannot resolve the transaction code. For instance, if
+ // the binder was not generated by AIDL.
+ public @Nullable String methodName;
public long cpuTimeMicros;
+ public long maxCpuTimeMicros;
public long latencyMicros;
public long maxLatencyMicros;
+ public long callCount;
+ // The following fields are only computed if mDetailedTracking is set.
public long maxRequestSizeBytes;
public long maxReplySizeBytes;
- public long callCount;
public long exceptionCount;
CallStat() {
@@ -323,7 +468,7 @@
@Override
public String toString() {
- return className + "/" + msg;
+ return className + "#" + (methodName == null ? msg : methodName);
}
}
@@ -411,6 +556,11 @@
}
@VisibleForTesting
+ public ArrayMap<String, Integer> getExceptionCounts() {
+ return mExceptionCounts;
+ }
+
+ @VisibleForTesting
public static <T> List<T> getHighestValues(List<T> list, ToDoubleFunction<T> toDouble,
double percentile) {
List<T> sortedList = new ArrayList<>(list);
diff --git a/core/java/com/android/internal/os/KernelSingleUidTimeReader.java b/core/java/com/android/internal/os/KernelSingleUidTimeReader.java
index 4283917..ad62852 100644
--- a/core/java/com/android/internal/os/KernelSingleUidTimeReader.java
+++ b/core/java/com/android/internal/os/KernelSingleUidTimeReader.java
@@ -53,8 +53,6 @@
private int mReadErrorCounter;
@GuardedBy("this")
private boolean mSingleUidCpuTimesAvailable = true;
- @GuardedBy("this")
- private boolean mHasStaleData;
// We use the freq count obtained from /proc/uid_time_in_state to decide how many longs
// to read from each /proc/uid/<uid>/time_in_state. On the first read, verify if this is
// correct and if not, set {@link #mSingleUidCpuTimesAvailable} to false. This flag will
@@ -196,18 +194,6 @@
return deltaTimesMs;
}
- public void markDataAsStale(boolean hasStaleData) {
- synchronized (this) {
- mHasStaleData = hasStaleData;
- }
- }
-
- public boolean hasStaleData() {
- synchronized (this) {
- return mHasStaleData;
- }
- }
-
public void setAllUidsCpuTimesMs(SparseArray<long[]> allUidsCpuTimesMs) {
synchronized (this) {
mLastUidCpuTimeMs.clear();
diff --git a/core/java/com/android/internal/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java
index 621619c..c3d33ca8 100644
--- a/core/java/com/android/internal/util/ArrayUtils.java
+++ b/core/java/com/android/internal/util/ArrayUtils.java
@@ -308,6 +308,23 @@
return array;
}
+ @SuppressWarnings("unchecked")
+ public static @NonNull <T> T[] concat(Class<T> kind, @Nullable T[] a, @Nullable T[] b) {
+ final int an = (a != null) ? a.length : 0;
+ final int bn = (b != null) ? b.length : 0;
+ if (an == 0 && bn == 0) {
+ if (kind == String.class) {
+ return (T[]) EmptyArray.STRING;
+ } else if (kind == Object.class) {
+ return (T[]) EmptyArray.OBJECT;
+ }
+ }
+ final T[] res = (T[]) Array.newInstance(kind, an + bn);
+ if (an > 0) System.arraycopy(a, 0, res, 0, an);
+ if (bn > 0) System.arraycopy(b, 0, res, an, bn);
+ return res;
+ }
+
/**
* Adds value to given array if not already present, providing set-like
* behavior.
@@ -626,4 +643,17 @@
public static @NonNull String[] defeatNullable(@Nullable String[] val) {
return (val != null) ? val : EmptyArray.STRING;
}
+
+ /**
+ * Throws {@link ArrayIndexOutOfBoundsException} if the index is out of bounds.
+ *
+ * @param len length of the array. Must be non-negative
+ * @param index the index to check
+ * @throws ArrayIndexOutOfBoundsException if the {@code index} is out of bounds of the array
+ */
+ public static void checkBounds(int len, int index) {
+ if (index < 0 || len <= index) {
+ throw new ArrayIndexOutOfBoundsException("length=" + len + "; index=" + index);
+ }
+ }
}
diff --git a/core/java/com/android/internal/util/ContrastColorUtil.java b/core/java/com/android/internal/util/ContrastColorUtil.java
index 60dd86b..16ca4fc 100644
--- a/core/java/com/android/internal/util/ContrastColorUtil.java
+++ b/core/java/com/android/internal/util/ContrastColorUtil.java
@@ -454,9 +454,12 @@
/**
* Resolves {@param color} to an actual color if it is {@link Notification#COLOR_DEFAULT}
*/
- public static int resolveColor(Context context, int color) {
+ public static int resolveColor(Context context, int color, boolean defaultBackgroundIsDark) {
if (color == Notification.COLOR_DEFAULT) {
- return context.getColor(com.android.internal.R.color.notification_default_color_light);
+ int res = defaultBackgroundIsDark
+ ? com.android.internal.R.color.notification_default_color_dark
+ : com.android.internal.R.color.notification_default_color_light;
+ return context.getColor(res);
}
return color;
}
@@ -486,7 +489,7 @@
*/
public static int resolveContrastColor(Context context, int notificationColor,
int backgroundColor, boolean isDark) {
- final int resolvedColor = resolveColor(context, notificationColor);
+ final int resolvedColor = resolveColor(context, notificationColor, isDark);
int color = resolvedColor;
color = ContrastColorUtil.ensureTextContrast(color, backgroundColor, isDark);
@@ -520,7 +523,8 @@
}
public static int resolveAmbientColor(Context context, int notificationColor) {
- final int resolvedColor = resolveColor(context, notificationColor);
+ final int resolvedColor = resolveColor(context, notificationColor,
+ true /* defaultBackgroundIsDark */);
int color = resolvedColor;
color = ContrastColorUtil.ensureTextContrastOnBlack(color);
@@ -538,8 +542,9 @@
return color;
}
- public static int resolvePrimaryColor(Context context, int backgroundColor) {
- boolean useDark = shouldUseDark(backgroundColor);
+ public static int resolvePrimaryColor(Context context, int backgroundColor,
+ boolean defaultBackgroundIsDark) {
+ boolean useDark = shouldUseDark(backgroundColor, defaultBackgroundIsDark);
if (useDark) {
return context.getColor(
com.android.internal.R.color.notification_primary_text_color_light);
@@ -549,8 +554,9 @@
}
}
- public static int resolveSecondaryColor(Context context, int backgroundColor) {
- boolean useDark = shouldUseDark(backgroundColor);
+ public static int resolveSecondaryColor(Context context, int backgroundColor,
+ boolean defaultBackgroundIsDark) {
+ boolean useDark = shouldUseDark(backgroundColor, defaultBackgroundIsDark);
if (useDark) {
return context.getColor(
com.android.internal.R.color.notification_secondary_text_color_light);
@@ -560,8 +566,9 @@
}
}
- public static int resolveDefaultColor(Context context, int backgroundColor) {
- boolean useDark = shouldUseDark(backgroundColor);
+ public static int resolveDefaultColor(Context context, int backgroundColor,
+ boolean defaultBackgroundIsDark) {
+ boolean useDark = shouldUseDark(backgroundColor, defaultBackgroundIsDark);
if (useDark) {
return context.getColor(
com.android.internal.R.color.notification_default_color_light);
@@ -591,12 +598,11 @@
return ColorUtilsFromCompat.LABToColor(result[0], result[1], result[2]);
}
- private static boolean shouldUseDark(int backgroundColor) {
- boolean useDark = backgroundColor == Notification.COLOR_DEFAULT;
- if (!useDark) {
- useDark = ColorUtilsFromCompat.calculateLuminance(backgroundColor) > 0.5;
+ private static boolean shouldUseDark(int backgroundColor, boolean defaultBackgroundIsDark) {
+ if (backgroundColor == Notification.COLOR_DEFAULT) {
+ return !defaultBackgroundIsDark;
}
- return useDark;
+ return ColorUtilsFromCompat.calculateLuminance(backgroundColor) > 0.5;
}
public static double calculateLuminance(int backgroundColor) {
diff --git a/core/java/com/android/internal/util/DumpUtils.java b/core/java/com/android/internal/util/DumpUtils.java
index 7fd83bc..f6d80a5 100644
--- a/core/java/com/android/internal/util/DumpUtils.java
+++ b/core/java/com/android/internal/util/DumpUtils.java
@@ -34,9 +34,18 @@
/**
* Helper functions for dumping the state of system services.
* Test:
- atest /android/pi-dev/frameworks/base/core/tests/coretests/src/com/android/internal/util/DumpUtilsTest.java
+ atest FrameworksCoreTests:DumpUtilsTest
*/
public final class DumpUtils {
+
+ /**
+ * List of component names that should be dumped in the bug report critical section.
+ *
+ * @hide
+ */
+ public static final ComponentName[] CRITICAL_SECTION_COMPONENTS = {
+ new ComponentName("com.android.systemui", "com.android.systemui.SystemUIService")
+ };
private static final String TAG = "DumpUtils";
private static final boolean DEBUG = false;
@@ -213,6 +222,45 @@
}
/**
+ * Return whether a package should be dumped in the critical section.
+ */
+ private static boolean isCriticalPackage(@Nullable ComponentName cname) {
+ if (cname == null) {
+ return false;
+ }
+
+ for (int i = 0; i < CRITICAL_SECTION_COMPONENTS.length; i++) {
+ if (cname.equals(CRITICAL_SECTION_COMPONENTS[i])) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Return whether a package name is considered to be part of the platform and in the critical
+ * section.
+ *
+ * @hide
+ */
+ public static boolean isPlatformCriticalPackage(@Nullable ComponentName.WithComponentName wcn) {
+ return (wcn != null) && isPlatformPackage(wcn.getComponentName()) &&
+ isCriticalPackage(wcn.getComponentName());
+ }
+
+ /**
+ * Return whether a package name is considered to be part of the platform but not in the the
+ * critical section.
+ *
+ * @hide
+ */
+ public static boolean isPlatformNonCriticalPackage(
+ @Nullable ComponentName.WithComponentName wcn) {
+ return (wcn != null) && isPlatformPackage(wcn.getComponentName()) &&
+ !isCriticalPackage(wcn.getComponentName());
+ }
+
+ /**
* Used for dumping providers and services. Return a predicate for a given filter string.
* @hide
*/
@@ -238,6 +286,16 @@
return DumpUtils::isNonPlatformPackage;
}
+ // Dump all platform-critical?
+ if ("all-platform-critical".equals(filterString)) {
+ return DumpUtils::isPlatformCriticalPackage;
+ }
+
+ // Dump all platform-non-critical?
+ if ("all-platform-non-critical".equals(filterString)) {
+ return DumpUtils::isPlatformNonCriticalPackage;
+ }
+
// Is the filter a component name? If so, do an exact match.
final ComponentName filterCname = ComponentName.unflattenFromString(filterString);
if (filterCname != null) {
diff --git a/core/java/com/android/internal/util/LatencyTracker.java b/core/java/com/android/internal/util/LatencyTracker.java
index 6c3a58c..989c58b 100644
--- a/core/java/com/android/internal/util/LatencyTracker.java
+++ b/core/java/com/android/internal/util/LatencyTracker.java
@@ -79,6 +79,11 @@
*/
public static final int ACTION_ROTATE_SCREEN = 6;
+ /*
+ * Time between we get a face acquired signal until we start with the unlock animation
+ */
+ public static final int ACTION_FACE_WAKE_AND_UNLOCK = 6;
+
private static final String[] NAMES = new String[] {
"expand panel",
"toggle recents",
@@ -86,7 +91,8 @@
"check credential",
"check credential unlocked",
"turn on screen",
- "rotate the screen"};
+ "rotate the screen",
+ "face wake-and-unlock" };
private static LatencyTracker sLatencyTracker;
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index 9ed1ffb..f76eddf 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -74,8 +74,8 @@
void hideMySoftInput(in IBinder token, int flags);
void showMySoftInput(in IBinder token, int flags);
void updateStatusIcon(in IBinder token, String packageName, int iconId);
- void setImeWindowStatus(in IBinder token, in IBinder startInputToken, int vis,
- int backDisposition);
+ void setImeWindowStatus(in IBinder token, int vis, int backDisposition);
+ void reportStartInput(in IBinder token, in IBinder startInputToken);
void registerSuggestionSpansForNotification(in SuggestionSpan[] spans);
boolean notifySuggestionPicked(in SuggestionSpan span, String originalString, int index);
InputMethodSubtype getCurrentInputMethodSubtype();
diff --git a/core/java/com/android/internal/view/RotationPolicy.java b/core/java/com/android/internal/view/RotationPolicy.java
index e9472fa..13425e5 100644
--- a/core/java/com/android/internal/view/RotationPolicy.java
+++ b/core/java/com/android/internal/view/RotationPolicy.java
@@ -77,7 +77,11 @@
final Point size = new Point();
final IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
try {
- wm.getInitialDisplaySize(Display.DEFAULT_DISPLAY, size);
+ final Display display = context.getDisplay();
+ final int displayId = display != null
+ ? display.getDisplayId()
+ : Display.DEFAULT_DISPLAY;
+ wm.getInitialDisplaySize(displayId, size);
return size.x < size.y ?
Configuration.ORIENTATION_PORTRAIT : Configuration.ORIENTATION_LANDSCAPE;
} catch (RemoteException e) {
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index d294933..80d8063 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -1528,7 +1528,7 @@
* @see StrongAuthTracker#isFingerprintAllowedForUser
*/
public boolean isFingerprintAllowedForUser(int userId) {
- return (getStrongAuthForUser(userId) & ~StrongAuthTracker.ALLOWING_FINGERPRINT) == 0;
+ return (getStrongAuthForUser(userId) & ~StrongAuthTracker.ALLOWING_BIOMETRIC) == 0;
}
public boolean isUserInLockdown(int userId) {
@@ -1733,11 +1733,10 @@
public static final int STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN = 0x20;
/**
- * Strong auth flags that do not prevent fingerprint from being accepted as auth.
- *
- * If any other flags are set, fingerprint is disabled.
+ * Strong auth flags that do not prevent biometric methods from being accepted as auth.
+ * If any other flags are set, biometric authentication is disabled.
*/
- private static final int ALLOWING_FINGERPRINT = STRONG_AUTH_NOT_REQUIRED
+ private static final int ALLOWING_BIOMETRIC = STRONG_AUTH_NOT_REQUIRED
| SOME_AUTH_REQUIRED_AFTER_USER_REQUEST;
private final SparseIntArray mStrongAuthRequiredForUser = new SparseIntArray();
@@ -1784,11 +1783,11 @@
}
/**
- * @return true if unlocking with fingerprint alone is allowed for {@param userId} by the
- * current strong authentication requirements.
+ * @return true if unlocking with a biometric method alone is allowed for {@param userId}
+ * by the current strong authentication requirements.
*/
- public boolean isFingerprintAllowedForUser(int userId) {
- return (getStrongAuthForUser(userId) & ~ALLOWING_FINGERPRINT) == 0;
+ public boolean isBiometricAllowedForUser(int userId) {
+ return (getStrongAuthForUser(userId) & ~ALLOWING_BIOMETRIC) == 0;
}
/**
diff --git a/core/java/com/android/server/BootReceiver.java b/core/java/com/android/server/BootReceiver.java
index 95534e2..621d5a6 100644
--- a/core/java/com/android/server/BootReceiver.java
+++ b/core/java/com/android/server/BootReceiver.java
@@ -258,13 +258,13 @@
// Start watching for new tombstone files; will record them as they occur.
// This gets registered with the singleton file observer thread.
- sTombstoneObserver = new FileObserver(TOMBSTONE_DIR.getPath(), FileObserver.CLOSE_WRITE) {
+ sTombstoneObserver = new FileObserver(TOMBSTONE_DIR.getPath(), FileObserver.CREATE) {
@Override
public void onEvent(int event, String path) {
HashMap<String, Long> timestamps = readTimestamps();
try {
File file = new File(TOMBSTONE_DIR, path);
- if (file.isFile()) {
+ if (file.isFile() && file.getName().startsWith("tombstone_")) {
addFileToDropBox(db, timestamps, headers, file.getPath(), LOG_SIZE,
TAG_TOMBSTONE);
}
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index d4903d8..b675698 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -126,7 +126,6 @@
"android/graphics/Camera.cpp",
"android/graphics/CanvasProperty.cpp",
"android/graphics/ColorFilter.cpp",
- "android/graphics/DrawFilter.cpp",
"android/graphics/FontFamily.cpp",
"android/graphics/FontUtils.cpp",
"android/graphics/CreateJavaOutputStreamAdaptor.cpp",
@@ -143,6 +142,7 @@
"android/graphics/NinePatch.cpp",
"android/graphics/NinePatchPeeker.cpp",
"android/graphics/Paint.cpp",
+ "android/graphics/PaintFilter.cpp",
"android/graphics/Path.cpp",
"android/graphics/PathMeasure.cpp",
"android/graphics/PathEffect.cpp",
@@ -220,7 +220,6 @@
"external/skia/src/image",
"external/skia/src/images",
"frameworks/base/media/jni",
- "libcore/include",
"system/media/camera/include",
"system/media/private/camera/include",
],
diff --git a/core/jni/android/graphics/DrawFilter.cpp b/core/jni/android/graphics/PaintFilter.cpp
similarity index 65%
rename from core/jni/android/graphics/DrawFilter.cpp
rename to core/jni/android/graphics/PaintFilter.cpp
index c1dc0dd..182b22b 100644
--- a/core/jni/android/graphics/DrawFilter.cpp
+++ b/core/jni/android/graphics/PaintFilter.cpp
@@ -15,36 +15,43 @@
** limitations under the License.
*/
-// This file was generated from the C++ include file: SkColorFilter.h
-// Any changes made to this file will be discarded by the build.
-// To change this file, either edit the include, or device/tools/gluemaker/main.cpp,
-// or one of the auxilary file specifications in device/tools/gluemaker.
-
#include "jni.h"
#include "GraphicsJNI.h"
#include <android_runtime/AndroidRuntime.h>
#include "core_jni_helpers.h"
-#include "SkDrawFilter.h"
-#include "SkPaintFlagsDrawFilter.h"
+#include "hwui/PaintFilter.h"
#include "SkPaint.h"
namespace android {
-// Custom version of SkPaintFlagsDrawFilter that also calls setFilterQuality.
-class CompatFlagsDrawFilter : public SkPaintFlagsDrawFilter {
+class PaintFlagsFilter : public PaintFilter {
public:
- CompatFlagsDrawFilter(uint32_t clearFlags, uint32_t setFlags,
- SkFilterQuality desiredQuality)
- : SkPaintFlagsDrawFilter(clearFlags, setFlags)
+ PaintFlagsFilter(uint32_t clearFlags, uint32_t setFlags) {
+ fClearFlags = static_cast<uint16_t>(clearFlags & SkPaint::kAllFlags);
+ fSetFlags = static_cast<uint16_t>(setFlags & SkPaint::kAllFlags);
+ }
+ void filter(SkPaint* paint) override {
+ paint->setFlags((paint->getFlags() & ~fClearFlags) | fSetFlags);
+ }
+
+private:
+ uint16_t fClearFlags;
+ uint16_t fSetFlags;
+};
+
+// Custom version of PaintFlagsDrawFilter that also calls setFilterQuality.
+class CompatPaintFlagsFilter : public PaintFlagsFilter {
+public:
+ CompatPaintFlagsFilter(uint32_t clearFlags, uint32_t setFlags, SkFilterQuality desiredQuality)
+ : PaintFlagsFilter(clearFlags, setFlags)
, fDesiredQuality(desiredQuality) {
}
- virtual bool filter(SkPaint* paint, Type type) {
- SkPaintFlagsDrawFilter::filter(paint, type);
+ virtual void filter(SkPaint* paint) {
+ PaintFlagsFilter::filter(paint);
paint->setFilterQuality(fDesiredQuality);
- return true;
}
private:
@@ -61,16 +68,16 @@
return result;
}
-class SkDrawFilterGlue {
+class PaintFilterGlue {
public:
static void finalizer(JNIEnv* env, jobject clazz, jlong objHandle) {
- SkDrawFilter* obj = reinterpret_cast<SkDrawFilter*>(objHandle);
+ PaintFilter* obj = reinterpret_cast<PaintFilter*>(objHandle);
SkSafeUnref(obj);
}
- static jlong CreatePaintFlagsDF(JNIEnv* env, jobject clazz,
- jint clearFlags, jint setFlags) {
+ static jlong CreatePaintFlagsFilter(JNIEnv* env, jobject clazz,
+ jint clearFlags, jint setFlags) {
if (clearFlags | setFlags) {
// Mask both groups of flags to remove FILTER_BITMAP_FLAG, which no
// longer has a Skia equivalent flag (instead it corresponds to
@@ -79,16 +86,16 @@
const bool turnFilteringOn = hadFiltering(setFlags);
const bool turnFilteringOff = hadFiltering(clearFlags);
- SkDrawFilter* filter;
+ PaintFilter* filter;
if (turnFilteringOn) {
// Turning filtering on overrides turning it off.
- filter = new CompatFlagsDrawFilter(clearFlags, setFlags,
+ filter = new CompatPaintFlagsFilter(clearFlags, setFlags,
kLow_SkFilterQuality);
} else if (turnFilteringOff) {
- filter = new CompatFlagsDrawFilter(clearFlags, setFlags,
+ filter = new CompatPaintFlagsFilter(clearFlags, setFlags,
kNone_SkFilterQuality);
} else {
- filter = new SkPaintFlagsDrawFilter(clearFlags, setFlags);
+ filter = new PaintFlagsFilter(clearFlags, setFlags);
}
return reinterpret_cast<jlong>(filter);
} else {
@@ -98,11 +105,11 @@
};
static const JNINativeMethod drawfilter_methods[] = {
- {"nativeDestructor", "(J)V", (void*) SkDrawFilterGlue::finalizer}
+ {"nativeDestructor", "(J)V", (void*) PaintFilterGlue::finalizer}
};
static const JNINativeMethod paintflags_methods[] = {
- {"nativeConstructor","(II)J", (void*) SkDrawFilterGlue::CreatePaintFlagsDF}
+ {"nativeConstructor","(II)J", (void*) PaintFilterGlue::CreatePaintFlagsFilter}
};
int register_android_graphics_DrawFilter(JNIEnv* env) {
@@ -110,7 +117,7 @@
NELEM(drawfilter_methods));
result |= RegisterMethodsOrDie(env, "android/graphics/PaintFlagsDrawFilter", paintflags_methods,
NELEM(paintflags_methods));
-
+
return 0;
}
diff --git a/core/jni/android_graphics_Canvas.cpp b/core/jni/android_graphics_Canvas.cpp
index 484b33f..3b024b4 100644
--- a/core/jni/android_graphics_Canvas.cpp
+++ b/core/jni/android_graphics_Canvas.cpp
@@ -22,13 +22,13 @@
#include <androidfw/ResourceTypes.h>
#include <hwui/Canvas.h>
#include <hwui/Paint.h>
+#include <hwui/PaintFilter.h>
#include <hwui/Typeface.h>
#include <minikin/Layout.h>
#include <nativehelper/ScopedPrimitiveArray.h>
#include <nativehelper/ScopedStringChars.h>
#include "Bitmap.h"
-#include "SkDrawFilter.h"
#include "SkGraphics.h"
#include "SkRegion.h"
#include "SkVertices.h"
@@ -582,8 +582,9 @@
env->ReleaseStringChars(text, jchars);
}
-static void setDrawFilter(jlong canvasHandle, jlong filterHandle) {
- get_canvas(canvasHandle)->setDrawFilter(reinterpret_cast<SkDrawFilter*>(filterHandle));
+static void setPaintFilter(jlong canvasHandle, jlong filterHandle) {
+ PaintFilter* paintFilter = reinterpret_cast<PaintFilter*>(filterHandle);
+ get_canvas(canvasHandle)->setPaintFilter(sk_ref_sp(paintFilter));
}
static void freeCaches(JNIEnv* env, jobject) {
@@ -633,7 +634,7 @@
{"nQuickReject","(JFFFF)Z", (void*)CanvasJNI::quickRejectRect},
{"nClipRect","(JFFFFI)Z", (void*) CanvasJNI::clipRect},
{"nClipPath","(JJI)Z", (void*) CanvasJNI::clipPath},
- {"nSetDrawFilter", "(JJ)V", (void*) CanvasJNI::setDrawFilter},
+ {"nSetDrawFilter", "(JJ)V", (void*) CanvasJNI::setPaintFilter},
};
// If called from Canvas these are regular JNI
diff --git a/core/jni/android_media_MediaMetricsJNI.h b/core/jni/android_media_MediaMetricsJNI.h
index 16081b4..b3cb4d2 100644
--- a/core/jni/android_media_MediaMetricsJNI.h
+++ b/core/jni/android_media_MediaMetricsJNI.h
@@ -17,7 +17,6 @@
#ifndef _ANDROID_MEDIA_MEDIAMETRICSJNI_H_
#define _ANDROID_MEDIA_MEDIAMETRICSJNI_H_
-#include <android_runtime/AndroidRuntime.h>
#include <jni.h>
#include <nativehelper/JNIHelp.h>
#include <media/MediaAnalyticsItem.h>
diff --git a/core/jni/android_os_GraphicsEnvironment.cpp b/core/jni/android_os_GraphicsEnvironment.cpp
index 4ecfd4b..dfa5de6 100644
--- a/core/jni/android_os_GraphicsEnvironment.cpp
+++ b/core/jni/android_os_GraphicsEnvironment.cpp
@@ -29,7 +29,8 @@
}
void setLayerPaths_native(JNIEnv* env, jobject clazz, jobject classLoader, jstring layerPaths) {
- android_namespace_t* appNamespace = android::FindNamespaceByClassLoader(env, classLoader);
+ android::NativeLoaderNamespace* appNamespace = android::FindNativeLoaderNamespaceByClassLoader(
+ env, classLoader);
ScopedUtfChars layerPathsChars(env, layerPaths);
android::GraphicsEnv::getInstance().setLayerPaths(appNamespace, layerPathsChars.c_str());
}
diff --git a/core/jni/android_os_Trace.cpp b/core/jni/android_os_Trace.cpp
index 4f4e5da..f7dab42 100644
--- a/core/jni/android_os_Trace.cpp
+++ b/core/jni/android_os_Trace.cpp
@@ -14,93 +14,80 @@
* limitations under the License.
*/
-#define LOG_TAG "Trace"
-// #define LOG_NDEBUG 0
-
-#include <inttypes.h>
+#include <jni.h>
#include <cutils/trace.h>
-#include <utils/String8.h>
#include <log/log.h>
-
#include <nativehelper/JNIHelp.h>
-#include <nativehelper/ScopedUtfChars.h>
-#include <nativehelper/ScopedStringChars.h>
+
+#include <array>
namespace android {
-static void sanitizeString(String8& utf8Chars) {
- size_t size = utf8Chars.size();
- char* str = utf8Chars.lockBuffer(size);
+inline static void sanitizeString(char* str, size_t size) {
for (size_t i = 0; i < size; i++) {
char c = str[i];
if (c == '\0' || c == '\n' || c == '|') {
str[i] = ' ';
}
}
- utf8Chars.unlockBuffer();
}
-static jlong android_os_Trace_nativeGetEnabledTags(JNIEnv* env, jclass clazz) {
+inline static void getString(JNIEnv* env, jstring jstring, char* outBuffer, jsize maxSize) {
+ jsize size = std::min(env->GetStringLength(jstring), maxSize);
+ env->GetStringUTFRegion(jstring, 0, size, outBuffer);
+ sanitizeString(outBuffer, size);
+ outBuffer[size] = '\0';
+}
+
+template<typename F>
+inline static void withString(JNIEnv* env, jstring jstr, F callback) {
+ std::array<char, 1024> buffer;
+ getString(env, jstr, buffer.data(), buffer.size());
+ callback(buffer.data());
+}
+
+static jlong android_os_Trace_nativeGetEnabledTags(JNIEnv*, jclass) {
return atrace_get_enabled_tags();
}
-static void android_os_Trace_nativeTraceCounter(JNIEnv* env, jclass clazz,
+static void android_os_Trace_nativeTraceCounter(JNIEnv* env, jclass,
jlong tag, jstring nameStr, jint value) {
- ScopedUtfChars name(env, nameStr);
-
- ALOGV("%s: %" PRId64 " %s %d", __FUNCTION__, tag, name.c_str(), value);
- atrace_int(tag, name.c_str(), value);
+ withString(env, nameStr, [tag, value](char* str) {
+ atrace_int(tag, str, value);
+ });
}
-static void android_os_Trace_nativeTraceBegin(JNIEnv* env, jclass clazz,
+static void android_os_Trace_nativeTraceBegin(JNIEnv* env, jclass,
jlong tag, jstring nameStr) {
- ScopedStringChars jchars(env, nameStr);
- String8 utf8Chars(reinterpret_cast<const char16_t*>(jchars.get()), jchars.size());
- sanitizeString(utf8Chars);
-
- ALOGV("%s: %" PRId64 " %s", __FUNCTION__, tag, utf8Chars.string());
- atrace_begin(tag, utf8Chars.string());
+ withString(env, nameStr, [tag](char* str) {
+ atrace_begin(tag, str);
+ });
}
-static void android_os_Trace_nativeTraceEnd(JNIEnv* env, jclass clazz,
- jlong tag) {
-
- ALOGV("%s: %" PRId64, __FUNCTION__, tag);
+static void android_os_Trace_nativeTraceEnd(JNIEnv*, jclass, jlong tag) {
atrace_end(tag);
}
-static void android_os_Trace_nativeAsyncTraceBegin(JNIEnv* env, jclass clazz,
+static void android_os_Trace_nativeAsyncTraceBegin(JNIEnv* env, jclass,
jlong tag, jstring nameStr, jint cookie) {
- ScopedStringChars jchars(env, nameStr);
- String8 utf8Chars(reinterpret_cast<const char16_t*>(jchars.get()), jchars.size());
- sanitizeString(utf8Chars);
-
- ALOGV("%s: %" PRId64 " %s %d", __FUNCTION__, tag, utf8Chars.string(), cookie);
- atrace_async_begin(tag, utf8Chars.string(), cookie);
+ withString(env, nameStr, [tag, cookie](char* str) {
+ atrace_async_begin(tag, str, cookie);
+ });
}
-static void android_os_Trace_nativeAsyncTraceEnd(JNIEnv* env, jclass clazz,
+static void android_os_Trace_nativeAsyncTraceEnd(JNIEnv* env, jclass,
jlong tag, jstring nameStr, jint cookie) {
- ScopedStringChars jchars(env, nameStr);
- String8 utf8Chars(reinterpret_cast<const char16_t*>(jchars.get()), jchars.size());
- sanitizeString(utf8Chars);
-
- ALOGV("%s: %" PRId64 " %s %d", __FUNCTION__, tag, utf8Chars.string(), cookie);
- atrace_async_end(tag, utf8Chars.string(), cookie);
+ withString(env, nameStr, [tag, cookie](char* str) {
+ atrace_async_end(tag, str, cookie);
+ });
}
-static void android_os_Trace_nativeSetAppTracingAllowed(JNIEnv* env,
- jclass clazz, jboolean allowed) {
- ALOGV("%s: %d", __FUNCTION__, allowed);
-
+static void android_os_Trace_nativeSetAppTracingAllowed(JNIEnv*, jclass, jboolean allowed) {
atrace_set_debuggable(allowed);
}
-static void android_os_Trace_nativeSetTracingEnabled(JNIEnv* env,
- jclass clazz, jboolean enabled) {
- ALOGV("%s: %d", __FUNCTION__, enabled);
-
+static void android_os_Trace_nativeSetTracingEnabled(JNIEnv*, jclass, jboolean enabled) {
atrace_set_tracing_enabled(enabled);
}
diff --git a/core/jni/android_os_VintfObject.cpp b/core/jni/android_os_VintfObject.cpp
index e8ef349..17ab956 100644
--- a/core/jni/android_os_VintfObject.cpp
+++ b/core/jni/android_os_VintfObject.cpp
@@ -150,7 +150,7 @@
}
jobject jMap = env->NewObject(gHashMapClazz, gHashMapInit);
for (const auto &vndk : manifest->vendorNdks()) {
- std::string key = vndk.version();
+ const std::string& key = vndk.version();
env->CallObjectMethod(jMap, gHashMapPut,
env->NewStringUTF(key.c_str()), toJavaStringArray(env, vndk.libraries()));
}
diff --git a/core/jni/android_text_MeasuredParagraph.cpp b/core/jni/android_text_MeasuredParagraph.cpp
index 41a81ac..9eb6f8d 100644
--- a/core/jni/android_text_MeasuredParagraph.cpp
+++ b/core/jni/android_text_MeasuredParagraph.cpp
@@ -17,7 +17,6 @@
#define LOG_TAG "MeasuredParagraph"
#include "GraphicsJNI.h"
-#include "ScopedIcuLocale.h"
#include "unicode/locid.h"
#include "unicode/brkiter.h"
#include "utils/misc.h"
diff --git a/core/jni/android_text_StaticLayout.cpp b/core/jni/android_text_StaticLayout.cpp
index ad84858..fec5b69 100644
--- a/core/jni/android_text_StaticLayout.cpp
+++ b/core/jni/android_text_StaticLayout.cpp
@@ -16,7 +16,6 @@
#define LOG_TAG "StaticLayout"
-#include "ScopedIcuLocale.h"
#include "unicode/locid.h"
#include "unicode/brkiter.h"
#include "utils/misc.h"
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index c3ba9ba..ecad6c0 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -108,7 +108,6 @@
jclass mClass;
jmethodID mGetInstance;
jmethodID mSendDeathNotice;
- jmethodID mDumpProxyDebugInfo;
// Object state.
jfieldID mNativeData; // Field holds native pointer to BinderProxyNativeData.
@@ -155,9 +154,8 @@
static constexpr int32_t PROXY_WARN_INTERVAL = 5000;
static constexpr uint32_t GC_INTERVAL = 1000;
-// Protected by gProxyLock. We warn if this gets too large.
-static int32_t gNumProxies = 0;
-static int32_t gProxiesWarned = 0;
+static std::atomic<uint32_t> gNumProxies(0);
+static std::atomic<uint32_t> gProxiesWarned(0);
// Number of GlobalRefs held by JavaBBinders.
static std::atomic<uint32_t> gNumLocalRefsCreated(0);
@@ -632,12 +630,6 @@
return (BinderProxyNativeData *) env->GetLongField(obj, gBinderProxyOffsets.mNativeData);
}
-static Mutex gProxyLock;
-
-// We may cache a single BinderProxyNativeData node to avoid repeat allocation.
-// All fields are null. Protected by gProxyLock.
-static BinderProxyNativeData *gNativeDataCache;
-
// If the argument is a JavaBBinder, return the Java object that was used to create it.
// Otherwise return a BinderProxy for the IBinder. If a previous call was passed the
// same IBinder, and the original BinderProxy is still alive, return the same BinderProxy.
@@ -652,36 +644,31 @@
return object;
}
- // For the rest of the function we will hold this lock, to serialize
- // looking/creation/destruction of Java proxies for native Binder proxies.
- AutoMutex _l(gProxyLock);
+ BinderProxyNativeData* nativeData = new BinderProxyNativeData();
+ nativeData->mOrgue = new DeathRecipientList;
+ nativeData->mObject = val;
- BinderProxyNativeData* nativeData = gNativeDataCache;
- if (nativeData == nullptr) {
- nativeData = new BinderProxyNativeData();
- }
- // gNativeDataCache is now logically empty.
jobject object = env->CallStaticObjectMethod(gBinderProxyOffsets.mClass,
gBinderProxyOffsets.mGetInstance, (jlong) nativeData, (jlong) val.get());
if (env->ExceptionCheck()) {
// In the exception case, getInstance still took ownership of nativeData.
- gNativeDataCache = nullptr;
return NULL;
}
BinderProxyNativeData* actualNativeData = getBPNativeData(env, object);
if (actualNativeData == nativeData) {
- // New BinderProxy; we still have exclusive access.
- nativeData->mOrgue = new DeathRecipientList;
- nativeData->mObject = val;
- gNativeDataCache = nullptr;
- ++gNumProxies;
- if (gNumProxies >= gProxiesWarned + PROXY_WARN_INTERVAL) {
- ALOGW("Unexpectedly many live BinderProxies: %d\n", gNumProxies);
- gProxiesWarned = gNumProxies;
+ // Created a new Proxy
+ uint32_t numProxies = gNumProxies.fetch_add(1, std::memory_order_relaxed);
+ uint32_t numLastWarned = gProxiesWarned.load(std::memory_order_relaxed);
+ if (numProxies >= numLastWarned + PROXY_WARN_INTERVAL) {
+ // Multiple threads can get here, make sure only one of them gets to
+ // update the warn counter.
+ if (gProxiesWarned.compare_exchange_strong(numLastWarned,
+ numLastWarned + PROXY_WARN_INTERVAL, std::memory_order_relaxed)) {
+ ALOGW("Unexpectedly many live BinderProxies: %d\n", numProxies);
+ }
}
} else {
- // nativeData wasn't used. Reuse it the next time.
- gNativeDataCache = nativeData;
+ delete nativeData;
}
return object;
@@ -959,8 +946,7 @@
jint android_os_Debug_getProxyObjectCount(JNIEnv* env, jobject clazz)
{
- AutoMutex _l(gProxyLock);
- return gNumProxies;
+ return gNumProxies.load();
}
jint android_os_Debug_getDeathObjectCount(JNIEnv* env, jobject clazz)
@@ -1007,18 +993,6 @@
static void android_os_BinderInternal_proxyLimitcallback(int uid)
{
JNIEnv *env = AndroidRuntime::getJNIEnv();
- {
- // Calls into BinderProxy must be serialized
- AutoMutex _l(gProxyLock);
- env->CallStaticObjectMethod(gBinderProxyOffsets.mClass,
- gBinderProxyOffsets.mDumpProxyDebugInfo);
- }
- if (env->ExceptionCheck()) {
- ScopedLocalRef<jthrowable> excep(env, env->ExceptionOccurred());
- report_exception(env, excep.get(),
- "*** Uncaught exception in dumpProxyDebugInfo");
- }
-
env->CallStaticVoidMethod(gBinderInternalOffsets.mClass,
gBinderInternalOffsets.mProxyLimitCallback,
uid);
@@ -1367,9 +1341,6 @@
static void BinderProxy_destroy(void* rawNativeData)
{
- // Don't race with construction/initialization
- AutoMutex _l(gProxyLock);
-
BinderProxyNativeData * nativeData = (BinderProxyNativeData *) rawNativeData;
LOGDEATH("Destroying BinderProxy: binder=%p drl=%p\n",
nativeData->mObject.get(), nativeData->mOrgue.get());
@@ -1408,8 +1379,6 @@
"(JJ)Landroid/os/BinderProxy;");
gBinderProxyOffsets.mSendDeathNotice = GetStaticMethodIDOrDie(env, clazz, "sendDeathNotice",
"(Landroid/os/IBinder$DeathRecipient;)V");
- gBinderProxyOffsets.mDumpProxyDebugInfo = GetStaticMethodIDOrDie(env, clazz, "dumpProxyDebugInfo",
- "()V");
gBinderProxyOffsets.mNativeData = GetFieldIDOrDie(env, clazz, "mNativeData", "J");
clazz = FindClassOrDie(env, "java/lang/Class");
diff --git a/core/jni/android_view_InputEventSender.cpp b/core/jni/android_view_InputEventSender.cpp
index 095252c..10da892 100644
--- a/core/jni/android_view_InputEventSender.cpp
+++ b/core/jni/android_view_InputEventSender.cpp
@@ -114,8 +114,8 @@
uint32_t publishedSeq = mNextPublishedSeq++;
status_t status = mInputPublisher.publishKeyEvent(publishedSeq,
- event->getDeviceId(), event->getSource(), event->getAction(), event->getFlags(),
- event->getKeyCode(), event->getScanCode(), event->getMetaState(),
+ event->getDeviceId(), event->getSource(), event->getDisplayId(), event->getAction(),
+ event->getFlags(), event->getKeyCode(), event->getScanCode(), event->getMetaState(),
event->getRepeatCount(), event->getDownTime(), event->getEventTime());
if (status) {
ALOGW("Failed to send key event on channel '%s'. status=%d",
@@ -135,8 +135,7 @@
for (size_t i = 0; i <= event->getHistorySize(); i++) {
publishedSeq = mNextPublishedSeq++;
status_t status = mInputPublisher.publishMotionEvent(publishedSeq,
- event->getDeviceId(), event->getSource(),
- event->getDisplayId(),
+ event->getDeviceId(), event->getSource(), event->getDisplayId(),
event->getAction(), event->getActionButton(), event->getFlags(),
event->getEdgeFlags(), event->getMetaState(), event->getButtonState(),
event->getXOffset(), event->getYOffset(),
diff --git a/core/jni/android_view_KeyEvent.cpp b/core/jni/android_view_KeyEvent.cpp
index 8a6e745..f010772 100644
--- a/core/jni/android_view_KeyEvent.cpp
+++ b/core/jni/android_view_KeyEvent.cpp
@@ -39,6 +39,7 @@
jfieldID mDeviceId;
jfieldID mSource;
+ jfieldID mDisplayId;
jfieldID mMetaState;
jfieldID mAction;
jfieldID mKeyCode;
@@ -65,6 +66,7 @@
event->getScanCode(),
event->getFlags(),
event->getSource(),
+ event->getDisplayId(),
NULL);
if (env->ExceptionCheck()) {
ALOGE("An exception occurred while obtaining a key event.");
@@ -79,6 +81,7 @@
KeyEvent* event) {
jint deviceId = env->GetIntField(eventObj, gKeyEventClassInfo.mDeviceId);
jint source = env->GetIntField(eventObj, gKeyEventClassInfo.mSource);
+ jint displayId = env->GetIntField(eventObj, gKeyEventClassInfo.mDisplayId);
jint metaState = env->GetIntField(eventObj, gKeyEventClassInfo.mMetaState);
jint action = env->GetIntField(eventObj, gKeyEventClassInfo.mAction);
jint keyCode = env->GetIntField(eventObj, gKeyEventClassInfo.mKeyCode);
@@ -88,7 +91,8 @@
jlong downTime = env->GetLongField(eventObj, gKeyEventClassInfo.mDownTime);
jlong eventTime = env->GetLongField(eventObj, gKeyEventClassInfo.mEventTime);
- event->initialize(deviceId, source, action, flags, keyCode, scanCode, metaState, repeatCount,
+ event->initialize(deviceId, source, displayId, action, flags, keyCode, scanCode, metaState,
+ repeatCount,
milliseconds_to_nanoseconds(downTime),
milliseconds_to_nanoseconds(eventTime));
return OK;
@@ -131,12 +135,14 @@
gKeyEventClassInfo.clazz = MakeGlobalRefOrDie(env, clazz);
gKeyEventClassInfo.obtain = GetStaticMethodIDOrDie(env, gKeyEventClassInfo.clazz,
- "obtain", "(JJIIIIIIIILjava/lang/String;)Landroid/view/KeyEvent;");
+ "obtain", "(JJIIIIIIIIILjava/lang/String;)Landroid/view/KeyEvent;");
gKeyEventClassInfo.recycle = GetMethodIDOrDie(env, gKeyEventClassInfo.clazz,
"recycle", "()V");
gKeyEventClassInfo.mDeviceId = GetFieldIDOrDie(env, gKeyEventClassInfo.clazz, "mDeviceId", "I");
gKeyEventClassInfo.mSource = GetFieldIDOrDie(env, gKeyEventClassInfo.clazz, "mSource", "I");
+ gKeyEventClassInfo.mDisplayId = GetFieldIDOrDie(env, gKeyEventClassInfo.clazz, "mDisplayId",
+ "I");
gKeyEventClassInfo.mMetaState = GetFieldIDOrDie(env, gKeyEventClassInfo.clazz, "mMetaState",
"I");
gKeyEventClassInfo.mAction = GetFieldIDOrDie(env, gKeyEventClassInfo.clazz, "mAction", "I");
diff --git a/core/jni/android_view_RenderNode.cpp b/core/jni/android_view_RenderNode.cpp
index 37ea810..46b19bd 100644
--- a/core/jni/android_view_RenderNode.cpp
+++ b/core/jni/android_view_RenderNode.cpp
@@ -222,6 +222,11 @@
RenderNode::GENERIC);
}
+static void android_view_RenderNode_setUsageHint(jlong renderNodePtr, jint usageHint) {
+ RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+ renderNode->setUsageHint(static_cast<UsageHint>(usageHint));
+}
+
static jboolean android_view_RenderNode_setElevation(jlong renderNodePtr, float elevation) {
return SET_AND_DIRTY(setElevation, elevation, RenderNode::Z);
}
@@ -431,6 +436,14 @@
return renderNode->stagingProperties().getPivotY();
}
+static jint android_view_RenderNode_getWidth(jlong renderNodePtr) {
+ return reinterpret_cast<RenderNode*>(renderNodePtr)->stagingProperties().getWidth();
+}
+
+static jint android_view_RenderNode_getHeight(jlong renderNodePtr) {
+ return reinterpret_cast<RenderNode*>(renderNodePtr)->stagingProperties().getHeight();
+}
+
// ----------------------------------------------------------------------------
// RenderProperties - Animations
// ----------------------------------------------------------------------------
@@ -606,6 +619,7 @@
{ "nSetAlpha", "(JF)Z", (void*) android_view_RenderNode_setAlpha },
{ "nSetHasOverlappingRendering", "(JZ)Z",
(void*) android_view_RenderNode_setHasOverlappingRendering },
+ { "nSetUsageHint", "(JI)V", (void*) android_view_RenderNode_setUsageHint },
{ "nSetElevation", "(JF)Z", (void*) android_view_RenderNode_setElevation },
{ "nSetTranslationX", "(JF)Z", (void*) android_view_RenderNode_setTranslationX },
{ "nSetTranslationY", "(JF)Z", (void*) android_view_RenderNode_setTranslationY },
@@ -648,6 +662,8 @@
{ "nGetPivotX", "(J)F", (void*) android_view_RenderNode_getPivotX },
{ "nGetPivotY", "(J)F", (void*) android_view_RenderNode_getPivotY },
+ { "nGetWidth", "(J)I", (void*) android_view_RenderNode_getWidth },
+ { "nGetHeight", "(J)I", (void*) android_view_RenderNode_getHeight },
};
int register_android_view_RenderNode(JNIEnv* env) {
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index 497b289..4a17742 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -1054,6 +1054,13 @@
Properties::contextPriority = contextPriority;
}
+static void android_view_ThreadedRenderer_allocateBuffers(JNIEnv* env, jobject clazz,
+ jlong proxyPtr, jobject jsurface) {
+ RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
+ sp<Surface> surface = android_view_Surface_getSurface(env, jsurface);
+ proxy->allocateBuffers(surface);
+}
+
// ----------------------------------------------------------------------------
// FrameMetricsObserver
// ----------------------------------------------------------------------------
@@ -1166,6 +1173,7 @@
{ "nSetDebuggingEnabled", "(Z)V", (void*)android_view_ThreadedRenderer_setDebuggingEnabled },
{ "nSetIsolatedProcess", "(Z)V", (void*)android_view_ThreadedRenderer_setIsolatedProcess },
{ "nSetContextPriority", "(I)V", (void*)android_view_ThreadedRenderer_setContextPriority },
+ { "nAllocateBuffers", "(JLandroid/view/Surface;)V", (void*)android_view_ThreadedRenderer_allocateBuffers },
};
static JavaVM* mJvm = nullptr;
diff --git a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
index cc2646c..dc04269 100644
--- a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
+++ b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
@@ -27,6 +27,7 @@
#include <zlib.h>
+#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
@@ -567,7 +568,14 @@
return 0;
}
- ZipFileRO* zipFile = ZipFileRO::openFd(fd, debugFilePath.c_str());
+ int dupedFd = dup(fd);
+ if (dupedFd == -1) {
+ jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
+ "Failed to dup FileDescriptor: %s", strerror(errno));
+ return 0;
+ }
+
+ ZipFileRO* zipFile = ZipFileRO::openFd(dupedFd, debugFilePath.c_str());
return reinterpret_cast<jlong>(zipFile);
}
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 12165d4..e30a3e7 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -45,6 +45,7 @@
#include <unistd.h>
#include "android-base/logging.h"
+#include <android-base/properties.h>
#include <android-base/file.h>
#include <android-base/stringprintf.h>
#include <cutils/fs.h>
@@ -70,6 +71,7 @@
using android::String8;
using android::base::StringPrintf;
using android::base::WriteStringToFile;
+using android::base::GetBoolProperty;
#define CREATE_ERROR(...) StringPrintf("%s:%d: ", __FILE__, __LINE__). \
append(StringPrintf(__VA_ARGS__))
@@ -535,17 +537,209 @@
return true;
}
+// Utility routine to specialize a zygote child process.
+static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray javaGids,
+ jint runtime_flags, jobjectArray javaRlimits,
+ jlong permittedCapabilities, jlong effectiveCapabilities,
+ jint mount_external, jstring java_se_info, jstring java_se_name,
+ bool is_system_server, bool is_child_zygote, jstring instructionSet,
+ jstring dataDir) {
+ std::string error_msg;
+
+ auto fail_fn = [env, java_se_name, is_system_server](const std::string& msg)
+ __attribute__ ((noreturn)) {
+ const char* se_name_c_str = nullptr;
+ std::unique_ptr<ScopedUtfChars> se_name;
+ if (java_se_name != nullptr) {
+ se_name.reset(new ScopedUtfChars(env, java_se_name));
+ se_name_c_str = se_name->c_str();
+ }
+ if (se_name_c_str == nullptr && is_system_server) {
+ se_name_c_str = "system_server";
+ }
+ const std::string& error_msg = (se_name_c_str == nullptr)
+ ? msg
+ : StringPrintf("(%s) %s", se_name_c_str, msg.c_str());
+ env->FatalError(error_msg.c_str());
+ __builtin_unreachable();
+ };
+
+ // Keep capabilities across UID change, unless we're staying root.
+ if (uid != 0) {
+ if (!EnableKeepCapabilities(&error_msg)) {
+ fail_fn(error_msg);
+ }
+ }
+
+ if (!SetInheritable(permittedCapabilities, &error_msg)) {
+ fail_fn(error_msg);
+ }
+ if (!DropCapabilitiesBoundingSet(&error_msg)) {
+ fail_fn(error_msg);
+ }
+
+ bool use_native_bridge = !is_system_server && (instructionSet != NULL)
+ && android::NativeBridgeAvailable();
+ if (use_native_bridge) {
+ ScopedUtfChars isa_string(env, instructionSet);
+ use_native_bridge = android::NeedsNativeBridge(isa_string.c_str());
+ }
+ if (use_native_bridge && dataDir == NULL) {
+ // dataDir should never be null if we need to use a native bridge.
+ // In general, dataDir will never be null for normal applications. It can only happen in
+ // special cases (for isolated processes which are not associated with any app). These are
+ // launched by the framework and should not be emulated anyway.
+ use_native_bridge = false;
+ ALOGW("Native bridge will not be used because dataDir == NULL.");
+ }
+
+ if (!MountEmulatedStorage(uid, mount_external, use_native_bridge, &error_msg)) {
+ ALOGW("Failed to mount emulated storage: %s (%s)", error_msg.c_str(), strerror(errno));
+ if (errno == ENOTCONN || errno == EROFS) {
+ // When device is actively encrypting, we get ENOTCONN here
+ // since FUSE was mounted before the framework restarted.
+ // When encrypted device is booting, we get EROFS since
+ // FUSE hasn't been created yet by init.
+ // In either case, continue without external storage.
+ } else {
+ fail_fn(error_msg);
+ }
+ }
+
+ // If this zygote isn't root, it won't be able to create a process group,
+ // since the directory is owned by root.
+ if (!is_system_server && getuid() == 0) {
+ int rc = createProcessGroup(uid, getpid());
+ if (rc != 0) {
+ if (rc == -EROFS) {
+ ALOGW("createProcessGroup failed, kernel missing CONFIG_CGROUP_CPUACCT?");
+ } else {
+ ALOGE("createProcessGroup(%d, %d) failed: %s", uid, 0/*pid*/, strerror(-rc));
+ }
+ }
+ }
+
+ if (!SetGids(env, javaGids, &error_msg)) {
+ fail_fn(error_msg);
+ }
+
+ if (!SetRLimits(env, javaRlimits, &error_msg)) {
+ fail_fn(error_msg);
+ }
+
+ if (use_native_bridge) {
+ ScopedUtfChars isa_string(env, instructionSet);
+ ScopedUtfChars data_dir(env, dataDir);
+ android::PreInitializeNativeBridge(data_dir.c_str(), isa_string.c_str());
+ }
+
+ int rc = setresgid(gid, gid, gid);
+ if (rc == -1) {
+ fail_fn(CREATE_ERROR("setresgid(%d) failed: %s", gid, strerror(errno)));
+ }
+
+ // Must be called when the new process still has CAP_SYS_ADMIN, in this case, before changing
+ // uid from 0, which clears capabilities. The other alternative is to call
+ // prctl(PR_SET_NO_NEW_PRIVS, 1) afterward, but that breaks SELinux domain transition (see
+ // b/71859146). As the result, privileged syscalls used below still need to be accessible in
+ // app process.
+ SetUpSeccompFilter(uid);
+
+ rc = setresuid(uid, uid, uid);
+ if (rc == -1) {
+ fail_fn(CREATE_ERROR("setresuid(%d) failed: %s", uid, strerror(errno)));
+ }
+
+ // The "dumpable" flag of a process, which controls core dump generation, is
+ // overwritten by the value in /proc/sys/fs/suid_dumpable when the effective
+ // user or group ID changes. See proc(5) for possible values. In most cases,
+ // the value is 0, so core dumps are disabled for zygote children. However,
+ // when running in a Chrome OS container, the value is already set to 2,
+ // which allows the external crash reporter to collect all core dumps. Since
+ // only system crashes are interested, core dump is disabled for app
+ // processes. This also ensures compliance with CTS.
+ int dumpable = prctl(PR_GET_DUMPABLE);
+ if (dumpable == -1) {
+ ALOGE("prctl(PR_GET_DUMPABLE) failed: %s", strerror(errno));
+ RuntimeAbort(env, __LINE__, "prctl(PR_GET_DUMPABLE) failed");
+ }
+ if (dumpable == 2 && uid >= AID_APP) {
+ if (prctl(PR_SET_DUMPABLE, 0, 0, 0, 0) == -1) {
+ ALOGE("prctl(PR_SET_DUMPABLE, 0) failed: %s", strerror(errno));
+ RuntimeAbort(env, __LINE__, "prctl(PR_SET_DUMPABLE, 0) failed");
+ }
+ }
+
+ if (NeedsNoRandomizeWorkaround()) {
+ // Work around ARM kernel ASLR lossage (http://b/5817320).
+ int old_personality = personality(0xffffffff);
+ int new_personality = personality(old_personality | ADDR_NO_RANDOMIZE);
+ if (new_personality == -1) {
+ ALOGW("personality(%d) failed: %s", new_personality, strerror(errno));
+ }
+ }
+
+ if (!SetCapabilities(permittedCapabilities, effectiveCapabilities, permittedCapabilities,
+ &error_msg)) {
+ fail_fn(error_msg);
+ }
+
+ if (!SetSchedulerPolicy(&error_msg)) {
+ fail_fn(error_msg);
+ }
+
+ const char* se_info_c_str = NULL;
+ ScopedUtfChars* se_info = NULL;
+ if (java_se_info != NULL) {
+ se_info = new ScopedUtfChars(env, java_se_info);
+ se_info_c_str = se_info->c_str();
+ if (se_info_c_str == NULL) {
+ fail_fn("se_info_c_str == NULL");
+ }
+ }
+ const char* se_name_c_str = NULL;
+ ScopedUtfChars* se_name = NULL;
+ if (java_se_name != NULL) {
+ se_name = new ScopedUtfChars(env, java_se_name);
+ se_name_c_str = se_name->c_str();
+ if (se_name_c_str == NULL) {
+ fail_fn("se_name_c_str == NULL");
+ }
+ }
+ rc = selinux_android_setcontext(uid, is_system_server, se_info_c_str, se_name_c_str);
+ if (rc == -1) {
+ fail_fn(CREATE_ERROR("selinux_android_setcontext(%d, %d, \"%s\", \"%s\") failed", uid,
+ is_system_server, se_info_c_str, se_name_c_str));
+ }
+
+ // Make it easier to debug audit logs by setting the main thread's name to the
+ // nice name rather than "app_process".
+ if (se_name_c_str == NULL && is_system_server) {
+ se_name_c_str = "system_server";
+ }
+ if (se_name_c_str != NULL) {
+ SetThreadName(se_name_c_str);
+ }
+
+ delete se_info;
+ delete se_name;
+
+ // Unset the SIGCHLD handler, but keep ignoring SIGHUP (rationale in SetSignalHandlers).
+ UnsetChldSignalHandler();
+
+ env->CallStaticVoidMethod(gZygoteClass, gCallPostForkChildHooks, runtime_flags,
+ is_system_server, is_child_zygote, instructionSet);
+ if (env->ExceptionCheck()) {
+ fail_fn("Error calling post fork hooks.");
+ }
+}
+
// Utility routine to fork zygote and specialize the child process.
-static pid_t ForkAndSpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray javaGids,
- jint runtime_flags, jobjectArray javaRlimits,
- jlong permittedCapabilities, jlong effectiveCapabilities,
- jint mount_external,
- jstring java_se_info, jstring java_se_name,
- bool is_system_server, jintArray fdsToClose,
- jintArray fdsToIgnore, bool is_child_zygote,
- jstring instructionSet, jstring dataDir) {
+static pid_t ForkCommon(JNIEnv* env, jstring java_se_name, bool is_system_server,
+ jintArray fdsToClose, jintArray fdsToIgnore) {
SetSignalHandlers();
+ // Block SIGCHLD prior to fork.
sigset_t sigchld;
sigemptyset(&sigchld);
sigaddset(&sigchld, SIGCHLD);
@@ -602,6 +796,7 @@
pid_t pid = fork();
if (pid == 0) {
+ // The child process.
PreApplicationInit();
// Clean up any descriptors which must be closed immediately
@@ -614,187 +809,11 @@
if (!gOpenFdTable->ReopenOrDetach(&error_msg)) {
fail_fn(error_msg);
}
+ }
- if (sigprocmask(SIG_UNBLOCK, &sigchld, nullptr) == -1) {
- fail_fn(CREATE_ERROR("sigprocmask(SIG_SETMASK, { SIGCHLD }) failed: %s", strerror(errno)));
- }
-
- // Keep capabilities across UID change, unless we're staying root.
- if (uid != 0) {
- if (!EnableKeepCapabilities(&error_msg)) {
- fail_fn(error_msg);
- }
- }
-
- if (!SetInheritable(permittedCapabilities, &error_msg)) {
- fail_fn(error_msg);
- }
- if (!DropCapabilitiesBoundingSet(&error_msg)) {
- fail_fn(error_msg);
- }
-
- bool use_native_bridge = !is_system_server && (instructionSet != NULL)
- && android::NativeBridgeAvailable();
- if (use_native_bridge) {
- ScopedUtfChars isa_string(env, instructionSet);
- use_native_bridge = android::NeedsNativeBridge(isa_string.c_str());
- }
- if (use_native_bridge && dataDir == NULL) {
- // dataDir should never be null if we need to use a native bridge.
- // In general, dataDir will never be null for normal applications. It can only happen in
- // special cases (for isolated processes which are not associated with any app). These are
- // launched by the framework and should not be emulated anyway.
- use_native_bridge = false;
- ALOGW("Native bridge will not be used because dataDir == NULL.");
- }
-
- if (!MountEmulatedStorage(uid, mount_external, use_native_bridge, &error_msg)) {
- ALOGW("Failed to mount emulated storage: %s (%s)", error_msg.c_str(), strerror(errno));
- if (errno == ENOTCONN || errno == EROFS) {
- // When device is actively encrypting, we get ENOTCONN here
- // since FUSE was mounted before the framework restarted.
- // When encrypted device is booting, we get EROFS since
- // FUSE hasn't been created yet by init.
- // In either case, continue without external storage.
- } else {
- fail_fn(error_msg);
- }
- }
-
- // If this zygote isn't root, it won't be able to create a process group,
- // since the directory is owned by root.
- if (!is_system_server && getuid() == 0) {
- int rc = createProcessGroup(uid, getpid());
- if (rc != 0) {
- if (rc == -EROFS) {
- ALOGW("createProcessGroup failed, kernel missing CONFIG_CGROUP_CPUACCT?");
- } else {
- ALOGE("createProcessGroup(%d, %d) failed: %s", uid, pid, strerror(-rc));
- }
- }
- }
-
- std::string error_msg;
- if (!SetGids(env, javaGids, &error_msg)) {
- fail_fn(error_msg);
- }
-
- if (!SetRLimits(env, javaRlimits, &error_msg)) {
- fail_fn(error_msg);
- }
-
- if (use_native_bridge) {
- ScopedUtfChars isa_string(env, instructionSet);
- ScopedUtfChars data_dir(env, dataDir);
- android::PreInitializeNativeBridge(data_dir.c_str(), isa_string.c_str());
- }
-
- int rc = setresgid(gid, gid, gid);
- if (rc == -1) {
- fail_fn(CREATE_ERROR("setresgid(%d) failed: %s", gid, strerror(errno)));
- }
-
- // Must be called when the new process still has CAP_SYS_ADMIN, in this case, before changing
- // uid from 0, which clears capabilities. The other alternative is to call
- // prctl(PR_SET_NO_NEW_PRIVS, 1) afterward, but that breaks SELinux domain transition (see
- // b/71859146). As the result, privileged syscalls used below still need to be accessible in
- // app process.
- SetUpSeccompFilter(uid);
-
- rc = setresuid(uid, uid, uid);
- if (rc == -1) {
- fail_fn(CREATE_ERROR("setresuid(%d) failed: %s", uid, strerror(errno)));
- }
-
- // The "dumpable" flag of a process, which controls core dump generation, is
- // overwritten by the value in /proc/sys/fs/suid_dumpable when the effective
- // user or group ID changes. See proc(5) for possible values. In most cases,
- // the value is 0, so core dumps are disabled for zygote children. However,
- // when running in a Chrome OS container, the value is already set to 2,
- // which allows the external crash reporter to collect all core dumps. Since
- // only system crashes are interested, core dump is disabled for app
- // processes. This also ensures compliance with CTS.
- int dumpable = prctl(PR_GET_DUMPABLE);
- if (dumpable == -1) {
- ALOGE("prctl(PR_GET_DUMPABLE) failed: %s", strerror(errno));
- RuntimeAbort(env, __LINE__, "prctl(PR_GET_DUMPABLE) failed");
- }
- if (dumpable == 2 && uid >= AID_APP) {
- if (prctl(PR_SET_DUMPABLE, 0, 0, 0, 0) == -1) {
- ALOGE("prctl(PR_SET_DUMPABLE, 0) failed: %s", strerror(errno));
- RuntimeAbort(env, __LINE__, "prctl(PR_SET_DUMPABLE, 0) failed");
- }
- }
-
- if (NeedsNoRandomizeWorkaround()) {
- // Work around ARM kernel ASLR lossage (http://b/5817320).
- int old_personality = personality(0xffffffff);
- int new_personality = personality(old_personality | ADDR_NO_RANDOMIZE);
- if (new_personality == -1) {
- ALOGW("personality(%d) failed: %s", new_personality, strerror(errno));
- }
- }
-
- if (!SetCapabilities(permittedCapabilities, effectiveCapabilities, permittedCapabilities,
- &error_msg)) {
- fail_fn(error_msg);
- }
-
- if (!SetSchedulerPolicy(&error_msg)) {
- fail_fn(error_msg);
- }
-
- const char* se_info_c_str = NULL;
- ScopedUtfChars* se_info = NULL;
- if (java_se_info != NULL) {
- se_info = new ScopedUtfChars(env, java_se_info);
- se_info_c_str = se_info->c_str();
- if (se_info_c_str == NULL) {
- fail_fn("se_info_c_str == NULL");
- }
- }
- const char* se_name_c_str = NULL;
- ScopedUtfChars* se_name = NULL;
- if (java_se_name != NULL) {
- se_name = new ScopedUtfChars(env, java_se_name);
- se_name_c_str = se_name->c_str();
- if (se_name_c_str == NULL) {
- fail_fn("se_name_c_str == NULL");
- }
- }
- rc = selinux_android_setcontext(uid, is_system_server, se_info_c_str, se_name_c_str);
- if (rc == -1) {
- fail_fn(CREATE_ERROR("selinux_android_setcontext(%d, %d, \"%s\", \"%s\") failed", uid,
- is_system_server, se_info_c_str, se_name_c_str));
- }
-
- // Make it easier to debug audit logs by setting the main thread's name to the
- // nice name rather than "app_process".
- if (se_name_c_str == NULL && is_system_server) {
- se_name_c_str = "system_server";
- }
- if (se_name_c_str != NULL) {
- SetThreadName(se_name_c_str);
- }
-
- delete se_info;
- delete se_name;
-
- // Unset the SIGCHLD handler, but keep ignoring SIGHUP (rationale in SetSignalHandlers).
- UnsetChldSignalHandler();
-
- env->CallStaticVoidMethod(gZygoteClass, gCallPostForkChildHooks, runtime_flags,
- is_system_server, is_child_zygote, instructionSet);
- if (env->ExceptionCheck()) {
- fail_fn("Error calling post fork hooks.");
- }
- } else if (pid > 0) {
- // the parent process
-
- // We blocked SIGCHLD prior to a fork, we unblock it here.
- if (sigprocmask(SIG_UNBLOCK, &sigchld, nullptr) == -1) {
- fail_fn(CREATE_ERROR("sigprocmask(SIG_SETMASK, { SIGCHLD }) failed: %s", strerror(errno)));
- }
+ // We blocked SIGCHLD prior to a fork, we unblock it here.
+ if (sigprocmask(SIG_UNBLOCK, &sigchld, nullptr) == -1) {
+ fail_fn(CREATE_ERROR("sigprocmask(SIG_SETMASK, { SIGCHLD }) failed: %s", strerror(errno)));
}
return pid;
}
@@ -881,22 +900,27 @@
// available.
capabilities &= GetEffectiveCapabilityMask(env);
- return ForkAndSpecializeCommon(env, uid, gid, gids, runtime_flags,
- rlimits, capabilities, capabilities, mount_external, se_info,
- se_name, false, fdsToClose, fdsToIgnore, is_child_zygote == JNI_TRUE,
- instructionSet, appDataDir);
+ pid_t pid = ForkCommon(env, se_name, false, fdsToClose, fdsToIgnore);
+ if (pid == 0) {
+ SpecializeCommon(env, uid, gid, gids, runtime_flags, rlimits,
+ capabilities, capabilities,
+ mount_external, se_info, se_name, false,
+ is_child_zygote == JNI_TRUE, instructionSet, appDataDir);
+ }
+ return pid;
}
static jint com_android_internal_os_Zygote_nativeForkSystemServer(
JNIEnv* env, jclass, uid_t uid, gid_t gid, jintArray gids,
jint runtime_flags, jobjectArray rlimits, jlong permittedCapabilities,
jlong effectiveCapabilities) {
- pid_t pid = ForkAndSpecializeCommon(env, uid, gid, gids,
- runtime_flags, rlimits,
- permittedCapabilities, effectiveCapabilities,
- MOUNT_EXTERNAL_DEFAULT, NULL, NULL, true, NULL,
- NULL, false, NULL, NULL);
- if (pid > 0) {
+ pid_t pid = ForkCommon(env, NULL, true, NULL, NULL);
+ if (pid == 0) {
+ SpecializeCommon(env, uid, gid, gids, runtime_flags, rlimits,
+ permittedCapabilities, effectiveCapabilities,
+ MOUNT_EXTERNAL_DEFAULT, NULL, NULL, true,
+ false, NULL, NULL);
+ } else if (pid > 0) {
// The zygote process checks whether the child process has died or not.
ALOGI("System server process %d has been created", pid);
gSystemServerPid = pid;
@@ -909,12 +933,16 @@
RuntimeAbort(env, __LINE__, "System server process has died. Restarting Zygote!");
}
- // Assign system_server to the correct memory cgroup.
- // Not all devices mount /dev/memcg so check for the file first
- // to avoid unnecessarily printing errors and denials in the logs.
- if (!access("/dev/memcg/system/tasks", F_OK) &&
+ bool low_ram_device = GetBoolProperty("ro.config.low_ram", false);
+ bool per_app_memcg = GetBoolProperty("ro.config.per_app_memcg", low_ram_device);
+ if (per_app_memcg) {
+ // Assign system_server to the correct memory cgroup.
+ // Not all devices mount /dev/memcg so check for the file first
+ // to avoid unnecessarily printing errors and denials in the logs.
+ if (!access("/dev/memcg/system/tasks", F_OK) &&
!WriteStringToFile(StringPrintf("%d", pid), "/dev/memcg/system/tasks")) {
- ALOGE("couldn't write %d to /dev/memcg/system/tasks", pid);
+ ALOGE("couldn't write %d to /dev/memcg/system/tasks", pid);
+ }
}
}
return pid;
diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto
index 39e65ca..5f6b6cc 100644
--- a/core/proto/android/os/incident.proto
+++ b/core/proto/android/os/incident.proto
@@ -71,6 +71,12 @@
(section).args = "getprop"
];
+ optional string kernel_version = 1002 [
+ (section).type = SECTION_FILE,
+ (section).args = "/proc/version",
+ (privacy).dest = DEST_AUTOMATIC
+ ];
+
// Device Logs
optional android.util.EventLogTagMapProto event_log_tag_map = 1100 [
(section).type = SECTION_FILE,
@@ -182,7 +188,7 @@
];
// System Services
- optional com.android.server.fingerprint.FingerprintServiceDumpProto fingerprint = 3000 [
+ optional com.android.server.biometrics.fingerprint.FingerprintServiceDumpProto fingerprint = 3000 [
(section).type = SECTION_DUMPSYS,
(section).args = "fingerprint --proto --incident"
];
diff --git a/core/proto/android/server/activitymanagerservice.proto b/core/proto/android/server/activitymanagerservice.proto
index 9d24588..ebf751c 100644
--- a/core/proto/android/server/activitymanagerservice.proto
+++ b/core/proto/android/server/activitymanagerservice.proto
@@ -67,6 +67,7 @@
// Whether or not the home activity is the recents activity. This is needed for the CTS tests to
// know what activity types to check for when invoking splitscreen multi-window.
optional bool is_home_recents_component = 6;
+ repeated .com.android.server.wm.IdentifierProto pending_activities = 7;
}
/* represents ActivityStackSupervisor.ActivityDisplay */
diff --git a/core/proto/android/server/face.proto b/core/proto/android/server/face.proto
new file mode 100644
index 0000000..6ecf328
--- /dev/null
+++ b/core/proto/android/server/face.proto
@@ -0,0 +1,67 @@
+/*
+ * 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.
+ */
+
+syntax = "proto2";
+package com.android.server.biometrics.face;
+
+import "frameworks/base/libs/incident/proto/android/privacy.proto";
+
+option java_multiple_files = true;
+option java_outer_classname = "FaceServiceProto";
+
+message FaceServiceDumpProto {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ // Each log may include multiple user_id for different users.
+ repeated FaceUserStatsProto users = 1;
+}
+
+message FaceUserStatsProto {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ // Refer to the UserHandle documentation.
+ optional int32 user_id = 1;
+
+ // The number of faces registered to this user.
+ optional int32 num_faces = 2;
+
+ // Normal face authentications stats (e.g. lockscreen).
+ optional FaceActionStatsProto normal = 3;
+
+ // Crypto authentications (e.g. to unlock password storage, make secure
+ // purchases, etc).
+ optional FaceActionStatsProto crypto = 4;
+}
+
+message FaceActionStatsProto {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ // Number of accepted faces.
+ optional int32 accept = 1;
+
+ // Number of rejected faces.
+ optional int32 reject = 2;
+
+ // Total number of acquisitions. Should be >= accept+reject due to poor
+ // image acquisition in some cases (too high, too low, poor gaze, etc.)
+ optional int32 acquire = 3;
+
+ // Total number of lockouts.
+ optional int32 lockout = 4;
+
+ // Total number of permanent lockouts.
+ optional int32 lockout_permanent = 5;
+}
diff --git a/core/proto/android/server/fingerprint.proto b/core/proto/android/server/fingerprint.proto
index 2a7fbc3..c5eb85c 100644
--- a/core/proto/android/server/fingerprint.proto
+++ b/core/proto/android/server/fingerprint.proto
@@ -15,7 +15,7 @@
*/
syntax = "proto2";
-package com.android.server.fingerprint;
+package com.android.server.biometrics.fingerprint;
import "frameworks/base/libs/incident/proto/android/privacy.proto";
@@ -46,7 +46,7 @@
optional PerformanceStatsProto crypto = 4;
}
-// A com.android.server.fingerprint.FingerpintService.PerformanceStats object.
+// A com.android.server.biometrics.fingerprint.FingerpintService.PerformanceStats object.
message PerformanceStatsProto {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index 7467d8f..b0ea5e0 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -288,10 +288,11 @@
optional int32 stack_id = 4;
optional .android.view.WindowLayoutParamsProto attributes = 5;
optional .android.graphics.RectProto given_content_insets = 6;
- optional .android.graphics.RectProto frame = 7;
- optional .android.graphics.RectProto containing_frame = 8;
- optional .android.graphics.RectProto parent_frame = 9;
- optional .android.graphics.RectProto content_frame = 10;
+ reserved 7 to 10;
+// optional .android.graphics.RectProto frame = 7;
+// optional .android.graphics.RectProto containing_frame = 8;
+// optional .android.graphics.RectProto parent_frame = 9;
+// optional .android.graphics.RectProto content_frame = 10;
optional .android.graphics.RectProto content_insets = 11;
optional .android.graphics.RectProto surface_insets = 12;
optional WindowStateAnimatorProto animator = 13;
@@ -304,21 +305,26 @@
optional int32 system_ui_visibility = 21;
optional bool has_surface = 22;
optional bool is_ready_for_display = 23;
- optional .android.graphics.RectProto display_frame = 24;
- optional .android.graphics.RectProto overscan_frame = 25;
- optional .android.graphics.RectProto visible_frame = 26;
- optional .android.graphics.RectProto decor_frame = 27;
- optional .android.graphics.RectProto outset_frame = 28;
+ reserved 24 to 28;
+// optional .android.graphics.RectProto display_frame = 24;
+// optional .android.graphics.RectProto overscan_frame = 25;
+// optional .android.graphics.RectProto visible_frame = 26;
+// optional .android.graphics.RectProto decor_frame = 27;
+// optional .android.graphics.RectProto outset_frame = 28;
optional .android.graphics.RectProto overscan_insets = 29;
optional .android.graphics.RectProto visible_insets = 30;
optional .android.graphics.RectProto stable_insets = 31;
optional .android.graphics.RectProto outsets = 32;
- optional .android.view.DisplayCutoutProto cutout = 33;
+ reserved 33;
+// optional .android.view.DisplayCutoutProto cutout = 33;
optional bool remove_on_exit = 34;
optional bool destroying = 35;
optional bool removed = 36;
optional bool is_on_screen = 37;
optional bool is_visible = 38;
+ optional bool pending_forced_seamless_rotation = 39;
+ optional int64 finished_forced_seamless_rotation_frame = 40;
+ optional WindowFramesProto window_frames = 41;
}
message IdentifierProto {
@@ -380,3 +386,19 @@
optional .android.content.ConfigurationProto full_configuration = 2;
optional .android.content.ConfigurationProto merged_override_configuration = 3;
}
+
+/* represents WindowFrames */
+message WindowFramesProto {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ optional .android.graphics.RectProto containing_frame = 1;
+ optional .android.graphics.RectProto content_frame = 2;
+ optional .android.graphics.RectProto decor_frame = 3;
+ optional .android.graphics.RectProto display_frame = 4;
+ optional .android.graphics.RectProto frame = 5;
+ optional .android.graphics.RectProto outset_frame = 6;
+ optional .android.graphics.RectProto overscan_frame = 7;
+ optional .android.graphics.RectProto parent_frame = 8;
+ optional .android.graphics.RectProto visible_frame = 9;
+ optional .android.view.DisplayCutoutProto cutout = 10;
+}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index e33ecb8..472df1a 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -262,6 +262,7 @@
<protected-broadcast android:name="android.intent.action.HEADSET_PLUG" />
<protected-broadcast android:name="android.media.action.HDMI_AUDIO_PLUG" />
+ <protected-broadcast android:name="android.media.action.MICROPHONE_MUTE_CHANGED" />
<protected-broadcast android:name="android.media.AUDIO_BECOMING_NOISY" />
<protected-broadcast android:name="android.media.RINGER_MODE_CHANGED" />
@@ -598,6 +599,8 @@
<protected-broadcast android:name="android.app.action.AFFILIATED_PROFILE_TRANSFER_OWNERSHIP_COMPLETE" />
<protected-broadcast android:name="android.app.action.DATA_SHARING_RESTRICTION_CHANGED" />
<protected-broadcast android:name="android.app.action.STATSD_STARTED" />
+ <protected-broadcast android:name="com.android.server.biometrics.fingerprint.ACTION_LOCKOUT_RESET" />
+ <protected-broadcast android:name="com.android.server.biometrics.face.ACTION_LOCKOUT_RESET" />
<!-- For IdleController -->
<protected-broadcast android:name="android.intent.action.DOCK_IDLE" />
@@ -821,6 +824,9 @@
android:label="@string/permgrouplab_location"
android:description="@string/permgroupdesc_location"
android:request="@string/permgrouprequest_location"
+ android:requestDetail="@string/permgrouprequestdetail_location"
+ android:backgroundRequest="@string/permgroupbackgroundrequest_location"
+ android:backgroundRequestDetail="@string/permgroupbackgroundrequestdetail_location"
android:priority="400" />
<!-- Allows an app to access precise location.
@@ -831,6 +837,7 @@
android:permissionGroup="android.permission-group.LOCATION"
android:label="@string/permlab_accessFineLocation"
android:description="@string/permdesc_accessFineLocation"
+ android:backgroundPermission="android.permission.ACCESS_BACKGROUND_LOCATION"
android:protectionLevel="dangerous|instant" />
<!-- Allows an app to access approximate location.
@@ -841,6 +848,7 @@
android:permissionGroup="android.permission-group.LOCATION"
android:label="@string/permlab_accessCoarseLocation"
android:description="@string/permdesc_accessCoarseLocation"
+ android:backgroundPermission="android.permission.ACCESS_BACKGROUND_LOCATION"
android:protectionLevel="dangerous|instant" />
<!-- Allows an app to access location in the background. If you
@@ -2251,7 +2259,8 @@
android:description="@string/permdesc_install_shortcut"
android:protectionLevel="normal"/>
- <!--This permission is no longer supported.
+ <!-- <p class="caution"><strong>Don't use this permission in your app.</strong><br>This
+ permission is no longer supported.
-->
<permission android:name="com.android.launcher.permission.UNINSTALL_SHORTCUT"
android:label="@string/permlab_uninstall_shortcut"
@@ -2428,7 +2437,8 @@
<permission android:name="android.permission.ASEC_RENAME"
android:protectionLevel="signature" />
- <!-- @SystemApi Allows applications to write the apn settings.
+ <!-- @SystemApi Allows applications to write the apn settings and read sensitive fields of
+ an existing apn settings like user and password.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.WRITE_APN_SETTINGS"
android:protectionLevel="signature|privileged" />
@@ -3037,6 +3047,15 @@
<permission android:name="android.permission.INSTALL_PACKAGE_UPDATES"
android:protectionLevel="signature|privileged" />
+ <!-- Allows an application to install existing system packages. This is a limited
+ version of {@link android.Manifest.permission#INSTALL_PACKAGES}.
+ <p>Not for use by third-party applications.
+ TODO(b/80204953): remove this permission once we have a long-term solution.
+ @hide
+ -->
+ <permission android:name="com.android.permission.INSTALL_EXISTING_PACKAGES"
+ android:protectionLevel="signature|privileged" />
+
<!-- @SystemApi Allows an application to clear user data.
<p>Not for use by third-party applications
@hide
@@ -3629,6 +3648,14 @@
<permission android:name="android.permission.RESET_FINGERPRINT_LOCKOUT"
android:protectionLevel="signature" />
+ <!-- Allows managing (adding, removing) facial templates. Reserved for the system. @hide -->
+ <permission android:name="android.permission.MANAGE_FACE"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- Allows an app to reset face authentication attempt counter. Reserved for the system. @hide -->
+ <permission android:name="android.permission.RESET_FACE_LOCKOUT"
+ android:protectionLevel="signature" />
+
<!-- Allows an application to control keyguard. Only allowed for system processes.
@hide -->
<permission android:name="android.permission.CONTROL_KEYGUARD"
@@ -3989,6 +4016,11 @@
<permission android:name="android.permission.DISABLE_HIDDEN_API_CHECKS"
android:protectionLevel="signature" />
+ <!-- Allows an application to read emergency info name.
+ @hide <p>Not for use by third-party applications. -->
+ <permission android:name="com.android.emergency.permission.READ_EMERGENCY_INFO_NAME"
+ android:protectionLevel="signature" />
+
<application android:process="system"
android:persistent="true"
android:hasCode="false"
diff --git a/core/res/res/anim/lock_screen_behind_enter_fade_in.xml b/core/res/res/anim/lock_screen_behind_enter_fade_in.xml
index e9475f5..ff95aea 100644
--- a/core/res/res/anim/lock_screen_behind_enter_fade_in.xml
+++ b/core/res/res/anim/lock_screen_behind_enter_fade_in.xml
@@ -16,7 +16,6 @@
-->
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
- android:background="#ff000000"
android:detachWallpaper="true"
android:shareInterpolator="false"
android:interpolator="@interpolator/linear"
diff --git a/core/res/res/drawable-hdpi/ic_grayedout_printer.png b/core/res/res/drawable-hdpi/ic_grayedout_printer.png
deleted file mode 100644
index 5e54970..0000000
--- a/core/res/res/drawable-hdpi/ic_grayedout_printer.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_grayedout_printer.png b/core/res/res/drawable-mdpi/ic_grayedout_printer.png
deleted file mode 100644
index 5e54970..0000000
--- a/core/res/res/drawable-mdpi/ic_grayedout_printer.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_grayedout_printer.png b/core/res/res/drawable-xhdpi/ic_grayedout_printer.png
deleted file mode 100644
index 5e54970..0000000
--- a/core/res/res/drawable-xhdpi/ic_grayedout_printer.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index fe52e9c..73db3a1 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -236,9 +236,6 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Vliegtuigmodus"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Vliegtuigmodus is AAN"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Vliegtuigmodus is AF"</string>
- <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Batterybespaarder"</string>
- <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Batterybespaarder is AF"</string>
- <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Batterybespaarder is AAN"</string>
<string name="global_action_settings" msgid="1756531602592545966">"Instellings"</string>
<string name="global_action_assist" msgid="3892832961594295030">"Help"</string>
<string name="global_action_voice_assist" msgid="7751191495200504480">"Stembystand"</string>
@@ -276,6 +273,9 @@
<string name="permgrouplab_location" msgid="7275582855722310164">"Ligging"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"toegang te verkry tot hierdie toestel se ligging"</string>
<string name="permgrouprequest_location" msgid="3788275734953323491">"Gee <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> toegang tot hierdie toestel se ligging?"</string>
+ <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"Die program sal net toegang tot die ligging hê terwyl jy die program gebruik."</string>
+ <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"Gee <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> altyd toegang tot toestelligging?"</string>
+ <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"Die program sal altyd toegang tot die ligging hê, selfs wanneer jy nie die program gebruik nie."</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Kalender"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"by jou kalender in te gaan"</string>
<string name="permgrouprequest_calendar" msgid="289900767793189421">"Gee <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> toegang tot jou kalender?"</string>
@@ -402,8 +402,7 @@
<string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Hierdie program kan kalendergebeurtenisse op jou foon byvoeg, verwyder of verander. Hierdie program kan boodskappe stuur wat lyk of dit van kalendereienaars af kom of gebeurtenisse verander sonder om hul eienaars in te lig."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"Kry toegang tot ekstra liggingverskaffer-bevele"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Gee die program toegang tot ekstra liggingverskaffer-bevele. Dit kan die program dalk toelaat om in te meng met die werking van die GPS of ander liggingbronne."</string>
- <!-- no translation found for permlab_accessFineLocation (6265109654698562427) -->
- <skip />
+ <string name="permlab_accessFineLocation" msgid="6265109654698562427">"kry net op die voorgrond toegang tot presiese ligging"</string>
<string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Hierdie program kan jou presiese ligging kry net wanneer dit op die voorgrond is. Hierdie liggingdienste moet aangeskakel wees en op jou foon beskikbaar wees sodat die program hulle kan gebruik. Dit kan veroorsaak dat meer batterykrag gebruik word."</string>
<string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"verkry toegang tot benaderde ligging (netwerkgegrond)"</string>
<string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Hierdie program kan jou ligging kry op grond van jou netwerkhulpbronne soos selfoontorings en Wi-Fi-netwerke. Hierdie liggingdienste moet aangeskakel en op jou tablet beskikbaar wees sodat die program hulle kan gebruik."</string>
@@ -518,6 +517,37 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Vingerafdrukikoon"</string>
+ <string name="permlab_manageFace" msgid="2137540986007309781">"bestuur gesigstawinghardeware"</string>
+ <string name="permdesc_manageFace" msgid="8919637120670185330">"Laat program toe om metodes te benut om gesigtemplate vir gebruik by te voeg en uit te vee."</string>
+ <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"gebruik gesigstawinghardeware"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Laat die program toe om gesigstawinghardeware vir stawing te gebruik"</string>
+ <string name="face_acquired_insufficient" msgid="5901287247766106330">"Kon nie gesig verwerk nie. Probeer weer."</string>
+ <string name="face_acquired_too_bright" msgid="610606792381297174">"Gesig is te helder. Probeer met minder lig."</string>
+ <string name="face_acquired_too_dark" msgid="7229162716976778371">"Gesig is te donker. Maak ligbron oop."</string>
+ <string name="face_acquired_too_close" msgid="1980310037427755293">"Beweeg sensor verder weg van gesig af."</string>
+ <string name="face_acquired_too_far" msgid="4494571381828850007">"Bring sensor nader aan gesig."</string>
+ <string name="face_acquired_too_high" msgid="228411096134808372">"Beweeg sensor na bo."</string>
+ <string name="face_acquired_too_low" msgid="4539774649296349109">"Beweeg sensor na onder."</string>
+ <string name="face_acquired_too_right" msgid="1650292067226118760">"Beweeg sensor na regs."</string>
+ <string name="face_acquired_too_left" msgid="2712489669456176505">"Beweeg sensor na links."</string>
+ <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Kyk na die sensor."</string>
+ <string name="face_acquired_not_detected" msgid="5707782294589511391">"Geen gesig bespeur nie."</string>
+ <string name="face_acquired_not_steady" msgid="3722829465011040042">"Hou gesig stil voor toestel."</string>
+ <string-array name="face_acquired_vendor">
+ </string-array>
+ <string name="face_error_hw_not_available" msgid="6255891785768984615">"Gesighardeware is nie beskikbaar nie."</string>
+ <string name="face_error_timeout" msgid="4014326147867150054">"Gesiguittelling is bereik. Probeer weer."</string>
+ <string name="face_error_no_space" msgid="8224993703466381314">"Gesig kan nie geberg word nie."</string>
+ <string name="face_error_canceled" msgid="283945501061931023">"Gesighandeling is gekanselleer."</string>
+ <string name="face_error_lockout" msgid="3407426963155388504">"Te veel pogings. Probeer later weer."</string>
+ <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Te veel pogings. Gesigstawingsensor is gedeaktiveer."</string>
+ <string name="face_error_unable_to_process" msgid="238761109287767270">"Probeer weer."</string>
+ <string name="face_error_not_enrolled" msgid="9166792142679691323">"Geen gesigte is geregistreer nie."</string>
+ <string name="face_error_hw_not_present" msgid="4737289254517095671">"Hierdie toestel het nie \'n gesigstawingsensor nie"</string>
+ <string name="face_name_template" msgid="7004562145809595384">"Gesig <xliff:g id="FACEID">%d</xliff:g>"</string>
+ <string-array name="face_error_vendor">
+ </string-array>
+ <string name="face_icon_content_description" msgid="4024817159806482191">"Gesig-ikoon"</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"lees sinkroniseer-instellings"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Laat die program toe om die sinkroniseringinstellings van \'n rekening te lees. Byvoorbeeld, dit kan bepaal of die People-program met \'n rekening gesinkroniseer is."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"wissel tussen sinkronisasie aan en af"</string>
@@ -1178,6 +1208,9 @@
<skip />
<string name="wifi_no_internet" msgid="8938267198124654938">"Wi‑Fi het geen internettoegang nie"</string>
<string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Tik vir opsies"</string>
+ <string name="wifi_softap_config_change" msgid="8475911871165857607">"Veranderings aan jou warmkolinstellings"</string>
+ <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"Jou warmkolband het verander."</string>
+ <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"Hierdie toestel steun nie jou voorkeur vir net 5 GHz nie. Hierdie toestel sal in plaas daarvan die 5 GHz-band gebruik wanneer dit beskikbaar is."</string>
<string name="network_switch_metered" msgid="4671730921726992671">"Het oorgeskakel na <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
<string name="network_switch_metered_detail" msgid="775163331794506615">"Toestel gebruik <xliff:g id="NEW_NETWORK">%1$s</xliff:g> wanneer <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> geen internettoegang het nie. Heffings kan geld."</string>
<string name="network_switch_metered_toast" msgid="5779283181685974304">"Het oorgeskakel van <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> na <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index 58f63bc..400f304 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -236,9 +236,6 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"የአውሮፕላን ሁነታ"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"የአውሮፕላንሁነታ በርቷል"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"የአውሮፕላንሁነታ ጠፍቷል"</string>
- <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"የባትሪ ኃይል ቆጣቢ"</string>
- <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"የባትሪ ኃይል ቆጣቢ ጠፍቷል"</string>
- <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"የባትሪ ኃይል ቆጣቢ በርቷል"</string>
<string name="global_action_settings" msgid="1756531602592545966">"ቅንብሮች"</string>
<string name="global_action_assist" msgid="3892832961594295030">"ደግፍ"</string>
<string name="global_action_voice_assist" msgid="7751191495200504480">"የድምጽ እርዳታ"</string>
@@ -276,6 +273,9 @@
<string name="permgrouplab_location" msgid="7275582855722310164">"መገኛ አካባቢ"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"የዚህን መሣሪያ አካባቢ ይድረሱበት"</string>
<string name="permgrouprequest_location" msgid="3788275734953323491">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> የዚህ መሣሪያ አካባቢን እንዲደርስ ይፈቀድለት?"</string>
+ <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"መተግበሪያው እርስዎ ሲጠቀሙበት ብቻ ነው የአካባቢው መዳረሻ የሚኖረው።"</string>
+ <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"ሁልጊዜ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> የዚህ መሣሪያ አካባቢን እንዲደርስ ይፈቀድለት?"</string>
+ <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"መተግበሪያው ሁልጊዜ የአካባቢው መዳረሽ ይኖረዋል፣ እርስዎ መተግበሪያውን እየተጠቀሙ ባይሆኑም እንኳ።"</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"ቀን መቁጠሪያ"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"የእርስዎን ቀን መቁጠሪያ ይድረሱበት"</string>
<string name="permgrouprequest_calendar" msgid="289900767793189421">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ቀን መቁጠሪያዎን እንዲደርስ ይፈቀድለት?"</string>
@@ -402,8 +402,7 @@
<string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"ይህ መተግበሪያ በእርስዎ ስልክ ላይ የቀን መቁጠሪያ ክስተቶችን ሊያክል፣ ሊያስወግድ ወይም ሊለውጥ ይችላል። ይህ መተግበሪያ ከቀን መቁጠሪያ የመጡ መስለው የሚታዩ መልእክቶችን ሊልክ ወይም ባለቤቶቹን ሳያሳውቅ ክስተቶችን ሊለውጥ ይችላል።"</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"ተጨማሪ ሥፍራ አቅራቢ ትዕዛዞችን ድረስ።"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"መተግበሪያው ተጨማሪ የአካባቢ አቅራቢ ትእዛዞችን እንዲደርስ ይፈቅድለታል። ይሄ መተግበሪያው በጂፒኤስ ወይም ሌላ የአካባቢ ምንጮች ስራ ላይ ጣልቃ እንዲገባ ሊፈቅድለት ይችላል።"</string>
- <!-- no translation found for permlab_accessFineLocation (6265109654698562427) -->
- <skip />
+ <string name="permlab_accessFineLocation" msgid="6265109654698562427">"መዳረሻ ከፊት ለፊት ብቻ ትክክለኛ ነው"</string>
<string name="permdesc_accessFineLocation" msgid="3520508381065331098">"ይህ መተግበሪያ ከፊት ላይ ሆኖ ሲበራ ብቻ ትክክለኛውን መገኛ አካባቢ ማግኘት ይችላል። እነዚህ የመገኛ አካባቢ አገልግሎቶች መተግበሪያው መጠቀም እንዲችል ሊበሩ እና በእርስዎ ስልክ ላይ ሊገኙ የሚችሉ መሆን አለባቸው።"</string>
<string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"ግምታዊ አካባቢን መድረስ (በአውታረ መረብ ላይ የተመሰረተ)"</string>
<string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"ይህ መተግበሪያ እንደ የሕዋስ ማማዎች እና የWi-Fi አውታረ መረቦች ከመሳሰሉ የአውታረ መረብ ምንጮች ላይ በመመርኮዝ የእርስዎን መገኛ አካባቢ ማግኘት ይችላል። እነዚህ የመገኛ አካባቢ አገልግሎቶች መተግበሪያው መጠቀም እንዲችል ሊበሩ እና በእርስዎ ጡባዊ ላይ ሊገኙ የሚችሉ መሆን አለባቸው።"</string>
@@ -518,6 +517,37 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"የጣት አሻራ አዶ"</string>
+ <string name="permlab_manageFace" msgid="2137540986007309781">"የማረጋገጫ ሃርድዌር ፊትን ያስተዳድሩ"</string>
+ <string name="permdesc_manageFace" msgid="8919637120670185330">"መተግበሪያው ጥቅም ላይ እንዲውሉ የፊት ቅንብር ደንቦችን ለማከል እና ለመሰረዝ የሚያስችሉ ስልቶችን እንዲያስጀምር ያስችለዋል።"</string>
+ <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"የፊት ማረጋገጫ ሃርድዌር ይጠቀሙ"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"መተግበሪያው የማረጋገጫ ሃርድዌር ለማረጋገጥ ሥራ እንዲጠቀም ያስችለዋል"</string>
+ <string name="face_acquired_insufficient" msgid="5901287247766106330">"ፊትን መሥራት አልተቻለም። እባክዎ እንደገና ይሞክሩ።"</string>
+ <string name="face_acquired_too_bright" msgid="610606792381297174">"ፊት ከልክ በላይ ብሩህ ነው። እባክዎ በዝቅተኛ ብርሃን ውስጥ ይሞክሩት።"</string>
+ <string name="face_acquired_too_dark" msgid="7229162716976778371">"ፊት ከልክ በላይ ጨለም ያለ ነው። እባክዎ የብርሃን ምንጩን ይግለጹት።"</string>
+ <string name="face_acquired_too_close" msgid="1980310037427755293">"እባክዎ ዳሳሹን ከፊት አሁንም ያርቁት።"</string>
+ <string name="face_acquired_too_far" msgid="4494571381828850007">"እባክዎ ዳሳሹን ወደ ፊት ያስጠጉት።"</string>
+ <string name="face_acquired_too_high" msgid="228411096134808372">"እባክዎ ዳሳሽን ከፍ ያድርጉት።"</string>
+ <string name="face_acquired_too_low" msgid="4539774649296349109">"እባክዎ ዳሳሽን ዝቅ ያድርጉት።"</string>
+ <string name="face_acquired_too_right" msgid="1650292067226118760">"እባክዎ ዳስሽን ወደ ቀኝ ያንቀሳቅሱት።"</string>
+ <string name="face_acquired_too_left" msgid="2712489669456176505">"እባክዎ ዳስሽን ወደ ግራ ያንቀሳቅሱት።"</string>
+ <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"እባክዎ ዳሳሹ ላይ ይመልከቱ።"</string>
+ <string name="face_acquired_not_detected" msgid="5707782294589511391">"ምንም መልክ አልተገኘም።"</string>
+ <string name="face_acquired_not_steady" msgid="3722829465011040042">"ፊትዎትን ከመሣሪያው ፊት ለፊት ሳያነቃንቁ ያቆዩት።"</string>
+ <string-array name="face_acquired_vendor">
+ </string-array>
+ <string name="face_error_hw_not_available" msgid="6255891785768984615">"የፊት ሃርድዌር አይገኝም።"</string>
+ <string name="face_error_timeout" msgid="4014326147867150054">"የፊት ማብቂያ ጊዜ ደርሷል። እንደገና ይሞክሩ።"</string>
+ <string name="face_error_no_space" msgid="8224993703466381314">"ፊት ሊከማች አይችልም።"</string>
+ <string name="face_error_canceled" msgid="283945501061931023">"የፊት ሥርዓተ ክወና ተሰርዟል።"</string>
+ <string name="face_error_lockout" msgid="3407426963155388504">"ከልክ በላይ ብዙ ሙከራዎች። በኋላ ላይ እንደገና ይሞክሩ።"</string>
+ <string name="face_error_lockout_permanent" msgid="8198354656746088890">"በጣም ብዙ ሙከራዎች። የፊት ማረጋገጫ ተሰናክሏል።"</string>
+ <string name="face_error_unable_to_process" msgid="238761109287767270">"እንደገና ይሞክሩ።"</string>
+ <string name="face_error_not_enrolled" msgid="9166792142679691323">"ምንም ፊት አልተመዘገበም።"</string>
+ <string name="face_error_hw_not_present" msgid="4737289254517095671">"ይህ መሣሪያ የፊት ማረጋገጫ ዳሳሽ የለውም"</string>
+ <string name="face_name_template" msgid="7004562145809595384">"ፊት <xliff:g id="FACEID">%d</xliff:g>"</string>
+ <string-array name="face_error_vendor">
+ </string-array>
+ <string name="face_icon_content_description" msgid="4024817159806482191">"የፊት አዶ"</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"የሥምሪያ ቅንብሮች አንብብ"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"መተግበሪያው የአንድ መለያ የማመሳሰል ቅንብሮችን እንዲያነብ ይፈቅድለታል። ለምሳሌ ይህ የሰዎች መተግበሪያ ከመለያ ጋር መመሳሰሉን አለመመሳሰሉን ሊወስን ይችላል።"</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"ማመሳሰያ በማብራትና በማጥፋት መካከል ቀያይር"</string>
@@ -1178,6 +1208,9 @@
<skip />
<string name="wifi_no_internet" msgid="8938267198124654938">"Wi-Fi በይነመረብ መዳረሻ የለውም"</string>
<string name="wifi_no_internet_detailed" msgid="8083079241212301741">"ለአማራጮች መታ ያድርጉ"</string>
+ <string name="wifi_softap_config_change" msgid="8475911871165857607">"በእርስዎ ሆትስፖት ቅንብሮች ላይ ለውጦች"</string>
+ <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"የእርስዎ ሆትስፖት ባንድ ተለውጧል።"</string>
+ <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"ይህ መሣሪያ የእርስዎን ምርጫ ለ5GHz ብቻ አይደግፍም። በምትኩ፣ ይህ መሣሪያ ሲገኝ 5GHz ባንድ ይጠቀማል።"</string>
<string name="network_switch_metered" msgid="4671730921726992671">"ወደ <xliff:g id="NETWORK_TYPE">%1$s</xliff:g> ተቀይሯል"</string>
<string name="network_switch_metered_detail" msgid="775163331794506615">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> ምንም ዓይነት የበይነመረብ ግንኙነት በማይኖረው ጊዜ መሣሪያዎች <xliff:g id="NEW_NETWORK">%1$s</xliff:g>ን ይጠቀማሉ። ክፍያዎች ተፈጻሚ ሊሆኑ ይችላሉ።"</string>
<string name="network_switch_metered_toast" msgid="5779283181685974304">"ከ<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> ወደ <xliff:g id="NEW_NETWORK">%2$s</xliff:g> ተቀይሯል"</string>
@@ -1792,7 +1825,7 @@
<string name="work_mode_turn_on" msgid="2062544985670564875">"አብራ"</string>
<string name="deprecated_target_sdk_message" msgid="1449696506742572767">"ይህ መተግበሪያ ለቆየ የAndroid ስሪት ነው የተገነባው፣ እና በአግባቡ ላይሰራ ይችላል። ዝማኔዎች ካሉ ለመመልከት ይሞክሩ፣ ወይም ደግሞ ገንቢውን ያነጋግሩ።"</string>
<string name="deprecated_target_sdk_app_store" msgid="5032340500368495077">"ዝማኔ ካለ አረጋግጥ"</string>
- <string name="new_sms_notification_title" msgid="8442817549127555977">"አዲስ መልእክቶች አለዎት"</string>
+ <string name="new_sms_notification_title" msgid="8442817549127555977">"አዲስ መልዕክቶች አለዎት"</string>
<string name="new_sms_notification_content" msgid="7002938807812083463">"ለመመልከት የኤስኤምኤስ መተግበሪያ ይክፈቱ"</string>
<string name="user_encrypted_title" msgid="9054897468831672082">"አንዳንድ ተግባሮች የተገደቡ ሊሆኑ ይችላሉ"</string>
<string name="user_encrypted_message" msgid="4923292604515744267">"ለመክፈት መታ ያድርጉ"</string>
@@ -1849,7 +1882,7 @@
<string name="etws_primary_default_message_earthquake" msgid="5541962250262769193">"ረጋ ይበሉና በአቅራቢያ ያለ መጠለያ ይፈልጉ።"</string>
<string name="etws_primary_default_message_tsunami" msgid="1887685943498368548">"ወዲያውኑ ከባህር ዳርቻ አካባቢዎች እና የወንዝ ዳርቻ አካባቢዎች ይውጡና እንደ ከፍ ያለ መሬት ያሉ ከአደጋ የተሻለ ደህንነት ወዳቸው ቦታዎች ይሂዱ።"</string>
<string name="etws_primary_default_message_earthquake_and_tsunami" msgid="998797956848445862">"ረጋ ይበሉና በአቅራቢያ ያለ መጠለያ ይፈልጉ።"</string>
- <string name="etws_primary_default_message_test" msgid="2709597093560037455">"የአስቸኳይ አደጋ መልእክቶች ሙከራ"</string>
+ <string name="etws_primary_default_message_test" msgid="2709597093560037455">"የአስቸኳይ አደጋ መልዕክቶች ሙከራ"</string>
<string name="notification_reply_button_accessibility" msgid="3621714652387814344">"ምላሽ ስጥ"</string>
<string name="etws_primary_default_message_others" msgid="6293148756130398971"></string>
<string name="mmcc_authentication_reject" msgid="5767701075994754356">"ሲም ለድምጽ አይፈቀድም"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 492def8..1470013 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -248,9 +248,6 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"وضع الطائرة"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"وضع الطائرة قيد التشغيل"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"وضع الطائرة متوقف"</string>
- <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"توفير شحن البطارية"</string>
- <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"توفير شحن البطارية غير مفعّل"</string>
- <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"توفير شحن البطارية مفعّل"</string>
<string name="global_action_settings" msgid="1756531602592545966">"الإعدادات"</string>
<string name="global_action_assist" msgid="3892832961594295030">"مساعدة"</string>
<string name="global_action_voice_assist" msgid="7751191495200504480">"المساعد الصوتي"</string>
@@ -288,6 +285,9 @@
<string name="permgrouplab_location" msgid="7275582855722310164">"الموقع الجغرافي"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"الوصول إلى موقع هذا الجهاز"</string>
<string name="permgrouprequest_location" msgid="3788275734953323491">"هل تريد السماح لتطبيق <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> بالوصول إلى الموقع الجغرافي لهذا الجهاز؟"</string>
+ <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"لن يكون بإمكان التطبيق الوصول إلى الموقع الجغرافي إلا عند استخدامك لهذا التطبيق."</string>
+ <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"هل تريد السماح دائمًا لتطبيق <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> بالوصول إلى الموقع الجغرافي لهذا الجهاز؟"</string>
+ <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"سيكون بإمكان التطبيق دائمًا الوصول إلى الموقع الجغرافي، حتى عند عدم استخدامك لهذا التطبيق."</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"التقويم"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"الوصول تقويمك"</string>
<string name="permgrouprequest_calendar" msgid="289900767793189421">"هل تريد السماح لتطبيق <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> بالدخول إلى التقويم؟"</string>
@@ -414,8 +414,7 @@
<string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"يمكن لهذا التطبيق إضافة أحداث تقويم أو إزالتها أو تغييرها على الهاتف. كما يمكنه إرسال رسائل يبدو أنها واردة من مالكي التقويم، ويمكنه كذلك تغيير الأحداث بدون إشعار مالكيها."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"الدخول إلى المزيد من أوامر موفر الموقع"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"للسماح للتطبيق بالدخول إلى أوامر إضافية لموفر الموقع. قد يتيح هذا للتطبيق التداخل مع تشغيل تقنية نظام تحديد المواقع العالمي (GPS) أو مصادر الموقع الأخرى."</string>
- <!-- no translation found for permlab_accessFineLocation (6265109654698562427) -->
- <skip />
+ <string name="permlab_accessFineLocation" msgid="6265109654698562427">"الوصول إلى الموقع الجغرافي الدقيق في الواجهة الأمامية فقط"</string>
<string name="permdesc_accessFineLocation" msgid="3520508381065331098">"لا يمكن لهذا التطبيق معرفة موقعك الجغرافي بالضبط إلا عندما يعمل في الخلفية. ويجب تفعيل خدمات الموقع الجغرافي هذه وأن تكون متاحة على الهاتف حتى يتمكن التطبيق من استخدامها. وقد يؤدي هذا إلى زيادة استهلاك طاقة البطارية."</string>
<string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"الوصول إلى الموقع التقريبي (استنادًا إلى الشبكة)"</string>
<string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"يمكن لهذا التطبيق معرفة موقعك من خلال مصادر الشبكات مثل أبراج الجوّال وشبكات Wi-Fi. ويجب تشغيل خدمات المواقع هذه وأن تكون متاحة على الجهاز اللوحي حتى يتمكن التطبيق من استخدامها."</string>
@@ -530,6 +529,37 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"رمز بصمة الإصبع"</string>
+ <string name="permlab_manageFace" msgid="2137540986007309781">"إدارة أجهزة مصادقة الوجه"</string>
+ <string name="permdesc_manageFace" msgid="8919637120670185330">"السماح للتطبيق باستدعاء طرق لإضافة نماذج من الوجوه وحذفها"</string>
+ <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"استخدام أجهزة مصادقة الوجه"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"السماح للتطبيق باستخدام أجهزة مصادقة الوجه"</string>
+ <string name="face_acquired_insufficient" msgid="5901287247766106330">"تعذَّر التعرُّف على الوجه. يُرجى إعادة المحاولة."</string>
+ <string name="face_acquired_too_bright" msgid="610606792381297174">"الوجه ساطع جدًا. يُرجى إعادة المحاولة بإضاءة أقل."</string>
+ <string name="face_acquired_too_dark" msgid="7229162716976778371">"الوجه مظلم جدًا. يُرجى الاستعانة بمصدر إضاءة."</string>
+ <string name="face_acquired_too_close" msgid="1980310037427755293">"يُرجى إبعاد جهاز الاستشعار عن الوجه."</string>
+ <string name="face_acquired_too_far" msgid="4494571381828850007">"يُرجى تقريب جهاز الاستشعار من الوجه."</string>
+ <string name="face_acquired_too_high" msgid="228411096134808372">"يُرجى تحريك جهاز الاستشعار للأعلى."</string>
+ <string name="face_acquired_too_low" msgid="4539774649296349109">"يُرجى تحريك جهاز الاستشعار للأسفل."</string>
+ <string name="face_acquired_too_right" msgid="1650292067226118760">"يُرجى تحريك جهاز الاستشعار جهة اليمين."</string>
+ <string name="face_acquired_too_left" msgid="2712489669456176505">"يُرجى تحريك جهاز الاستشعار جهة اليسار."</string>
+ <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"يُرجى النظر إلى جهاز الاستشعار."</string>
+ <string name="face_acquired_not_detected" msgid="5707782294589511391">"لم يتم رصد أي وجه."</string>
+ <string name="face_acquired_not_steady" msgid="3722829465011040042">"يُرجى جعل الوجه ثابتًا أمام الجهاز."</string>
+ <string-array name="face_acquired_vendor">
+ </string-array>
+ <string name="face_error_hw_not_available" msgid="6255891785768984615">"أجهزة مصادقة الوجه غير متاحة."</string>
+ <string name="face_error_timeout" msgid="4014326147867150054">"انتهت مهلة التعرُّف على الوجه. أعِد المحاولة."</string>
+ <string name="face_error_no_space" msgid="8224993703466381314">"يتعذَّر حفظ الوجه."</string>
+ <string name="face_error_canceled" msgid="283945501061931023">"تمّ إلغاء عملية مصادقة الوجه."</string>
+ <string name="face_error_lockout" msgid="3407426963155388504">"تمّ إجراء محاولات كثيرة. أعِد المحاولة لاحقًا."</string>
+ <string name="face_error_lockout_permanent" msgid="8198354656746088890">"تمّ إجراء محاولات كثيرة. ميزة مصادقة الوجه متوقفة."</string>
+ <string name="face_error_unable_to_process" msgid="238761109287767270">"يُرجى إعادة المحاولة."</string>
+ <string name="face_error_not_enrolled" msgid="9166792142679691323">"ليس هناك وجه مسجّل."</string>
+ <string name="face_error_hw_not_present" msgid="4737289254517095671">"لا يحتوي هذا الجهاز على جهاز استشعار مصادقة الوجه."</string>
+ <string name="face_name_template" msgid="7004562145809595384">"الوجه <xliff:g id="FACEID">%d</xliff:g>"</string>
+ <string-array name="face_error_vendor">
+ </string-array>
+ <string name="face_icon_content_description" msgid="4024817159806482191">"رمز الوجه"</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"قراءة إعدادات المزامنة"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"للسماح للتطبيق بقراءة الإعدادات المتزامنة لحساب ما. على سبيل المثال، يمكن أن يؤدي هذا إلى تحديد ما إذا تمت مزامنة تطبيق \"الأشخاص\" مع حساب ما."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"التبديل بين تشغيل المزامنة وإيقافها"</string>
@@ -1191,7 +1221,7 @@
<string name="android_start_title" product="default" msgid="4536778526365907780">"جارٍ بدء تشغيل الهاتف…"</string>
<string name="android_start_title" product="tablet" msgid="4929837533850340472">"جارٍ بدء تشغيل الجهاز اللوحي…"</string>
<string name="android_start_title" product="device" msgid="7467484093260449437">"جارٍ بدء تشغيل الجهاز…"</string>
- <string name="android_upgrading_fstrim" msgid="8036718871534640010">"جارٍ تحسين السعة التخزينية."</string>
+ <string name="android_upgrading_fstrim" msgid="8036718871534640010">"جارٍ تحسين سعة التخزين."</string>
<string name="android_upgrading_notification_title" product="default" msgid="1511552415039349062">"جارٍ إنهاء تحديث النظام…"</string>
<string name="app_upgrading_toast" msgid="3008139776215597053">"جارٍ ترقية <xliff:g id="APPLICATION">%1$s</xliff:g>…"</string>
<string name="android_upgrading_apk" msgid="7904042682111526169">"جارٍ تحسين التطبيق <xliff:g id="NUMBER_0">%1$d</xliff:g> من <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
@@ -1266,6 +1296,9 @@
<skip />
<string name="wifi_no_internet" msgid="8938267198124654938">"شبكة Wi-Fi غير متصلة بالإنترنت"</string>
<string name="wifi_no_internet_detailed" msgid="8083079241212301741">"انقر للحصول على الخيارات."</string>
+ <string name="wifi_softap_config_change" msgid="8475911871165857607">"التغييرات التي طرأت على إعدادات نقطة الاتصال"</string>
+ <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"تمّ تغيير نطاق نقطة الاتصال الخاصة بك."</string>
+ <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"لا يتوافق هذا الجهاز مع إعدادك المفضّل الخاص باستخدام النطاق 5 غيغاهرتز فقط. وسيستخدم الجهاز بدلاً من ذلك النطاق 5 غيغاهرتز عندما يكون متاحًا."</string>
<string name="network_switch_metered" msgid="4671730921726992671">"تم التبديل إلى <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
<string name="network_switch_metered_detail" msgid="775163331794506615">"يستخدم الجهاز <xliff:g id="NEW_NETWORK">%1$s</xliff:g> عندما لا يتوفر اتصال بالإنترنت في شبكة <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>، ويمكن أن يتم فرض رسوم مقابل ذلك."</string>
<string name="network_switch_metered_toast" msgid="5779283181685974304">"تم التبديل من <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> إلى <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
@@ -1533,7 +1566,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"المزيد من الخيارات"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s، %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s، %2$s، %3$s"</string>
- <string name="storage_internal" msgid="3570990907910199483">"السعة التخزينية المشتركة الداخلية"</string>
+ <string name="storage_internal" msgid="3570990907910199483">"سعة التخزين المشتركة الداخلية"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"بطاقة SD"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"بطاقة SD من <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"محرك أقراص USB"</string>
@@ -1954,7 +1987,7 @@
<string name="app_category_news" msgid="7496506240743986873">"الأخبار والمجلات"</string>
<string name="app_category_maps" msgid="5878491404538024367">"الخرائط والتنقل"</string>
<string name="app_category_productivity" msgid="3742083261781538852">"الإنتاجية"</string>
- <string name="device_storage_monitor_notification_channel" msgid="3295871267414816228">"السعة التخزينية للجهاز"</string>
+ <string name="device_storage_monitor_notification_channel" msgid="3295871267414816228">"سعة التخزين للجهاز"</string>
<string name="adb_debugging_notification_channel_tv" msgid="5537766997350092316">"تصحيح أخطاء USB"</string>
<string name="time_picker_hour_label" msgid="2979075098868106450">"ساعة"</string>
<string name="time_picker_minute_label" msgid="5168864173796598399">"دقيقة"</string>
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index 5e6dccc..2f9e444 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -236,9 +236,6 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"এয়াৰপ্লেইন ম\'ড"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"এয়াৰপ্লেইন ম\'ড অন কৰা আছে"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"এয়াৰপ্লেইন ম\'ড অফ কৰা আছে"</string>
- <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"বেটাৰি সঞ্চয়কাৰী"</string>
- <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"বেটাৰি সঞ্চয়কাৰী অফ হৈ আছে"</string>
- <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"বেটাৰি সঞ্চয়কাৰী অন হৈ আছে"</string>
<string name="global_action_settings" msgid="1756531602592545966">"ছেটিংসমূহ"</string>
<string name="global_action_assist" msgid="3892832961594295030">"সহায়"</string>
<string name="global_action_voice_assist" msgid="7751191495200504480">"কণ্ঠধ্বনিৰে সহায়"</string>
@@ -276,6 +273,9 @@
<string name="permgrouplab_location" msgid="7275582855722310164">"অৱস্থান"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"এই ডিভাইচৰ অৱস্থান ব্যৱহাৰ কৰিব পাৰে"</string>
<string name="permgrouprequest_location" msgid="3788275734953323491">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>ক এই ডিভাইচটোৰ অৱস্থান জানিবলৈ অনুমতি দিবনে?"</string>
+ <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"আপুনি এই এপ্ ব্য়ৱহাৰ কৰি থাকোঁতে ই সদায় অৱস্থান চাব পাৰে।"</string>
+ <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>ক সদায় এই ডিভাইচৰ অৱস্থান চাবলৈ অনুমতি দিবনে?"</string>
+ <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"আপুনি এই এপ্ ব্য়ৱহাৰ কৰি থকা নাই যদিও ই সদায় অৱস্থান চাব পাৰে।"</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"কেলেণ্ডাৰ"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"আপোনাৰ কেলেণ্ডাৰ ব্যৱহাৰ কৰিব পাৰে"</string>
<string name="permgrouprequest_calendar" msgid="289900767793189421">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>ক আপোনাৰ কেলেণ্ডাৰ চাবলৈ অনুমতি দিবনে?"</string>
@@ -402,8 +402,7 @@
<string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"এই এপে আপোনাৰ ফ\'নৰ কেলেণ্ডাৰত কার্যক্ৰম যোগ দিব, আঁতৰাব বা সলনি কৰিব পাৰে। ই এনে বাৰ্তা পঠিয়াব পাৰে যিবোৰ কেলেণ্ডাৰৰ গৰাকীৰ পৰা অহা যেন লাগে বা ই গৰাকীক নজনোৱাকৈ কাৰ্যক্ৰম সলনি কৰিব পাৰে৷"</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"অতিৰিক্ত অৱস্থান দেখুওৱা নির্দেশত প্ৰৱেশ কৰক"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"অৱস্থানৰ অতিৰিক্ত নির্দেশনাসমূহত প্ৰৱেশ কৰিবলৈ এপক অনুমতি দিয়ে। ইয়ে এপটোক জিপিএছ বা অন্য অৱস্থান উৎসসমূহৰ কাৰ্যকলাপত হস্তক্ষেপ কৰাৰ সুযোগ দিব পাৰে।"</string>
- <!-- no translation found for permlab_accessFineLocation (6265109654698562427) -->
- <skip />
+ <string name="permlab_accessFineLocation" msgid="6265109654698562427">"কেৱল অগ্ৰভূমিত অৱস্থানৰ সঠিক তথ্য় পাওক"</string>
<string name="permdesc_accessFineLocation" msgid="3520508381065331098">"এই এপটোৱে যেতিয়া ই নেপথ্যত চলি থাকে তেতিয়া আপোনাৰ সঠিক অৱস্থান নিৰ্ণয় কৰিব পাৰে। এপটোৱে ব্যৱহাৰ কৰিব পৰাকৈ এই অৱস্থান সেৱাসমূহ অন হৈ থাকিবই লাগিব আৰু আপোনাৰ ফ\'নত উপলব্ধ হ\'ব লাগিব। ইয়াৰ ফলত বেটাৰিৰ খৰচ বাঢ়িব পাৰে।"</string>
<string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"অনুমানিক অৱস্থান (নেটৱর্ক ভিত্তিক) ব্যৱহাৰ কৰিব পাৰে"</string>
<string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"চেল টাৱাৰ আৰু ৱাই-ফাই নেটৱর্কৰ দৰে নেটৱর্কৰ উৎসসমূহক ভিত্তি কৰি এই এপটোৱে আপোনাৰ অৱস্থান নিৰ্ণয় কৰিব পাৰে। এই অৱস্থানৰ সেৱাসমূহ অন হৈ থাকিলে আৰু আপোনাৰ টেবলেটটোত উপলব্ধ হ\'লেহে এপটোৱে সেইবোৰ ব্যৱহাৰ কৰিবলৈ সক্ষম হ\'ব।"</string>
@@ -518,6 +517,37 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"ফিংগাৰপ্ৰিণ্ট আইকন"</string>
+ <string name="permlab_manageFace" msgid="2137540986007309781">"মুখমণ্ডল সত্যাপন হাৰ্ডৱেৰ পৰিচালনা কৰক"</string>
+ <string name="permdesc_manageFace" msgid="8919637120670185330">"মুখমণ্ডলৰ টেম্প্লেট যোগ কৰাৰ বা মচাৰ পদ্ধতি কামত লগাবলৈ আহ্বান কৰিবলৈ এপটোক অনুমতি দিয়ে।"</string>
+ <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"মুখমণ্ডল সত্যাপন হাৰ্ডৱেৰ ব্যৱহাৰ কৰক"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"বিশ্বাসযোগ্য়তা প্ৰমাণীকৰণৰ বাবে এপক মুখমণ্ডল সত্যাপন হাৰ্ডৱেৰ ব্য়ৱহাৰ কৰিবলৈ অনুমতি দিয়ে"</string>
+ <string name="face_acquired_insufficient" msgid="5901287247766106330">"মুখমণ্ডল চিনাক্ত কৰিব পৰা নাই; আকৌ চেষ্টা কৰক।"</string>
+ <string name="face_acquired_too_bright" msgid="610606792381297174">"মুখমণ্ডল অত্যন্ত উজ্জ্বল হৈছে। অনুগ্ৰহ কৰি পোহৰ কম থকা ঠাইত চেষ্টা কৰক।"</string>
+ <string name="face_acquired_too_dark" msgid="7229162716976778371">"মুখমণ্ডল অত্যন্ত আন্ধাৰ হৈছে। অনুগ্ৰহ কৰি পোহৰ থকা ঠাইলৈ যাওক।"</string>
+ <string name="face_acquired_too_close" msgid="1980310037427755293">"অনুগ্ৰহ কৰি মুখৰ পৰা ছেন্সৰ অলপ দূৰত ৰাখক।"</string>
+ <string name="face_acquired_too_far" msgid="4494571381828850007">"অনুগ্ৰহ কৰি ছেন্সৰটো মুখৰ ওচৰলৈ আনক।"</string>
+ <string name="face_acquired_too_high" msgid="228411096134808372">"অনুগ্ৰহ কৰি ছেন্সৰটো ওপৰলৈ নিয়ক।"</string>
+ <string name="face_acquired_too_low" msgid="4539774649296349109">"অনুগ্ৰহ কৰি ছেন্সৰটো তললৈ নিয়ক।"</string>
+ <string name="face_acquired_too_right" msgid="1650292067226118760">"অনুগ্ৰহ কৰি ছেন্সৰটো সোঁফাললৈ নিয়ক।"</string>
+ <string name="face_acquired_too_left" msgid="2712489669456176505">"অনুগ্ৰহ কৰি ছেন্সৰটো বাওঁফাললৈ নিয়ক।"</string>
+ <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"অনুগ্ৰহ কৰি ছেন্সৰটোলৈ চাওক।"</string>
+ <string name="face_acquired_not_detected" msgid="5707782294589511391">"কোনো মুখমণ্ডল চিনাক্ত কৰিব পৰা নগ’ল।"</string>
+ <string name="face_acquired_not_steady" msgid="3722829465011040042">"ডিভাইচৰ আগত মুখখন স্থিৰ কৰি ৰাখক।"</string>
+ <string-array name="face_acquired_vendor">
+ </string-array>
+ <string name="face_error_hw_not_available" msgid="6255891785768984615">"মুখমণ্ডলৰ হাৰ্ডৱেৰ উপলব্ধ নহয়।"</string>
+ <string name="face_error_timeout" msgid="4014326147867150054">"মুখমণ্ডল গ্ৰহণৰ সময়সীমা উকলি গৈছে। আকৌ চেষ্টা কৰক।"</string>
+ <string name="face_error_no_space" msgid="8224993703466381314">"মুখমণ্ডল সঞ্চয় কৰিব নোৱাৰি।"</string>
+ <string name="face_error_canceled" msgid="283945501061931023">"মুখমণ্ডলৰ প্ৰক্ৰিয়া বাতিল কৰা হ’ল।"</string>
+ <string name="face_error_lockout" msgid="3407426963155388504">"অত্যধিক ভুল প্ৰয়াস। কিছুসময়ৰ পাছত আকৌ চেষ্টা কৰক।"</string>
+ <string name="face_error_lockout_permanent" msgid="8198354656746088890">"অত্যধিক প্ৰয়াস। মুখমণ্ডলৰ জৰিয়তে সত্যাপন অক্ষম কৰা হ’ল।"</string>
+ <string name="face_error_unable_to_process" msgid="238761109287767270">"আকৌ চেষ্টা কৰক।"</string>
+ <string name="face_error_not_enrolled" msgid="9166792142679691323">"কোনো মুখমণ্ডল যোগ কৰা নহ’ল।"</string>
+ <string name="face_error_hw_not_present" msgid="4737289254517095671">"এই ডিভাইচটোত মুখমণ্ডল সত্যাপন ছেন্সৰ নাই।"</string>
+ <string name="face_name_template" msgid="7004562145809595384">"মুখমণ্ডল <xliff:g id="FACEID">%d</xliff:g>"</string>
+ <string-array name="face_error_vendor">
+ </string-array>
+ <string name="face_icon_content_description" msgid="4024817159806482191">"মুখমণ্ডলৰ আইকন"</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"ছিংকৰ ছেটিংসমূহ পঢ়ক"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"একাউণ্টৰ ছিংক ছেটিংবোৰ পঢ়িবলৈ এপক অনুমতি দিয়ে। যেনে, People এপ কোনো একাউণ্টত ছিংক কৰা হৈছে নে নাই সেয়া নির্ধাৰণ কৰিব পাৰে।"</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"ছিংকক অন আৰু অফ ট\'গল কৰক"</string>
@@ -1178,6 +1208,9 @@
<skip />
<string name="wifi_no_internet" msgid="8938267198124654938">"ৱাই-ফাইত ইন্টাৰনেট নাই"</string>
<string name="wifi_no_internet_detailed" msgid="8083079241212301741">"অধিক বিকল্পৰ বাবে টিপক"</string>
+ <string name="wifi_softap_config_change" msgid="8475911871165857607">"আপোনাৰ হটস্পট ছেটিংসমূহত কৰা সালসলনি"</string>
+ <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"আপোনাৰ হটস্পটৰ বেণ্ড সলনি কৰা হ’ল।"</string>
+ <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"আপোনাৰ কেৱল ৫গিগাহাৰ্টজৰ প্ৰতি অগ্ৰাধিকাৰ এই ডিভাচইচটোৱে সমৰ্থন নকৰে। ইয়াৰ পৰিৱৰ্তে, ডিভাচইচটোৱে যেতিয়া ৫গিগাহাৰ্টজ বেণ্ড উপলব্ধ হ’ব তেতিয়া সেইয়া ব্যৱহাৰ কৰিব।"</string>
<string name="network_switch_metered" msgid="4671730921726992671">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g>লৈ সলনি কৰা হ\'ল"</string>
<string name="network_switch_metered_detail" msgid="775163331794506615">"যেতিয়া <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>ত ইণ্টাৰনেট নাথাকে, তেতিয়া ডিভাইচে <xliff:g id="NEW_NETWORK">%1$s</xliff:g>ক ব্যৱহাৰ কৰে। মাচুল প্ৰযোজ্য হ\'ব পাৰে।"</string>
<string name="network_switch_metered_toast" msgid="5779283181685974304">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g>ৰ পৰা <xliff:g id="NEW_NETWORK">%2$s</xliff:g> লৈ সলনি কৰা হ\'ল"</string>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index 6c9fb75..7ddd88c2 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -236,9 +236,6 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Təyyarə rejimi"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Uçuş rejimi açıqdır"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Təyyarə rejimi qapalıdır"</string>
- <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Batareya qənaəti"</string>
- <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Batareya qənaəti DEAKTİVDİR"</string>
- <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Batareya qənaəti AKTİVDİR"</string>
<string name="global_action_settings" msgid="1756531602592545966">"Ayarlar"</string>
<string name="global_action_assist" msgid="3892832961594295030">"Yardım"</string>
<string name="global_action_voice_assist" msgid="7751191495200504480">"Səs Yardımçısı"</string>
@@ -276,6 +273,9 @@
<string name="permgrouplab_location" msgid="7275582855722310164">"Yer"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"cihazın yerini bilmək"</string>
<string name="permgrouprequest_location" msgid="3788275734953323491">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> tətbiqinə bu cihazın məkanına daxil olmaq icazəsi verilsin?"</string>
+ <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"Tətbiq yalnız ondan istifadə etiyiniz zaman məkanı əldə edə bilər."</string>
+ <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> tətbiqinə bu cihazın məkanına daxil olmaq icazəsi verilsin?"</string>
+ <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"Tətbiq hətta ondan istifadə etmədiyiniz zaman belə məkanı əldə edə bilər."</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Təqvim"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"təqvimə daxil olun"</string>
<string name="permgrouprequest_calendar" msgid="289900767793189421">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> tətbiqinə təqvimə daxil olmaq icazəsi verilsin?"</string>
@@ -402,8 +402,7 @@
<string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Bu tətbiq telefonunuzda təqvim tədbirləri əlavə edə, silə və ya dəyişiklik edə bilər. Həmçinin bu tətbiq təqvim sahiblərindən gəlmə ehtimalı olan mesajları göndərə və ya sahiblərinə bildirmədən tədbirlərdə dəyişiklik edə bilər."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"əlavə məkan provayderi əmrlərinə çıxış"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Tətbiqə ekstra məkan provayder əmrlərinə girişə imkan verir. Bu, tətbiqə GPS və ya digər lokal mənbələrlə əməliyyata müdaxiləyə imkan verə bilər."</string>
- <!-- no translation found for permlab_accessFineLocation (6265109654698562427) -->
- <skip />
+ <string name="permlab_accessFineLocation" msgid="6265109654698562427">"yalnız ön planda dəqiq məkana daxil olun"</string>
<string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Bu tətbiq yalnız ön fonda olduqda dəqiq məkanınızı əldə edə bilər. Tətbiqin bunlardan istifadə etməsi üçün bu məkan xidmətləri aktiv edilməlidir və telefonda əlçatan olmalıdır. Bu, batareya sərfiyyatını artıra bilər."</string>
<string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"təxmini məkana (şəbəkə əsaslı) giriş"</string>
<string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Bu tətbiq mobil qüllələr və Wi-Fi şəbəkələri kimi şəbəkə mənbələrinin əassında əkanınızı əldə edə bilər. Bu məkan xidmətləri aktiv edilməlidir və planşetdə tətbiq tərəfindən istifadə üçün əlçatan olmalıdır."</string>
@@ -518,6 +517,37 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Barmaq izi ikonası"</string>
+ <string name="permlab_manageFace" msgid="2137540986007309781">"üz identifikasiyası proqramını idarə edin"</string>
+ <string name="permdesc_manageFace" msgid="8919637120670185330">"Proqramdan istifadə üçün barmaq izi şablonlarını əlavə etmək və silmək məqsədilə üsullara müraciət etməyə imkan verir."</string>
+ <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"üz identifikasiyası proqramından istifadə edin"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Tətbiqin üz identifikasiyası proqramından identifikasiya zamanı istifadə etməsinə icazə verir"</string>
+ <string name="face_acquired_insufficient" msgid="5901287247766106330">"Üz tanınmadı. Yenidən cəhd edin."</string>
+ <string name="face_acquired_too_bright" msgid="610606792381297174">"Üz çox parlaqdır. Daha zəif işıqda sınayın."</string>
+ <string name="face_acquired_too_dark" msgid="7229162716976778371">"Üz çox tünddür. İşığı ortaya çıxarın."</string>
+ <string name="face_acquired_too_close" msgid="1980310037427755293">"Sensoru üzdən kənara gətirin."</string>
+ <string name="face_acquired_too_far" msgid="4494571381828850007">"Sensoru üzə biraz da yaxınlaşdırın."</string>
+ <string name="face_acquired_too_high" msgid="228411096134808372">"Sensoru daha yuxarı gətirin."</string>
+ <string name="face_acquired_too_low" msgid="4539774649296349109">"Sensoru daha aşağı gətirin."</string>
+ <string name="face_acquired_too_right" msgid="1650292067226118760">"Sensoru sağa gətirin."</string>
+ <string name="face_acquired_too_left" msgid="2712489669456176505">"Sensoru sola gətirin."</string>
+ <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Sensora baxın."</string>
+ <string name="face_acquired_not_detected" msgid="5707782294589511391">"Üz aşkarlanmadı."</string>
+ <string name="face_acquired_not_steady" msgid="3722829465011040042">"Üzü cihazın qarşısında sabit saxlayın."</string>
+ <string-array name="face_acquired_vendor">
+ </string-array>
+ <string name="face_error_hw_not_available" msgid="6255891785768984615">"Üz proqramı əlçatan deyil."</string>
+ <string name="face_error_timeout" msgid="4014326147867150054">"Üz proqramı taymerinin vaxtı bitdi. Yenidən cəhd edin."</string>
+ <string name="face_error_no_space" msgid="8224993703466381314">"Üz bərpa edilmədi."</string>
+ <string name="face_error_canceled" msgid="283945501061931023">"Üz əməliyyatı ləğv edildi."</string>
+ <string name="face_error_lockout" msgid="3407426963155388504">"Həddindən çox cəhd. Sonraya saxlayın."</string>
+ <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Həddindən çox cəhd. Üz identifikasiyası deaktiv edildi."</string>
+ <string name="face_error_unable_to_process" msgid="238761109287767270">"Yenidən cəhd edin."</string>
+ <string name="face_error_not_enrolled" msgid="9166792142679691323">"Üz qeydə alınmayıb."</string>
+ <string name="face_error_hw_not_present" msgid="4737289254517095671">"Bu cihazda üz identifikasiyası sensoru yoxdur."</string>
+ <string name="face_name_template" msgid="7004562145809595384">"Üz <xliff:g id="FACEID">%d</xliff:g>"</string>
+ <string-array name="face_error_vendor">
+ </string-array>
+ <string name="face_icon_content_description" msgid="4024817159806482191">"Üz işarəsi"</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"sinx ayarlarını oxu"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Tətbiqə hesablar üçün sinxronizasiya nizamlarını oxuma icazəsi verir. Məsələn, bu Şəxslər tətbiqinin sinxronizə olunub-olunmadığını təyin edə bilər."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"sinxronizasiyaya davam edir və onu söndürür"</string>
@@ -1178,6 +1208,9 @@
<skip />
<string name="wifi_no_internet" msgid="8938267198124654938">"Wi-Fi şəbəkəsinin internetə girişi yoxdur"</string>
<string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Seçimlər üçün tıklayın"</string>
+ <string name="wifi_softap_config_change" msgid="8475911871165857607">"Hotspot ayarlarınızda dəyişiklik"</string>
+ <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"Hotspot qrupu dəyişdi."</string>
+ <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"Bu cihaz yalnız 5GHz üçün tərcihinizi dəstəkləmir. Əvəzinə əlçatan olduqda bu cihaz 5GHz qrupundan istifadə edəcək."</string>
<string name="network_switch_metered" msgid="4671730921726992671">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> şəbəkə növünə keçirildi"</string>
<string name="network_switch_metered_detail" msgid="775163331794506615">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> şəbəkəsinin internetə girişi olmadıqda, cihaz <xliff:g id="NEW_NETWORK">%1$s</xliff:g> şəbəkəsini istifadə edir. Xidmət haqqı tutula bilər."</string>
<string name="network_switch_metered_toast" msgid="5779283181685974304">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> şəbəkəsindən <xliff:g id="NEW_NETWORK">%2$s</xliff:g> şəbəkəsinə keçirildi"</string>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index 386c8ca..6338e69 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -239,9 +239,6 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Režim rada u avionu"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Režim rada u avionu je UKLJUČEN"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Režim rada u avionu je ISKLJUČEN"</string>
- <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Ušteda baterije"</string>
- <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Ušteda baterije je ISKLJUČENA"</string>
- <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Ušteda baterije je UKLJUČENA"</string>
<string name="global_action_settings" msgid="1756531602592545966">"Podešavanja"</string>
<string name="global_action_assist" msgid="3892832961594295030">"Pomoć"</string>
<string name="global_action_voice_assist" msgid="7751191495200504480">"Glasovna pomoć"</string>
@@ -279,6 +276,9 @@
<string name="permgrouplab_location" msgid="7275582855722310164">"Lokacija"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"pristupi lokaciji ovog uređaja"</string>
<string name="permgrouprequest_location" msgid="3788275734953323491">"Želite li da omogućite da <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> pristupa lokaciji ovog uređaja?"</string>
+ <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"Aplikacija će imati pristup lokaciji samo dok koristite aplikaciju."</string>
+ <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"Želite li da omogućite da <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> pristupa lokaciji ovog uređaja?"</string>
+ <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"Aplikacija će uvek imati pristup lokaciji, čak i kada ne koristite aplikaciju."</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Kalendar"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"pristupi kalendaru"</string>
<string name="permgrouprequest_calendar" msgid="289900767793189421">"Želite li da omogućite da <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> pristupa kalendaru?"</string>
@@ -405,8 +405,7 @@
<string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Ova aplikaciji može da dodaje, uklanja ili menja događaje iz kalendara na telefonu. Ova aplikacija može da šalje poruke koje izgledaju kao da ih šalju vlasnici kalendara ili da menja događaje bez znanja vlasnika."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"pristup dodatnim komandama dobavljača lokacije"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Omogućava aplikaciji da pristupa dodatnim komandama davaoca usluga lokacije. To može da omogući aplikaciji da utiče na rad GPS-a ili drugih izvora lokacije."</string>
- <!-- no translation found for permlab_accessFineLocation (6265109654698562427) -->
- <skip />
+ <string name="permlab_accessFineLocation" msgid="6265109654698562427">"pristup preciznoj lokaciji samo u prvom planu"</string>
<string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Ova aplikacija može da odredi vašu tačnu lokaciju samo kada radi u prvom planu. Ove usluge lokacije moraju da budu uključene i dostupne na telefonu da bi aplikacija mogla da ih koristi. To može da poveća potrošnju baterije."</string>
<string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"pristup približnoj lokaciji (utvrđena preko mreže)"</string>
<string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Ova aplikacija može da pristupi vašoj lokaciji pomoću izvora mreže, kao što su mobilni predajnici i Wi-Fi mreže. Ove usluge lokacije moraju da budu uključene i dostupne na tabletu da bi aplikacija mogla da ih koristi."</string>
@@ -521,6 +520,37 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Ikona otiska prsta"</string>
+ <string name="permlab_manageFace" msgid="2137540986007309781">"upravljanje hardv. za potvrdu identiteta pomoću lica"</string>
+ <string name="permdesc_manageFace" msgid="8919637120670185330">"Dozvoljava da aplikacija aktivira metode za dodavanje i brisanje šablona lica radi korišćenja."</string>
+ <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"korišćenje hardv. za potvrdu identiteta pomoću lica"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Dozvoljava da aplikacija koristi hardver za potvrdu identiteta pomoću lica"</string>
+ <string name="face_acquired_insufficient" msgid="5901287247766106330">"Obrada lica nije uspela. Probajte ponovo."</string>
+ <string name="face_acquired_too_bright" msgid="610606792381297174">"Lice je presvetlo. Probajte sa slabijim osvetljenjem."</string>
+ <string name="face_acquired_too_dark" msgid="7229162716976778371">"Lice je isuviše tamno. Otkrijte izvor svetla."</string>
+ <string name="face_acquired_too_close" msgid="1980310037427755293">"Udaljite senzor od lica."</string>
+ <string name="face_acquired_too_far" msgid="4494571381828850007">"Približite senzor licu."</string>
+ <string name="face_acquired_too_high" msgid="228411096134808372">"Pomerite senzor naviše."</string>
+ <string name="face_acquired_too_low" msgid="4539774649296349109">"Pomerite senzor naniže."</string>
+ <string name="face_acquired_too_right" msgid="1650292067226118760">"Pomerite senzor udesno."</string>
+ <string name="face_acquired_too_left" msgid="2712489669456176505">"Pomerite senzor ulevo."</string>
+ <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Gledajte u senzor."</string>
+ <string name="face_acquired_not_detected" msgid="5707782294589511391">"Nije otkriveno nijedno lice."</string>
+ <string name="face_acquired_not_steady" msgid="3722829465011040042">"Zadržite lice ispred uređaja bez pomeranja."</string>
+ <string-array name="face_acquired_vendor">
+ </string-array>
+ <string name="face_error_hw_not_available" msgid="6255891785768984615">"Harvder za lice nije dostupan."</string>
+ <string name="face_error_timeout" msgid="4014326147867150054">"Isteklo je vreme za proveru lica. Probajte ponovo."</string>
+ <string name="face_error_no_space" msgid="8224993703466381314">"Nije moguće sačuvati lice."</string>
+ <string name="face_error_canceled" msgid="283945501061931023">"Obrada lica je otkazana."</string>
+ <string name="face_error_lockout" msgid="3407426963155388504">"Previše pokušaja. Probajte ponovo kasnije."</string>
+ <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Više pokušaja. Potvrda identiteta je onemogućena."</string>
+ <string name="face_error_unable_to_process" msgid="238761109287767270">"Probajte ponovo."</string>
+ <string name="face_error_not_enrolled" msgid="9166792142679691323">"Nije registrovano nijedno lice."</string>
+ <string name="face_error_hw_not_present" msgid="4737289254517095671">"Ovaj uređaj nema senzor za potvrdu identiteta pomoću lica"</string>
+ <string name="face_name_template" msgid="7004562145809595384">"Lice <xliff:g id="FACEID">%d</xliff:g>"</string>
+ <string-array name="face_error_vendor">
+ </string-array>
+ <string name="face_icon_content_description" msgid="4024817159806482191">"Ikona lica"</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"čitanje podešavanja sinhronizacije"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Dozvoljava aplikaciji da čita podešavanja sinhronizacije za nalog. Na primer, ovako može da se utvrdi da li je aplikacija Ljudi sinhronizovana sa nalogom."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"uključivanje i isključivanje sinhronizacije"</string>
@@ -1200,6 +1230,9 @@
<skip />
<string name="wifi_no_internet" msgid="8938267198124654938">"Wi-Fi nema pristup internetu"</string>
<string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Dodirnite za opcije"</string>
+ <string name="wifi_softap_config_change" msgid="8475911871165857607">"Promene podešavanja za hotspot"</string>
+ <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"Opseg hotspota je promenjen."</string>
+ <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"Ovaj uređaj ne podržava podešavanje samo za 5 GHz. Uređaj će koristiti opseg od 5 GHz kada bude dostupan."</string>
<string name="network_switch_metered" msgid="4671730921726992671">"Prešli ste na tip mreže <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
<string name="network_switch_metered_detail" msgid="775163331794506615">"Uređaj koristi tip mreže <xliff:g id="NEW_NETWORK">%1$s</xliff:g> kada tip mreže <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> nema pristup internetu. Možda će se naplaćivati troškovi."</string>
<string name="network_switch_metered_toast" msgid="5779283181685974304">"Prešli ste sa tipa mreže <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> na tip mreže <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index eb6b7c0..b063839 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -242,9 +242,6 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Рэжым палёту"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Уключаны рэжым \"У самалёце\""</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Рэжым \"У самалёце\" адключаны"</string>
- <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Эканомія зараду"</string>
- <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Рэжым эканоміі зараду выключаны"</string>
- <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Рэжым эканоміі зараду ўключаны"</string>
<string name="global_action_settings" msgid="1756531602592545966">"Налады"</string>
<string name="global_action_assist" msgid="3892832961594295030">"Дапамога"</string>
<string name="global_action_voice_assist" msgid="7751191495200504480">"Галас. дапамога"</string>
@@ -282,6 +279,9 @@
<string name="permgrouplab_location" msgid="7275582855722310164">"Месцазнаходжанне"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"атрымліваць доступ да месцазнаходжання гэтай прылады"</string>
<string name="permgrouprequest_location" msgid="3788275734953323491">"Дазволіць праграме <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> доступ да звестак аб месцазнаходжанні гэтай прылады?"</string>
+ <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"Праграма будзе мець доступ да звестак пра месцазнаходжанне, толькі калі яна выкарыстоўваецца."</string>
+ <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"Дазволіць праграме <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> мець доступ да месцазнаходжання?"</string>
+ <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"Праграма заўсёды будзе мець доступ да звестак пра месцазнаходжанне, нават калі яна не выкарыстоўваецца."</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Каляндар"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"атрымліваць доступ да вашага календара"</string>
<string name="permgrouprequest_calendar" msgid="289900767793189421">"Дазволіць праграме <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> доступ да вашага календара?"</string>
@@ -408,8 +408,7 @@
<string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Гэта праграма можа дадаваць, выдаляць або змяняць падзеі календара на вашым тэлефоне. Гэта праграма можа адпраўляць паведамленні, якія могуць здавацца адпраўленымі ўладальнікамі календара, або змяняць падзеі без апавяшчэння іх уладальнікаў."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"доступ да дадатковых камандаў пастаўшчыка месцазнаходжання"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Дазваляе праграме атрымліваць доступ да дадатковых каманд службаў вызначэння месцазнаходжання. Гэта можа дазволіць праграме ўмешвацца ў функцыянаванне GPS або іншых крыніц даных аб месцазнаходжаннi."</string>
- <!-- no translation found for permlab_accessFineLocation (6265109654698562427) -->
- <skip />
+ <string name="permlab_accessFineLocation" msgid="6265109654698562427">"доступ да дакладнага месцазнаходжання толькі ў асноўным рэжыме"</string>
<string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Гэта праграма можа атрымліваць звесткі пра ваша дакладнае месцазнаходжанне толькі ў асноўным рэжыме. Службы геалакацыі павінны быць уключаны і даступныя на вашым тэлефоне, каб праграма магла імі карыстацца. Гэта можа павялічыць спажыванне зараду акумулятара."</string>
<string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"атрымліваць доступ да прыблізнага месцазнаходжання (на аснове даных сеткі)"</string>
<string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Гэта праграма можа атрымліваць звесткі пра ваша месцазнаходжанне на падставе даных сеткавых крыніц, такіх як вышкі сотавай сувязі і сеткі Wi-Fi. Гэтыя сэрвісы вызначэння месцазнаходжання павінны быць уключаны i даступныя на вашым планшэце, каб праграма магла іх выкарыстоўваць."</string>
@@ -524,6 +523,37 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Значок адбіткаў пальцаў"</string>
+ <string name="permlab_manageFace" msgid="2137540986007309781">"кіраваць абсталяваннем для распазнавання твару"</string>
+ <string name="permdesc_manageFace" msgid="8919637120670185330">"Праграма зможа дадаваць і выдаляць шаблоны твару."</string>
+ <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"карыстацца абсталяваннем для распазнавання твару"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Праграма зможа выкарыстоўваць абсталяванне распазнавання твару для аўтэнтыфікацыі"</string>
+ <string name="face_acquired_insufficient" msgid="5901287247766106330">"Не ўдалося распазнаць твар. Паўтарыце спробу."</string>
+ <string name="face_acquired_too_bright" msgid="610606792381297174">"Твар занадта светлы. Паспрабуйце зменшыць святло."</string>
+ <string name="face_acquired_too_dark" msgid="7229162716976778371">"Твар занадта цёмны. Павялічце асвятленне."</string>
+ <string name="face_acquired_too_close" msgid="1980310037427755293">"Перамясціце датчык далей ад твару."</string>
+ <string name="face_acquired_too_far" msgid="4494571381828850007">"Наблізьце датчык да твару."</string>
+ <string name="face_acquired_too_high" msgid="228411096134808372">"Падыміце датчык вышэй."</string>
+ <string name="face_acquired_too_low" msgid="4539774649296349109">"Апусціце датчык ніжэй."</string>
+ <string name="face_acquired_too_right" msgid="1650292067226118760">"Перамясціце датчык управа."</string>
+ <string name="face_acquired_too_left" msgid="2712489669456176505">"Перамясціце датчык улева."</string>
+ <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Глядзіце на датчык."</string>
+ <string name="face_acquired_not_detected" msgid="5707782294589511391">"Твар не знойдзены."</string>
+ <string name="face_acquired_not_steady" msgid="3722829465011040042">"Трымайце твар перад прыладай."</string>
+ <string-array name="face_acquired_vendor">
+ </string-array>
+ <string name="face_error_hw_not_available" msgid="6255891785768984615">"Абсталяванне для распазнавання твару недаступнае."</string>
+ <string name="face_error_timeout" msgid="4014326147867150054">"Час чакання твару выйшаў. Паўтарыце спробу."</string>
+ <string name="face_error_no_space" msgid="8224993703466381314">"Не ўдалося захаваць твар."</string>
+ <string name="face_error_canceled" msgid="283945501061931023">"Распазнаванне твару скасавана."</string>
+ <string name="face_error_lockout" msgid="3407426963155388504">"Занадта шмат спроб. Паўтарыце спробу пазней."</string>
+ <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Занадта шмат спроб. Аўтэнтыфікацыя твару адключана"</string>
+ <string name="face_error_unable_to_process" msgid="238761109287767270">"Паўтарыце спробу."</string>
+ <string name="face_error_not_enrolled" msgid="9166792142679691323">"Твар не зарэгістраваны."</string>
+ <string name="face_error_hw_not_present" msgid="4737289254517095671">"На гэтай прыладзе адсутнічае датчык аўтэнтыфікацыі твару"</string>
+ <string name="face_name_template" msgid="7004562145809595384">"Твар <xliff:g id="FACEID">%d</xliff:g>"</string>
+ <string-array name="face_error_vendor">
+ </string-array>
+ <string name="face_icon_content_description" msgid="4024817159806482191">"Значок твару"</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"чытаць параметры сінхранізацыі"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Дазваляе прыкладанням чытаць параметры сінхранізацыі для ўліковага запісу. Напрыклад, яны могуць вызначыць, цi сiнхранiзавана з улiковым запiсам прыкладанне \"Кантакты\"."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"уключэнне ці адключэнне сінхранізацыi"</string>
@@ -1222,6 +1252,9 @@
<skip />
<string name="wifi_no_internet" msgid="8938267198124654938">"У Wi-Fi няма доступу да інтэрнэту"</string>
<string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Дакраніцеся, каб убачыць параметры"</string>
+ <string name="wifi_softap_config_change" msgid="8475911871165857607">"Змяненні ў наладах хот-спота"</string>
+ <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"Частата хот-спота змянілася."</string>
+ <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"Прылада не можа працаваць толькі на частаце 5 ГГц. Гэта частата будзе выкарыстоўвацца, калі гэта магчыма."</string>
<string name="network_switch_metered" msgid="4671730921726992671">"Выкананы пераход да <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
<string name="network_switch_metered_detail" msgid="775163331794506615">"Прылада выкарыстоўвае сетку <xliff:g id="NEW_NETWORK">%1$s</xliff:g>, калі ў сетцы <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> няма доступу да інтэрнэту. Можа спаганяцца плата."</string>
<string name="network_switch_metered_toast" msgid="5779283181685974304">"Выкананы пераход з <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> да <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 176239f..d05d78c 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -236,9 +236,6 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Самолетен режим"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Самолетният режим е ВКЛЮЧЕН"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Самолетният режим е ИЗКЛЮЧЕН"</string>
- <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Режим за запазване на батерията"</string>
- <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Режимът за запазване на батерията е ИЗКЛЮЧЕН"</string>
- <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Режимът за запазване на батерията е ВКЛЮЧЕН"</string>
<string name="global_action_settings" msgid="1756531602592545966">"Настройки"</string>
<string name="global_action_assist" msgid="3892832961594295030">"Помощ"</string>
<string name="global_action_voice_assist" msgid="7751191495200504480">"Гласова помощ"</string>
@@ -276,6 +273,9 @@
<string name="permgrouplab_location" msgid="7275582855722310164">"Местоположение"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"получи достъп до местоположението на това устройство"</string>
<string name="permgrouprequest_location" msgid="3788275734953323491">"Да се разреши ли на <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> да осъществява достъп до местоположението на това устройство?"</string>
+ <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"Само когато използвате приложението, то ще има достъп до местоположението."</string>
+ <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"Да се разреши ли на <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> достъп до местопол. на у-вото?"</string>
+ <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"Дори когато не използвате приложението, то винаги ще има достъп до местоположението."</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Календар"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"има достъп до календара ви"</string>
<string name="permgrouprequest_calendar" msgid="289900767793189421">"Да се разреши ли на <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> да осъществява достъп до календара ви?"</string>
@@ -402,8 +402,7 @@
<string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Това приложение може да добавя, премахва или променя събития в календара на телефона ви. То е в състояние да изпраща съобщения от името на собственици на календари или да променя събития, без да уведомява собствениците."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"достъп до допълнителни команди на доставчика на местоположение"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Разрешава на приложението достъп до допълнителни команди на доставчика на местоположение. Това може да позволи на приложението да смущава работата на GPS или на другите източници на местоположение."</string>
- <!-- no translation found for permlab_accessFineLocation (6265109654698562427) -->
- <skip />
+ <string name="permlab_accessFineLocation" msgid="6265109654698562427">"достъп до точното местоположение само на преден план"</string>
<string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Приложението може да получава данни за точното ви местоположение само когато работи на преден план. Тези услуги за местоположение трябва да са включени и налице на телефона ви, за да могат да се използват от приложението. Това може да увеличи потреблението на батерията."</string>
<string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"достъп до приблизителното местоположение (въз основа на мрежата)"</string>
<string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Приложението може да получава данни за местоположението ви въз основа на мрежови източници, като клетъчни кули и Wi-Fi. Тези услуги за местоположение трябва да са включени и налице на таблета ви, за да могат да се използват от приложението."</string>
@@ -518,6 +517,37 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Икона за отпечатък"</string>
+ <string name="permlab_manageFace" msgid="2137540986007309781">"управление на хардуера за удостоверяване с лице"</string>
+ <string name="permdesc_manageFace" msgid="8919637120670185330">"Разрешава на прил. да извиква методи за добавяне и изтриване на лицеви шаблони за ползване"</string>
+ <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"използване на хардуера за удостоверяване с лице"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Разрешава на приложението при необходимост да използва хардуера за удостоверяване с лице"</string>
+ <string name="face_acquired_insufficient" msgid="5901287247766106330">"Лицето не можа да се обработи. Опитайте отново."</string>
+ <string name="face_acquired_too_bright" msgid="610606792381297174">"Лицето е твърде осветено. Oпитайте на по-тъмно."</string>
+ <string name="face_acquired_too_dark" msgid="7229162716976778371">"Лицето е твърде тъмно. Моля, осветете го по-добре."</string>
+ <string name="face_acquired_too_close" msgid="1980310037427755293">"Моля, отдалечете сензора от лицето си."</string>
+ <string name="face_acquired_too_far" msgid="4494571381828850007">"Моля, приближете сензора към лицето си."</string>
+ <string name="face_acquired_too_high" msgid="228411096134808372">"Моля, повдигнете сензора."</string>
+ <string name="face_acquired_too_low" msgid="4539774649296349109">"Моля, преместете сензора надолу."</string>
+ <string name="face_acquired_too_right" msgid="1650292067226118760">"Моля, преместете сензора надясно."</string>
+ <string name="face_acquired_too_left" msgid="2712489669456176505">"Моля, преместете сензора наляво."</string>
+ <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Моля, гледайте към сензора."</string>
+ <string name="face_acquired_not_detected" msgid="5707782294589511391">"Не е открито лице."</string>
+ <string name="face_acquired_not_steady" msgid="3722829465011040042">"Задръжте лицето си неподвижно пред устройството."</string>
+ <string-array name="face_acquired_vendor">
+ </string-array>
+ <string name="face_error_hw_not_available" msgid="6255891785768984615">"Няма достъп до хардуера за лице."</string>
+ <string name="face_error_timeout" msgid="4014326147867150054">"Времето за изчакване изтече. Опитайте отново."</string>
+ <string name="face_error_no_space" msgid="8224993703466381314">"Лицето не може да бъде съхранено."</string>
+ <string name="face_error_canceled" msgid="283945501061931023">"Операцията с лице е анулирана."</string>
+ <string name="face_error_lockout" msgid="3407426963155388504">"Твърде много опити. Опитайте отново по-късно."</string>
+ <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Твърде много опити. Удост. с лице е деактивирано."</string>
+ <string name="face_error_unable_to_process" msgid="238761109287767270">"Опитайте отново."</string>
+ <string name="face_error_not_enrolled" msgid="9166792142679691323">"Няма регистрирано лице."</string>
+ <string name="face_error_hw_not_present" msgid="4737289254517095671">"Това устройство няма сензор за удостоверяване с лице"</string>
+ <string name="face_name_template" msgid="7004562145809595384">"Лице <xliff:g id="FACEID">%d</xliff:g>"</string>
+ <string-array name="face_error_vendor">
+ </string-array>
+ <string name="face_icon_content_description" msgid="4024817159806482191">"Икона на лице"</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"четене на настройките за синхронизиране"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Разрешава на приложението да чете настройките за синхронизиране на профил. Например това може да определи дали приложението Хора е синхронизирано с даден профил."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"включване и изключване на синхронизирането"</string>
@@ -1178,6 +1208,9 @@
<skip />
<string name="wifi_no_internet" msgid="8938267198124654938">"Wi-Fi мрежата няма достъп до интернет"</string>
<string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Докоснете за опции"</string>
+ <string name="wifi_softap_config_change" msgid="8475911871165857607">"Промени в настройките ви за точка за достъп"</string>
+ <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"Честотната лента на точката ви за достъп е променена."</string>
+ <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"Това устройство не поддържа предпочитанието ви за използване само на честотната лента от 5 ГХц. Вместо това то ще я ползва, когато е възможно."</string>
<string name="network_switch_metered" msgid="4671730921726992671">"Превключи се към <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
<string name="network_switch_metered_detail" msgid="775163331794506615">"Устройството използва <xliff:g id="NEW_NETWORK">%1$s</xliff:g>, когато <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> няма достъп до интернет. Възможно е да бъдете таксувани."</string>
<string name="network_switch_metered_toast" msgid="5779283181685974304">"Превключи се от <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> към <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index 0bca847..949d206 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -236,9 +236,6 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"বিমান মোড"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"বিমান মোড চালু করা আছে"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"বিমান মোড বন্ধ করা আছে"</string>
- <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"ব্যাটারি সেভার"</string>
- <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"ব্যাটারি সেভার বন্ধ আছে"</string>
- <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"ব্যাটারি সেভার চালু আছে"</string>
<string name="global_action_settings" msgid="1756531602592545966">"সেটিংস"</string>
<string name="global_action_assist" msgid="3892832961594295030">"সহযোগিতা"</string>
<string name="global_action_voice_assist" msgid="7751191495200504480">"ভয়েস সহায়তা"</string>
@@ -276,6 +273,9 @@
<string name="permgrouplab_location" msgid="7275582855722310164">"লোকেশন"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"এই ডিভাইসের লোকেশন অ্যাক্সেস"</string>
<string name="permgrouprequest_location" msgid="3788275734953323491">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>-কে এই ডিভাইসের লোকেশন অ্যাক্সেস করতে দেবেন?"</string>
+ <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"আপনি এই অ্যাপটি ব্যবহার করার সময়েই সেটি আপনার লোকেশন অ্যাক্সেস করতে পারবে।"</string>
+ <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>কে সবসময় এই ডিভাইসের লোকেশন অ্যাক্সেস করতে দেবেন?"</string>
+ <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"এই অ্যাপটি সবসময় আপনার লোকেশন অ্যাক্সেস করতে পারবে, এমনকি আপনি অ্যাপটি ব্যবহার না করার সময়েও।"</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"ক্যালেন্ডার"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"আপনার ক্যালেন্ডারে অ্যাক্সেস"</string>
<string name="permgrouprequest_calendar" msgid="289900767793189421">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>-কে আপনার ক্যালেন্ডারে অ্যাক্সেস দেবেন?"</string>
@@ -402,8 +402,7 @@
<string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"এই অ্যাপটি আপনার ফোনে ক্যালেন্ডার ইভেন্টগুলি যোগ করতে, সরাতে বা পরিবর্তিত করতে পারে৷ এই অ্যাপটি মেসেজ পাঠাতে পারে যা ক্যালেন্ডারের মাললিকের থেকে এসেছে বলে মনে হয় বা ইভেন্টগুলিকে তাদের মালিকদের না জানিয়েই পরিবর্তিত করতে পারে৷"</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"অতিরিক্ত লোকেশন প্রদানকারী কমান্ডগুলি অ্যাক্সেস করে"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"লোকেশনের সাথে সম্পর্কিত তথ্য প্রদানকারীর অতিরিক্ত কম্যান্ডগুলিকে অ্যাপ্লিকেশানটিকে মঞ্জুর করে৷ এটি অ্যাপ্লিকেশানটিকে GPS অথবা অন্যান্য লোকেশন নির্ণয়ের সাথে সম্পর্কিত উৎসগুলির ক্রিয়াপ্রণালীর নিয়ন্ত্রণকে মঞ্জুর করতে পারে৷"</string>
- <!-- no translation found for permlab_accessFineLocation (6265109654698562427) -->
- <skip />
+ <string name="permlab_accessFineLocation" msgid="6265109654698562427">"শুধুমাত্র অ্যাপটি খোলা থাকলে আপনার যথাযথ লোকেশন অ্যাক্সেস করা"</string>
<string name="permdesc_accessFineLocation" msgid="3520508381065331098">"এই অ্যাপটি ফোরগ্রাউন্ডে চলতে থাকলে যেকোনও সময়ে আপনার যথাযথ লোকেশন জানতে পারবে। এই লোকেশন পরিষেবাগুলি অবশ্যই চালু রাখতে হবে এবং আপনার ফোনে সেগুলি উপলভ্য থাকতে হবে যাতে অ্যাপটি সেগুলি ব্যবহার করতে পারে। এর জন্য অতিরিক্ত ব্যাটারি খরচ হতে পারে।"</string>
<string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"আনুমানিক লোকেশন (নেটওয়ার্ক-ভিত্তিক) অ্যাক্সেস করুন"</string>
<string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"মোবাইল টাওয়ার এবং ওয়াই-ফাই নেটওয়ার্কগুলির মত নেটওয়ার্ক উৎসগুলির উপর ভিত্তি করে এই অ্যাপটি আপনার লোকেশন শনাক্ত করতে পারে৷ এই লোকেশন পরিষেবাগুলি অবশ্যই চালু রাখতে হবে এবং অ্যাপটি যাতে সেগুলি ব্যবহার করতে পারে সেইজন্য সেগুলিকে আপনার ট্যাবলেটে উপলব্ধ করে রাখতে হবে৷"</string>
@@ -518,6 +517,37 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"আঙ্গুলের ছাপ আইকন"</string>
+ <string name="permlab_manageFace" msgid="2137540986007309781">"ফেস যাচাইকরণ হার্ডওয়্যার পরিচালনা করুন"</string>
+ <string name="permdesc_manageFace" msgid="8919637120670185330">"ব্যবহার করার জন্য ফেস টেম্পলেট যোগ করা এবং মোছার পদ্ধতি গ্রহণ করতে অ্যাপটিকে অনুমতি দেয়৷"</string>
+ <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"ফেস যাচাইকরণ হার্ডওয়্যার ব্যবহার করুন"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"প্রমাণীকরণের জন্য ফেস যাচাইকরণ হার্ডওয়্যার ব্যবহার করার অনুমতি অ্যাপটিকে দেয়"</string>
+ <string name="face_acquired_insufficient" msgid="5901287247766106330">"ফেস প্রক্রিয়া করা যায়নি৷ আবার চেষ্টা করুন৷"</string>
+ <string name="face_acquired_too_bright" msgid="610606792381297174">"ফেসটি খুব উজ্জ্বল লাগছে। কম আলোতে চেষ্টা করুন।"</string>
+ <string name="face_acquired_too_dark" msgid="7229162716976778371">"ফেসটি খুব অন্ধকার লাগছে। বেশি আলোতে চেষ্টা করুন।"</string>
+ <string name="face_acquired_too_close" msgid="1980310037427755293">"অনুগ্রহ করে ফেস থেকে সেন্সরটি দূরে সরান।"</string>
+ <string name="face_acquired_too_far" msgid="4494571381828850007">"অনুগ্রহ করে ফেসের কাছাকাছি সেন্সরটি আনুন।"</string>
+ <string name="face_acquired_too_high" msgid="228411096134808372">"অনুগ্রহ করে সেন্সরটি উঁচুতে নিয়ে যান।"</string>
+ <string name="face_acquired_too_low" msgid="4539774649296349109">"অনুগ্রহ করে সেন্সরটি নিচে নিয়ে যান।"</string>
+ <string name="face_acquired_too_right" msgid="1650292067226118760">"অনুগ্রহ করে সেন্সরটি ডান দিকে নিয়ে যান।"</string>
+ <string name="face_acquired_too_left" msgid="2712489669456176505">"অনুগ্রহ করে সেন্সরটি বাঁ দিকে নিয়ে যান।"</string>
+ <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"অনুগ্রহ করে সেন্সরের দিকে তাকান।"</string>
+ <string name="face_acquired_not_detected" msgid="5707782294589511391">"কোনও ফেস শনাক্ত করা যায়নি।"</string>
+ <string name="face_acquired_not_steady" msgid="3722829465011040042">"ডিভাইসের সামনে ফেসটি স্থির রাখুন।"</string>
+ <string-array name="face_acquired_vendor">
+ </string-array>
+ <string name="face_error_hw_not_available" msgid="6255891785768984615">"ফেসের হার্ডওয়্যার উপলভ্য নয়৷"</string>
+ <string name="face_error_timeout" msgid="4014326147867150054">"ফেসের ছাপ নেওয়ার সময়সীমা শেষ৷ আবার চেষ্টা করুন৷"</string>
+ <string name="face_error_no_space" msgid="8224993703466381314">"ফেস স্টোর করা যাবে না।"</string>
+ <string name="face_error_canceled" msgid="283945501061931023">"ফেস অপারেশন বাতিল করা হয়েছে৷"</string>
+ <string name="face_error_lockout" msgid="3407426963155388504">"অনেকবার চেষ্টা করা হয়েছে। পরে আবার চেষ্টা করুন।"</string>
+ <string name="face_error_lockout_permanent" msgid="8198354656746088890">"অনেকবার চেষ্টা করা হয়েছে৷ ফেস যাচাইকরণ বন্ধ আছে।"</string>
+ <string name="face_error_unable_to_process" msgid="238761109287767270">"আবার চেষ্টা করুন।"</string>
+ <string name="face_error_not_enrolled" msgid="9166792142679691323">"কোনও ফেস নথিভুক্ত করা হয়নি।"</string>
+ <string name="face_error_hw_not_present" msgid="4737289254517095671">"এই ডিভাইসে ফেস যাচাইকরণ সেন্সর নেই"</string>
+ <string name="face_name_template" msgid="7004562145809595384">"<xliff:g id="FACEID">%d</xliff:g> ফেস"</string>
+ <string-array name="face_error_vendor">
+ </string-array>
+ <string name="face_icon_content_description" msgid="4024817159806482191">"ফেস আইকন"</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"সিঙ্ক সেটিংস পড়ে"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"অ্যাপ্লিকেশানটিকে একটি অ্যাকাউন্টের জন্য সিঙ্ক সেটিংস পড়ার অনুমতি দেয়৷ উদাহরণস্বরূপ, \'পিপল\' অ্যাপ্লিকেশানটি কোনো অ্যাকাউন্টের সাথে সিঙ্ক করা আছে কিনা তা নির্ধারণ করতে পারে৷"</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"সমন্বয় চালু এবং বন্ধ করা টগল করুন"</string>
@@ -1178,6 +1208,9 @@
<skip />
<string name="wifi_no_internet" msgid="8938267198124654938">"ওয়াই-ফাই এ ইন্টারনেট অ্যাক্সেস নেই"</string>
<string name="wifi_no_internet_detailed" msgid="8083079241212301741">"বিকল্পগুলির জন্য আলতো চাপুন"</string>
+ <string name="wifi_softap_config_change" msgid="8475911871165857607">"আপনার হটস্পট সেটিংসে পরিবর্তনগুলি"</string>
+ <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"আপনার হটস্পট ব্যান্ড পরিবর্তন করা হয়েছে।"</string>
+ <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"এই ডিভাইসটি শুধুমাত্র 5GHz এর জন্য আপনার পছন্দ সমর্থন করে না। পরিবর্তে, এই ডিভাইসটি 5GHz ব্যান্ড ব্যবহার করবে।"</string>
<string name="network_switch_metered" msgid="4671730921726992671">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> এ পাল্টানো হয়েছে"</string>
<string name="network_switch_metered_detail" msgid="775163331794506615">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> এ ইন্টারনেট অ্যাক্সেস না থাকলে <xliff:g id="NEW_NETWORK">%1$s</xliff:g> ব্যবহার করা হয়৷ ডেটা চার্জ প্রযোজ্য৷"</string>
<string name="network_switch_metered_toast" msgid="5779283181685974304">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> থেকে <xliff:g id="NEW_NETWORK">%2$s</xliff:g> এ পাল্টানো হয়েছে"</string>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index 151dc076..6a88c30 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -239,9 +239,6 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Način rada u avionu"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Uključen je način rada u avionu"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Način rada u avionu ugašen"</string>
- <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Ušteda baterije"</string>
- <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Ušteda baterije je ISKLJUČENA"</string>
- <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Ušteda baterije je UKLJUČENA"</string>
<string name="global_action_settings" msgid="1756531602592545966">"Postavke"</string>
<string name="global_action_assist" msgid="3892832961594295030">"Pomoć"</string>
<string name="global_action_voice_assist" msgid="7751191495200504480">"Glasovna pomoć"</string>
@@ -279,6 +276,9 @@
<string name="permgrouplab_location" msgid="7275582855722310164">"Lokacija"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"pristupa lokaciji ovog uređaja"</string>
<string name="permgrouprequest_location" msgid="3788275734953323491">"Dozvoliti aplikaciji <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> pristup lokaciji ovog uređaja?"</string>
+ <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"Ova aplikacija će moći pristupiti lokaciji uređaja samo kada je koristite."</string>
+ <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"Uvijek dozvoliti da <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> pristupi lokaciji uređaja?"</string>
+ <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"Ova aplikacija će uvijek moći pristupiti lokaciji uređaja, čak i kada je ne koristite."</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Kalendar"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"pristupa vašem kalendaru"</string>
<string name="permgrouprequest_calendar" msgid="289900767793189421">"Dozvoliti aplikaciji <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> da pristupi vašem kalendaru?"</string>
@@ -405,8 +405,7 @@
<string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Ova aplikacija može dodavati, uklanjati ili mijenjati događaje u kalendaru na vašem telefonu. Aplikacija može slati poruke koje mogu izgledati kao da dolazi od vlasnika kalendara ili promijeniti događaje bez obavještenja vlasnika."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"pristup dodatnim informacijama o lokaciji"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Dozvoljava aplikaciji pristup dodatnim naredbama pružatelja lokacija. Ovim se aplikaciji može dozvoliti da ometa rad GPS-a ili drugih izvora lokacija."</string>
- <!-- no translation found for permlab_accessFineLocation (6265109654698562427) -->
- <skip />
+ <string name="permlab_accessFineLocation" msgid="6265109654698562427">"pristup tačnoj lokaciji samo u prvom planu"</string>
<string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Ova aplikacija može odrediti vašu tačnu lokaciju samo kada je u prvom planu. Ove usluge lokacije moraju biti uključene i dostupne na vašem telefonu da ih aplikacija može koristiti. To može dovesti do povećane potrošnje baterije."</string>
<string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"pristup približnoj lokaciji (utvrđena preko mreže)"</string>
<string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Ova aplikacija može odrediti vašu lokaciju na osnovu izvora mreže kao što su predajnici za mobilnu mrežu i WiFi mreže. Ove usluge za određivanje lokacije moraju biti uključene i omogućene na vašem tabletu kako bi ih aplikacija mogla koristiti."</string>
@@ -461,7 +460,7 @@
<string name="permlab_accessNetworkState" msgid="4951027964348974773">"prikaz mrežnih veza"</string>
<string name="permdesc_accessNetworkState" msgid="8318964424675960975">"Omogućava aplikaciji pregled informacija o mrežnim vezama, npr. koje mreže postoje i koje su povezane."</string>
<string name="permlab_createNetworkSockets" msgid="7934516631384168107">"ima potpuni pristup mreži"</string>
- <string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"Omogućava aplikaciji kreiranje spojnih tačaka sa mrežom i korištenje prilagođenih mrežnih protokola. Preglednik i druge aplikacije omogućavaju slanje podataka na internet, tako da ovo odobrenje nije potrebno za vršenje te radnje."</string>
+ <string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"Omogućava aplikaciji kreiranje spojnih tačaka s mrežom i korištenje prilagođenih mrežnih protokola. Preglednik i druge aplikacije omogućavaju slanje podataka na internet, tako da ovo odobrenje nije potrebno za vršenje te radnje."</string>
<string name="permlab_changeNetworkState" msgid="958884291454327309">"izmjene povezivanja na mrežu"</string>
<string name="permdesc_changeNetworkState" msgid="6789123912476416214">"Dozvoljava aplikaciji izmjenu stanja mrežne povezanosti."</string>
<string name="permlab_changeTetherState" msgid="5952584964373017960">"izmjene podijeljenog povezivanja"</string>
@@ -505,11 +504,11 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Prst je uklonjen presporo. Pokušajte ponovo."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="fingerprint_not_recognized" msgid="2690661881608146617">"Nije prepoznat"</string>
+ <string name="fingerprint_not_recognized" msgid="2690661881608146617">"Nije prepoznato"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Otisak prsta je potvrđen"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Hardver za otisak prsta nije dostupan."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Otisak prsta se ne može pohraniti. Uklonite postojeći otisak prsta."</string>
- <string name="fingerprint_error_timeout" msgid="3927186043737732875">"Vremensko ograničenje za otisak prsta je isteklo. Pokušajte ponovo."</string>
+ <string name="fingerprint_error_timeout" msgid="3927186043737732875">"Vrijeme za prepoznavanje otiska prsta je isteklo. Pokušajte ponovo."</string>
<string name="fingerprint_error_canceled" msgid="4402024612660774395">"Radnja sa otiskom prsta je otkazana."</string>
<string name="fingerprint_error_user_canceled" msgid="7999639584615291494">"Korisnik je otkazao radnju s otiskom prsta."</string>
<string name="fingerprint_error_lockout" msgid="5536934748136933450">"Previše pokušaja. Pokušajte ponovo kasnije."</string>
@@ -521,6 +520,37 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Ikona za otisak prsta"</string>
+ <string name="permlab_manageFace" msgid="2137540986007309781">"upravljanje hardverom za autentifikaciju licem"</string>
+ <string name="permdesc_manageFace" msgid="8919637120670185330">"Omogućava aplikaciji da koristi metode za dodavanje i brisanje šablona lica za upotrebu."</string>
+ <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"upotreba hardvera za autentifikaciju licem"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Omogućava aplikaciji da za autentifikaciju koristi hardver za autentifikaciju licem"</string>
+ <string name="face_acquired_insufficient" msgid="5901287247766106330">"Obrada lica nije uspjela. Pokušajte ponovo."</string>
+ <string name="face_acquired_too_bright" msgid="610606792381297174">"Lice je presvijetlo. Pokušajte s manje svjetla."</string>
+ <string name="face_acquired_too_dark" msgid="7229162716976778371">"Lice je pretamno. Pojačajte izvor svjetlosti."</string>
+ <string name="face_acquired_too_close" msgid="1980310037427755293">"Odmaknite senzor od lica."</string>
+ <string name="face_acquired_too_far" msgid="4494571381828850007">"Približite senzor licu."</string>
+ <string name="face_acquired_too_high" msgid="228411096134808372">"Pomjerite senzor prema gore."</string>
+ <string name="face_acquired_too_low" msgid="4539774649296349109">"Pomjerite senzor prema dolje."</string>
+ <string name="face_acquired_too_right" msgid="1650292067226118760">"Pomjerite senzor udesno."</string>
+ <string name="face_acquired_too_left" msgid="2712489669456176505">"Pomjerite senzor ulijevo."</string>
+ <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Gledajte u senzor."</string>
+ <string name="face_acquired_not_detected" msgid="5707782294589511391">"Nije pronađeno nijedno lice."</string>
+ <string name="face_acquired_not_steady" msgid="3722829465011040042">"Mirno držite lice ispred uređaja."</string>
+ <string-array name="face_acquired_vendor">
+ </string-array>
+ <string name="face_error_hw_not_available" msgid="6255891785768984615">"Hardver za prepoznavanje lica nije dostupan."</string>
+ <string name="face_error_timeout" msgid="4014326147867150054">"Vrijeme za prepoznavanje lica je isteklo. Pokušajte ponovo."</string>
+ <string name="face_error_no_space" msgid="8224993703466381314">"Nije moguće pohraniti lice."</string>
+ <string name="face_error_canceled" msgid="283945501061931023">"Prepoznavanje lica je otkazano."</string>
+ <string name="face_error_lockout" msgid="3407426963155388504">"Previše pokušaja. Pokušajte ponovo kasnije."</string>
+ <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Previše pokušaja. Autentifikacija lica onemogućena."</string>
+ <string name="face_error_unable_to_process" msgid="238761109287767270">"Pokušajte ponovo."</string>
+ <string name="face_error_not_enrolled" msgid="9166792142679691323">"Nije prijavljeno nijedno lice."</string>
+ <string name="face_error_hw_not_present" msgid="4737289254517095671">"Ovaj uređaj nema senzor za autentifikaciju lica"</string>
+ <string name="face_name_template" msgid="7004562145809595384">"Lice <xliff:g id="FACEID">%d</xliff:g>"</string>
+ <string-array name="face_error_vendor">
+ </string-array>
+ <string name="face_icon_content_description" msgid="4024817159806482191">"Ikona lica"</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"čitanje postavki za sinhroniziranje"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Omogućava aplikaciji čitanje postavki sinhroniziranja za račun. Naprimjer, ovim se može utvrditi da li je aplikacija People sinhronizirana sa računom."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"aktiviranje/deaktiviranje sinhroniziranja"</string>
@@ -1202,6 +1232,9 @@
<skip />
<string name="wifi_no_internet" msgid="8938267198124654938">"WiFi nema pristup internetu"</string>
<string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Dodirnite za opcije"</string>
+ <string name="wifi_softap_config_change" msgid="8475911871165857607">"Promjene postavki vaše pristupne tačke"</string>
+ <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"Opseg vaše pristupne tačke je promijenjen."</string>
+ <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"Ovaj uređaj ne podržava vašu postavku za mreže od isključivo 5GHz. Uređaj će koristiti opseg of 5GHz kada je dostupan."</string>
<string name="network_switch_metered" msgid="4671730921726992671">"Prebačeno na: <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
<string name="network_switch_metered_detail" msgid="775163331794506615">"Kada <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> nema pristup internetu, uređaj koristi mrežu <xliff:g id="NEW_NETWORK">%1$s</xliff:g>. Moguća je naplata usluge."</string>
<string name="network_switch_metered_toast" msgid="5779283181685974304">"Prebačeno iz mreže <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> u <xliff:g id="NEW_NETWORK">%2$s</xliff:g> mrežu"</string>
@@ -1226,7 +1259,7 @@
<string name="accept" msgid="1645267259272829559">"Prihvati"</string>
<string name="decline" msgid="2112225451706137894">"Odbijte"</string>
<string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"Pozivnica poslana"</string>
- <string name="wifi_p2p_invitation_to_connect_title" msgid="4958803948658533637">"Poziv za povezivanje"</string>
+ <string name="wifi_p2p_invitation_to_connect_title" msgid="4958803948658533637">"Pozivnica za povezivanje"</string>
<string name="wifi_p2p_from_message" msgid="570389174731951769">"Pošiljalac:"</string>
<string name="wifi_p2p_to_message" msgid="248968974522044099">"Prima:"</string>
<string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Unesite potrebni PIN:"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 99b4715..2079925 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -236,9 +236,6 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Mode d\'avió"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Mode d\'avió activat"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Mode d\'avió desactivat"</string>
- <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Estalvi de bateria"</string>
- <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"El mode d\'estalvi de bateria està desactivat"</string>
- <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"El mode d\'estalvi de bateria està activat"</string>
<string name="global_action_settings" msgid="1756531602592545966">"Configuració"</string>
<string name="global_action_assist" msgid="3892832961594295030">"Assistència"</string>
<string name="global_action_voice_assist" msgid="7751191495200504480">"Assist. per veu"</string>
@@ -276,6 +273,9 @@
<string name="permgrouplab_location" msgid="7275582855722310164">"Ubicació"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"accedir a la ubicació del dispositiu"</string>
<string name="permgrouprequest_location" msgid="3788275734953323491">"Vols permetre que <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> accedeixi a la ubicació del dispositiu?"</string>
+ <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"L\'aplicació només tindrà accés a la ubicació quan l\'estiguis utilitzant."</string>
+ <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"Vols permetre que <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> accedeixi sempre a la ubicació del dispositiu?"</string>
+ <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"L\'aplicació tindrà accés sempre a la ubicació, fins i tot quan no l\'estiguis utilitzant."</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Calendari"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"accedir al calendari"</string>
<string name="permgrouprequest_calendar" msgid="289900767793189421">"Vols permetre que <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> accedeixi al calendari?"</string>
@@ -402,8 +402,7 @@
<string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Aquesta aplicació pot afegir, suprimir i canviar esdeveniments del calendari al telèfon. També pot enviar missatges que sembli que provenen dels propietaris del calendari o canviar esdeveniments sense notificar-los-ho."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"accedir a ordres del proveïdor d\'ubicació addicionals"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Permet que l\'aplicació accedeixi a ordres addicionals del proveïdor d\'ubicacions; per tant, és possible que l\'aplicació pugui interferir en el funcionament del GPS o d\'altres fonts d\'ubicacions."</string>
- <!-- no translation found for permlab_accessFineLocation (6265109654698562427) -->
- <skip />
+ <string name="permlab_accessFineLocation" msgid="6265109654698562427">"accedeix a la ubicació exacta només en primer pla"</string>
<string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Aquesta aplicació pot obtenir la teva ubicació exacta només quan està en primer pla. Aquests serveis d\'ubicació han d\'estar activats i disponibles al telèfon perquè l\'aplicació els pugui utilitzar, i això pot fer que el consum de bateria augmenti."</string>
<string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"accedir a la ubicació aproximada (basada en la xarxa)"</string>
<string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Aquesta aplicació pot obtenir la teva ubicació a partir de fonts de xarxa, com ara torres de telefonia mòbil i xarxes Wi-Fi. Aquests serveis d\'ubicació han d\'estar activats i disponibles a la tauleta perquè l\'aplicació els pugui utilitzar."</string>
@@ -518,6 +517,37 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Icona d\'empremta digital"</string>
+ <string name="permlab_manageFace" msgid="2137540986007309781">"gestiona el maquinari d\'autenticació facial"</string>
+ <string name="permdesc_manageFace" msgid="8919637120670185330">"Permet que l\'aplicació afegeixi i suprimeixi plantilles de cares que es puguin fer servir."</string>
+ <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"utilitza el maquinari d\'autenticació facial"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Permet que l\'aplicació faci servir maquinari d\'autenticació facial"</string>
+ <string name="face_acquired_insufficient" msgid="5901287247766106330">"Error en processar la cara. Torna-ho a provar."</string>
+ <string name="face_acquired_too_bright" msgid="610606792381297174">"La cara brilla massa. Prova amb menys llum."</string>
+ <string name="face_acquired_too_dark" msgid="7229162716976778371">"La cara és massa fosca. Prova amb més llum."</string>
+ <string name="face_acquired_too_close" msgid="1980310037427755293">"Allunya el sensor de la cara."</string>
+ <string name="face_acquired_too_far" msgid="4494571381828850007">"Apropa el sensor a la cara."</string>
+ <string name="face_acquired_too_high" msgid="228411096134808372">"Puja el sensor més amunt."</string>
+ <string name="face_acquired_too_low" msgid="4539774649296349109">"Baixa el sensor més avall."</string>
+ <string name="face_acquired_too_right" msgid="1650292067226118760">"Mou el sensor cap a la dreta."</string>
+ <string name="face_acquired_too_left" msgid="2712489669456176505">"Mou el sensor cap a l\'esquerra."</string>
+ <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Mira el sensor."</string>
+ <string name="face_acquired_not_detected" msgid="5707782294589511391">"No s\'ha detectat cap cara."</string>
+ <string name="face_acquired_not_steady" msgid="3722829465011040042">"Mantén la cara quieta davant del dispositiu."</string>
+ <string-array name="face_acquired_vendor">
+ </string-array>
+ <string name="face_error_hw_not_available" msgid="6255891785768984615">"Maquinari de reconeixement facial no disponible."</string>
+ <string name="face_error_timeout" msgid="4014326147867150054">"S\'ha esgotat el temps d\'espera. Torna-ho a provar."</string>
+ <string name="face_error_no_space" msgid="8224993703466381314">"La cara no es pot desar."</string>
+ <string name="face_error_canceled" msgid="283945501061931023">"S\'ha cancel·lat el reconeixement facial."</string>
+ <string name="face_error_lockout" msgid="3407426963155388504">"Massa intents. Torna-ho a provar més tard."</string>
+ <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Massa intents. Autenticació facial desactivada."</string>
+ <string name="face_error_unable_to_process" msgid="238761109287767270">"Torna-ho a provar."</string>
+ <string name="face_error_not_enrolled" msgid="9166792142679691323">"No s\'ha registrat cap cara."</string>
+ <string name="face_error_hw_not_present" msgid="4737289254517095671">"Aquest dispositiu no té sensor d\'autenticació facial"</string>
+ <string name="face_name_template" msgid="7004562145809595384">"Cara <xliff:g id="FACEID">%d</xliff:g>"</string>
+ <string-array name="face_error_vendor">
+ </string-array>
+ <string name="face_icon_content_description" msgid="4024817159806482191">"Icona facial"</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"llegir la configuració de sincronització"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Permet que l\'aplicació llegeixi la configuració de sincronització d\'un compte. Per exemple, això pot determinar que l\'aplicació Persones estigui sincronitzada amb un compte."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"activar o desactivar la sincronització"</string>
@@ -1178,6 +1208,9 @@
<skip />
<string name="wifi_no_internet" msgid="8938267198124654938">"La Wi-Fi no té accés a Internet"</string>
<string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Toca per veure les opcions"</string>
+ <string name="wifi_softap_config_change" msgid="8475911871165857607">"Canvis en la configuració del punt d\'accés Wi-Fi"</string>
+ <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"Ha canviat la teva banda del punt d\'accés Wi-Fi."</string>
+ <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"Aquest dispositiu no admet utilitzar exclusivament una banda de 5 GHz. El dispositiu utilitzarà una banda de 5 GHz quan estigui disponible."</string>
<string name="network_switch_metered" msgid="4671730921726992671">"Actualment en ús: <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
<string name="network_switch_metered_detail" msgid="775163331794506615">"El dispositiu utilitza <xliff:g id="NEW_NETWORK">%1$s</xliff:g> en cas que <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> no tingui accés a Internet. És possible que s\'hi apliquin càrrecs."</string>
<string name="network_switch_metered_toast" msgid="5779283181685974304">"Abans es feia servir la xarxa <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g>; ara s\'utilitza <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index a57059c..46abc80 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -242,9 +242,6 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Režim Letadlo"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Režim Letadlo je ZAPNUTÝ"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Režim Letadlo je VYPNUTÝ"</string>
- <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Spořič baterie"</string>
- <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Spořič baterie je VYPNUTÝ"</string>
- <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Spořič baterie je ZAPNUTÝ"</string>
<string name="global_action_settings" msgid="1756531602592545966">"Nastavení"</string>
<string name="global_action_assist" msgid="3892832961594295030">"Asistence"</string>
<string name="global_action_voice_assist" msgid="7751191495200504480">"Hlas. asistence"</string>
@@ -282,6 +279,9 @@
<string name="permgrouplab_location" msgid="7275582855722310164">"Poloha"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"přístup k poloze tohoto zařízení"</string>
<string name="permgrouprequest_location" msgid="3788275734953323491">"Povolit aplikaci <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> přístup k poloze tohoto zařízení?"</string>
+ <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"Aplikace bude mít přístup k poloze, pouze když ji budete používat."</string>
+ <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"Povolit aplikaci <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> přístup k poloze tohoto zařízení?"</string>
+ <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"Aplikace bude mít přístup k poloze vždy, i když ji nebudete používat."</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Kalendář"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"přístup ke kalendáři"</string>
<string name="permgrouprequest_calendar" msgid="289900767793189421">"Povolit aplikaci <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> přístup ke kalendáři?"</string>
@@ -408,8 +408,7 @@
<string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Tato aplikace může přidat, odstranit či změnit události v kalendáři uložené v telefonu. Může také odesílat zprávy, které budou zdánlivě přicházet od vlastníka kalendáře, nebo upravovat události bez vědomí vlastníka."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"přístup k dalším příkazům poskytovatele polohy"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Umožňuje aplikaci přístup k dalším příkazům poskytovatele polohy. To aplikaci umožní zasahovat do fungování systému GPS a dalších zdrojů polohy."</string>
- <!-- no translation found for permlab_accessFineLocation (6265109654698562427) -->
- <skip />
+ <string name="permlab_accessFineLocation" msgid="6265109654698562427">"přístup k přesné poloze jen na popředí"</string>
<string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Tato aplikace může zjistit vaši přesnou polohu, jen když běží na popředí. Aby tyto služby určování polohy mohla aplikace používat, musí být v telefonu dostupné a musí být zapnuté. Tyto služby mohou způsobit rychlejší vybíjení baterie."</string>
<string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"přístup k přibližné poloze (pomocí sítě)"</string>
<string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Tato aplikace může zjistit vaši polohu podle zdrojů sítě, jako jsou vysílací věže nebo sítě Wi-Fi. Aby tyto služby určování polohy mohla aplikace používat, musí být v tabletu dostupné a musí být zapnuté."</string>
@@ -524,6 +523,37 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Ikona otisku prstů"</string>
+ <string name="permlab_manageFace" msgid="2137540986007309781">"správa hardwaru k ověření obličeje"</string>
+ <string name="permdesc_manageFace" msgid="8919637120670185330">"Umožňuje aplikaci volat metody k přidání a smazání šablon obličeje, které budou použity."</string>
+ <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"použití hardwaru k ověření obličeje"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Umožňuje aplikaci provést ověření pomocí hardwaru k ověření obličeje"</string>
+ <string name="face_acquired_insufficient" msgid="5901287247766106330">"Zpracování obličeje se nezdařilo. Zkuste to znovu."</string>
+ <string name="face_acquired_too_bright" msgid="610606792381297174">"Obličej je moc jasný. Zkuste to v menším světle."</string>
+ <string name="face_acquired_too_dark" msgid="7229162716976778371">"Obličej je moc tmavý. Odkryjte zdroj světla."</string>
+ <string name="face_acquired_too_close" msgid="1980310037427755293">"Oddalte senzor od obličeje."</string>
+ <string name="face_acquired_too_far" msgid="4494571381828850007">"Posuňte senzor blíž k obličeji."</string>
+ <string name="face_acquired_too_high" msgid="228411096134808372">"Posuňte senzor výš."</string>
+ <string name="face_acquired_too_low" msgid="4539774649296349109">"Posuňte senzor níž."</string>
+ <string name="face_acquired_too_right" msgid="1650292067226118760">"Posuňte senzor doprava."</string>
+ <string name="face_acquired_too_left" msgid="2712489669456176505">"Posuňte senzor doleva."</string>
+ <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Podívejte se do senzoru."</string>
+ <string name="face_acquired_not_detected" msgid="5707782294589511391">"Nebyl rozpoznán žádný obličej."</string>
+ <string name="face_acquired_not_steady" msgid="3722829465011040042">"Držte obličej nehybně před zařízením."</string>
+ <string-array name="face_acquired_vendor">
+ </string-array>
+ <string name="face_error_hw_not_available" msgid="6255891785768984615">"Není k dispozici hardware ke snímání obličeje."</string>
+ <string name="face_error_timeout" msgid="4014326147867150054">"Limit ověření obličeje vypršel. Zkuste to znovu."</string>
+ <string name="face_error_no_space" msgid="8224993703466381314">"Obličej nelze uložit."</string>
+ <string name="face_error_canceled" msgid="283945501061931023">"Operace snímání obličeje byla zrušena."</string>
+ <string name="face_error_lockout" msgid="3407426963155388504">"Příliš mnoho pokusů. Zkuste to později."</string>
+ <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Příliš mnoho pokusů. Ověření obličeje je zakázáno."</string>
+ <string name="face_error_unable_to_process" msgid="238761109287767270">"Zkuste to znovu."</string>
+ <string name="face_error_not_enrolled" msgid="9166792142679691323">"Není zaregistrován žádný obličej."</string>
+ <string name="face_error_hw_not_present" msgid="4737289254517095671">"Toto zařízení nemá snímač ověření obličeje"</string>
+ <string name="face_name_template" msgid="7004562145809595384">"Obličej <xliff:g id="FACEID">%d</xliff:g>"</string>
+ <string-array name="face_error_vendor">
+ </string-array>
+ <string name="face_icon_content_description" msgid="4024817159806482191">"Ikona obličeje"</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"čtení nastavení synchronizace"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Umožňuje aplikaci číst nastavení synchronizace v účtu. Může například určit, zda je s účtem synchronizována aplikace Lidé."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"vypnutí nebo zapnutí synchronizace"</string>
@@ -1222,6 +1252,9 @@
<skip />
<string name="wifi_no_internet" msgid="8938267198124654938">"Wi-Fi nemá přístup k internetu"</string>
<string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Klepnutím zobrazíte možnosti"</string>
+ <string name="wifi_softap_config_change" msgid="8475911871165857607">"Změny nastavení hotspotu"</string>
+ <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"Pásmo hotspotu se změnilo."</string>
+ <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"Toto zařízení nepodporuje vaše nastavení jen 5GHz pásma. Zařízení použije pásmo 5 GHz, jen když bude dostupné."</string>
<string name="network_switch_metered" msgid="4671730921726992671">"Přechod na síť <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
<string name="network_switch_metered_detail" msgid="775163331794506615">"Když síť <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> nebude mít přístup k internetu, zařízení použije síť <xliff:g id="NEW_NETWORK">%1$s</xliff:g>. Mohou být účtovány poplatky."</string>
<string name="network_switch_metered_toast" msgid="5779283181685974304">"Přechod ze sítě <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> na síť <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 4fabf17..8c86ac8 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -236,9 +236,6 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Flytilstand"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Flytilstand er TIL"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Flytilstand er slået FRA"</string>
- <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Batterisparefunktion"</string>
- <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Batterisparefunktion er slået FRA"</string>
- <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Batterisparefunktion er slået TIL"</string>
<string name="global_action_settings" msgid="1756531602592545966">"Indstillinger"</string>
<string name="global_action_assist" msgid="3892832961594295030">"Assistance"</string>
<string name="global_action_voice_assist" msgid="7751191495200504480">"Taleassistent"</string>
@@ -276,6 +273,9 @@
<string name="permgrouplab_location" msgid="7275582855722310164">"Placering"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"få adgang til enhedens placering"</string>
<string name="permgrouprequest_location" msgid="3788275734953323491">"Vil du give <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> adgang til enhedens placering?"</string>
+ <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"Appen har kun adgang til placeringen, når du bruger appen."</string>
+ <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"Skal <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> altid have adgang til enhedens placering?"</string>
+ <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"Appen har altid adgang til placeringen, også selvom du ikke bruger appen."</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Kalender"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"have adgang til din kalender"</string>
<string name="permgrouprequest_calendar" msgid="289900767793189421">"Vil du give <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> adgang til din kalender?"</string>
@@ -402,8 +402,7 @@
<string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Denne app kan tilføje, fjerne eller ændre kalenderbegivenheder på din telefon. Denne app kan sende meddelelser, der kan se ud, som om de kommer fra kalenderejere, eller ændre begivenheder uden at give ejeren besked."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"få adgang til yderligere kommandoer for placeringsudbyder"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Tillader, at appen kan få adgang til yderligere kommandoer for placeringsudbydere. Dette kan gøre det muligt for appen at forstyrre GPS-funktionen eller andre placeringskilder."</string>
- <!-- no translation found for permlab_accessFineLocation (6265109654698562427) -->
- <skip />
+ <string name="permlab_accessFineLocation" msgid="6265109654698562427">"få kun adgang til nøjagtig placering i forgrunden"</string>
<string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Denne app kan kun få din nøjagtige placering, når den er i forgrunden. Disse placeringstjenester skal være aktiverede og tilgængelige på din telefon, før appen kan bruge dem. Dette kan øge batteriforbruget."</string>
<string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"få adgang til omtrentlig placering (netværksbaseret)"</string>
<string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Denne app kan bestemme din placering ved hjælp af netværkskilder, som f.eks. mobilmaster og Wi-Fi-netværk. Disse placeringstjenester skal være aktiverede og tilgængelige på din tablet, før appen kan bruge dem."</string>
@@ -518,6 +517,37 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Ikon for fingeraftryk"</string>
+ <string name="permlab_manageFace" msgid="2137540986007309781">"administrer hardware til ansigtsgenkendelse"</string>
+ <string name="permdesc_manageFace" msgid="8919637120670185330">"Tillader, at appen kan bruge metoder til at tilføje og slette ansigtsskabeloner."</string>
+ <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"brug hardware til ansigtsgenkendelse"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Tillader, at appen bruger ansigtsgenkendelseshardware til godkendelse"</string>
+ <string name="face_acquired_insufficient" msgid="5901287247766106330">"Ansigtet kunne ikke behandles. Prøv igen."</string>
+ <string name="face_acquired_too_bright" msgid="610606792381297174">"Ansigtet er for lyst. Prøv i svagere belysning."</string>
+ <string name="face_acquired_too_dark" msgid="7229162716976778371">"Ansigtet er for mørkt. Ryk tættere på en lyskilde."</string>
+ <string name="face_acquired_too_close" msgid="1980310037427755293">"Flyt sensoren længere væk fra ansigtet."</string>
+ <string name="face_acquired_too_far" msgid="4494571381828850007">"Ryk sensoren tættere på ansigtet."</string>
+ <string name="face_acquired_too_high" msgid="228411096134808372">"Flyt sensoren højere op."</string>
+ <string name="face_acquired_too_low" msgid="4539774649296349109">"Flyt sensoren længere ned."</string>
+ <string name="face_acquired_too_right" msgid="1650292067226118760">"Flyt sensoren til højre."</string>
+ <string name="face_acquired_too_left" msgid="2712489669456176505">"Flyt sensoren til venstre."</string>
+ <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Kig på sensoren."</string>
+ <string name="face_acquired_not_detected" msgid="5707782294589511391">"Der er ikke registreret noget ansigt."</string>
+ <string name="face_acquired_not_steady" msgid="3722829465011040042">"Hold ansigtet stille foran enheden."</string>
+ <string-array name="face_acquired_vendor">
+ </string-array>
+ <string name="face_error_hw_not_available" msgid="6255891785768984615">"Hardwaren til ansigtsregistrering er ikke klar."</string>
+ <string name="face_error_timeout" msgid="4014326147867150054">"Ansigtsgenkendelse fik timeout. Prøv igen."</string>
+ <string name="face_error_no_space" msgid="8224993703466381314">"Ansigtet kan ikke gemmes."</string>
+ <string name="face_error_canceled" msgid="283945501061931023">"Ansigtshandlingen blev annulleret."</string>
+ <string name="face_error_lockout" msgid="3407426963155388504">"Du har prøvet for mange gange. Prøv igen senere."</string>
+ <string name="face_error_lockout_permanent" msgid="8198354656746088890">"For mange forsøg – Ansigtsgenkendelse deaktiveret."</string>
+ <string name="face_error_unable_to_process" msgid="238761109287767270">"Prøv igen."</string>
+ <string name="face_error_not_enrolled" msgid="9166792142679691323">"Der er ikke registreret nogen ansigter."</string>
+ <string name="face_error_hw_not_present" msgid="4737289254517095671">"Denne enhed har ingen sensor til ansigtsgenkendelse"</string>
+ <string name="face_name_template" msgid="7004562145809595384">"Ansigt <xliff:g id="FACEID">%d</xliff:g>"</string>
+ <string-array name="face_error_vendor">
+ </string-array>
+ <string name="face_icon_content_description" msgid="4024817159806482191">"Ansigt"</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"læse indstillinger for synkronisering"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Tillader, at appen kan læse synkroniseringsindstillingerne for en konto. Denne tilladelse kan f.eks. fastslå, om appen Personer er synkroniseret med en konto."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"slå synkronisering til og fra"</string>
@@ -590,7 +620,7 @@
<string name="policylab_resetPassword" msgid="4934707632423915395">"Skifte skærmlås"</string>
<string name="policydesc_resetPassword" msgid="1278323891710619128">"Skifter skærmlås"</string>
<string name="policylab_forceLock" msgid="2274085384704248431">"Låse skærmen"</string>
- <string name="policydesc_forceLock" msgid="1141797588403827138">"Kontrollerer, hvordan og hvornår skærmen låses."</string>
+ <string name="policydesc_forceLock" msgid="1141797588403827138">"Administrerer, hvordan og hvornår skærmen låses."</string>
<string name="policylab_wipeData" msgid="3910545446758639713">"Slette alle data"</string>
<string name="policydesc_wipeData" product="tablet" msgid="4306184096067756876">"Slet din tablets data uden varsel ved at gendanne fabriksindstillingerne."</string>
<string name="policydesc_wipeData" product="tv" msgid="5816221315214527028">"Slet tv\'ets data uden varsel ved at nulstille til fabrinksindstillingerne."</string>
@@ -722,7 +752,7 @@
<string name="sipAddressTypeHome" msgid="6093598181069359295">"Hjem"</string>
<string name="sipAddressTypeWork" msgid="6920725730797099047">"Arbejde"</string>
<string name="sipAddressTypeOther" msgid="4408436162950119849">"Andet"</string>
- <string name="quick_contacts_not_available" msgid="746098007828579688">"Der blev ikke fundet nogen applikation, som kan vise denne kontaktperson."</string>
+ <string name="quick_contacts_not_available" msgid="746098007828579688">"Der blev ikke fundet nogen applikation, som kan vise denne kontakt."</string>
<string name="keyguard_password_enter_pin_code" msgid="3037685796058495017">"Angiv pinkode"</string>
<string name="keyguard_password_enter_puk_code" msgid="4800725266925845333">"Angiv PUK- og pinkode"</string>
<string name="keyguard_password_enter_puk_prompt" msgid="1341112146710087048">"PUK-kode"</string>
@@ -1178,6 +1208,9 @@
<skip />
<string name="wifi_no_internet" msgid="8938267198124654938">"Wi-Fi-netværket har ikke internetadgang"</string>
<string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Tryk for at se valgmuligheder"</string>
+ <string name="wifi_softap_config_change" msgid="8475911871165857607">"Ændringer af dine indstillinger for hotspot"</string>
+ <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"Dit hotspotbånd er ændret."</string>
+ <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"Denne enhed understøtter ikke din præference om kun 5 GHz. Denne enhed vil i stedet bruge 5 GHz-båndet, når det er muligt."</string>
<string name="network_switch_metered" msgid="4671730921726992671">"Der blev skiftet til <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
<string name="network_switch_metered_detail" msgid="775163331794506615">"Enheden benytter <xliff:g id="NEW_NETWORK">%1$s</xliff:g>, når der ikke er internetadgang via <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>. Der opkræves muligvis betaling."</string>
<string name="network_switch_metered_toast" msgid="5779283181685974304">"Der blev skiftet fra <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> til <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 5b1e45f..8dc0ba4 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -236,9 +236,6 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Flugmodus"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Flugmodus ist AN."</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Flugmodus ist AUS."</string>
- <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Energiesparmodus"</string>
- <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Energiesparmodus ist aus"</string>
- <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Energiesparmodus ist an"</string>
<string name="global_action_settings" msgid="1756531602592545966">"Einstellungen"</string>
<string name="global_action_assist" msgid="3892832961594295030">"Assistent"</string>
<string name="global_action_voice_assist" msgid="7751191495200504480">"Sprachassistent"</string>
@@ -276,6 +273,9 @@
<string name="permgrouplab_location" msgid="7275582855722310164">"Standort"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"auf den Standort deines Geräts zugreifen"</string>
<string name="permgrouprequest_location" msgid="3788275734953323491">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> erlauben, den Gerätestandort abzurufen?"</string>
+ <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"Die App hat nur Zugriff auf den Gerätestandort, wenn du sie verwendest."</string>
+ <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> immer erlauben, den Gerätestandort abzurufen?"</string>
+ <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"Die App hat immer Zugriff auf den Gerätestandort, auch wenn du sie gerade nicht verwendest."</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Kalender"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"auf deinen Kalender zugreifen"</string>
<string name="permgrouprequest_calendar" msgid="289900767793189421">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> Zugriff auf deinen Kalender erlauben?"</string>
@@ -402,8 +402,7 @@
<string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Diese App kann Kalendertermine auf deinem Smartphone hinzufügen, entfernen oder ändern. Diese App kann Nachrichten senden, die scheinbar von Kalendereigentümern stammen, oder Termine ohne Benachrichtigung der Eigentümer ändern."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"Auf zusätzliche Dienstanbieterbefehle für Standort zugreifen"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Ermöglicht der App, auf zusätzliche Standortanbieterbefehle zuzugreifen. Damit könnte die App die Funktionsweise von GPS oder anderen Standortquellen beeinträchtigen."</string>
- <!-- no translation found for permlab_accessFineLocation (6265109654698562427) -->
- <skip />
+ <string name="permlab_accessFineLocation" msgid="6265109654698562427">"Nur bei Ausführung im Vordergrund auf den genauen Standort zugreifen"</string>
<string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Diese App kann deinen genauen Standort nur dann ermitteln, wenn sie im Vordergrund ausgeführt wird. Die App kann diese Standortdienste nur verwenden, wenn sie auf deinem Smartphone aktiviert und verfügbar sind. Hierdurch kann sich der Akkuverbrauch erhöhen."</string>
<string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"Auf den ungefähren Standort zugreifen (netzwerkbasiert)"</string>
<string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Diese App kann deinen Standort mithilfe von Netzwerkquellen wie Mobilfunkmasten und WLAN ermitteln. Die App kann diese Standortdienste nur verwenden, wenn sie auf deinem Tablet aktiviert und verfügbar sind."</string>
@@ -518,6 +517,37 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Fingerabdruck-Symbol"</string>
+ <string name="permlab_manageFace" msgid="2137540986007309781">"Gesichtserkennungshardware verwalten"</string>
+ <string name="permdesc_manageFace" msgid="8919637120670185330">"Ermöglicht der App, Methoden zum Hinzufügen oder Entfernen von Gesichtsvorlagen anzuwenden."</string>
+ <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"Gesichtserkennungshardware verwenden"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Ermöglicht der App, für die Authentifizierung Gesichtserkennungshardware zu verwenden"</string>
+ <string name="face_acquired_insufficient" msgid="5901287247766106330">"Kann Gesicht nicht verarbeiten. Versuch es erneut."</string>
+ <string name="face_acquired_too_bright" msgid="610606792381297174">"Gesicht zu hell. Bei weniger Licht versuchen."</string>
+ <string name="face_acquired_too_dark" msgid="7229162716976778371">"Gesicht zu dunkel. Bei mehr Licht versuchen."</string>
+ <string name="face_acquired_too_close" msgid="1980310037427755293">"Sensor weiter weg vom Gesicht halten."</string>
+ <string name="face_acquired_too_far" msgid="4494571381828850007">"Sensor näher an das Gesicht halten."</string>
+ <string name="face_acquired_too_high" msgid="228411096134808372">"Sensor höher halten."</string>
+ <string name="face_acquired_too_low" msgid="4539774649296349109">"Sensor niedriger halten."</string>
+ <string name="face_acquired_too_right" msgid="1650292067226118760">"Sensor nach rechts bewegen."</string>
+ <string name="face_acquired_too_left" msgid="2712489669456176505">"Sensor nach links bewegen."</string>
+ <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Blick auf den Sensor richten."</string>
+ <string name="face_acquired_not_detected" msgid="5707782294589511391">"Kein Gesicht erkannt."</string>
+ <string name="face_acquired_not_steady" msgid="3722829465011040042">"Gerät vor das Gesicht halten und nicht bewegen."</string>
+ <string-array name="face_acquired_vendor">
+ </string-array>
+ <string name="face_error_hw_not_available" msgid="6255891785768984615">"Hardware zur Gesichtserkennung nicht verfügbar."</string>
+ <string name="face_error_timeout" msgid="4014326147867150054">"Zeitüberschreitung für Gesicht. Versuch es erneut."</string>
+ <string name="face_error_no_space" msgid="8224993703466381314">"Gesicht kann nicht gespeichert werden."</string>
+ <string name="face_error_canceled" msgid="283945501061931023">"Gesichtserkennung abgebrochen."</string>
+ <string name="face_error_lockout" msgid="3407426963155388504">"Zu viele Versuche. Versuch es später noch einmal."</string>
+ <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Zu viele Versuche. Gesichtserkennung deaktiviert."</string>
+ <string name="face_error_unable_to_process" msgid="238761109287767270">"Versuch es noch einmal."</string>
+ <string name="face_error_not_enrolled" msgid="9166792142679691323">"Kein Gesicht erfasst."</string>
+ <string name="face_error_hw_not_present" msgid="4737289254517095671">"Dieses Gerät hat keinen Gesichtserkennungssensor"</string>
+ <string name="face_name_template" msgid="7004562145809595384">"Gesicht <xliff:g id="FACEID">%d</xliff:g>"</string>
+ <string-array name="face_error_vendor">
+ </string-array>
+ <string name="face_icon_content_description" msgid="4024817159806482191">"Gesichtssymbol"</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"Synchronisierungseinstellungen lesen"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Ermöglicht der App, die Synchronisierungseinstellungen eines Kontos zu lesen. Beispielsweise kann damit festgestellt werden, ob Kontakte mit einem Konto synchronisiert werden."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"Synchronisierung aktivieren oder deaktivieren"</string>
@@ -1178,6 +1208,9 @@
<skip />
<string name="wifi_no_internet" msgid="8938267198124654938">"WLAN hat keinen Internetzugriff"</string>
<string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Für Optionen tippen"</string>
+ <string name="wifi_softap_config_change" msgid="8475911871165857607">"Änderungen an deinen Hotspot-Einstellungen"</string>
+ <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"Dein Hotspot-Band hat sich geändert."</string>
+ <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"Dieses Gerät unterstützt die ausschließliche Nutzung von 5 GHz nicht. Es greift aber immer auf das 5-GHz-Band zurück, wenn dieses verfügbar ist."</string>
<string name="network_switch_metered" msgid="4671730921726992671">"Zu <xliff:g id="NETWORK_TYPE">%1$s</xliff:g> gewechselt"</string>
<string name="network_switch_metered_detail" msgid="775163331794506615">"Auf dem Gerät werden <xliff:g id="NEW_NETWORK">%1$s</xliff:g> genutzt, wenn über <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> kein Internet verfügbar ist. Eventuell fallen Gebühren an."</string>
<string name="network_switch_metered_toast" msgid="5779283181685974304">"Von \"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g>\" zu \"<xliff:g id="NEW_NETWORK">%2$s</xliff:g>\" gewechselt"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 43f7ec3..c6acc9e 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -236,9 +236,6 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Λειτουργία πτήσης"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Η λειτουργία πτήσης είναι ενεργοποιημένη."</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Λειτ. πτήσης είναι ανενεργή"</string>
- <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Εξοικονόμηση μπαταρίας"</string>
- <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Η εξοικονόμηση μπαταρίας είναι ΑΠΕΝΕΡΓΟΠΟΙΗΜΕΝΗ"</string>
- <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Η Εξοικονόμηση μπαταρίας είναι ΕΝΕΡΓΗ"</string>
<string name="global_action_settings" msgid="1756531602592545966">"Ρυθμίσεις"</string>
<string name="global_action_assist" msgid="3892832961594295030">"Βοήθεια"</string>
<string name="global_action_voice_assist" msgid="7751191495200504480">"Φων.υποβοηθ."</string>
@@ -276,6 +273,9 @@
<string name="permgrouplab_location" msgid="7275582855722310164">"Τοποθεσία"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"έχει πρόσβαση στην τοποθεσία της συσκευής"</string>
<string name="permgrouprequest_location" msgid="3788275734953323491">"Να επιτρέπεται στην εφαρμογή <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> να έχει πρόσβαση στην τοποθεσία αυτής της συσκευής;"</string>
+ <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"Η εφαρμογή θα έχει πρόσβαση στην τοποθεσία μόνο κατά τη διάρκεια χρήσης της εφαρμογής."</string>
+ <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"Να επιτρέπεται πάντα στην εφαρμογή <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> να έχει πρόσβαση στην τοποθεσία αυτής της συσκευής;"</string>
+ <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"Η εφαρμογή θα έχει πάντα πρόσβαση στην τοποθεσία, ακόμα και όταν δεν χρησιμοποιείτε την εφαρμογή."</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Ημερολόγιο"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"έχει πρόσβαση στο ημερολόγιό σας"</string>
<string name="permgrouprequest_calendar" msgid="289900767793189421">"Να επιτρέπεται στην εφαρμογή <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> να έχει πρόσβαση στο ημερολόγιό σας;"</string>
@@ -402,8 +402,7 @@
<string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Αυτή η εφαρμογή μπορεί να προσθέσει, να καταργήσει ή να αλλάξει συμβάντα ημερολογίου στο τηλέφωνό σας. Αυτή η εφαρμογή μπορεί να στέλνει μηνύματα τα οποία μπορεί να φαίνεται ότι προέρχονται από κατόχους ημερολογίου ή να αλλάζει συμβάντα χωρίς να ειδοποιεί τους κατόχους."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"έχει πρόσβαση σε επιπλέον εντολές παρόχου τοποθεσίας"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Επιτρέπει στην εφαρμογή την πρόσβαση σε επιπλέον εντολές παρόχου τοποθεσίας. Αυτό μπορεί να δώσει τη δυνατότητα στην εφαρμογή να παρέμβει στη λειτουργία του GPS ή άλλων πηγών τοποθεσίας."</string>
- <!-- no translation found for permlab_accessFineLocation (6265109654698562427) -->
- <skip />
+ <string name="permlab_accessFineLocation" msgid="6265109654698562427">"πρόσβαση στην ακριβή τοποθεσία μόνο στο προσκήνιο"</string>
<string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Αυτή η εφαρμογή μπορεί να ανιχνεύσει την ακριβή τοποθεσία σας όταν βρίσκεται στο προσκήνιο. Αυτές οι υπηρεσίες τοποθεσίας θα πρέπει να είναι ενεργοποιημένες και διαθέσιμες στο τηλέφωνό σας, προκειμένου να μπορεί να τις χρησιμοποιήσει η εφαρμογή. Με την ενεργοποίηση αυτής της ρύθμισης, μπορεί να αυξηθεί η κατανάλωση μπαταρίας."</string>
<string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"έχει πρόσβαση στην τοποθεσία κατά προσέγγιση (με βάση το δίκτυο)"</string>
<string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Αυτή η εφαρμογή μπορεί να ανιχνεύσει την τοποθεσία σας βάσει πηγών δικτύου, όπως κεραίες κινητής τηλεφωνίας και δίκτυα Wi-Fi. Αυτές οι υπηρεσίες τοποθεσίας θα πρέπει να είναι ενεργοποιημένες και διαθέσιμες στο tablet που χρησιμοποιείτε, προκειμένου να μπορεί να τις χρησιμοποιήσει η εφαρμογή."</string>
@@ -518,6 +517,37 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Εικονίδιο δακτυλικών αποτυπωμάτων"</string>
+ <string name="permlab_manageFace" msgid="2137540986007309781">"διαχείριση υλικολογισμ. ελέγχου ταυτότ. προσώπου"</string>
+ <string name="permdesc_manageFace" msgid="8919637120670185330">"Επιτρέπει στην εφαρμογή να επικαλείται μεθόδους προσθήκης/διαγραφής προτύπων για χρήση."</string>
+ <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"χρήση υλικολογισμικού ελέγχου ταυτότητας προσώπου"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Επιτρέπει στην εφαρμογή να χρησιμοποιεί υλικολογισμικό για έλεγχο ταυτότητας"</string>
+ <string name="face_acquired_insufficient" msgid="5901287247766106330">"Αδυναμία επεξεργασίας προσώπου. Δοκιμάστε ξανά."</string>
+ <string name="face_acquired_too_bright" msgid="610606792381297174">"Υψηλή φωτεινότητα. Δοκιμάστε χαμηλότερο φωτισμό."</string>
+ <string name="face_acquired_too_dark" msgid="7229162716976778371">"Πολύ σκοτεινό πρόσωπο. Ξεσκεπάστε την πηγή φωτός."</string>
+ <string name="face_acquired_too_close" msgid="1980310037427755293">"Μετακινήστε τον αισθητήρα πιο μακριά."</string>
+ <string name="face_acquired_too_far" msgid="4494571381828850007">"Τοποθετήστε τον αισθητήρα πιο κοντά στο πρόσωπο."</string>
+ <string name="face_acquired_too_high" msgid="228411096134808372">"Μετακινήστε τον αισθητήρα ψηλότερα."</string>
+ <string name="face_acquired_too_low" msgid="4539774649296349109">"Μετακινήστε τον αισθητήρα χαμηλότερα."</string>
+ <string name="face_acquired_too_right" msgid="1650292067226118760">"Μετακινήστε τον αισθητήρα προς τα δεξιά."</string>
+ <string name="face_acquired_too_left" msgid="2712489669456176505">"Μετακινήστε τον αισθητήρα προς τα αριστερά."</string>
+ <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Κοιτάξτε στον αισθητήρα."</string>
+ <string name="face_acquired_not_detected" msgid="5707782294589511391">"Δεν εντοπίστηκε κάποιο πρόσωπο."</string>
+ <string name="face_acquired_not_steady" msgid="3722829465011040042">"Διατηρήστε το πρόσωπο σταθερό μπροστά στη συσκευή."</string>
+ <string-array name="face_acquired_vendor">
+ </string-array>
+ <string name="face_error_hw_not_available" msgid="6255891785768984615">"Το υλικολογισμικό προσώπου δεν είναι διαθέσιμο."</string>
+ <string name="face_error_timeout" msgid="4014326147867150054">"Λήξη χρονικού ορίου προσώπου. Δοκιμάστε ξανά."</string>
+ <string name="face_error_no_space" msgid="8224993703466381314">"Δεν είναι δυνατή η αποθήκευση του προσώπου."</string>
+ <string name="face_error_canceled" msgid="283945501061931023">"Η ενέργεια προσώπου ακυρώθηκε."</string>
+ <string name="face_error_lockout" msgid="3407426963155388504">"Πάρα πολλές προσπάθειες. Δοκιμάστε ξανά αργότερα."</string>
+ <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Πολλές προσπάθειες. Αποτυχία ελέγ. ταυτ. προσώπου."</string>
+ <string name="face_error_unable_to_process" msgid="238761109287767270">"Δοκιμάστε ξανά."</string>
+ <string name="face_error_not_enrolled" msgid="9166792142679691323">"Δεν έχει καταχωριστεί κάποιο πρόσωπο."</string>
+ <string name="face_error_hw_not_present" msgid="4737289254517095671">"Η συσκευή δεν διαθέτει αισθητήρα ελέγχου ταυτότητας προσώπου"</string>
+ <string name="face_name_template" msgid="7004562145809595384">"Πρόσωπο <xliff:g id="FACEID">%d</xliff:g>"</string>
+ <string-array name="face_error_vendor">
+ </string-array>
+ <string name="face_icon_content_description" msgid="4024817159806482191">"Εικ. προσ."</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"διαβάζει τις ρυθμίσεις συγχρονισμού"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Επιτρέπει στην εφαρμογή την ανάγνωση των ρυθμίσεων συγχρονισμού για έναν λογαριασμό. Για παράδειγμα, αυτό μπορεί να καθορίσει εάν η εφαρμογή \"Άτομα\" είναι συγχρονισμένη με έναν λογαριασμό."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"ενεργοποιεί/απενεργοποιεί τον συγχρονισμό"</string>
@@ -1178,6 +1208,9 @@
<skip />
<string name="wifi_no_internet" msgid="8938267198124654938">"Το Wi-Fi δεν έχει πρόσβαση στο διαδίκτυο"</string>
<string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Πατήστε για να δείτε τις επιλογές"</string>
+ <string name="wifi_softap_config_change" msgid="8475911871165857607">"Αλλαγές στις ρυθμίσεις σημείου πρόσβασης Wi-Fi"</string>
+ <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"Το εύρος σημείου πρόσβασης Wi-Fi άλλαξε."</string>
+ <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"Αυτή η συσκευή δεν υποστηρίζει την προτίμησή σας για τη ζώνη 5 GHz μόνο. Αντ\' αυτού, αυτή η συσκευή θα χρησιμοποιεί τη ζώνη 5 GHz όταν είναι διαθέσιμη."</string>
<string name="network_switch_metered" msgid="4671730921726992671">"Μετάβαση σε δίκτυο <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
<string name="network_switch_metered_detail" msgid="775163331794506615">"Η συσκευή χρησιμοποιεί το δίκτυο <xliff:g id="NEW_NETWORK">%1$s</xliff:g> όταν το δίκτυο <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> δεν έχει πρόσβαση στο διαδίκτυο. Μπορεί να ισχύουν χρεώσεις."</string>
<string name="network_switch_metered_toast" msgid="5779283181685974304">"Μετάβαση από το δίκτυο <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> στο δίκτυο <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index 0c0fd74..f70ba3b 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -236,9 +236,6 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Aeroplane mode"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Aeroplane mode is ON"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Aeroplane mode is OFF"</string>
- <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Battery saver"</string>
- <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Battery saver is OFF"</string>
- <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Battery saver is ON"</string>
<string name="global_action_settings" msgid="1756531602592545966">"Settings"</string>
<string name="global_action_assist" msgid="3892832961594295030">"Assist"</string>
<string name="global_action_voice_assist" msgid="7751191495200504480">"Voice Assist"</string>
@@ -276,6 +273,9 @@
<string name="permgrouplab_location" msgid="7275582855722310164">"Location"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"access this device\'s location"</string>
<string name="permgrouprequest_location" msgid="3788275734953323491">"Allow <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> to access this device\'s location?"</string>
+ <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"The app will only have access to the location while you’re using the app."</string>
+ <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"Always allow <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> to access this device’s location?"</string>
+ <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"The app will always have access to the location, even when you’re not using the app."</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Calendar"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"access your calendar"</string>
<string name="permgrouprequest_calendar" msgid="289900767793189421">"Allow <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> to access your calendar?"</string>
@@ -402,8 +402,7 @@
<string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"This app can add, remove or change calendar events on your phone. This app can send messages that may appear to come from calendar owners or change events without notifying their owners."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"access extra location provider commands"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Allows the app to access extra location provider commands. This may allow the app to interfere with the operation of the GPS or other location sources."</string>
- <!-- no translation found for permlab_accessFineLocation (6265109654698562427) -->
- <skip />
+ <string name="permlab_accessFineLocation" msgid="6265109654698562427">"access precise location only in the foreground"</string>
<string name="permdesc_accessFineLocation" msgid="3520508381065331098">"This app can get your exact location only when it is in the foreground. These location services must be turned on and available on your phone for the app to be able to use them. This may increase battery consumption."</string>
<string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"access approximate location (network-based)"</string>
<string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"This app can pick up your location based on network sources such as phone masts and Wi-Fi networks. These location services must be turned on and available on your tablet for the app to be able to use them."</string>
@@ -518,6 +517,37 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Fingerprint icon"</string>
+ <string name="permlab_manageFace" msgid="2137540986007309781">"manage face authentication hardware"</string>
+ <string name="permdesc_manageFace" msgid="8919637120670185330">"Allows the app to invoke methods to add and delete facial templates for use."</string>
+ <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"use face authentication hardware"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Allows the app to use face authentication hardware for authentication"</string>
+ <string name="face_acquired_insufficient" msgid="5901287247766106330">"Couldn’t process face. Please try again."</string>
+ <string name="face_acquired_too_bright" msgid="610606792381297174">"Face is too bright. Please try in lower light."</string>
+ <string name="face_acquired_too_dark" msgid="7229162716976778371">"Face is too dark. Please uncover light source."</string>
+ <string name="face_acquired_too_close" msgid="1980310037427755293">"Please move sensor farther away from face."</string>
+ <string name="face_acquired_too_far" msgid="4494571381828850007">"Please bring sensor closer to face."</string>
+ <string name="face_acquired_too_high" msgid="228411096134808372">"Please move sensor higher."</string>
+ <string name="face_acquired_too_low" msgid="4539774649296349109">"Please move sensor lower."</string>
+ <string name="face_acquired_too_right" msgid="1650292067226118760">"Please move sensor to the right."</string>
+ <string name="face_acquired_too_left" msgid="2712489669456176505">"Please move sensor to the left."</string>
+ <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Please look at the sensor."</string>
+ <string name="face_acquired_not_detected" msgid="5707782294589511391">"No face detected."</string>
+ <string name="face_acquired_not_steady" msgid="3722829465011040042">"Keep face steady in front of device."</string>
+ <string-array name="face_acquired_vendor">
+ </string-array>
+ <string name="face_error_hw_not_available" msgid="6255891785768984615">"Face hardware not available."</string>
+ <string name="face_error_timeout" msgid="4014326147867150054">"Face time out reached. Try again."</string>
+ <string name="face_error_no_space" msgid="8224993703466381314">"Face can’t be stored."</string>
+ <string name="face_error_canceled" msgid="283945501061931023">"Face operation cancelled."</string>
+ <string name="face_error_lockout" msgid="3407426963155388504">"Too many attempts. Try again later."</string>
+ <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Too many attempts. Facial authentication disabled."</string>
+ <string name="face_error_unable_to_process" msgid="238761109287767270">"Try again."</string>
+ <string name="face_error_not_enrolled" msgid="9166792142679691323">"No face enrolled."</string>
+ <string name="face_error_hw_not_present" msgid="4737289254517095671">"This device does not have a face authentication sensor"</string>
+ <string name="face_name_template" msgid="7004562145809595384">"Face <xliff:g id="FACEID">%d</xliff:g>"</string>
+ <string-array name="face_error_vendor">
+ </string-array>
+ <string name="face_icon_content_description" msgid="4024817159806482191">"Face icon"</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"read sync settings"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Allows the app to read the sync settings for an account. For example, this can determine whether the People app is synced with an account."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"toggle sync on and off"</string>
@@ -1178,6 +1208,9 @@
<skip />
<string name="wifi_no_internet" msgid="8938267198124654938">"Wi-Fi has no Internet access"</string>
<string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Tap for options"</string>
+ <string name="wifi_softap_config_change" msgid="8475911871165857607">"Changes to your hotspot settings"</string>
+ <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"Your hotspot band has changed."</string>
+ <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"This device doesn’t support your preference for 5 GHz only. Instead, this device will use the 5 GHz band when available."</string>
<string name="network_switch_metered" msgid="4671730921726992671">"Switched to <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
<string name="network_switch_metered_detail" msgid="775163331794506615">"Device uses <xliff:g id="NEW_NETWORK">%1$s</xliff:g> when <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> has no Internet access. Charges may apply."</string>
<string name="network_switch_metered_toast" msgid="5779283181685974304">"Switched from <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> to <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index c5f059e..1ab78f1 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -236,9 +236,6 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Airplane mode"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Airplane mode is ON"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Airplane mode is OFF"</string>
- <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Battery saver"</string>
- <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Battery saver is OFF"</string>
- <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Battery saver is ON"</string>
<string name="global_action_settings" msgid="1756531602592545966">"Settings"</string>
<string name="global_action_assist" msgid="3892832961594295030">"Assist"</string>
<string name="global_action_voice_assist" msgid="7751191495200504480">"Voice Assist"</string>
@@ -276,6 +273,9 @@
<string name="permgrouplab_location" msgid="7275582855722310164">"Location"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"access this device\'s location"</string>
<string name="permgrouprequest_location" msgid="3788275734953323491">"Allow <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> to access this device\'s location?"</string>
+ <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"The app will only have access to the location while you’re using the app."</string>
+ <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"Always allow <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> to access this device’s location?"</string>
+ <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"The app will always have access to the location, even when you’re not using the app."</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Calendar"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"access your calendar"</string>
<string name="permgrouprequest_calendar" msgid="289900767793189421">"Allow <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> to access your calendar?"</string>
@@ -402,8 +402,7 @@
<string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"This app can add, remove or change calendar events on your phone. This app can send messages that may appear to come from calendar owners or change events without notifying their owners."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"access extra location provider commands"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Allows the app to access extra location provider commands. This may allow the app to interfere with the operation of the GPS or other location sources."</string>
- <!-- no translation found for permlab_accessFineLocation (6265109654698562427) -->
- <skip />
+ <string name="permlab_accessFineLocation" msgid="6265109654698562427">"access precise location only in the foreground"</string>
<string name="permdesc_accessFineLocation" msgid="3520508381065331098">"This app can get your exact location only when it is in the foreground. These location services must be turned on and available on your phone for the app to be able to use them. This may increase battery consumption."</string>
<string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"access approximate location (network-based)"</string>
<string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"This app can pick up your location based on network sources such as phone masts and Wi-Fi networks. These location services must be turned on and available on your tablet for the app to be able to use them."</string>
@@ -518,6 +517,37 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Fingerprint icon"</string>
+ <string name="permlab_manageFace" msgid="2137540986007309781">"manage face authentication hardware"</string>
+ <string name="permdesc_manageFace" msgid="8919637120670185330">"Allows the app to invoke methods to add and delete facial templates for use."</string>
+ <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"use face authentication hardware"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Allows the app to use face authentication hardware for authentication"</string>
+ <string name="face_acquired_insufficient" msgid="5901287247766106330">"Couldn’t process face. Please try again."</string>
+ <string name="face_acquired_too_bright" msgid="610606792381297174">"Face is too bright. Please try in lower light."</string>
+ <string name="face_acquired_too_dark" msgid="7229162716976778371">"Face is too dark. Please uncover light source."</string>
+ <string name="face_acquired_too_close" msgid="1980310037427755293">"Please move sensor farther away from face."</string>
+ <string name="face_acquired_too_far" msgid="4494571381828850007">"Please bring sensor closer to face."</string>
+ <string name="face_acquired_too_high" msgid="228411096134808372">"Please move sensor higher."</string>
+ <string name="face_acquired_too_low" msgid="4539774649296349109">"Please move sensor lower."</string>
+ <string name="face_acquired_too_right" msgid="1650292067226118760">"Please move sensor to the right."</string>
+ <string name="face_acquired_too_left" msgid="2712489669456176505">"Please move sensor to the left."</string>
+ <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Please look at the sensor."</string>
+ <string name="face_acquired_not_detected" msgid="5707782294589511391">"No face detected."</string>
+ <string name="face_acquired_not_steady" msgid="3722829465011040042">"Keep face steady in front of device."</string>
+ <string-array name="face_acquired_vendor">
+ </string-array>
+ <string name="face_error_hw_not_available" msgid="6255891785768984615">"Face hardware not available."</string>
+ <string name="face_error_timeout" msgid="4014326147867150054">"Face time out reached. Try again."</string>
+ <string name="face_error_no_space" msgid="8224993703466381314">"Face can’t be stored."</string>
+ <string name="face_error_canceled" msgid="283945501061931023">"Face operation cancelled."</string>
+ <string name="face_error_lockout" msgid="3407426963155388504">"Too many attempts. Try again later."</string>
+ <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Too many attempts. Facial authentication disabled."</string>
+ <string name="face_error_unable_to_process" msgid="238761109287767270">"Try again."</string>
+ <string name="face_error_not_enrolled" msgid="9166792142679691323">"No face enrolled."</string>
+ <string name="face_error_hw_not_present" msgid="4737289254517095671">"This device does not have a face authentication sensor"</string>
+ <string name="face_name_template" msgid="7004562145809595384">"Face <xliff:g id="FACEID">%d</xliff:g>"</string>
+ <string-array name="face_error_vendor">
+ </string-array>
+ <string name="face_icon_content_description" msgid="4024817159806482191">"Face icon"</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"read sync settings"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Allows the app to read the sync settings for an account. For example, this can determine whether the People app is synced with an account."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"toggle sync on and off"</string>
@@ -1178,6 +1208,9 @@
<skip />
<string name="wifi_no_internet" msgid="8938267198124654938">"Wi-Fi has no Internet access"</string>
<string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Tap for options"</string>
+ <string name="wifi_softap_config_change" msgid="8475911871165857607">"Changes to your hotspot settings"</string>
+ <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"Your hotspot band has changed."</string>
+ <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"This device doesn’t support your preference for 5 GHz only. Instead, this device will use the 5 GHz band when available."</string>
<string name="network_switch_metered" msgid="4671730921726992671">"Switched to <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
<string name="network_switch_metered_detail" msgid="775163331794506615">"Device uses <xliff:g id="NEW_NETWORK">%1$s</xliff:g> when <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> has no Internet access. Charges may apply."</string>
<string name="network_switch_metered_toast" msgid="5779283181685974304">"Switched from <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> to <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index 0c0fd74..f70ba3b 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -236,9 +236,6 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Aeroplane mode"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Aeroplane mode is ON"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Aeroplane mode is OFF"</string>
- <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Battery saver"</string>
- <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Battery saver is OFF"</string>
- <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Battery saver is ON"</string>
<string name="global_action_settings" msgid="1756531602592545966">"Settings"</string>
<string name="global_action_assist" msgid="3892832961594295030">"Assist"</string>
<string name="global_action_voice_assist" msgid="7751191495200504480">"Voice Assist"</string>
@@ -276,6 +273,9 @@
<string name="permgrouplab_location" msgid="7275582855722310164">"Location"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"access this device\'s location"</string>
<string name="permgrouprequest_location" msgid="3788275734953323491">"Allow <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> to access this device\'s location?"</string>
+ <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"The app will only have access to the location while you’re using the app."</string>
+ <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"Always allow <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> to access this device’s location?"</string>
+ <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"The app will always have access to the location, even when you’re not using the app."</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Calendar"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"access your calendar"</string>
<string name="permgrouprequest_calendar" msgid="289900767793189421">"Allow <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> to access your calendar?"</string>
@@ -402,8 +402,7 @@
<string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"This app can add, remove or change calendar events on your phone. This app can send messages that may appear to come from calendar owners or change events without notifying their owners."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"access extra location provider commands"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Allows the app to access extra location provider commands. This may allow the app to interfere with the operation of the GPS or other location sources."</string>
- <!-- no translation found for permlab_accessFineLocation (6265109654698562427) -->
- <skip />
+ <string name="permlab_accessFineLocation" msgid="6265109654698562427">"access precise location only in the foreground"</string>
<string name="permdesc_accessFineLocation" msgid="3520508381065331098">"This app can get your exact location only when it is in the foreground. These location services must be turned on and available on your phone for the app to be able to use them. This may increase battery consumption."</string>
<string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"access approximate location (network-based)"</string>
<string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"This app can pick up your location based on network sources such as phone masts and Wi-Fi networks. These location services must be turned on and available on your tablet for the app to be able to use them."</string>
@@ -518,6 +517,37 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Fingerprint icon"</string>
+ <string name="permlab_manageFace" msgid="2137540986007309781">"manage face authentication hardware"</string>
+ <string name="permdesc_manageFace" msgid="8919637120670185330">"Allows the app to invoke methods to add and delete facial templates for use."</string>
+ <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"use face authentication hardware"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Allows the app to use face authentication hardware for authentication"</string>
+ <string name="face_acquired_insufficient" msgid="5901287247766106330">"Couldn’t process face. Please try again."</string>
+ <string name="face_acquired_too_bright" msgid="610606792381297174">"Face is too bright. Please try in lower light."</string>
+ <string name="face_acquired_too_dark" msgid="7229162716976778371">"Face is too dark. Please uncover light source."</string>
+ <string name="face_acquired_too_close" msgid="1980310037427755293">"Please move sensor farther away from face."</string>
+ <string name="face_acquired_too_far" msgid="4494571381828850007">"Please bring sensor closer to face."</string>
+ <string name="face_acquired_too_high" msgid="228411096134808372">"Please move sensor higher."</string>
+ <string name="face_acquired_too_low" msgid="4539774649296349109">"Please move sensor lower."</string>
+ <string name="face_acquired_too_right" msgid="1650292067226118760">"Please move sensor to the right."</string>
+ <string name="face_acquired_too_left" msgid="2712489669456176505">"Please move sensor to the left."</string>
+ <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Please look at the sensor."</string>
+ <string name="face_acquired_not_detected" msgid="5707782294589511391">"No face detected."</string>
+ <string name="face_acquired_not_steady" msgid="3722829465011040042">"Keep face steady in front of device."</string>
+ <string-array name="face_acquired_vendor">
+ </string-array>
+ <string name="face_error_hw_not_available" msgid="6255891785768984615">"Face hardware not available."</string>
+ <string name="face_error_timeout" msgid="4014326147867150054">"Face time out reached. Try again."</string>
+ <string name="face_error_no_space" msgid="8224993703466381314">"Face can’t be stored."</string>
+ <string name="face_error_canceled" msgid="283945501061931023">"Face operation cancelled."</string>
+ <string name="face_error_lockout" msgid="3407426963155388504">"Too many attempts. Try again later."</string>
+ <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Too many attempts. Facial authentication disabled."</string>
+ <string name="face_error_unable_to_process" msgid="238761109287767270">"Try again."</string>
+ <string name="face_error_not_enrolled" msgid="9166792142679691323">"No face enrolled."</string>
+ <string name="face_error_hw_not_present" msgid="4737289254517095671">"This device does not have a face authentication sensor"</string>
+ <string name="face_name_template" msgid="7004562145809595384">"Face <xliff:g id="FACEID">%d</xliff:g>"</string>
+ <string-array name="face_error_vendor">
+ </string-array>
+ <string name="face_icon_content_description" msgid="4024817159806482191">"Face icon"</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"read sync settings"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Allows the app to read the sync settings for an account. For example, this can determine whether the People app is synced with an account."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"toggle sync on and off"</string>
@@ -1178,6 +1208,9 @@
<skip />
<string name="wifi_no_internet" msgid="8938267198124654938">"Wi-Fi has no Internet access"</string>
<string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Tap for options"</string>
+ <string name="wifi_softap_config_change" msgid="8475911871165857607">"Changes to your hotspot settings"</string>
+ <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"Your hotspot band has changed."</string>
+ <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"This device doesn’t support your preference for 5 GHz only. Instead, this device will use the 5 GHz band when available."</string>
<string name="network_switch_metered" msgid="4671730921726992671">"Switched to <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
<string name="network_switch_metered_detail" msgid="775163331794506615">"Device uses <xliff:g id="NEW_NETWORK">%1$s</xliff:g> when <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> has no Internet access. Charges may apply."</string>
<string name="network_switch_metered_toast" msgid="5779283181685974304">"Switched from <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> to <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index 0c0fd74..f70ba3b 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -236,9 +236,6 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Aeroplane mode"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Aeroplane mode is ON"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Aeroplane mode is OFF"</string>
- <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Battery saver"</string>
- <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Battery saver is OFF"</string>
- <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Battery saver is ON"</string>
<string name="global_action_settings" msgid="1756531602592545966">"Settings"</string>
<string name="global_action_assist" msgid="3892832961594295030">"Assist"</string>
<string name="global_action_voice_assist" msgid="7751191495200504480">"Voice Assist"</string>
@@ -276,6 +273,9 @@
<string name="permgrouplab_location" msgid="7275582855722310164">"Location"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"access this device\'s location"</string>
<string name="permgrouprequest_location" msgid="3788275734953323491">"Allow <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> to access this device\'s location?"</string>
+ <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"The app will only have access to the location while you’re using the app."</string>
+ <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"Always allow <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> to access this device’s location?"</string>
+ <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"The app will always have access to the location, even when you’re not using the app."</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Calendar"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"access your calendar"</string>
<string name="permgrouprequest_calendar" msgid="289900767793189421">"Allow <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> to access your calendar?"</string>
@@ -402,8 +402,7 @@
<string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"This app can add, remove or change calendar events on your phone. This app can send messages that may appear to come from calendar owners or change events without notifying their owners."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"access extra location provider commands"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Allows the app to access extra location provider commands. This may allow the app to interfere with the operation of the GPS or other location sources."</string>
- <!-- no translation found for permlab_accessFineLocation (6265109654698562427) -->
- <skip />
+ <string name="permlab_accessFineLocation" msgid="6265109654698562427">"access precise location only in the foreground"</string>
<string name="permdesc_accessFineLocation" msgid="3520508381065331098">"This app can get your exact location only when it is in the foreground. These location services must be turned on and available on your phone for the app to be able to use them. This may increase battery consumption."</string>
<string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"access approximate location (network-based)"</string>
<string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"This app can pick up your location based on network sources such as phone masts and Wi-Fi networks. These location services must be turned on and available on your tablet for the app to be able to use them."</string>
@@ -518,6 +517,37 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Fingerprint icon"</string>
+ <string name="permlab_manageFace" msgid="2137540986007309781">"manage face authentication hardware"</string>
+ <string name="permdesc_manageFace" msgid="8919637120670185330">"Allows the app to invoke methods to add and delete facial templates for use."</string>
+ <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"use face authentication hardware"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Allows the app to use face authentication hardware for authentication"</string>
+ <string name="face_acquired_insufficient" msgid="5901287247766106330">"Couldn’t process face. Please try again."</string>
+ <string name="face_acquired_too_bright" msgid="610606792381297174">"Face is too bright. Please try in lower light."</string>
+ <string name="face_acquired_too_dark" msgid="7229162716976778371">"Face is too dark. Please uncover light source."</string>
+ <string name="face_acquired_too_close" msgid="1980310037427755293">"Please move sensor farther away from face."</string>
+ <string name="face_acquired_too_far" msgid="4494571381828850007">"Please bring sensor closer to face."</string>
+ <string name="face_acquired_too_high" msgid="228411096134808372">"Please move sensor higher."</string>
+ <string name="face_acquired_too_low" msgid="4539774649296349109">"Please move sensor lower."</string>
+ <string name="face_acquired_too_right" msgid="1650292067226118760">"Please move sensor to the right."</string>
+ <string name="face_acquired_too_left" msgid="2712489669456176505">"Please move sensor to the left."</string>
+ <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Please look at the sensor."</string>
+ <string name="face_acquired_not_detected" msgid="5707782294589511391">"No face detected."</string>
+ <string name="face_acquired_not_steady" msgid="3722829465011040042">"Keep face steady in front of device."</string>
+ <string-array name="face_acquired_vendor">
+ </string-array>
+ <string name="face_error_hw_not_available" msgid="6255891785768984615">"Face hardware not available."</string>
+ <string name="face_error_timeout" msgid="4014326147867150054">"Face time out reached. Try again."</string>
+ <string name="face_error_no_space" msgid="8224993703466381314">"Face can’t be stored."</string>
+ <string name="face_error_canceled" msgid="283945501061931023">"Face operation cancelled."</string>
+ <string name="face_error_lockout" msgid="3407426963155388504">"Too many attempts. Try again later."</string>
+ <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Too many attempts. Facial authentication disabled."</string>
+ <string name="face_error_unable_to_process" msgid="238761109287767270">"Try again."</string>
+ <string name="face_error_not_enrolled" msgid="9166792142679691323">"No face enrolled."</string>
+ <string name="face_error_hw_not_present" msgid="4737289254517095671">"This device does not have a face authentication sensor"</string>
+ <string name="face_name_template" msgid="7004562145809595384">"Face <xliff:g id="FACEID">%d</xliff:g>"</string>
+ <string-array name="face_error_vendor">
+ </string-array>
+ <string name="face_icon_content_description" msgid="4024817159806482191">"Face icon"</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"read sync settings"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Allows the app to read the sync settings for an account. For example, this can determine whether the People app is synced with an account."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"toggle sync on and off"</string>
@@ -1178,6 +1208,9 @@
<skip />
<string name="wifi_no_internet" msgid="8938267198124654938">"Wi-Fi has no Internet access"</string>
<string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Tap for options"</string>
+ <string name="wifi_softap_config_change" msgid="8475911871165857607">"Changes to your hotspot settings"</string>
+ <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"Your hotspot band has changed."</string>
+ <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"This device doesn’t support your preference for 5 GHz only. Instead, this device will use the 5 GHz band when available."</string>
<string name="network_switch_metered" msgid="4671730921726992671">"Switched to <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
<string name="network_switch_metered_detail" msgid="775163331794506615">"Device uses <xliff:g id="NEW_NETWORK">%1$s</xliff:g> when <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> has no Internet access. Charges may apply."</string>
<string name="network_switch_metered_toast" msgid="5779283181685974304">"Switched from <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> to <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml
index 3968012..a4c8615 100644
--- a/core/res/res/values-en-rXC/strings.xml
+++ b/core/res/res/values-en-rXC/strings.xml
@@ -236,9 +236,6 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Airplane mode"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Airplane mode is ON"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Airplane mode is OFF"</string>
- <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Battery saver"</string>
- <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Battery saver is OFF"</string>
- <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Battery saver is ON"</string>
<string name="global_action_settings" msgid="1756531602592545966">"Settings"</string>
<string name="global_action_assist" msgid="3892832961594295030">"Assist"</string>
<string name="global_action_voice_assist" msgid="7751191495200504480">"Voice Assist"</string>
@@ -276,6 +273,9 @@
<string name="permgrouplab_location" msgid="7275582855722310164">"Location"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"access this device\'s location"</string>
<string name="permgrouprequest_location" msgid="3788275734953323491">"Allow <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> to access this device\'s location?"</string>
+ <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"The app will only have access to the location while you’re using the app."</string>
+ <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"Always allow <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> to access this device’s location?"</string>
+ <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"The app will always have access to the location, even when you’re not using the app."</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Calendar"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"access your calendar"</string>
<string name="permgrouprequest_calendar" msgid="289900767793189421">"Allow <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> to access your calendar?"</string>
@@ -402,8 +402,7 @@
<string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"This app can add, remove, or change calendar events on your phone. This app can send messages that may appear to come from calendar owners, or change events without notifying their owners."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"access extra location provider commands"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Allows the app to access extra location provider commands. This may allow the app to interfere with the operation of the GPS or other location sources."</string>
- <!-- no translation found for permlab_accessFineLocation (6265109654698562427) -->
- <skip />
+ <string name="permlab_accessFineLocation" msgid="6265109654698562427">"access precise location only in the foreground"</string>
<string name="permdesc_accessFineLocation" msgid="3520508381065331098">"This app can get your exact location only when it is in the foreground. These location services must be turned on and available on your phone for the app to be able to use them. This may increase battery consumption."</string>
<string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"access approximate location (network-based)"</string>
<string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"This app can get your location based on network sources such as cell towers and Wi-Fi networks. These location services must be turned on and available on your tablet for the app to be able to use them."</string>
@@ -518,6 +517,37 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Fingerprint icon"</string>
+ <string name="permlab_manageFace" msgid="2137540986007309781">"manage face authentication hardware"</string>
+ <string name="permdesc_manageFace" msgid="8919637120670185330">"Allows the app to invoke methods to add and delete facial templates for use."</string>
+ <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"use face authentication hardware"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Allows the app to use face authentication hardware for authentication"</string>
+ <string name="face_acquired_insufficient" msgid="5901287247766106330">"Couldn’t process face. Please try again."</string>
+ <string name="face_acquired_too_bright" msgid="610606792381297174">"Face is too bright. Please try in lower light."</string>
+ <string name="face_acquired_too_dark" msgid="7229162716976778371">"Face is too dark. Please uncover light source."</string>
+ <string name="face_acquired_too_close" msgid="1980310037427755293">"Please move sensor farther from face."</string>
+ <string name="face_acquired_too_far" msgid="4494571381828850007">"Please bring sensor closer to face."</string>
+ <string name="face_acquired_too_high" msgid="228411096134808372">"Please move sensor higher."</string>
+ <string name="face_acquired_too_low" msgid="4539774649296349109">"Please move sensor lower."</string>
+ <string name="face_acquired_too_right" msgid="1650292067226118760">"Please move sensor to the right."</string>
+ <string name="face_acquired_too_left" msgid="2712489669456176505">"Please move sensor to the left."</string>
+ <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Please look at the sensor."</string>
+ <string name="face_acquired_not_detected" msgid="5707782294589511391">"No face detected."</string>
+ <string name="face_acquired_not_steady" msgid="3722829465011040042">"Keep face steady infront of device."</string>
+ <string-array name="face_acquired_vendor">
+ </string-array>
+ <string name="face_error_hw_not_available" msgid="6255891785768984615">"Face hardware not available."</string>
+ <string name="face_error_timeout" msgid="4014326147867150054">"Face time out reached. Try again."</string>
+ <string name="face_error_no_space" msgid="8224993703466381314">"Face can’t be stored."</string>
+ <string name="face_error_canceled" msgid="283945501061931023">"Face operation canceled."</string>
+ <string name="face_error_lockout" msgid="3407426963155388504">"Too many attempts. Try again later."</string>
+ <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Too many attempts. Facial authentication disabled."</string>
+ <string name="face_error_unable_to_process" msgid="238761109287767270">"Try again."</string>
+ <string name="face_error_not_enrolled" msgid="9166792142679691323">"No face enrolled."</string>
+ <string name="face_error_hw_not_present" msgid="4737289254517095671">"This device does not have a face authentication sensor"</string>
+ <string name="face_name_template" msgid="7004562145809595384">"Face <xliff:g id="FACEID">%d</xliff:g>"</string>
+ <string-array name="face_error_vendor">
+ </string-array>
+ <string name="face_icon_content_description" msgid="4024817159806482191">"Face icon"</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"read sync settings"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Allows the app to read the sync settings for an account. For example, this can determine whether the People app is synced with an account."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"toggle sync on and off"</string>
@@ -1178,6 +1208,9 @@
<skip />
<string name="wifi_no_internet" msgid="8938267198124654938">"Wi-Fi has no internet access"</string>
<string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Tap for options"</string>
+ <string name="wifi_softap_config_change" msgid="8475911871165857607">"Changes to your hotspot settings"</string>
+ <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"Your hotspot band has changed."</string>
+ <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"This device doesn’t support your preference for 5GHz only. Instead, this device will use the 5GHz band when available."</string>
<string name="network_switch_metered" msgid="4671730921726992671">"Switched to <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
<string name="network_switch_metered_detail" msgid="775163331794506615">"Device uses <xliff:g id="NEW_NETWORK">%1$s</xliff:g> when <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> has no internet access. Charges may apply."</string>
<string name="network_switch_metered_toast" msgid="5779283181685974304">"Switched from <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> to <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 75a3f24..2b7c4bf 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -236,9 +236,6 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Modo avión"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"El modo avión está Activado"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"El modo avión está Desactivado"</string>
- <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Ahorro de batería"</string>
- <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Ahorro de batería DESACTIVADO"</string>
- <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Ahorro de batería ACTIVADO"</string>
<string name="global_action_settings" msgid="1756531602592545966">"Configuración"</string>
<string name="global_action_assist" msgid="3892832961594295030">"Asistencia"</string>
<string name="global_action_voice_assist" msgid="7751191495200504480">"Asistente voz"</string>
@@ -276,6 +273,9 @@
<string name="permgrouplab_location" msgid="7275582855722310164">"Ubicación"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"acceder a la ubicación de este dispositivo"</string>
<string name="permgrouprequest_location" msgid="3788275734953323491">"¿Permitir que <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> acceda a la ubicación de este dispositivo?"</string>
+ <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"La app solo tendrá acceso a la ubicación cuando la uses."</string>
+ <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"¿Permitir que <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> acceda a la ubicación?"</string>
+ <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"La app siempre tendrá acceso a la ubicación, incluso cuando no la uses."</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Calendario"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"acceder al calendario"</string>
<string name="permgrouprequest_calendar" msgid="289900767793189421">"¿Permitir que <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> acceda a tu calendario?"</string>
@@ -402,8 +402,7 @@
<string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Esta app puede agregar, quitar o cambiar eventos del calendario en tu teléfono. Puede enviar mensajes que parecen proceder de propietarios del calendario o cambiar eventos sin notificarlos."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"acceder a comandos adicionales del proveedor del lugar"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Permite que la aplicación acceda a comandos adicionales del proveedor de ubicación. Esto puede permitirle a la aplicación interferir con el funcionamiento del GPS o de otras fuentes de ubicación."</string>
- <!-- no translation found for permlab_accessFineLocation (6265109654698562427) -->
- <skip />
+ <string name="permlab_accessFineLocation" msgid="6265109654698562427">"acceder a la ubicación exacta solo en primer plano"</string>
<string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Esta app puede obtener tu ubicación exacta solo cuando está en primer plano. Los servicios de ubicación deben estar activados y disponibles en el teléfono para que la app pueda usarlos. Es posible que aumente el consumo de batería."</string>
<string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"acceder a la ubicación aproximada (según la red)"</string>
<string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Esta app puede obtener tu ubicación a través de fuentes de red, como torres de telefonía móvil y redes Wi-Fi. Estos servicios de ubicación deben estar activados y disponibles en tu tablet para que la app pueda usarlos."</string>
@@ -518,6 +517,37 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Ícono de huella digital"</string>
+ <string name="permlab_manageFace" msgid="2137540986007309781">"administrar el hardware de autenticación facial"</string>
+ <string name="permdesc_manageFace" msgid="8919637120670185330">"Permite que la app emplee métodos para agregar y borrar plantillas de rostros para su uso."</string>
+ <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"usar el hardware de autenticación facial"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Permite que la app use el hardware de autenticación facial para reconocerte"</string>
+ <string name="face_acquired_insufficient" msgid="5901287247766106330">"Error al procesar el rostro. Vuelve a intentarlo."</string>
+ <string name="face_acquired_too_bright" msgid="610606792381297174">"El rostro se ve muy claro. Prueba con menos luz."</string>
+ <string name="face_acquired_too_dark" msgid="7229162716976778371">"El rostro se ve muy oscuro. Prueba con más luz."</string>
+ <string name="face_acquired_too_close" msgid="1980310037427755293">"Aleja el sensor del rostro."</string>
+ <string name="face_acquired_too_far" msgid="4494571381828850007">"Acerca el sensor al rostro."</string>
+ <string name="face_acquired_too_high" msgid="228411096134808372">"Coloca el sensor más arriba."</string>
+ <string name="face_acquired_too_low" msgid="4539774649296349109">"Coloca el sensor más abajo."</string>
+ <string name="face_acquired_too_right" msgid="1650292067226118760">"Mueve el sensor hacia la derecha."</string>
+ <string name="face_acquired_too_left" msgid="2712489669456176505">"Mueve el sensor hacia la izquierda."</string>
+ <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Mira al sensor."</string>
+ <string name="face_acquired_not_detected" msgid="5707782294589511391">"No se detectó ningún rostro."</string>
+ <string name="face_acquired_not_steady" msgid="3722829465011040042">"Mantén el rostro fijo enfrente del dispositivo."</string>
+ <string-array name="face_acquired_vendor">
+ </string-array>
+ <string name="face_error_hw_not_available" msgid="6255891785768984615">"Hardware de reconocimiento facial no disponible"</string>
+ <string name="face_error_timeout" msgid="4014326147867150054">"Se agotó el tiempo. Vuelve a intentarlo."</string>
+ <string name="face_error_no_space" msgid="8224993703466381314">"No se puede almacenar el rostro."</string>
+ <string name="face_error_canceled" msgid="283945501061931023">"Se canceló el reconocimiento facial."</string>
+ <string name="face_error_lockout" msgid="3407426963155388504">"Demasiados intentos. Inténtalo de nuevo más tarde."</string>
+ <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Demasiados intentos. Autent. facial inhabilitada."</string>
+ <string name="face_error_unable_to_process" msgid="238761109287767270">"Vuelve a intentarlo."</string>
+ <string name="face_error_not_enrolled" msgid="9166792142679691323">"No registraste ningún rostro."</string>
+ <string name="face_error_hw_not_present" msgid="4737289254517095671">"Este dispositivo no tiene sensor de autenticación facial"</string>
+ <string name="face_name_template" msgid="7004562145809595384">"Rostro <xliff:g id="FACEID">%d</xliff:g>"</string>
+ <string-array name="face_error_vendor">
+ </string-array>
+ <string name="face_icon_content_description" msgid="4024817159806482191">"Ícono cara"</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"leer la configuración de sincronización"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Este permiso permite que la aplicación consulte la configuración de sincronización de una cuenta. Esto permite, por ejemplo, determinar si la aplicación Personas está sincronizada con una cuenta."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"activar y desactivar la sincronización"</string>
@@ -1178,6 +1208,9 @@
<skip />
<string name="wifi_no_internet" msgid="8938267198124654938">"La red Wi-Fi no tiene acceso a Internet"</string>
<string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Presiona para ver opciones"</string>
+ <string name="wifi_softap_config_change" msgid="8475911871165857607">"Cambios en la configuración de tu hotspot"</string>
+ <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"Cambió la banda de tu hotspot."</string>
+ <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"Si bien este dispositivo no admite la opción para conectarse exclusivamente a bandas de 5 GHz, las usará cuando estén disponibles."</string>
<string name="network_switch_metered" msgid="4671730921726992671">"Se cambió a <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
<string name="network_switch_metered_detail" msgid="775163331794506615">"El dispositivo usa <xliff:g id="NEW_NETWORK">%1$s</xliff:g> cuando <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> no tiene acceso a Internet. Es posible que se apliquen cargos."</string>
<string name="network_switch_metered_toast" msgid="5779283181685974304">"Se cambió de <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> a <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 41c9251..c4b74af 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -236,9 +236,6 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Modo avión"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Modo avión activado. Desactivar"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Modo avión desactivado. Activar"</string>
- <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Ahorro de batería"</string>
- <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Ahorro de batería desactivado"</string>
- <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Ahorro de batería activado"</string>
<string name="global_action_settings" msgid="1756531602592545966">"Ajustes"</string>
<string name="global_action_assist" msgid="3892832961594295030">"Asistencia"</string>
<string name="global_action_voice_assist" msgid="7751191495200504480">"Asistente voz"</string>
@@ -256,7 +253,7 @@
<string name="notification_channel_network_alerts" msgid="2895141221414156525">"Alertas de la red"</string>
<string name="notification_channel_network_available" msgid="4531717914138179517">"Red disponible"</string>
<string name="notification_channel_vpn" msgid="8330103431055860618">"Estado de la VPN"</string>
- <string name="notification_channel_device_admin" msgid="1568154104368069249">"Administración del dispositivo"</string>
+ <string name="notification_channel_device_admin" msgid="1568154104368069249">"Administración de dispositivos"</string>
<string name="notification_channel_alerts" msgid="4496839309318519037">"Alertas"</string>
<string name="notification_channel_retail_mode" msgid="6088920674914038779">"Demo para tiendas"</string>
<string name="notification_channel_usb" msgid="9006850475328924681">"Conexión USB"</string>
@@ -276,6 +273,9 @@
<string name="permgrouplab_location" msgid="7275582855722310164">"Ubicación"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"acceder a la ubicación de este dispositivo"</string>
<string name="permgrouprequest_location" msgid="3788275734953323491">"¿Quieres permitir que <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> acceda a la ubicación de este dispositivo?"</string>
+ <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"La aplicación solo podrá acceder a la ubicación cuando estés usando la aplicación."</string>
+ <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"¿Permites que <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> acceda a ubic. del disp.?"</string>
+ <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"La aplicación siempre podrá acceder a la ubicación, aunque no estés usando la aplicación."</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Calendario"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"acceder a tu calendario"</string>
<string name="permgrouprequest_calendar" msgid="289900767793189421">"¿Quieres permitir que <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> acceda a tu calendario?"</string>
@@ -402,8 +402,7 @@
<string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Esta aplicación puede añadir, quitar o cambiar eventos de calendario almacenados en tu teléfono. También puede enviar mensajes que parecen provenir del propietario del calendario o cambiar eventos sin notificárselo a su propietario."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"acceder a comandos de proveedor de ubicación adicional"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Permite que la aplicación acceda a otros comandos del proveedor de ubicación. De esta forma, la aplicación podrá interferir en el funcionamiento del GPS o de otras fuentes de ubicación."</string>
- <!-- no translation found for permlab_accessFineLocation (6265109654698562427) -->
- <skip />
+ <string name="permlab_accessFineLocation" msgid="6265109654698562427">"acceder a la ubicación exacta solo en primer plano"</string>
<string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Esta aplicación solo puede obtener tu ubicación exacta cuando está en primer plano. Estos servicios de ubicación deben estar activados y disponibles en tu teléfono para que la aplicación pueda utilizarlos. Es posible que aumente el consumo de batería."</string>
<string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"acceder a tu ubicación aproximada (basada en red)"</string>
<string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Esta aplicación puede obtener tu ubicación a partir de fuentes de red como las antenas de telefonía móvil y las redes Wi-Fi. Estos servicios de ubicación deben estar activados y disponibles en tu tablet para que la aplicación pueda usarlos."</string>
@@ -518,6 +517,37 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Icono de huella digital"</string>
+ <string name="permlab_manageFace" msgid="2137540986007309781">"gestionar el hardware de autenticación facial"</string>
+ <string name="permdesc_manageFace" msgid="8919637120670185330">"Permite que la app use métodos para añadir y suprimir plantillas de caras para su uso."</string>
+ <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"usar el hardware de autenticación facial"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Permite que la aplicación utilice el hardware de autenticación facial para autenticarte"</string>
+ <string name="face_acquired_insufficient" msgid="5901287247766106330">"No se ha reconocido la cara. Vuelve a intentarlo."</string>
+ <string name="face_acquired_too_bright" msgid="610606792381297174">"La cara se ve muy clara. Inténtalo con menos luz."</string>
+ <string name="face_acquired_too_dark" msgid="7229162716976778371">"La cara se ve muy oscura. Inténtalo con más luz."</string>
+ <string name="face_acquired_too_close" msgid="1980310037427755293">"Aleja la cara del sensor."</string>
+ <string name="face_acquired_too_far" msgid="4494571381828850007">"Acerca la cara al sensor."</string>
+ <string name="face_acquired_too_high" msgid="228411096134808372">"Coloca el sensor más arriba."</string>
+ <string name="face_acquired_too_low" msgid="4539774649296349109">"Coloca el sensor más abajo."</string>
+ <string name="face_acquired_too_right" msgid="1650292067226118760">"Mueve el sensor hacia la derecha."</string>
+ <string name="face_acquired_too_left" msgid="2712489669456176505">"Mueve el sensor hacia la izquierda."</string>
+ <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Mira al sensor."</string>
+ <string name="face_acquired_not_detected" msgid="5707782294589511391">"No se ha detectado ninguna cara."</string>
+ <string name="face_acquired_not_steady" msgid="3722829465011040042">"Mantén la cara fija enfrente del dispositivo."</string>
+ <string-array name="face_acquired_vendor">
+ </string-array>
+ <string name="face_error_hw_not_available" msgid="6255891785768984615">"Hardware de reconocimiento facial no disponible."</string>
+ <string name="face_error_timeout" msgid="4014326147867150054">"Has sobrepasado el tiempo. Inténtalo de nuevo."</string>
+ <string name="face_error_no_space" msgid="8224993703466381314">"No se pueden registrar más caras."</string>
+ <string name="face_error_canceled" msgid="283945501061931023">"Se ha cancelado el reconocimiento facial."</string>
+ <string name="face_error_lockout" msgid="3407426963155388504">"Demasiados intentos. Inténtalo de nuevo más tarde."</string>
+ <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Demasiados intentos. Autent. facial inhabilitada."</string>
+ <string name="face_error_unable_to_process" msgid="238761109287767270">"Inténtalo de nuevo."</string>
+ <string name="face_error_not_enrolled" msgid="9166792142679691323">"No has registrado ninguna cara."</string>
+ <string name="face_error_hw_not_present" msgid="4737289254517095671">"Este dispositivo no tiene sensor de autenticación facial"</string>
+ <string name="face_name_template" msgid="7004562145809595384">"Cara <xliff:g id="FACEID">%d</xliff:g>"</string>
+ <string-array name="face_error_vendor">
+ </string-array>
+ <string name="face_icon_content_description" msgid="4024817159806482191">"Icono cara"</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"leer la configuración de sincronización"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Permite que la aplicación consulte la configuración de sincronización de una cuenta. La aplicación puede utilizar este permiso, por ejemplo, para determinar si la aplicación Contactos está sincronizada con una cuenta."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"activar y desactivar la sincronización"</string>
@@ -608,7 +638,7 @@
<string name="policylab_disableCamera" msgid="6395301023152297826">"Inhabilitar cámaras"</string>
<string name="policydesc_disableCamera" msgid="2306349042834754597">"Evitar el uso de las cámaras del dispositivo"</string>
<string name="policylab_disableKeyguardFeatures" msgid="8552277871075367771">"Inhabilitar algunas funciones del bloqueo de pantalla"</string>
- <string name="policydesc_disableKeyguardFeatures" msgid="2044755691354158439">"Evita el uso de algunas funciones del bloqueo de pantalla."</string>
+ <string name="policydesc_disableKeyguardFeatures" msgid="2044755691354158439">"Evitar el uso de algunas funciones del bloqueo de pantalla"</string>
<string-array name="phoneTypes">
<item msgid="8901098336658710359">"Casa"</item>
<item msgid="869923650527136615">"Móvil"</item>
@@ -1178,6 +1208,9 @@
<skip />
<string name="wifi_no_internet" msgid="8938267198124654938">"La red Wi-Fi no tiene acceso a Internet"</string>
<string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Toca para ver opciones"</string>
+ <string name="wifi_softap_config_change" msgid="8475911871165857607">"Cambios en los ajustes de tu punto de acceso"</string>
+ <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"La banda de tu punto de acceso ha cambiado."</string>
+ <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"Este dispositivo no admite la opción de conectarse exclusivamente a bandas de 5 GHz, pero las usará cuando estén disponibles."</string>
<string name="network_switch_metered" msgid="4671730921726992671">"Se ha cambiado a <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
<string name="network_switch_metered_detail" msgid="775163331794506615">"El dispositivo utiliza <xliff:g id="NEW_NETWORK">%1$s</xliff:g> cuando <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> no tiene acceso a Internet. Es posible que se apliquen cargos."</string>
<string name="network_switch_metered_toast" msgid="5779283181685974304">"Se ha cambiado de <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> a <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index e966040..1e73bf1 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -236,9 +236,6 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Lennurežiim"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Lennurežiim on SEES"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Lennurežiim on VÄLJAS"</string>
- <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Akusäästja"</string>
- <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Akusäästja on VÄLJA lülitatud"</string>
- <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Akusäästja on SISSE lülitatud"</string>
<string name="global_action_settings" msgid="1756531602592545966">"Seaded"</string>
<string name="global_action_assist" msgid="3892832961594295030">"Abi"</string>
<string name="global_action_voice_assist" msgid="7751191495200504480">"Häälabi"</string>
@@ -276,6 +273,9 @@
<string name="permgrouplab_location" msgid="7275582855722310164">"Asukoht"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"pääseda juurde selle seadme asukohale"</string>
<string name="permgrouprequest_location" msgid="3788275734953323491">"Kas lubada rakendusele <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> juurdepääs selle seadme asukohale?"</string>
+ <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"Rakendusel on juurdepääs asukohale vaid siis, kui rakendust kasutate."</string>
+ <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"Kas lubada rak. <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> alati juurdepääs seadme asukohale?"</string>
+ <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"Rakendusel on alati juurdepääs asukohale isegi siis, kui te rakendust ei kasuta."</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Kalender"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"juurdepääs kalendrile"</string>
<string name="permgrouprequest_calendar" msgid="289900767793189421">"Kas lubada rakendusele <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> juurdepääs teie kalendrile?"</string>
@@ -402,8 +402,7 @@
<string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"See rakendus võib teie telefoni kalendrisündmusi lisada, neid eemaldada või muuta. See rakendus võib saata sõnumeid, mis näivad pärinevat kalendri omanikelt, või muuta sündmusi ilma omanikke teavitamata."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"juurdepääs asukohapakkuja lisakäskudele"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Võimaldab rakendusel juurde pääseda asukohapakkuja erikäskudele. See võib lubada rakendusel mõjutada GPS-i või muude asukohaallikate tööd."</string>
- <!-- no translation found for permlab_accessFineLocation (6265109654698562427) -->
- <skip />
+ <string name="permlab_accessFineLocation" msgid="6265109654698562427">"juurdepääs täpsele asukohale ainult esiplaanil"</string>
<string name="permdesc_accessFineLocation" msgid="3520508381065331098">"See rakendus hangib teie täpse asukoha ainult siis, kui see töötab esiplaanil. Need asukohateenused peavad olema sisse lülitatud ja teie telefonis saadaval, et rakendus saaks neid kasutada. See võib suurendada akutoite tarbimist."</string>
<string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"juurdepääs ligikaudsele asukohale (võrgupõhine)"</string>
<string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"See rakendus näeb võrguallikate (nt mobiilimastid ja WiFi-võrgud) abil teie asukohta. Need asukohateenused peavad olema sisse lülitatud ja teie tahvelarvutis saadaval, et rakendus neid kasutada saaks."</string>
@@ -518,6 +517,37 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Sõrmejälje ikoon"</string>
+ <string name="permlab_manageFace" msgid="2137540986007309781">"hallata näo autentimise riistvara"</string>
+ <string name="permdesc_manageFace" msgid="8919637120670185330">"Lubab rakendusel tühistada meetodid kasutatavate näomallide lisamiseks ja kustutamiseks."</string>
+ <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"kasutada näo autentimise riistvara"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Võimaldab rakendusel autentimiseks kasutada näo autentimise riistvara"</string>
+ <string name="face_acquired_insufficient" msgid="5901287247766106330">"Nägu ei õnnestunud töödelda. Proovige uuesti."</string>
+ <string name="face_acquired_too_bright" msgid="610606792381297174">"Nägu on liiga hele. Proovige hämaramas."</string>
+ <string name="face_acquired_too_dark" msgid="7229162716976778371">"Nägu on liiga tume. Kasutage valgusallikat."</string>
+ <string name="face_acquired_too_close" msgid="1980310037427755293">"Liigutage andur näost kaugemale."</string>
+ <string name="face_acquired_too_far" msgid="4494571381828850007">"Liigutage andur näole lähemale."</string>
+ <string name="face_acquired_too_high" msgid="228411096134808372">"Liigutage andurit kõrgemale."</string>
+ <string name="face_acquired_too_low" msgid="4539774649296349109">"Liigutage andurit madalamale."</string>
+ <string name="face_acquired_too_right" msgid="1650292067226118760">"Liigutage andurit paremale."</string>
+ <string name="face_acquired_too_left" msgid="2712489669456176505">"Liigutage andurit vasakule."</string>
+ <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Vaadake andurit."</string>
+ <string name="face_acquired_not_detected" msgid="5707782294589511391">"Nägu ei tuvastatud."</string>
+ <string name="face_acquired_not_steady" msgid="3722829465011040042">"Hoidke nägu seadme ees paigal."</string>
+ <string-array name="face_acquired_vendor">
+ </string-array>
+ <string name="face_error_hw_not_available" msgid="6255891785768984615">"Näotuvastuse riistvara pole saadaval."</string>
+ <string name="face_error_timeout" msgid="4014326147867150054">"Näotuvastuse taimeri ajalõpp. Proovige uuesti."</string>
+ <string name="face_error_no_space" msgid="8224993703466381314">"Nägu ei saa salvestada."</string>
+ <string name="face_error_canceled" msgid="283945501061931023">"Näotuvastuse toiming tühistati."</string>
+ <string name="face_error_lockout" msgid="3407426963155388504">"Liiga palju katseid. Proovige hiljem uuesti."</string>
+ <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Liiga palju katseid. Näotuvastus on keelatud."</string>
+ <string name="face_error_unable_to_process" msgid="238761109287767270">"Proovige uuesti."</string>
+ <string name="face_error_not_enrolled" msgid="9166792142679691323">"Nägu pole registreeritud."</string>
+ <string name="face_error_hw_not_present" msgid="4737289254517095671">"Sellel seadmel pole näotuvastuse andurit"</string>
+ <string name="face_name_template" msgid="7004562145809595384">"Nägu <xliff:g id="FACEID">%d</xliff:g>"</string>
+ <string-array name="face_error_vendor">
+ </string-array>
+ <string name="face_icon_content_description" msgid="4024817159806482191">"Näoikoon"</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"loe sünkroonimisseadeid"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Võimaldab rakendusel lugeda konto sünkroonimisseadeid. Näiteks võib see määrata, kas rakendus Inimesed on kontoga sünkroonitud."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"lülitage sünkroonimine sisse ja välja"</string>
@@ -1178,6 +1208,9 @@
<skip />
<string name="wifi_no_internet" msgid="8938267198124654938">"WiFi-võrgul pole juurdepääsu Internetile"</string>
<string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Puudutage valikute nägemiseks"</string>
+ <string name="wifi_softap_config_change" msgid="8475911871165857607">"Muudatused teie leviala seadetes"</string>
+ <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"Teie leviala riba on muutunud."</string>
+ <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"See seade ei toeta teie eelistatud ainult 5 GHz riba. Seade kasutab 5 GHz riba ainult siis, kui see on saadaval."</string>
<string name="network_switch_metered" msgid="4671730921726992671">"Lülitati võrgule <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
<string name="network_switch_metered_detail" msgid="775163331794506615">"Seade kasutab võrku <xliff:g id="NEW_NETWORK">%1$s</xliff:g>, kui võrgul <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> puudub juurdepääs Internetile. Rakenduda võivad tasud."</string>
<string name="network_switch_metered_toast" msgid="5779283181685974304">"Lülitati võrgult <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> võrgule <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index 43d1483..aa6edaa 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -236,9 +236,6 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Hegaldi modua"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Hegaldi modua AKTIBATUTA dago"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Hegaldi modua DESAKTIBATUTA dago"</string>
- <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Bateria-aurrezlea"</string>
- <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"DESAKTIBATUTA dago bateria-aurrezlea"</string>
- <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"AKTIBATUTA dago bateria-aurrezlea"</string>
<string name="global_action_settings" msgid="1756531602592545966">"Ezarpenak"</string>
<string name="global_action_assist" msgid="3892832961594295030">"Lagundu"</string>
<string name="global_action_voice_assist" msgid="7751191495200504480">"Ahots-laguntza"</string>
@@ -276,6 +273,9 @@
<string name="permgrouplab_location" msgid="7275582855722310164">"Kokapena"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"atzitu gailuaren kokapena"</string>
<string name="permgrouprequest_location" msgid="3788275734953323491">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> aplikazioari gailuaren kokapena atzitzea baimendu nahi diozu?"</string>
+ <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"Aplikazioa erabiltzen ari zarenean soilik atzituko du aplikazioak kokapena."</string>
+ <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> aplikazioari gailuaren kokapena beti atzitzea baimendu?"</string>
+ <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"Aplikazioak beti atzituko du kokapena, baita aplikazioa erabiltzen ari ez bazara ere."</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Egutegia"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"atzitu egutegia"</string>
<string name="permgrouprequest_calendar" msgid="289900767793189421">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> aplikazioari egutegia atzitzea baimendu nahi diozu?"</string>
@@ -402,8 +402,7 @@
<string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Telefonoko gertaerak gehitzeko, kentzeko edo aldatzeko aukera du aplikazioak. Gainera, egutegien jabeenak diruditen mezuak bidal ditzake, eta gertaerak alda ditzake jabeei beraiei jakinarazi gabe."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"atzitu kokapen-hornitzaileen komando gehigarriak"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Kokapen-hornitzailearen agindu gehigarriak atzitzea baimentzen die aplikazioei. Horrela, agian aplikazioek GPSaren edo bestelako kokapenaren iturburuen funtzionamenduan eragina izan dezakete."</string>
- <!-- no translation found for permlab_accessFineLocation (6265109654698562427) -->
- <skip />
+ <string name="permlab_accessFineLocation" msgid="6265109654698562427">"lortu kokapen zehatza aurreko planoan bakarrik"</string>
<string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Aplikazioak zure kokapen zehatza lor dezake aurreko planoan funtzionatzen duenean bakarrik. Kokapen-zerbitzu horiek aktibatuta eta erabilgarri izan behar dituzu telefonoan, aplikazioak erabil ditzan. Baliteke bateria gehiago erabiltzea."</string>
<string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"atzitu gutxi gorabeherako kokapena (sarean oinarrituta)"</string>
<string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Aplikazioak zure kokapenaren berri izan dezake sareen iturburuak (adibidez, telefonia mugikorreko dorreak eta Wi-Fi sareak) erabilita. Kokapen-zerbitzu horiek aktibatuta eta erabilgarri izan behar dituzu tabletan, aplikazioak erabil ditzan."</string>
@@ -518,6 +517,37 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Hatz-markaren ikonoa"</string>
+ <string name="permlab_manageFace" msgid="2137540986007309781">"kudeatu aurpegi bidez autentifikatzeko hardwarea"</string>
+ <string name="permdesc_manageFace" msgid="8919637120670185330">"Aurpegi-txantiloiak gehitu eta ezabatzeko metodoei dei egitea baimentzen dio aplikazioari."</string>
+ <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"erabili aurpegi bidez autentifikatzeko hardwarea"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Aurpegi bidez autentifikatzeko hardwarea erabiltzea baimentzen dio aplikazioari"</string>
+ <string name="face_acquired_insufficient" msgid="5901287247766106330">"Ezin izan da prozesatu aurpegia. Saiatu berriro."</string>
+ <string name="face_acquired_too_bright" msgid="610606792381297174">"Aurpegiak distira gehiegi du. Murriztu argitasuna."</string>
+ <string name="face_acquired_too_dark" msgid="7229162716976778371">"Aurpegia ilunegi dago. Estalgabetu argi-iturburua."</string>
+ <string name="face_acquired_too_close" msgid="1980310037427755293">"Urrundu sentsorea aurpegitik."</string>
+ <string name="face_acquired_too_far" msgid="4494571381828850007">"Hurbildu sentsorea aurpegira."</string>
+ <string name="face_acquired_too_high" msgid="228411096134808372">"Eraman sentsorea gora."</string>
+ <string name="face_acquired_too_low" msgid="4539774649296349109">"Eraman sentsorea behera."</string>
+ <string name="face_acquired_too_right" msgid="1650292067226118760">"Eraman sentsorea eskuinera."</string>
+ <string name="face_acquired_too_left" msgid="2712489669456176505">"Eraman sentsorea ezkerrera."</string>
+ <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Begiratu sentsoreari."</string>
+ <string name="face_acquired_not_detected" msgid="5707782294589511391">"Ez dugu hauteman aurpegirik."</string>
+ <string name="face_acquired_not_steady" msgid="3722829465011040042">"Egon geldi, aurpegia gailuaren aurrean jarrita."</string>
+ <string-array name="face_acquired_vendor">
+ </string-array>
+ <string name="face_error_hw_not_available" msgid="6255891785768984615">"Aurpegia hautemateko hardwarea ez dago erabilgarri."</string>
+ <string name="face_error_timeout" msgid="4014326147867150054">"Gainditu da aurpegiak prozesatzeko denbora-muga. Saiatu berriro."</string>
+ <string name="face_error_no_space" msgid="8224993703466381314">"Ezin da gorde aurpegia."</string>
+ <string name="face_error_canceled" msgid="283945501061931023">"Utzi da aurpegiaren bidezko eragiketa."</string>
+ <string name="face_error_lockout" msgid="3407426963155388504">"Saiakera gehiegi egin dituzu. Saiatu berriro geroago."</string>
+ <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Saiakera gehiegi egin dituzu. Desgaitu egin da autentifikazioa."</string>
+ <string name="face_error_unable_to_process" msgid="238761109287767270">"Saiatu berriro."</string>
+ <string name="face_error_not_enrolled" msgid="9166792142679691323">"Ez dago aurpegirik erregistratuta."</string>
+ <string name="face_error_hw_not_present" msgid="4737289254517095671">"Gailu honek ez du aurpegia autentifikatzeko sentsorerik"</string>
+ <string name="face_name_template" msgid="7004562145809595384">"<xliff:g id="FACEID">%d</xliff:g> aurpegia"</string>
+ <string-array name="face_error_vendor">
+ </string-array>
+ <string name="face_icon_content_description" msgid="4024817159806482191">"Aurpegiaren ikonoa"</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"irakurri sinkronizazio-ezarpenak"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Kontu baten sinkronizazio-ezarpenak irakurtzeko baimena ematen die aplikazioei. Adibidez, Jendea aplikazioa konturen batekin sinkronizatuta dagoen zehatz dezake."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"aktibatu eta desaktibatu sinkronizazioa"</string>
@@ -1178,6 +1208,9 @@
<skip />
<string name="wifi_no_internet" msgid="8938267198124654938">"Ezin da konektatu Internetera Wi-Fi bidez"</string>
<string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Sakatu aukerak ikusteko"</string>
+ <string name="wifi_softap_config_change" msgid="8475911871165857607">"Aldaketak egin dira sare publikoaren ezarpenetan"</string>
+ <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"Aldatu da sare publikoaren banda."</string>
+ <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"Gailuak ez du onartzen 5 GHz-ko banda soilik erabiltzeko hobespena. Horren ordez, erabilgarri dagoen bakoitzean erabiliko da 5 GHz-ko banda."</string>
<string name="network_switch_metered" msgid="4671730921726992671">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> erabiltzen ari zara orain"</string>
<string name="network_switch_metered_detail" msgid="775163331794506615">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> Internetera konektatzeko gauza ez denean, <xliff:g id="NEW_NETWORK">%1$s</xliff:g> erabiltzen du gailuak. Agian kostuak ordaindu beharko dituzu."</string>
<string name="network_switch_metered_toast" msgid="5779283181685974304">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> erabiltzen ari zinen, baina <xliff:g id="NEW_NETWORK">%2$s</xliff:g> erabiltzen ari zara orain"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 32be39a..5e107ee 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -236,9 +236,6 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"حالت هواپیما"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"حالت هواپیما روشن است"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"حالت هواپیما خاموش است"</string>
- <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"بهینهسازی باتری"</string>
- <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"بهینهسازی باتری خاموش است"</string>
- <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"بهینهسازی باتری روشن است"</string>
<string name="global_action_settings" msgid="1756531602592545966">"تنظیمات"</string>
<string name="global_action_assist" msgid="3892832961594295030">"دستیار"</string>
<string name="global_action_voice_assist" msgid="7751191495200504480">"دستیار صوتی"</string>
@@ -276,6 +273,9 @@
<string name="permgrouplab_location" msgid="7275582855722310164">"مکان"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"دسترسی به موقعیت مکانی این دستگاه"</string>
<string name="permgrouprequest_location" msgid="3788275734953323491">"به <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> اجازه داده شود به مکان این دستگاه دسترسی پیدا کند؟"</string>
+ <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"این برنامه تنها هنگامی که از آن استفاده میکنید، به مکان دسترسی خواهد داشت."</string>
+ <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"همیشه به <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> اجازه داده شود به مکان این دستگاه دسترسی داشته باشد؟"</string>
+ <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"این برنامه همیشه به مکان دسترسی خواهد داشت، حتی اگر از آن استفاده نکنید."</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"تقویم"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"دسترسی به تقویم شما"</string>
<string name="permgrouprequest_calendar" msgid="289900767793189421">"به <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> اجازه داده شود به تقویم شما دسترسی پیدا کند؟"</string>
@@ -402,8 +402,7 @@
<string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"این برنامه میتواند در تلفن شما رویدادهای تقویم اضافه کند، آنها را حذف کند یا تغییر دهد. این برنامه میتواند پیامهایی ارسال کند که گویی از طرف مالکان تقویم هستند یا رویدادها را بدون اطلاع مالکان تغییر دهد."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"دسترسی به فرمانهای بیشتر ارائه دهنده مکان"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"به برنامه اجازه میدهد به دستورات ارائهدهنده مکان تکمیلی دسترسی داشته باشد. این کار ممکن است به برنامه امکان دهد با کارکرد GPS یا منابع دیگر مکان تداخل داشته باشد."</string>
- <!-- no translation found for permlab_accessFineLocation (6265109654698562427) -->
- <skip />
+ <string name="permlab_accessFineLocation" msgid="6265109654698562427">"دسترسی به مکان دقیق فقط در پیشزمینه"</string>
<string name="permdesc_accessFineLocation" msgid="3520508381065331098">"این برنامه فقط زمانی میتواند موقعیت مکانی دقیق شما را دریافت کند که در پیشزمینه باشد. برای اینکه برنامه بتواند از خدمات مکان استفاده کند، این خدمات باید در تلفنتان روشن و دردسترس باشد. ممکن است با این کار مصرف باتری افزایش یابد."</string>
<string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"دسترسی به مکان تقریبی (مبتنی بر شبکه)"</string>
<string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"این برنامه میتواند براساس منابع شبکه مانند دکلهای مخابراتی و شبکههای Wi-Fi، مکان شما را تشخیص دهد. این خدمات مکان باید روشن و در رایانه لوحی شما دردسترس باشند تا برنامه بتواند از آنها استفاده کند."</string>
@@ -518,6 +517,37 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"نماد اثر انگشت"</string>
+ <string name="permlab_manageFace" msgid="2137540986007309781">"مدیریت سختافزار احراز هویت با چهره"</string>
+ <string name="permdesc_manageFace" msgid="8919637120670185330">"به برنامه امکان میدهد روشهایی را برای افزودن و حذف الگوهای چهره جهت استفاده فرابخواند."</string>
+ <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"استفاده از سختافزار احراز هویت با چهره"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"به برنامه امکان میدهد از سختافزار احراز هویت با چهره برای احراز هویت استفاده کند"</string>
+ <string name="face_acquired_insufficient" msgid="5901287247766106330">"چهره پردازش نشد. لطفاً دوباره امتحان کنید."</string>
+ <string name="face_acquired_too_bright" msgid="610606792381297174">"چهره خیلی روشن است. لطفاً در نور کمتری دوباره امتحان کنید."</string>
+ <string name="face_acquired_too_dark" msgid="7229162716976778371">"چهره خیلی تاریک است. لطفاً منبع نور را نپوشانید."</string>
+ <string name="face_acquired_too_close" msgid="1980310037427755293">"لطفاً حسگر را از صورتتان دورتر کنید."</string>
+ <string name="face_acquired_too_far" msgid="4494571381828850007">"لطفاً حسگر را به صورتتان نزدیکتر کنید."</string>
+ <string name="face_acquired_too_high" msgid="228411096134808372">"لطفاً حسگر را بالاتر ببرید."</string>
+ <string name="face_acquired_too_low" msgid="4539774649296349109">"لطفاً حسگر را پایینتر بیاورید."</string>
+ <string name="face_acquired_too_right" msgid="1650292067226118760">"لطفاً حسگر را به راست حرکت دهید."</string>
+ <string name="face_acquired_too_left" msgid="2712489669456176505">"لطفاً حسگر را به چپ حرکت دهید."</string>
+ <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"لطفاً به حسگر نگاه کنید."</string>
+ <string name="face_acquired_not_detected" msgid="5707782294589511391">"چهرهای شناسایی نشد."</string>
+ <string name="face_acquired_not_steady" msgid="3722829465011040042">"صورتتان را ثابت در جلوی دستگاه نگه دارید."</string>
+ <string-array name="face_acquired_vendor">
+ </string-array>
+ <string name="face_error_hw_not_available" msgid="6255891785768984615">"سختافزار چهره دردسترس نیست."</string>
+ <string name="face_error_timeout" msgid="4014326147867150054">"مهلت زمانی شناسایی چهره تمام شد. دوباره امتحان کنید"</string>
+ <string name="face_error_no_space" msgid="8224993703466381314">"نمیتوان چهره را ذخیره کرد."</string>
+ <string name="face_error_canceled" msgid="283945501061931023">"عملیات شناسایی چهره لغو شد."</string>
+ <string name="face_error_lockout" msgid="3407426963155388504">"تعداد زیادی تلاش ناموفق. بعداً دوباره امتحان کنید."</string>
+ <string name="face_error_lockout_permanent" msgid="8198354656746088890">"چندین تلاش ناموفق. احراز هویت با چهره غیرفعال شد."</string>
+ <string name="face_error_unable_to_process" msgid="238761109287767270">"دوباره امتحان کنید."</string>
+ <string name="face_error_not_enrolled" msgid="9166792142679691323">"هیچ چهرهای ثبت نشده است."</string>
+ <string name="face_error_hw_not_present" msgid="4737289254517095671">"این دستگاه حسگر احراز هویت با چهره ندارد."</string>
+ <string name="face_name_template" msgid="7004562145809595384">"چهره <xliff:g id="FACEID">%d</xliff:g>"</string>
+ <string-array name="face_error_vendor">
+ </string-array>
+ <string name="face_icon_content_description" msgid="4024817159806482191">"نماد چهره"</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"خواندن تنظیمات همگامسازی"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"به برنامه اجازه میدهد تنظیمات را برای یک حساب بخواند. بهعنوان مثال، این ویژگی میتواند تعیین کند آیا حساب «افراد» شما با یک حساب همگامسازی شده است."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"تغییر وضعیت همگامسازی بین فعال و غیرفعال"</string>
@@ -1178,6 +1208,9 @@
<skip />
<string name="wifi_no_internet" msgid="8938267198124654938">"Wi-Fi به اینترنت دسترسی ندارد"</string>
<string name="wifi_no_internet_detailed" msgid="8083079241212301741">"برای گزینهها ضربه بزنید"</string>
+ <string name="wifi_softap_config_change" msgid="8475911871165857607">"تغییرات در تنظیمات نقطه اتصال"</string>
+ <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"نوار نقطه اتصال شما تغییر کرد."</string>
+ <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"این دستگاه از اولویت فقط ۵ گیگاهرتز شما پشتیبانی نمیکند. هرزمان نوار ۵ گیگاهرتزی دردسترس باشد این دستگاه از آن استفاده خواهد کرد."</string>
<string name="network_switch_metered" msgid="4671730921726992671">"به <xliff:g id="NETWORK_TYPE">%1$s</xliff:g> تغییر کرد"</string>
<string name="network_switch_metered_detail" msgid="775163331794506615">"وقتی <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> به اینترنت دسترسی نداشته باشد، دستگاه از <xliff:g id="NEW_NETWORK">%1$s</xliff:g> استفاده میکند. ممکن است هزینههایی اعمال شود."</string>
<string name="network_switch_metered_toast" msgid="5779283181685974304">"از <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> به <xliff:g id="NEW_NETWORK">%2$s</xliff:g> تغییر کرد"</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 30509b0..36a656b0 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -236,9 +236,6 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Lentokonetila"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Lentokonetila on KÄYTÖSSÄ"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Lentokonetila on POIS KÄYTÖSTÄ"</string>
- <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Virransäästö"</string>
- <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Virransäästö on POISSA KÄYTÖSTÄ"</string>
- <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Virransäästö on KÄYTÖSSÄ"</string>
<string name="global_action_settings" msgid="1756531602592545966">"Asetukset"</string>
<string name="global_action_assist" msgid="3892832961594295030">"Auta"</string>
<string name="global_action_voice_assist" msgid="7751191495200504480">"Ääniapuri"</string>
@@ -276,6 +273,9 @@
<string name="permgrouplab_location" msgid="7275582855722310164">"Sijainti"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"käyttää laitteen sijaintia"</string>
<string name="permgrouprequest_location" msgid="3788275734953323491">"Saako <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> tämän laitteen sijainnin käyttöoikeuden?"</string>
+ <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"Sovellus saa sijainnin käyttöoikeuden vain jos käytät sovellusta."</string>
+ <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"Saako <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> tämän laitteen sijainnin käyttöoikeuden?"</string>
+ <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"Sovellus saa sijainnin käyttöoikeuden aina, vaikka et käyttäisi sovellusta."</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Kalenteri"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"käyttää kalenteria"</string>
<string name="permgrouprequest_calendar" msgid="289900767793189421">"Saako <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> kalenterisi käyttöoikeuden?"</string>
@@ -402,8 +402,7 @@
<string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Tämä sovellus voi lisätä, poistaa tai muuttaa puhelimen kalenteritapahtumia. Sovellus voi lähettää viestejä, jotka näyttävät tulevan kalenterin omistajilta, tai muuttaa kalenteritapahtumia ilmoittamatta omistajille."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"käytä lisää sijainnintarjoajakomentoja"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Antaa sovelluksen käyttää ylimääräisiä sijaintipalvelukomentoja. Sovellus saattaa tällöin häiritä GPS:n tai muiden sijaintilähteiden toimintaa."</string>
- <!-- no translation found for permlab_accessFineLocation (6265109654698562427) -->
- <skip />
+ <string name="permlab_accessFineLocation" msgid="6265109654698562427">"käyttää tarkkaa sijaintia vain etualalla"</string>
<string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Tämä sovellus saa tarkat sijaintitietosi käyttöönsä vain etualalla. Näiden sijaintipalveluiden tulee olla käytössä ja käytettävissä puhelimellasi, jotta sovellus voi käyttää niitä. Tämä voi lisätä akun kulutusta."</string>
<string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"käyttää likimääräistä sijaintia (verkkopohjainen)"</string>
<string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Tämä sovellus voi määrittää sijaintisi matkapuhelinverkon tukiasemien, Wi-Fi-verkkojen ja muiden verkkolähteiden perusteella. Näiden sijaintipalveluiden tulee olla käytössä ja käytettävissä tabletillasi, jotta sovellus voi käyttää niitä."</string>
@@ -518,6 +517,37 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Sormenjälkikuvake"</string>
+ <string name="permlab_manageFace" msgid="2137540986007309781">"hallinnoida kasvojentodennuslaitteistoa"</string>
+ <string name="permdesc_manageFace" msgid="8919637120670185330">"Sallii sovelluksen käyttää menetelmiä, joilla voidaan lisätä tai poistaa kasvomalleja."</string>
+ <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"käyttää kasvojentodennuslaitteistoa"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Sallii sovelluksen käyttää todennuslaitteistoa todennukseen"</string>
+ <string name="face_acquired_insufficient" msgid="5901287247766106330">"Kasvojen käsittely epäonnistui. Yritä uudelleen."</string>
+ <string name="face_acquired_too_bright" msgid="610606792381297174">"Kasvokuva on liian kirkas. Kokeile hämärää valoa."</string>
+ <string name="face_acquired_too_dark" msgid="7229162716976778371">"Kasvokuva on liian tumma. Älä peitä valonlähdettä."</string>
+ <string name="face_acquired_too_close" msgid="1980310037427755293">"Siirrä anturia kauemmas kasvoista."</string>
+ <string name="face_acquired_too_far" msgid="4494571381828850007">"Tuo anturi lähemmäs kasvoja."</string>
+ <string name="face_acquired_too_high" msgid="228411096134808372">"Siirrä anturia korkeammalle."</string>
+ <string name="face_acquired_too_low" msgid="4539774649296349109">"Siirrä anturia alemmas."</string>
+ <string name="face_acquired_too_right" msgid="1650292067226118760">"Siirrä anturia oikealle."</string>
+ <string name="face_acquired_too_left" msgid="2712489669456176505">"Siirrä anturia vasemmalle."</string>
+ <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Katso anturia."</string>
+ <string name="face_acquired_not_detected" msgid="5707782294589511391">"Kasvoja ei havaittu."</string>
+ <string name="face_acquired_not_steady" msgid="3722829465011040042">"Pidä kasvot paikoillaan laitteen edessä."</string>
+ <string-array name="face_acquired_vendor">
+ </string-array>
+ <string name="face_error_hw_not_available" msgid="6255891785768984615">"Kasvolaitteisto ei ole käytettävissä."</string>
+ <string name="face_error_timeout" msgid="4014326147867150054">"Kasvotoiminto aikakatkaistiin. Yritä uudelleen."</string>
+ <string name="face_error_no_space" msgid="8224993703466381314">"Kasvoja ei voi tallentaa."</string>
+ <string name="face_error_canceled" msgid="283945501061931023">"Kasvotoiminto peruutettu"</string>
+ <string name="face_error_lockout" msgid="3407426963155388504">"Liian monta yritystä. Yritä myöhemmin uudelleen."</string>
+ <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Liikaa yrityksiä. Kasvojentodennus ei käytössä."</string>
+ <string name="face_error_unable_to_process" msgid="238761109287767270">"Yritä uudelleen."</string>
+ <string name="face_error_not_enrolled" msgid="9166792142679691323">"Kasvoja ei ole otettu käyttöön."</string>
+ <string name="face_error_hw_not_present" msgid="4737289254517095671">"Laitteessa ei ole kasvojentodennusanturia."</string>
+ <string name="face_name_template" msgid="7004562145809595384">"Kasvot <xliff:g id="FACEID">%d</xliff:g>"</string>
+ <string-array name="face_error_vendor">
+ </string-array>
+ <string name="face_icon_content_description" msgid="4024817159806482191">"Kasvokuvake"</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"lue synkronointiasetuksia"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Antaa sovelluksen lukea tilien synkronointiasetuksia. Sovellus voi esimerkiksi määrittää, onko Henkilöt-sovellus synkronoitu tilin kanssa."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"ota synkronointi käyttöön tai poista se käytöstä"</string>
@@ -1178,6 +1208,9 @@
<skip />
<string name="wifi_no_internet" msgid="8938267198124654938">"Wi-Fi ei ole yhteydessä internetiin"</string>
<string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Näytä vaihtoehdot napauttamalla."</string>
+ <string name="wifi_softap_config_change" msgid="8475911871165857607">"Hotspot-asetustesi muutokset"</string>
+ <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"Hotspot-taajuutesi on muuttunut."</string>
+ <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"Tämä laite ei tue asetustasi (vain 5 GHz). Sen sijaan laite käyttää 5 GHz:n taajuutta sen ollessa käytettävissä."</string>
<string name="network_switch_metered" msgid="4671730921726992671">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> otettiin käyttöön"</string>
<string name="network_switch_metered_detail" msgid="775163331794506615">"<xliff:g id="NEW_NETWORK">%1$s</xliff:g> otetaan käyttöön, kun <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> ei voi muodostaa yhteyttä internetiin. Veloitukset ovat mahdollisia."</string>
<string name="network_switch_metered_toast" msgid="5779283181685974304">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> poistettiin käytöstä ja <xliff:g id="NEW_NETWORK">%2$s</xliff:g> otettiin käyttöön."</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index 2ae1b62..eaa2ba0 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -236,9 +236,6 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Mode Avion"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Le mode Avion est activé."</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Le mode Avion est désactivé."</string>
- <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Économie d\'énergie"</string>
- <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"La fonction Économie d\'énergie est désactivée"</string>
- <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"La fonction Économie d\'énergie est activée"</string>
<string name="global_action_settings" msgid="1756531602592545966">"Paramètres"</string>
<string name="global_action_assist" msgid="3892832961594295030">"Assistance"</string>
<string name="global_action_voice_assist" msgid="7751191495200504480">"Assist. vocale"</string>
@@ -272,22 +269,25 @@
<string name="managed_profile_label" msgid="8947929265267690522">"Passer au profil professionnel"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Contacts"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"accéder à vos contacts"</string>
- <string name="permgrouprequest_contacts" msgid="6032805601881764300">"Autoriser « <xliff:g id="APP_NAME">%1$s</xliff:g> » à accéder à vos contacts?"</string>
+ <string name="permgrouprequest_contacts" msgid="6032805601881764300">"Autoriser <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> à accéder à vos contacts?"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"Localisation"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"accéder à la position de cet appareil"</string>
<string name="permgrouprequest_location" msgid="3788275734953323491">"Autoriser <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> à accéder à la position de cet appareil?"</string>
+ <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"L\'application aura uniquement accès à la position lorsque vous l\'utilisez."</string>
+ <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"Toujours autoriser <xliff:g id="APP_NAME">%1$s</xliff:g> à accéder à la position de cet appareil?"</string>
+ <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"L\'application aura toujours accès à la position, même lorsque vous ne l\'utilisez pas."</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Agenda"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"accéder à votre agenda"</string>
- <string name="permgrouprequest_calendar" msgid="289900767793189421">"Autoriser « <xliff:g id="APP_NAME">%1$s</xliff:g> » à accéder à votre agenda?"</string>
+ <string name="permgrouprequest_calendar" msgid="289900767793189421">"Autoriser <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> à accéder à votre agenda?"</string>
<string name="permgrouplab_sms" msgid="228308803364967808">"Messagerie texte"</string>
<string name="permgroupdesc_sms" msgid="4656988620100940350">"envoyer et afficher des messages texte"</string>
- <string name="permgrouprequest_sms" msgid="7168124215838204719">"Autoriser « <xliff:g id="APP_NAME">%1$s</xliff:g> » à envoyer et à afficher des messages texte?"</string>
+ <string name="permgrouprequest_sms" msgid="7168124215838204719">"Autoriser <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> à envoyer et à afficher des messages texte?"</string>
<string name="permgrouplab_storage" msgid="1971118770546336966">"Stockage"</string>
<string name="permgroupdesc_storage" msgid="637758554581589203">"accéder aux photos, aux contenus multimédias et aux fichiers sur votre appareil"</string>
<string name="permgrouprequest_storage" msgid="7885942926944299560">"Autoriser « <xliff:g id="APP_NAME">%1$s</xliff:g> » à accéder aux photos, aux médias et aux fichiers de votre appareil?"</string>
<string name="permgrouplab_microphone" msgid="171539900250043464">"Microphone"</string>
<string name="permgroupdesc_microphone" msgid="4988812113943554584">"enregistrer des fichiers audio"</string>
- <string name="permgrouprequest_microphone" msgid="9167492350681916038">"Autoriser « <xliff:g id="APP_NAME">%1$s</xliff:g> » à enregistrer de l\'audio?"</string>
+ <string name="permgrouprequest_microphone" msgid="9167492350681916038">"Autoriser <b><xliff:g id="APP_NAME">%1$s</xliff:g></b&gt à enregistrer l\'audio?"</string>
<string name="permgrouplab_camera" msgid="4820372495894586615">"Appareil photo"</string>
<string name="permgroupdesc_camera" msgid="3250611594678347720">"prendre des photos et filmer des vidéos"</string>
<string name="permgrouprequest_camera" msgid="1299833592069671756">"Autoriser « <xliff:g id="APP_NAME">%1$s</xliff:g> » à prendre des photos et à filmer des vidéos?"</string>
@@ -296,7 +296,7 @@
<string name="permgrouprequest_calllog" msgid="8487355309583773267">"Autoriser <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> à accéder à vos journaux d\'appels?"</string>
<string name="permgrouplab_phone" msgid="5229115638567440675">"Téléphone"</string>
<string name="permgroupdesc_phone" msgid="6234224354060641055">"faire et gérer des appels téléphoniques"</string>
- <string name="permgrouprequest_phone" msgid="9166979577750581037">"Autoriser « <xliff:g id="APP_NAME">%1$s</xliff:g> » à faire et à gérer les appels téléphoniques?"</string>
+ <string name="permgrouprequest_phone" msgid="9166979577750581037">"Autoriser <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> à faire et à gérer les appels téléphoniques?"</string>
<string name="permgrouplab_sensors" msgid="416037179223226722">"Capteurs corporels"</string>
<string name="permgroupdesc_sensors" msgid="7147968539346634043">"accéder aux données des capteurs sur vos signes vitaux"</string>
<string name="permgrouprequest_sensors" msgid="6349806962814556786">"Autoriser « <xliff:g id="APP_NAME">%1$s</xliff:g> » à accéder aux données des capteurs pour vos signes vitaux?"</string>
@@ -402,8 +402,7 @@
<string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Cette application peut ajouter, supprimer et modifier des événements d\'agenda sur votre téléphone. Elle peut aussi envoyer des messages qui pourraient sembler venir des propriétaires d\'agenda en question ou modifier des événements sans avertir leur propriétaire."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"accéder aux commandes de fournisseur de position géographique supplémentaires"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Permet à l\'application d\'accéder à des commandes de localisation supplémentaires offertes par le fournisseur. Elle est ainsi susceptible d\'interférer avec le bon fonctionnement du GPS ou de toute autre source de localisation."</string>
- <!-- no translation found for permlab_accessFineLocation (6265109654698562427) -->
- <skip />
+ <string name="permlab_accessFineLocation" msgid="6265109654698562427">"accéder à votre position précise seulement en avant-plan"</string>
<string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Cette application peut obtenir votre position exacte seulement lorsqu\'elle fonctionne en avant-plan. Ces services de localisation doivent être activés et accessibles sur votre téléviseur pour que l\'application puisse les utiliser. Cela peut entraîner une utilisation accrue de la pile."</string>
<string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"accéder à votre position approximative (réseau)"</string>
<string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Cette application peut déterminer votre position à l\'aide de différentes sources de localisation sur le réseau, comme les tours de téléphonie cellulaire et les réseaux Wi-Fi. Ces services de localisation doivent être activés et accessibles sur votre tablette pour que l\'application puisse les utiliser."</string>
@@ -518,6 +517,37 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Icône d\'empreinte digitale"</string>
+ <string name="permlab_manageFace" msgid="2137540986007309781">"gérer le matériel d\'authentification de visage"</string>
+ <string name="permdesc_manageFace" msgid="8919637120670185330">"Permet à l\'appli d\'employer des méthodes d\'aj. et de suppr. de modèles de reconn. visage."</string>
+ <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"utiliser le matériel d\'authentification de visage"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Permet à l\'appli d\'utiliser du matériel de reconnaissance du visage pour l\'authentification"</string>
+ <string name="face_acquired_insufficient" msgid="5901287247766106330">"Impossible de traiter le visage. Réessayez."</string>
+ <string name="face_acquired_too_bright" msgid="610606792381297174">"Visage trop lumineux. Essayez avec moins de lumière."</string>
+ <string name="face_acquired_too_dark" msgid="7229162716976778371">"Visage trop sombre. Essayez avec plus de lumière."</string>
+ <string name="face_acquired_too_close" msgid="1980310037427755293">"Veuillez éloigner le capteur du visage."</string>
+ <string name="face_acquired_too_far" msgid="4494571381828850007">"Veuillez rapprocher le capteur du visage."</string>
+ <string name="face_acquired_too_high" msgid="228411096134808372">"Veuillez déplacer le capteur plus haut."</string>
+ <string name="face_acquired_too_low" msgid="4539774649296349109">"Veuillez déplacer le capteur plus bas."</string>
+ <string name="face_acquired_too_right" msgid="1650292067226118760">"Veuillez déplacer le capteur vers la droite."</string>
+ <string name="face_acquired_too_left" msgid="2712489669456176505">"Veuillez déplacer le capteur vers la gauche."</string>
+ <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Veuillez regarder le capteur."</string>
+ <string name="face_acquired_not_detected" msgid="5707782294589511391">"Aucun visage détecté."</string>
+ <string name="face_acquired_not_steady" msgid="3722829465011040042">"Gardez le visage stable devant l\'appareil."</string>
+ <string-array name="face_acquired_vendor">
+ </string-array>
+ <string name="face_error_hw_not_available" msgid="6255891785768984615">"Matériel de reconnaissance du visage indisponible."</string>
+ <string name="face_error_timeout" msgid="4014326147867150054">"Temps de reconn. visage écoulé. Veuillez réessayer."</string>
+ <string name="face_error_no_space" msgid="8224993703466381314">"Impossible de stocker le visage."</string>
+ <string name="face_error_canceled" msgid="283945501061931023">"Opération de reconnaissance du visage annulée."</string>
+ <string name="face_error_lockout" msgid="3407426963155388504">"Trop de tentatives. Veuillez réessayer plus tard."</string>
+ <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Trop de tentatives. Capt. reconn. visage désactivé."</string>
+ <string name="face_error_unable_to_process" msgid="238761109287767270">"Réessayez."</string>
+ <string name="face_error_not_enrolled" msgid="9166792142679691323">"Aucun visage inscrit."</string>
+ <string name="face_error_hw_not_present" msgid="4737289254517095671">"Cet appareil ne possède pas de capteur de reconn. du visage"</string>
+ <string name="face_name_template" msgid="7004562145809595384">"Visage <xliff:g id="FACEID">%d</xliff:g>"</string>
+ <string-array name="face_error_vendor">
+ </string-array>
+ <string name="face_icon_content_description" msgid="4024817159806482191">"Icône visage"</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"lire les paramètres de synchronisation"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Permet à l\'application d\'accéder aux paramètres de synchronisation d\'un compte. Par exemple, cette autorisation peut permettre de déterminer si l\'application Contacts est synchronisée avec un compte ou non."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"activer ou désactiver la synchronisation"</string>
@@ -1178,6 +1208,9 @@
<skip />
<string name="wifi_no_internet" msgid="8938267198124654938">"Le réseau Wi-Fi ne dispose d\'aucun accès à Internet"</string>
<string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Touchez pour afficher les options"</string>
+ <string name="wifi_softap_config_change" msgid="8475911871165857607">"Modifications apportées à vos paramètres de point d\'accès"</string>
+ <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"La bande de votre point d\'accès a changé."</string>
+ <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"Cet appareil ne prend pas en charge votre préférence pour la bande de 5 GHz seulement. Au lieu de cela, cet appareil utilisera la bande de 5 GHz lorsqu\'elle sera disponible."</string>
<string name="network_switch_metered" msgid="4671730921726992671">"Passé au réseau <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
<string name="network_switch_metered_detail" msgid="775163331794506615">"L\'appareil utilise <xliff:g id="NEW_NETWORK">%1$s</xliff:g> quand <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> n\'a pas d\'accès à Internet. Des frais peuvent s\'appliquer."</string>
<string name="network_switch_metered_toast" msgid="5779283181685974304">"Passé du réseau <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> au <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 4af3faa..33053d2 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -236,9 +236,6 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Mode Avion"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Le mode Avion est activé."</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Le mode Avion est désactivé."</string>
- <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Économiseur de batterie"</string>
- <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Économiseur de batterie DÉSACTIVÉ"</string>
- <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Économiseur de batterie ACTIVÉ"</string>
<string name="global_action_settings" msgid="1756531602592545966">"Paramètres"</string>
<string name="global_action_assist" msgid="3892832961594295030">"Assistance"</string>
<string name="global_action_voice_assist" msgid="7751191495200504480">"Assistance vocale"</string>
@@ -276,6 +273,9 @@
<string name="permgrouplab_location" msgid="7275582855722310164">"Localisation"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"accéder à la position de l\'appareil"</string>
<string name="permgrouprequest_location" msgid="3788275734953323491">"Permettre à <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> d\'accéder à la position de cet appareil ?"</string>
+ <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"L\'application n\'a accès à la position de l\'appareil que lorsqu\'elle est en cours d\'utilisation."</string>
+ <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"Toujours autoriser <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> à accéder à la position de l\'appareil ?"</string>
+ <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"L\'application a toujours accès à la position de l\'appareil, même lorsqu\'elle n\'est pas en cours d\'utilisation."</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Agenda"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"accéder à votre agenda"</string>
<string name="permgrouprequest_calendar" msgid="289900767793189421">"Permettre à <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> d\'accéder à votre agenda ?"</string>
@@ -293,7 +293,7 @@
<string name="permgrouprequest_camera" msgid="1299833592069671756">"Permettre à <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> de prendre des photos et de filmer des vidéos ?"</string>
<string name="permgrouplab_calllog" msgid="8798646184930388160">"Journaux d\'appels"</string>
<string name="permgroupdesc_calllog" msgid="3006237336748283775">"Lire et écrire les journaux d\'appels du téléphone"</string>
- <string name="permgrouprequest_calllog" msgid="8487355309583773267">"Autoriser <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> à accéder aux journaux d\'appels de votre téléphone ?"</string>
+ <string name="permgrouprequest_calllog" msgid="8487355309583773267">"Permettre à <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> d\'accéder aux journaux d\'appels de votre téléphone ?"</string>
<string name="permgrouplab_phone" msgid="5229115638567440675">"Téléphone"</string>
<string name="permgroupdesc_phone" msgid="6234224354060641055">"effectuer et gérer des appels téléphoniques"</string>
<string name="permgrouprequest_phone" msgid="9166979577750581037">"Permettre à <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> de passer et gérer des appels téléphoniques ?"</string>
@@ -402,8 +402,7 @@
<string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Cette application peut ajouter, supprimer ou modifier les événements d\'agenda enregistrés sur votre téléphone. Elle peut en outre envoyer des messages qui semblent provenir du propriétaire de l\'agenda, ou modifier des événements sans en informer leur propriétaire."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"Accès aux commandes de fournisseur de position géographique supplémentaires"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Permet à l\'application d\'accéder à des commandes de localisation supplémentaires offertes par le fournisseur. Elle est ainsi susceptible d\'interférer avec le bon fonctionnement du GPS ou de toute autre source de localisation."</string>
- <!-- no translation found for permlab_accessFineLocation (6265109654698562427) -->
- <skip />
+ <string name="permlab_accessFineLocation" msgid="6265109654698562427">"accéder à la position exacte au premier plan uniquement"</string>
<string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Cette application peut obtenir votre position exacte uniquement lorsqu\'elle s\'exécute au premier plan. Ces services de localisation doivent être activés et disponibles sur votre téléphone pour que l\'application puisse les utiliser. Ceci peut réduire l\'autonomie de la batterie."</string>
<string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"accéder à votre position approximative (selon le réseau)"</string>
<string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Cette application peut obtenir votre position via des sources de réseau telles que les antennes-relais et les réseaux Wi-Fi. Ces services de localisation doivent être activés et disponibles sur votre tablette pour que l\'application puisse les utiliser."</string>
@@ -518,6 +517,37 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Icône d\'empreinte digitale"</string>
+ <string name="permlab_manageFace" msgid="2137540986007309781">"gérer le matériel d\'authentification faciale"</string>
+ <string name="permdesc_manageFace" msgid="8919637120670185330">"Autorise l\'appli à invoquer des méthodes pour ajouter et supprimer des modèles de visages."</string>
+ <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"utiliser le matériel d\'authentification faciale"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Autorise l\'appli à utiliser le matériel d\'authentification faciale pour l\'authentification"</string>
+ <string name="face_acquired_insufficient" msgid="5901287247766106330">"Impossible reconnaître visage. Veuillez réessayer."</string>
+ <string name="face_acquired_too_bright" msgid="610606792381297174">"Visage trop éclairé. Veuillez réduire la lumière."</string>
+ <string name="face_acquired_too_dark" msgid="7229162716976778371">"Visage trop sombre. Veuillez accroître la lumière."</string>
+ <string name="face_acquired_too_close" msgid="1980310037427755293">"Veuillez éloigner le capteur de votre visage."</string>
+ <string name="face_acquired_too_far" msgid="4494571381828850007">"Veuillez rapprocher le capteur de votre visage."</string>
+ <string name="face_acquired_too_high" msgid="228411096134808372">"Veuillez déplacer le capteur vers le haut."</string>
+ <string name="face_acquired_too_low" msgid="4539774649296349109">"Veuillez déplacer le capteur vers le bas."</string>
+ <string name="face_acquired_too_right" msgid="1650292067226118760">"Veuillez déplacer le capteur vers la droite."</string>
+ <string name="face_acquired_too_left" msgid="2712489669456176505">"Veuillez déplacer le capteur vers la gauche."</string>
+ <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Veuillez regarder le capteur."</string>
+ <string name="face_acquired_not_detected" msgid="5707782294589511391">"Aucun visage détecté."</string>
+ <string name="face_acquired_not_steady" msgid="3722829465011040042">"Maintenez votre visage stable devant l\'appareil."</string>
+ <string-array name="face_acquired_vendor">
+ </string-array>
+ <string name="face_error_hw_not_available" msgid="6255891785768984615">"Matériel de reconnaissance faciale indisponible."</string>
+ <string name="face_error_timeout" msgid="4014326147867150054">"Délai de détection du visage expiré. Réessayez."</string>
+ <string name="face_error_no_space" msgid="8224993703466381314">"Impossible de stocker les informations du visage."</string>
+ <string name="face_error_canceled" msgid="283945501061931023">"Opération de reconnaissance faciale annulée."</string>
+ <string name="face_error_lockout" msgid="3407426963155388504">"Trop de tentatives. Réessayez plus tard."</string>
+ <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Trop d\'essais. Authentification faciale désactivée."</string>
+ <string name="face_error_unable_to_process" msgid="238761109287767270">"Réessayez."</string>
+ <string name="face_error_not_enrolled" msgid="9166792142679691323">"Aucun visage enregistré."</string>
+ <string name="face_error_hw_not_present" msgid="4737289254517095671">"Aucun capteur d\'authentification faciale sur cet appareil"</string>
+ <string name="face_name_template" msgid="7004562145809595384">"Visage <xliff:g id="FACEID">%d</xliff:g>"</string>
+ <string-array name="face_error_vendor">
+ </string-array>
+ <string name="face_icon_content_description" msgid="4024817159806482191">"Icône visage"</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"lire les paramètres de synchronisation"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Permet à l\'application d\'accéder aux paramètres de synchronisation d\'un compte. Par exemple, cette autorisation peut permettre de déterminer si l\'application Contacts est synchronisée avec un compte ou non."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"activer/désactiver la synchronisation"</string>
@@ -608,7 +638,7 @@
<string name="policylab_disableCamera" msgid="6395301023152297826">"Désactiver les appareils photo"</string>
<string name="policydesc_disableCamera" msgid="2306349042834754597">"Empêcher l\'utilisation de tous les appareils photos"</string>
<string name="policylab_disableKeyguardFeatures" msgid="8552277871075367771">"Désact. options du verr. écran"</string>
- <string name="policydesc_disableKeyguardFeatures" msgid="2044755691354158439">"Empêchez l\'utilisation de certaines fonctionnalités du verrouillage de l\'écran."</string>
+ <string name="policydesc_disableKeyguardFeatures" msgid="2044755691354158439">"Empêcher l\'utilisation de certaines fonctionnalités du verrouillage de l\'écran."</string>
<string-array name="phoneTypes">
<item msgid="8901098336658710359">"Domicile"</item>
<item msgid="869923650527136615">"Mobile"</item>
@@ -1178,6 +1208,9 @@
<skip />
<string name="wifi_no_internet" msgid="8938267198124654938">"Impossible de se connecter à Internet via le réseau Wi-Fi"</string>
<string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Appuyez ici pour afficher des options."</string>
+ <string name="wifi_softap_config_change" msgid="8475911871165857607">"Modifications apportées à vos paramètres de point d\'accès"</string>
+ <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"Votre bande de point d\'accès a été modifiée."</string>
+ <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"Cet appareil n\'est pas compatible avec votre préférence d\'utilisation de la bande 5 GHz uniquement. Il utilisera la bande 5 GHz lorsqu\'elle sera disponible."</string>
<string name="network_switch_metered" msgid="4671730921726992671">"Nouveau réseau : <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
<string name="network_switch_metered_detail" msgid="775163331794506615">"L\'appareil utilise <xliff:g id="NEW_NETWORK">%1$s</xliff:g> lorsque <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> n\'a pas de connexion Internet. Des frais peuvent s\'appliquer."</string>
<string name="network_switch_metered_toast" msgid="5779283181685974304">"Ancien réseau : <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g>. Nouveau réseau : <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index c59d928..a5327f6 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -236,9 +236,6 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Modo avión"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"O modo avión está activado"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"O modo avión está desactivado"</string>
- <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Aforro de batería"</string>
- <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"A función Aforro de batería está DESACTIVADA"</string>
- <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"A función Aforro de batería está ACTIVADA"</string>
<string name="global_action_settings" msgid="1756531602592545966">"Configuración"</string>
<string name="global_action_assist" msgid="3892832961594295030">"Asistencia"</string>
<string name="global_action_voice_assist" msgid="7751191495200504480">"Asistente voz"</string>
@@ -276,6 +273,9 @@
<string name="permgrouplab_location" msgid="7275582855722310164">"Localización"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"acceder á localización deste dispositivo"</string>
<string name="permgrouprequest_location" msgid="3788275734953323491">"Queres permitir que a aplicación <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> acceda á localización deste dispositivo?"</string>
+ <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"A aplicación só terá acceso á localización mentres a esteas utilizando."</string>
+ <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"Queres permitir que <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> acceda á localización?"</string>
+ <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"A aplicación sempre terá acceso á localización, aínda que non a esteas utilizando."</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Calendario"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"acceder ao teu calendario"</string>
<string name="permgrouprequest_calendar" msgid="289900767793189421">"Queres permitir que a aplicación <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> acceda ao teu calendario?"</string>
@@ -402,8 +402,7 @@
<string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Esta aplicación pode engadir, quitar ou cambiar eventos do calendario almacenados no teu teléfono. Tamén pode enviar mensaxes que parezan dos propietarios do calendario e cambiar eventos sen comunicárllelo aos propietarios."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"acceder a comandos adicionais do provedor de localización"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Permite á aplicación acceder a comandos adicionais de fornecedor de localizacións. É posible que isto provoque que a aplicación interfira co funcionamento do GPS ou doutras fontes da localización."</string>
- <!-- no translation found for permlab_accessFineLocation (6265109654698562427) -->
- <skip />
+ <string name="permlab_accessFineLocation" msgid="6265109654698562427">"acceder á localización exacta só en primeiro plano"</string>
<string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Esta aplicación pode obter a túa localización exacta só cando se atope en primeiro plano. É necesario activar estes servizos de localización e deben estar dispoñibles no teléfono para que a aplicación poida utilizalos. Ademais, poden supoñer un aumento do consumo de batería."</string>
<string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"acceder á localización aproximada (baseada na rede)"</string>
<string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Esta aplicación pode obter a túa localización a partir de fontes de rede como torres de telecomunicacións e redes wifi. Para que a aplicación poida utilizar os servizos de localización, deben estar activados e dispoñibles na túa tableta."</string>
@@ -518,6 +517,37 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Icona de impresión dixital"</string>
+ <string name="permlab_manageFace" msgid="2137540986007309781">"xestionar hardware de autenticación facial"</string>
+ <string name="permdesc_manageFace" msgid="8919637120670185330">"Permite que a aplicación invoque métodos para engadir e eliminar modelos faciais de uso."</string>
+ <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"usar hardware de autenticación facial"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Permite que a aplicación utilice hardware facial para a autenticación"</string>
+ <string name="face_acquired_insufficient" msgid="5901287247766106330">"Non se puido procesar a cara. Téntao de novo."</string>
+ <string name="face_acquired_too_bright" msgid="610606792381297174">"A cara vese demasiado brillante. Proba con menos luz."</string>
+ <string name="face_acquired_too_dark" msgid="7229162716976778371">"A cara vese demasiado escura. Proba con máis luz."</string>
+ <string name="face_acquired_too_close" msgid="1980310037427755293">"Afasta o sensor da cara."</string>
+ <string name="face_acquired_too_far" msgid="4494571381828850007">"Achega o sensor á cara."</string>
+ <string name="face_acquired_too_high" msgid="228411096134808372">"Sube máis o sensor."</string>
+ <string name="face_acquired_too_low" msgid="4539774649296349109">"Baixa o sensor."</string>
+ <string name="face_acquired_too_right" msgid="1650292067226118760">"Move o sensor cara á dereita."</string>
+ <string name="face_acquired_too_left" msgid="2712489669456176505">"Move o sensor cara á esquerda."</string>
+ <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Mira ao sensor."</string>
+ <string name="face_acquired_not_detected" msgid="5707782294589511391">"Non se detectou ningunha cara."</string>
+ <string name="face_acquired_not_steady" msgid="3722829465011040042">"Mantén a cara fixa diante do dispositivo."</string>
+ <string-array name="face_acquired_vendor">
+ </string-array>
+ <string name="face_error_hw_not_available" msgid="6255891785768984615">"O hardware facial non está dispoñible."</string>
+ <string name="face_error_timeout" msgid="4014326147867150054">"Esgotouse o tempo de espera. Téntao de novo."</string>
+ <string name="face_error_no_space" msgid="8224993703466381314">"Non se puido almacenar a cara."</string>
+ <string name="face_error_canceled" msgid="283945501061931023">"Cancelouse a operación relacionada coa cara"</string>
+ <string name="face_error_lockout" msgid="3407426963155388504">"Demasiados intentos. Téntao de novo máis tarde."</string>
+ <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Demasiados intentos. Autenticación desactivada."</string>
+ <string name="face_error_unable_to_process" msgid="238761109287767270">"Téntao de novo."</string>
+ <string name="face_error_not_enrolled" msgid="9166792142679691323">"Non se rexistrou ningunha cara."</string>
+ <string name="face_error_hw_not_present" msgid="4737289254517095671">"Este dispositivo non ten un sensor de autenticación facial"</string>
+ <string name="face_name_template" msgid="7004562145809595384">"Cara <xliff:g id="FACEID">%d</xliff:g>"</string>
+ <string-array name="face_error_vendor">
+ </string-array>
+ <string name="face_icon_content_description" msgid="4024817159806482191">"Icona cara"</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"ler a configuración de sincronización"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Permite á aplicación ler a configuración de sincronización dunha conta. Por exemplo, esta acción pode determinar se a aplicación Contactos se sincroniza cunha conta."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"activar e desactivar a sincronización"</string>
@@ -1178,6 +1208,9 @@
<skip />
<string name="wifi_no_internet" msgid="8938267198124654938">"A wifi non ten acceso a Internet"</string>
<string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Toca para ver opcións."</string>
+ <string name="wifi_softap_config_change" msgid="8475911871165857607">"Cambios na configuración da zona wifi"</string>
+ <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"Modificouse a banda da zona wifi."</string>
+ <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"Este dispositivo non admite a opción de conectarse só a bandas de 5 GHz, pero usaraas se están dispoñibles."</string>
<string name="network_switch_metered" msgid="4671730921726992671">"Cambiouse a: <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
<string name="network_switch_metered_detail" msgid="775163331794506615">"O dispositivo utiliza <xliff:g id="NEW_NETWORK">%1$s</xliff:g> cando <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> non ten acceso a Internet. Pódense aplicar cargos."</string>
<string name="network_switch_metered_toast" msgid="5779283181685974304">"Cambiouse de <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> a <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index 6599fd3..d41a923 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -236,9 +236,6 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"એરપ્લેન મોડ"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"એરપ્લેન મોડ ચાલુ છે."</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"એરપ્લેન મોડ બંધ છે."</string>
- <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"બૅટરી સેવર"</string>
- <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"બૅટરી સેવર બંધ છે"</string>
- <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"બૅટરી સેવર ચાલુ છે"</string>
<string name="global_action_settings" msgid="1756531602592545966">"સેટિંગ્સ"</string>
<string name="global_action_assist" msgid="3892832961594295030">"સહાય"</string>
<string name="global_action_voice_assist" msgid="7751191495200504480">"વૉઇસ સહાય"</string>
@@ -276,6 +273,9 @@
<string name="permgrouplab_location" msgid="7275582855722310164">"સ્થાન"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"આ ઉપકરણના સ્થાનને ઍક્સેસ કરવાની"</string>
<string name="permgrouprequest_location" msgid="3788275734953323491">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>ને આ ઉપકરણના સ્થાનને ઍક્સેસ કરવાની મંજૂરી આપીએ?"</string>
+ <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"માત્ર જ્યારે તમે ઍપનો ઉપયોગ કરી રહ્યાં હોય ત્યારે જ ઍપ સ્થાનને ઍક્સેસ કરી શકશે."</string>
+ <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>ને હંમેશાં આ ઉપકરણના સ્થાનને ઍક્સેસ કરવાની મંજૂરી આપીએ?"</string>
+ <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"તમે ઍપનો ઉપયોગ કરી રહ્યાં ન હોય, તો પણ ઍપ હંમેશાં સ્થાનને ઍક્સેસ કરી શકશે."</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"કૅલેન્ડર"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"તમારા કેલેન્ડરને ઍક્સેસ કરવાની"</string>
<string name="permgrouprequest_calendar" msgid="289900767793189421">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>ને તમારા કૅલેન્ડરને ઍક્સેસ કરવાની મંજૂરી આપીએ?"</string>
@@ -402,8 +402,7 @@
<string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"આ ઍપ્લિકેશન, તમારા ફોન પર કૅલેન્ડર ઇવેન્ટ્સ ઉમેરી, દૂર કરી અથવા બદલી શકે છે. આ ઍપ્લિકેશન, કૅલેન્ડર માલિકો તરફથી આવતાં હોય તેવા લાગતાં સંદેશા મોકલી શકે છે અથવા તેમના માલિકોને સૂચિત કર્યા વિના ઇવેન્ટ્સ બદલી શકે છે."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"વધારાના સ્થાન પ્રદાતા આદેશોને ઍક્સેસ કરો"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"એપ્લિકેશનને વધારાના સ્થાન પ્રદાતા આદેશોને ઍક્સેસ કરવાની મંજૂરી આપે છે. આ એપ્લિકેશનને GPS અથવા અન્ય સ્થાન સ્રોતોના ઓપરેશનમાં દખલ કરવાની મંજૂરી આપી શકે છે."</string>
- <!-- no translation found for permlab_accessFineLocation (6265109654698562427) -->
- <skip />
+ <string name="permlab_accessFineLocation" msgid="6265109654698562427">"ફૉરગ્રાઉન્ડમાં ફક્ત ચોક્કસ સ્થાન ઍક્સેસ કરો"</string>
<string name="permdesc_accessFineLocation" msgid="3520508381065331098">"આ ઍપ ફક્ત બૅકગ્રાઉન્ડમાં હોય ત્યારે જ તમારું ચોક્કસ સ્થાન મેળવી શકે છે. ઍપ આ સ્થાન સેવાઓનો ઉપયોગ કરી શકે તે માટે તમારા ફોન પર આ સેવાઓ ઉપલબ્ધ અને ચાલુ હોવી આવશ્યક છે. આ બૅટરી વપરાશ વધારી શકે છે."</string>
<string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"અંદાજિત સ્થાન ઍક્સેસ કરો (નેટવર્ક-આધારિત)"</string>
<string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"આ ઍપ્લિકેશન, સેલ ટાવર્સ અને વાઇ-ફાઇ નેટવર્ક્સ જેવા નેટવર્ક સ્રોતોના આધારે તમારું સ્થાન મેળવી શકે છે. ઍપ્લિકેશન દ્વારા આ સ્થાન સેવાઓનો ઉપયોગ કરવામાં સમર્થ થવા માટે તમારા ટેબ્લેટ પર આ ઉપલબ્ધ અને ચાલુ હોવી આવશ્યક છે."</string>
@@ -518,6 +517,37 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"ફિંગરપ્રિન્ટ આયકન"</string>
+ <string name="permlab_manageFace" msgid="2137540986007309781">"ચહેરા પ્રમાણીકરણના હાર્ડવેરને મેનેજ કરો"</string>
+ <string name="permdesc_manageFace" msgid="8919637120670185330">"ઍપને ઉપયોગ માટે ચહેરાના નમૂના ઉમેરવા અને ડિલીટ કરવાની પદ્ધતિને રદ કરવાની મંજૂરી આપે છે."</string>
+ <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"ચહેરા પ્રમાણીકરણના હાર્ડવેરનો ઉપયોગ કરો"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"ઍપને પ્રમાણીકરણ માટે ચહેરા પ્રમાણીકરણના હાર્ડવેરનો ઉપયોગ કરવાની મંજૂરી આપે છે"</string>
+ <string name="face_acquired_insufficient" msgid="5901287247766106330">"ચહેરાની પ્રક્રિયા કરી શકાઈ નથી. ફરી પ્રયાસ કરો."</string>
+ <string name="face_acquired_too_bright" msgid="610606792381297174">"ચહેરો ખૂબ ચળકે છે. કૃપા કરીને ઓછા પ્રકાશવાળા સ્થાનમાં પ્રયાસ કરો."</string>
+ <string name="face_acquired_too_dark" msgid="7229162716976778371">"ચહેરો ખૂબ શ્યામ છે. કૃપા કરીને પ્રકાશના સૉર્સ ઉઘાડો."</string>
+ <string name="face_acquired_too_close" msgid="1980310037427755293">"કૃપા કરીને સેન્સરને ચહેરાથી દૂર ખસેડો."</string>
+ <string name="face_acquired_too_far" msgid="4494571381828850007">"કૃપા કરીને સેન્સરને ચહેરાની નજીક ખસેડો."</string>
+ <string name="face_acquired_too_high" msgid="228411096134808372">"કૃપા કરીને સેન્સરને ઉપર ખસેડો."</string>
+ <string name="face_acquired_too_low" msgid="4539774649296349109">"કૃપા કરીને સેન્સર નીચે ખસેડો."</string>
+ <string name="face_acquired_too_right" msgid="1650292067226118760">"કૃપા કરીને સેન્સરને જમણી બાજુ ખસેડો."</string>
+ <string name="face_acquired_too_left" msgid="2712489669456176505">"કૃપા કરીને સેન્સરને ડાબી બાજુ ખસેડો."</string>
+ <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"કૃપા કરીને સેન્સરની સામે જુઓ."</string>
+ <string name="face_acquired_not_detected" msgid="5707782294589511391">"કોઈ ચહેરો મળ્યો નથી."</string>
+ <string name="face_acquired_not_steady" msgid="3722829465011040042">"ઉપકરણની સામે ચહેરો સ્થિર રાખો."</string>
+ <string-array name="face_acquired_vendor">
+ </string-array>
+ <string name="face_error_hw_not_available" msgid="6255891785768984615">"ચહેરા માટેનું હાર્ડવેર ઉપલબ્ધ નથી."</string>
+ <string name="face_error_timeout" msgid="4014326147867150054">"ચહેરા માટેનો સમય સમાપ્ત થયો. ફરી પ્રયાસ કરો."</string>
+ <string name="face_error_no_space" msgid="8224993703466381314">"ચહેરો સંગ્રહિત કરી શકાશે નહીં."</string>
+ <string name="face_error_canceled" msgid="283945501061931023">"ચહેરા સંબંધિત કાર્યવાહી રદ કરવામાં આવી છે."</string>
+ <string name="face_error_lockout" msgid="3407426963155388504">"ઘણા બધા પ્રયત્નો. થોડા સમય પછી ફરી પ્રયાસ કરો."</string>
+ <string name="face_error_lockout_permanent" msgid="8198354656746088890">"ઘણા બધા પ્રયત્નો. ચહેરાનું પ્રમાણીકરણ બંધ કરવામાં આવ્યું છે."</string>
+ <string name="face_error_unable_to_process" msgid="238761109287767270">"ફરી પ્રયાસ કરો."</string>
+ <string name="face_error_not_enrolled" msgid="9166792142679691323">"કોઈ ચહેરાની નોંધણી કરવામાં આવી નથી."</string>
+ <string name="face_error_hw_not_present" msgid="4737289254517095671">"આ ઉપકરણમાં ચહેરાના પ્રમાણીકરણ માટે કોઈ સેન્સર નથી"</string>
+ <string name="face_name_template" msgid="7004562145809595384">"ચહેરાનું <xliff:g id="FACEID">%d</xliff:g>"</string>
+ <string-array name="face_error_vendor">
+ </string-array>
+ <string name="face_icon_content_description" msgid="4024817159806482191">"ચહેરા આઇકન"</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"સમન્વયન સેટિંગ્સ વાંચો"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"ઍપ્લિકેશનને એકાઉન્ટ માટે સમન્વયન સેટિંગ્સને વાંચવાની મંજૂરી આપે છે. ઉદાહરણ તરીકે, આ એકાઉન્ટ સાથે લોકો ઍપ્લિકેશન સમન્વયિત થઈ છે કે કેમ તે નિર્ધારિત કરી શકે છે."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"સમન્વયન ચાલુ અને બંધ ટોગલ કરો"</string>
@@ -1178,6 +1208,9 @@
<skip />
<string name="wifi_no_internet" msgid="8938267198124654938">"વાઇ-ફાઇને કોઈ ઇન્ટરનેટ ઍક્સેસ નથી"</string>
<string name="wifi_no_internet_detailed" msgid="8083079241212301741">"વિકલ્પો માટે ટૅપ કરો"</string>
+ <string name="wifi_softap_config_change" msgid="8475911871165857607">"તમારી હૉટસ્પૉટ સેટિંગને બદલે છે"</string>
+ <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"તમારું હૉટસ્પૉટ બેન્ડ બદલાયેલ છે."</string>
+ <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"આ ઉપકરણ તમારી ફક્ત 5GHz માટેની પસંદગીને સમર્થન આપતું નથી. તેના બદલે, જ્યારે આ ઉપકરણ જ્યારે 5GHz બેન્ડ ઉપલબ્ધ હશે ત્યારે તેનો ઉપયોગ કરશે."</string>
<string name="network_switch_metered" msgid="4671730921726992671">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> પર સ્વિચ કર્યું"</string>
<string name="network_switch_metered_detail" msgid="775163331794506615">"જ્યારે <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> પાસે કોઈ ઇન્ટરનેટ ઍક્સેસ ન હોય ત્યારે ઉપકરણ <xliff:g id="NEW_NETWORK">%1$s</xliff:g>નો ઉપયોગ કરે છે. શુલ્ક લાગુ થઈ શકે છે."</string>
<string name="network_switch_metered_toast" msgid="5779283181685974304">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> પરથી <xliff:g id="NEW_NETWORK">%2$s</xliff:g> પર સ્વિચ કર્યું"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index d6924ee..8c7a0d3 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -236,9 +236,6 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"हवाई जहाज मोड"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"हवाई जहाज मोड चालू है"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"हवाई जहाज मोड बंद है"</string>
- <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"बैटरी सेवर"</string>
- <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"बैटरी सेवर बंद है"</string>
- <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"बैटरी सेवर चालू है"</string>
<string name="global_action_settings" msgid="1756531602592545966">"सेटिंग"</string>
<string name="global_action_assist" msgid="3892832961594295030">"सहायता"</string>
<string name="global_action_voice_assist" msgid="7751191495200504480">"आवाज़ से डिवाइस का इस्तेमाल"</string>
@@ -275,7 +272,10 @@
<string name="permgrouprequest_contacts" msgid="6032805601881764300">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> को अपने संपर्क देखने की अनुमति देना चाहते हैं?"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"जगह"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"इस डिवाइस की जगह तक पहुंचने दें"</string>
- <string name="permgrouprequest_location" msgid="3788275734953323491">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> को इस डिवाइस की जगह की जानकारी देखने की अनुमति देना चाहते हैं?"</string>
+ <string name="permgrouprequest_location" msgid="3788275734953323491">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> को इस डिवाइस की \'जगह की जानकारी\' ऐक्सेस करने की अनुमति देना चाहते हैं?"</string>
+ <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"ऐप्लिकेशन, डिवाइस की \'जगह की जानकारी\' सिर्फ़ तभी देख पाएगा जब आप इसका इस्तेमाल कर रहे हों."</string>
+ <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> को हमेशा डिवाइस की \'जगह की जानकारी\' एक्सेस करने दें?"</string>
+ <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"ऐप्लिकेशन के पास हमेशा डिवाइस की \'जगह की जानकारी\' देखने की मंज़ूरी होगी, तब भी जब आप इसका इस्तेमाल न कर रहे हों."</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"कैलेंडर"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"अपने कैलेंडर को ऐक्सेस करने"</string>
<string name="permgrouprequest_calendar" msgid="289900767793189421">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> को अपना कैलेंडर देखने की अनुमति देना चाहते हैं?"</string>
@@ -402,18 +402,14 @@
<string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"यह ऐप्लिकेशन आपके फ़ोन पर मौजूद कैलेंडर इवेंट जोड़, निकाल या बदल सकता है. यह ऐप्लिकेशन ऐसे संदेश भेज सकता है जो कैलेंडर स्वामियों से आए हुए लग सकते हैं या यह स्वामियों को सूचित किए बिना इवेंट में बदलाव कर सकता है."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"कुछ और जगह बताने वाले आदेशों तक पहुंच"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"ऐप को कुछ और जगह की जानकारी देने वाले आदेशों की पहुंच पाने देता है. इससे ऐप जीपीएस या जगह की जानकारी देने वाले दूसरे स्रोतों के काम में रोक-टोक कर सकता है."</string>
- <!-- no translation found for permlab_accessFineLocation (6265109654698562427) -->
- <skip />
- <!-- no translation found for permdesc_accessFineLocation (3520508381065331098) -->
- <skip />
+ <string name="permlab_accessFineLocation" msgid="6265109654698562427">"ऐप्लिकेशन \'जगह की सटीक जानकारी\' सिर्फ़ सामने खुली होने पर एक्सेस करे"</string>
+ <string name="permdesc_accessFineLocation" msgid="3520508381065331098">"यह ऐप्लिकेशन सिर्फ़ तब आपकी \'जगह की सटीक जानकारी\' का इस्तेमाल कर सकता है जब यह स्क्रीन पर दिखाई दे रहा हो. यह ज़रूरी है कि \'जगह की जानकारी\' वाली ये सेवाएं आपके फ़ोन में मौजूद हों और चालू की गई हों ताकि ऐप्लिकेशन उनका इस्तेमाल कर पाए. ऐसा करने से ज़्यादा बैटरी खर्च हो सकती है."</string>
<string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"अनुमानित जगह की पहुंच दें (नेटवर्क-आधारित)"</string>
<string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"यह ऐप सेल टावर और वाई-फ़ाई नेटवर्क जैसे नेटवर्क के स्रोतों के आधार पर आपकी जगह का पता कर सकता है. ये जगह की जानकारी आपके टैबलेट पर चालू और मौजूद होनी चाहिए ताकि ऐप उनका इस्तेमाल कर सके."</string>
<string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"यह ऐप सेल टावर और वाई-फ़ाई नेटवर्क जैसे नेटवर्क के स्रोतों के आधार पर आपकी जगह का पता कर सकता है. ये जगह की जानकारी आपके टीवी पर चालू और मौजूद होनी चाहिए ताकि ऐप उनका इस्तेमाल कर सके."</string>
<string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"यह ऐप सेल टावर और वाई-फ़ाई नेटवर्क जैसे नेटवर्क के स्रोतों के आधार पर आपकी जगह का पता कर सकता है. ये जगह की जानकारी आपके फ़ोन पर चालू और मौजूद होनी चाहिए ताकि ऐप उनका इस्तेमाल कर सके."</string>
- <!-- no translation found for permlab_accessBackgroundLocation (5742466381902568536) -->
- <skip />
- <!-- no translation found for permdesc_accessBackgroundLocation (6371533283380774135) -->
- <skip />
+ <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"बैकग्राउंड में होने पर \'जगह की सटीक जानकारी\' एक्सेस करें"</string>
+ <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"यह ऐप्लिकेशन बैकग्राउंड में चलते हुए, किसी भी समय आपकी \'जगह की सटीक जानकारी\' का इस्तेमाल कर सकता है. यह ज़रूरी है कि \'जगह की जानकारी\' वाली ये सेवाएं आपके फ़ोन में मौजूद हों और चालू की गई हों ताकि ऐप्लिकेशन उनका इस्तेमाल कर पाए. ऐसा करने से ज़्यादा बैटरी खर्च हो सकती है."</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"अपनी ऑडियो सेटिंग बदलें"</string>
<string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"ऐप्स को वैश्विक ऑडियो सेटिंग, जैसे वॉल्यूम और कौन-सा स्पीकर आउटपुट के लिए उपयोग किया गया, संशोधित करने देता है."</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"ऑडियो रिकॉर्ड करने"</string>
@@ -521,6 +517,37 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"फ़िंगरप्रिंट आइकॉन"</string>
+ <string name="permlab_manageFace" msgid="2137540986007309781">"चेहरे की पुष्टि करने वाला हार्डवेयर प्रबंधित करें"</string>
+ <string name="permdesc_manageFace" msgid="8919637120670185330">"ऐप्लिकेशन को चेहरे के टेम्पलेट इस्तेमाल के तरीके जोड़ने और मिटाने की मंज़ूरी मिलती है."</string>
+ <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"चेहरे की पुष्टि करने वाला हार्डवेयर इस्तेमाल करें"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"ऐप्लिकेशन को चेहरे की पुष्टि करने वाले हार्डवेयर का इस्तेमाल करने की मंज़ूरी मिलती है"</string>
+ <string name="face_acquired_insufficient" msgid="5901287247766106330">"चेहरे की पहचान नहीं हो पाई. कृपया फिर कोशिश करें."</string>
+ <string name="face_acquired_too_bright" msgid="610606792381297174">"चेहरे पर रोशनी ज़्यादा है. कृपया कम रोशनी में कोशिश करें."</string>
+ <string name="face_acquired_too_dark" msgid="7229162716976778371">"चेहरे पर रोशनी बहुत कम है. कृपया रोशनी बढ़ाएं."</string>
+ <string name="face_acquired_too_close" msgid="1980310037427755293">"कृपया डिवाइस को चेहरे से दूर ले जाएं."</string>
+ <string name="face_acquired_too_far" msgid="4494571381828850007">"कृपया डिवाइस को चेहरे के करीब लाएं."</string>
+ <string name="face_acquired_too_high" msgid="228411096134808372">"कृपया डिवाइस को ऊपर करें."</string>
+ <string name="face_acquired_too_low" msgid="4539774649296349109">"कृपया डिवाइस को नीचे की ओर ले जाएं."</string>
+ <string name="face_acquired_too_right" msgid="1650292067226118760">"कृपया डिवाइस को चेहरे की दाईं ओर ले जाएं."</string>
+ <string name="face_acquired_too_left" msgid="2712489669456176505">"कृपया डिवाइस को चेहरे के बाईं ओर ले जाएं."</string>
+ <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"कृपया सेंसर की ओर देखें."</string>
+ <string name="face_acquired_not_detected" msgid="5707782294589511391">"चेहरे की पहचान नहीं हो पाई."</string>
+ <string name="face_acquired_not_steady" msgid="3722829465011040042">"डिवाइस के सामने चेहरे को स्थिर रखें."</string>
+ <string-array name="face_acquired_vendor">
+ </string-array>
+ <string name="face_error_hw_not_available" msgid="6255891785768984615">"चेहरे की पहचान करने वाला हार्डवेयर मौजूद नहीं है."</string>
+ <string name="face_error_timeout" msgid="4014326147867150054">"चेहरे की पहचान का समय खत्म हुआ. फिर से कोशिश करें."</string>
+ <string name="face_error_no_space" msgid="8224993703466381314">"चेहरा सेव करने की सीमा पूरी हो गई है."</string>
+ <string name="face_error_canceled" msgid="283945501061931023">"चेहरा पहचानने की कार्रवाई रद्द की गई."</string>
+ <string name="face_error_lockout" msgid="3407426963155388504">"कई बार कोशिश की गई. बाद में कोशिश करें."</string>
+ <string name="face_error_lockout_permanent" msgid="8198354656746088890">"कई बार कोशिश की. चेहरा पहचानने की सुविधा बंद हुई."</string>
+ <string name="face_error_unable_to_process" msgid="238761109287767270">"फिर से कोशिश करें."</string>
+ <string name="face_error_not_enrolled" msgid="9166792142679691323">"कोई चेहरा रजिस्टर नहीं किया गया है."</string>
+ <string name="face_error_hw_not_present" msgid="4737289254517095671">"इस डिवाइस में चेहरे की पहचान करने वाला सेंसर नहीं है"</string>
+ <string name="face_name_template" msgid="7004562145809595384">"चेहरा <xliff:g id="FACEID">%d</xliff:g>"</string>
+ <string-array name="face_error_vendor">
+ </string-array>
+ <string name="face_icon_content_description" msgid="4024817159806482191">"चेहरे का आइकॉन"</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"समन्वयन सेटिंग पढ़ें"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"ऐप्स को किसी खाते की समन्वयन सेटिंग पढ़ने देता है. उदाहरण के लिए, इससे यह निर्धारित किया जा सकता है कि लोग ऐप्स किसी खाते के साथ समन्वयित है या नहीं."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"समन्वयन बंद या चालू टॉगल करें"</string>
@@ -1181,6 +1208,9 @@
<skip />
<string name="wifi_no_internet" msgid="8938267198124654938">"वाई-फ़ाई के लिए इंटरनेट नहीं मिल रहा है"</string>
<string name="wifi_no_internet_detailed" msgid="8083079241212301741">"विकल्पों के लिए टैप करें"</string>
+ <string name="wifi_softap_config_change" msgid="8475911871165857607">"आपकी हॉटस्पॉट सेटिंग के हिसाब से बदलाव हो गए हैं"</string>
+ <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"आपका हॉटस्पॉट बैंड बदल गया है."</string>
+ <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"यह डिवाइस सिर्फ़ 5 गीगाहर्ट्ज़ की आपकी पसंद की सेटिंग पर काम नहीं करता, लेकिन जब भी 5 गीगाहर्ट्ज़ बैंड मौजूद होगा, डिवाइस उसका इस्तेमाल करने लगेगा."</string>
<string name="network_switch_metered" msgid="4671730921726992671">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> पर ले जाया गया"</string>
<string name="network_switch_metered_detail" msgid="775163331794506615">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> में इंटरनेट की सुविधा नहीं होने पर डिवाइस <xliff:g id="NEW_NETWORK">%1$s</xliff:g> का इस्तेमाल करता है. इसके लिए शुल्क लिया जा सकता है."</string>
<string name="network_switch_metered_toast" msgid="5779283181685974304">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> से <xliff:g id="NEW_NETWORK">%2$s</xliff:g> पर ले जाया गया"</string>
@@ -1679,7 +1709,7 @@
<item quantity="other"><xliff:g id="COUNT">%d</xliff:g> सेकंड में पुन: प्रयास करें</item>
</plurals>
<string name="restr_pin_try_later" msgid="973144472490532377">"बाद में फिर से प्रयास करें"</string>
- <string name="immersive_cling_title" msgid="8394201622932303336">"पूरे स्क्रीन पर देखें"</string>
+ <string name="immersive_cling_title" msgid="8394201622932303336">"आप पूरे स्क्रीन पर देख रहे हैं"</string>
<string name="immersive_cling_description" msgid="3482371193207536040">"बाहर निकलने के लिए, ऊपर से नीचे स्वाइप करें."</string>
<string name="immersive_cling_positive" msgid="5016839404568297683">"ठीक है"</string>
<string name="done_label" msgid="2093726099505892398">"हो गया"</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 13dc029..3baaf01 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -239,9 +239,6 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Način rada u zrakoplovu"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Uključen je način rada u zrakoplovu"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Isključen je način rada u zrakoplovu"</string>
- <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Štednja baterije"</string>
- <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Štednja baterije isključena"</string>
- <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Štednja baterije uključena"</string>
<string name="global_action_settings" msgid="1756531602592545966">"Postavke"</string>
<string name="global_action_assist" msgid="3892832961594295030">"Pomoć"</string>
<string name="global_action_voice_assist" msgid="7751191495200504480">"Glasovna pomoć"</string>
@@ -279,6 +276,9 @@
<string name="permgrouplab_location" msgid="7275582855722310164">"Lokacija"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"pristupiti lokaciji ovog uređaja"</string>
<string name="permgrouprequest_location" msgid="3788275734953323491">"Želite li dopustiti aplikaciji <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> da pristupa lokaciji ovog uređaja?"</string>
+ <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"Aplikacija će imati pristup lokaciji samo dok upotrebljavate aplikaciju."</string>
+ <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"Želite li dopustiti aplikaciji <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> da pristupa lokaciji ovog uređaja?"</string>
+ <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"Aplikacija će uvijek imati pristup lokaciji, čak i dok ne upotrebljavate aplikaciju."</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Kalendar"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"pristupati kalendaru"</string>
<string name="permgrouprequest_calendar" msgid="289900767793189421">"Želite li dopustiti aplikaciji <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> da pristupa vašem kalendaru?"</string>
@@ -405,8 +405,7 @@
<string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Aplikacija može dodavati, uklanjati ili mijenjati kalendarske događaje na telefonu. Može slati poruke koje izgledaju kao da ih je poslao vlasnik kalendara ili mijenjati događaje bez znanja vlasnika."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"pristup dodatnim naredbama davatelja lokacije"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Omogućuje aplikaciji pristup dodatnim naredbama davatelja usluga lokacije. To može omogućiti aplikaciji ometanje rada GPS-a ili drugih izvora lokacije."</string>
- <!-- no translation found for permlab_accessFineLocation (6265109654698562427) -->
- <skip />
+ <string name="permlab_accessFineLocation" msgid="6265109654698562427">"pristupiti preciznoj lokaciji samo u prednjem planu"</string>
<string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Aplikacija može dobiti vašu točnu lokaciju samo kada je u prednjem planu. Te usluge lokacije moraju biti uključene i dostupne na telefonu da bi ih aplikacija mogla upotrebljavati. To može pojačati potrošnju baterije."</string>
<string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"pristupati približnoj lokaciji (na temelju mreža)"</string>
<string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Aplikacija može dohvatiti vašu lokaciju putem mrežnih izvora poput baznih stanica i Wi-Fi mreža. Te usluge lokacije moraju biti uključene i dostupne na tabletu da bi ih aplikacija mogla upotrebljavati."</string>
@@ -521,6 +520,37 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Ikona otiska prsta"</string>
+ <string name="permlab_manageFace" msgid="2137540986007309781">"upravljati hardverom za autentifikaciju lica"</string>
+ <string name="permdesc_manageFace" msgid="8919637120670185330">"Aplikaciji omogućuje pozivanje načina za dodavanje i brisanje predložaka lica za upotrebu."</string>
+ <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"upotrebljavati hardver za autentifikaciju lica"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Aplikaciji omogućuje upotrebu hardvera za autentifikaciju lica radi autentifikacije"</string>
+ <string name="face_acquired_insufficient" msgid="5901287247766106330">"Obrada lica nije uspjela. Pokušajte ponovo."</string>
+ <string name="face_acquired_too_bright" msgid="610606792381297174">"Lice je presvijetlo. Smanjite osvjetljenje."</string>
+ <string name="face_acquired_too_dark" msgid="7229162716976778371">"Lice je pretamno. Otkrijte izvor svjetlosti."</string>
+ <string name="face_acquired_too_close" msgid="1980310037427755293">"Odmaknite senzor od lica."</string>
+ <string name="face_acquired_too_far" msgid="4494571381828850007">"Približite senzor licu."</string>
+ <string name="face_acquired_too_high" msgid="228411096134808372">"Podignite senzor."</string>
+ <string name="face_acquired_too_low" msgid="4539774649296349109">"Spustite senzor."</string>
+ <string name="face_acquired_too_right" msgid="1650292067226118760">"Pomaknite senzor udesno."</string>
+ <string name="face_acquired_too_left" msgid="2712489669456176505">"Pomaknite senzor ulijevo."</string>
+ <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Gledajte senzor."</string>
+ <string name="face_acquired_not_detected" msgid="5707782294589511391">"Lice nije otkriveno."</string>
+ <string name="face_acquired_not_steady" msgid="3722829465011040042">"Držite lice mirno ispred uređaja."</string>
+ <string-array name="face_acquired_vendor">
+ </string-array>
+ <string name="face_error_hw_not_available" msgid="6255891785768984615">"Hardver za lice nije dostupan."</string>
+ <string name="face_error_timeout" msgid="4014326147867150054">"Isteklo je vrijeme čekanja za lice. Pokušajte opet"</string>
+ <string name="face_error_no_space" msgid="8224993703466381314">"Nije moguće pohraniti lice."</string>
+ <string name="face_error_canceled" msgid="283945501061931023">"Otkazana je radnja s licem."</string>
+ <string name="face_error_lockout" msgid="3407426963155388504">"Previše pokušaja. Pokušajte ponovo kasnije."</string>
+ <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Previše pokušaja. Autentifikacija lica onemogućena"</string>
+ <string name="face_error_unable_to_process" msgid="238761109287767270">"Pokušajte ponovo."</string>
+ <string name="face_error_not_enrolled" msgid="9166792142679691323">"Nije registrirano nijedno lice."</string>
+ <string name="face_error_hw_not_present" msgid="4737289254517095671">"Ovaj uređaj nema senzor za autentifikaciju lica"</string>
+ <string name="face_name_template" msgid="7004562145809595384">"Lice <xliff:g id="FACEID">%d</xliff:g>"</string>
+ <string-array name="face_error_vendor">
+ </string-array>
+ <string name="face_icon_content_description" msgid="4024817159806482191">"Ikona lica"</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"čitanje postavki sinkronizacije"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Aplikaciji omogućuje čitanje postavki sinkronizacije za račun. Time se, primjerice, može utvrditi je li aplikacija Osobe sinkronizirana s računom."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"uključivanje/isključivanje sinkronizacije"</string>
@@ -1200,6 +1230,9 @@
<skip />
<string name="wifi_no_internet" msgid="8938267198124654938">"Wi-Fi nema pristup internetu"</string>
<string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Dodirnite za opcije"</string>
+ <string name="wifi_softap_config_change" msgid="8475911871165857607">"Promjene postavki vaše žarišne točke"</string>
+ <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"Promijenila se frekvencija vaše žarišne točke."</string>
+ <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"Ovaj uređaj ne podržava vašu postavku za upotrebu samo 5 GHz. Upotrebljavat će frekvenciju od 5 GHz kada je dostupna."</string>
<string name="network_switch_metered" msgid="4671730921726992671">"Prelazak na drugu mrežu: <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
<string name="network_switch_metered_detail" msgid="775163331794506615">"Kada <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> nema pristup internetu, na uređaju se upotrebljava <xliff:g id="NEW_NETWORK">%1$s</xliff:g>. Moguća je naplata naknade."</string>
<string name="network_switch_metered_toast" msgid="5779283181685974304">"Mreža je promijenjena: <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> > <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 443663a..106f497 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -236,9 +236,6 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Repülőgép üzemmód"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Repülőgép üzemmód bekapcsolva"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Repülőgép üzemmód kikapcsolva"</string>
- <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Akkumulátorkímélő mód"</string>
- <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Akkumulátorkímélő mód kikapcsolva"</string>
- <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Akkumulátorkímélő mód bekapcsolva"</string>
<string name="global_action_settings" msgid="1756531602592545966">"Beállítások"</string>
<string name="global_action_assist" msgid="3892832961594295030">"Segítség"</string>
<string name="global_action_voice_assist" msgid="7751191495200504480">"Hangsegéd"</string>
@@ -276,6 +273,9 @@
<string name="permgrouplab_location" msgid="7275582855722310164">"Helyadatok"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"hozzáférés az eszköz földrajzi helyéhez"</string>
<string name="permgrouprequest_location" msgid="3788275734953323491">"Engedélyezi a(z) <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> számára, hogy hozzáférjen az eszköz helyadataihoz?"</string>
+ <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"Az alkalmazás csak akkor férhet hozzá a helyadatokhoz, amikor használja az alkalmazást."</string>
+ <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"Engedélyezi, hogy a(z) <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> hozzáférjen a helyadatokhoz?"</string>
+ <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"Az alkalmazás bármikor hozzáférhet majd a helyadatokhoz, akkor is, amikor nem használja az alkalmazást."</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Naptár"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"hozzáférés a naptárhoz"</string>
<string name="permgrouprequest_calendar" msgid="289900767793189421">"Engedélyezi a(z) <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> számára, hogy hozzáférjen a naptárhoz?"</string>
@@ -402,8 +402,7 @@
<string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Az alkalmazás hozzáadhatja, eltávolíthatja vagy módosíthatja a telefonon található naptáreseményeket. Az alkalmazás olyan üzeneteket küldhet, amelyekről úgy tűnhet, hogy a naptár tulajdonosaitól származnak, illetve a tulajdonosok értesítése nélkül módosíthat eseményeket."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"további helyszolgáltatói parancsok elérése"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Lehetővé teszi az alkalmazás számára további helyszolgáltatói parancsok elérését. Ezáltal az alkalmazás beavatkozhat a GPS vagy más helyforrások működésébe."</string>
- <!-- no translation found for permlab_accessFineLocation (6265109654698562427) -->
- <skip />
+ <string name="permlab_accessFineLocation" msgid="6265109654698562427">"pontos helyadatokhoz való hozzáférés csak előtérben"</string>
<string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Ez az alkalmazás csak akkor férhet hozzá az eszköz pontos helyadataihoz, amikor az előtérben fut. A helyszolgáltatásoknak bekapcsolt és hozzáférhető állapotban kell lenniük a telefonon ahhoz, hogy az alkalmazás használhassa őket. Ez növelheti az akkumulátorhasználatot."</string>
<string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"hozzáférés a hozzávetőleges (hálózatalapú) helyadatokhoz"</string>
<string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Az alkalmazás hozzáférhet hálózati forrásokon (például az adótornyokon és a Wi-Fi-hálózatokon) alapuló helyadataihoz. A helyszolgáltatásoknak bekapcsolt és elérhető állapotban kell lenniük a táblagépen ahhoz, hogy az alkalmazás használhassa őket."</string>
@@ -518,6 +517,37 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Ujjlenyomat ikon"</string>
+ <string name="permlab_manageFace" msgid="2137540986007309781">"arcfelismerő hardver kezelése"</string>
+ <string name="permdesc_manageFace" msgid="8919637120670185330">"Engedélyezi, hogy az alkalmazás arcsablon-hozzáadási és -törlési metódusokat hívjon."</string>
+ <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"arcfelismerő hardver használata"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Engedélyezi, hogy az alkalmazás hitelesítésre használja az arcfelismerő hardvert"</string>
+ <string name="face_acquired_insufficient" msgid="5901287247766106330">"Nem sikerült feldolgozni az arcot. Próbálja újra."</string>
+ <string name="face_acquired_too_bright" msgid="610606792381297174">"Az arc túl világos. Kevesebb fény szükséges."</string>
+ <string name="face_acquired_too_dark" msgid="7229162716976778371">"Az arc túl sötét. Több fény szükséges."</string>
+ <string name="face_acquired_too_close" msgid="1980310037427755293">"Tartsa az érzékelőt távolabb az arctól."</string>
+ <string name="face_acquired_too_far" msgid="4494571381828850007">"Tartsa az érzékelőt közelebb az archoz."</string>
+ <string name="face_acquired_too_high" msgid="228411096134808372">"Emelje magasabbra az érzékelőt."</string>
+ <string name="face_acquired_too_low" msgid="4539774649296349109">"Engedje lejjebb az érzékelőt."</string>
+ <string name="face_acquired_too_right" msgid="1650292067226118760">"Mozdítsa el jobbra az érzékelőt."</string>
+ <string name="face_acquired_too_left" msgid="2712489669456176505">"Mozdítsa el balra az érzékelőt."</string>
+ <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Nézzen az érzékelőbe."</string>
+ <string name="face_acquired_not_detected" msgid="5707782294589511391">"Nem észlelhető arc."</string>
+ <string name="face_acquired_not_steady" msgid="3722829465011040042">"Tartsa az eszközt egyenesen az arc előtt."</string>
+ <string-array name="face_acquired_vendor">
+ </string-array>
+ <string name="face_error_hw_not_available" msgid="6255891785768984615">"Az arcfelismerő hardver nem áll rendelkezésre."</string>
+ <string name="face_error_timeout" msgid="4014326147867150054">"Időtúllépés az arcbeolvasásnál. Próbálja újra."</string>
+ <string name="face_error_no_space" msgid="8224993703466381314">"Az arc nem tárolható."</string>
+ <string name="face_error_canceled" msgid="283945501061931023">"Az arccal kapcsolatos művelet törölve."</string>
+ <string name="face_error_lockout" msgid="3407426963155388504">"Túl sok próbálkozás. Próbálja újra később."</string>
+ <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Túl sok próbálkozás. Arcfelismerés letiltva."</string>
+ <string name="face_error_unable_to_process" msgid="238761109287767270">"Próbálkozzon újra."</string>
+ <string name="face_error_not_enrolled" msgid="9166792142679691323">"Nincs regisztrált arc."</string>
+ <string name="face_error_hw_not_present" msgid="4737289254517095671">"Ez az eszköz nem rendelkezik arcfelismerő érzékelővel"</string>
+ <string name="face_name_template" msgid="7004562145809595384">"<xliff:g id="FACEID">%d</xliff:g> arc"</string>
+ <string-array name="face_error_vendor">
+ </string-array>
+ <string name="face_icon_content_description" msgid="4024817159806482191">"Arcikon"</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"szinkronizálási beállítások olvasása"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Lehetővé teszi az alkalmazás számára egy fiók szinkronizálási beállításainak beolvasását. Például ellenőrizheti, hogy a Személyek alkalmazás szinkronizálva van-e egy fiókkal."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"szinkronizálás be-és kikapcsolása"</string>
@@ -1178,6 +1208,9 @@
<skip />
<string name="wifi_no_internet" msgid="8938267198124654938">"A Wi-Fi-hálózaton nincs internetkapcsolat"</string>
<string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Koppintson a beállítások megjelenítéséhez"</string>
+ <string name="wifi_softap_config_change" msgid="8475911871165857607">"A hotspot beállításainak módosítása"</string>
+ <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"A hotspot sávja megváltozott."</string>
+ <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"Ez az eszköz nem támogatja a csak 5 GHz-es sávra vonatkozó beállítást. Az eszköz akkor használ 5 GHz-es sávot, ha a sáv rendelkezésre áll."</string>
<string name="network_switch_metered" msgid="4671730921726992671">"Átváltva erre: <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
<string name="network_switch_metered_detail" msgid="775163331794506615">"<xliff:g id="NEW_NETWORK">%1$s</xliff:g> használata, ha nincs internet-hozzáférés <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>-kapcsolaton keresztül. A szolgáltató díjat számíthat fel."</string>
<string name="network_switch_metered_toast" msgid="5779283181685974304">"Átváltva <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g>-hálózatról erre: <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index ba4b73e..60fe2d9 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -219,16 +219,16 @@
<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="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>
- <string name="bugreport_option_interactive_summary" msgid="229299488536107968">"Հիմնականում օգտագործեք այս տարբերակը: Այն ձեզ թույլ է տալիս հետևել զեկույցի ստեղծման գործընթացին, խնդրի մասին լրացուցիչ տեղեկություններ մուտքագրել և էկրանի պատկերներ կորզել: Կարող է բաց թողնել քիչ օգտագործվող որոշ բաժինները, որոնց ստեղծումը երկար է տևում:"</string>
+ <string name="bugreport_option_interactive_summary" msgid="229299488536107968">"Հիմնականում օգտագործեք այս տարբերակը: Այն ձեզ թույլ է տալիս հետևել զեկույցի ստեղծման գործընթացին, խնդրի մասին լրացուցիչ տեղեկություններ մուտքագրել և սքրինշոթներ ստեղծել: Կարող է բաց թողնել քիչ օգտագործվող որոշ բաժիններ, որոնց ստեղծումը երկար է տևում:"</string>
<string name="bugreport_option_full_title" msgid="6354382025840076439">"Ամբողջական զեկույց"</string>
<string name="bugreport_option_full_summary" msgid="7210859858969115745">"Օգտագործեք այս տարբերակը համակարգի միջամտությունը նվազեցնելու համար՝ երբ սարքը չի արձագանքում կամ շատ դանդաղ է աշխատում, կամ երբ ձեզ հարկավոր են զեկույցի բոլոր բաժինները: Թույլ չի տալիս լրացուցիչ տվյալներ մուտքագրել կամ էկրանի լրացուցիչ պատկերներ ստանալ:"</string>
<plurals name="bugreport_countdown" formatted="false" msgid="6878900193900090368">
- <item quantity="one">Վրիպակի զեկույցի համար էկրանի պատկերի լուսանկարումը կատարվելու է <xliff:g id="NUMBER_1">%d</xliff:g> վայրկյանից:</item>
- <item quantity="other">Վրիպակի զեկույցի համար էկրանի պատկերի լուսանկարումը կատարվելու է <xliff:g id="NUMBER_1">%d</xliff:g> վայրկյանից:</item>
+ <item quantity="one">Սքրինշոթը կարվի <xliff:g id="NUMBER_1">%d</xliff:g> վայրկյանից:</item>
+ <item quantity="other">Սքրինշոթը կարվի <xliff:g id="NUMBER_1">%d</xliff:g> վայրկյանից:</item>
</plurals>
<string name="global_action_toggle_silent_mode" msgid="8219525344246810925">"Անձայն ռեժիմ"</string>
<string name="global_action_silent_mode_on_status" msgid="3289841937003758806">"Ձայնը անջատված է"</string>
@@ -236,9 +236,6 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Ինքնաթիռի ռեժիմ"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Ինքնաթիռի ռեժիմը միացված է"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Ինքնաթիռի ռեժիմը անջատված է"</string>
- <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Մարտկոցի տնտեսում"</string>
- <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Անջատել"</string>
- <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Միացնել"</string>
<string name="global_action_settings" msgid="1756531602592545966">"Կարգավորումներ"</string>
<string name="global_action_assist" msgid="3892832961594295030">"Օգնական"</string>
<string name="global_action_voice_assist" msgid="7751191495200504480">"Ձայնային օգնութ"</string>
@@ -276,6 +273,9 @@
<string name="permgrouplab_location" msgid="7275582855722310164">"Տեղորոշում"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"տեղորոշել այս սարքը"</string>
<string name="permgrouprequest_location" msgid="3788275734953323491">"Թույլ տա՞լ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> հավելվածին օգտագործել այս սարքի տեղադրության տվյալները:"</string>
+ <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"Տեղադրության տվյալները հավելվածին հասանելի կլինեն միայն, երբ այն օգտագործելիս լինեք:"</string>
+ <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"Միշտ թույլ տա՞լ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>-ին օգտագործել սարքի տեղադրությունը:"</string>
+ <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"Տեղադրության տվյալները հավելվածին միշտ հասանելի կլինեն, նույնիսկ եթե այն չեք օգտագործում:"</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Օրացույց"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"օգտագործել օրացույցը"</string>
<string name="permgrouprequest_calendar" msgid="289900767793189421">"Թույլ տա՞լ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> հավելվածին օգտագործել ձեր օրացույցը:"</string>
@@ -402,8 +402,7 @@
<string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Այս հավելվածը կարող է ավելացնել, հեռացնել կամ փոխել օրացույցի միջոցառումները ձեր հեռախոսում: Այս հավելվածը կարող է ուղարկել հաղորդագրություններ օրացույցի սեփականատերերի անունից կամ փոխել միջոցառումները առանց դրանց սեփականատերերին ծանուցելու:"</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"օգտագործել տեղադրություն տրամադրող հավելվյալ հրամաններ"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Ծրագրին թույլ է տալիս օգտագործել տեղադրության մասին տվյալների աղբյուրների կառավարման լրացուցիչ հրահանգներ: Սա կարող է ծրագրին թույլ տալ միջամտել GPS-ի կամ տեղադրության մասին տվյալների այլ աղբյուրների գործառույթներին:"</string>
- <!-- no translation found for permlab_accessFineLocation (6265109654698562427) -->
- <skip />
+ <string name="permlab_accessFineLocation" msgid="6265109654698562427">"Տեղադրության ճշգրիտ տվյալների հասանելիություն միայն ֆոնային ռեժիմում"</string>
<string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Հավելվածը կարող է ստանալ ձեր տեղադրության տվյալները ֆոնային ռեժիմում։ Այս տեղորոշման ծառայությունները պետք է միացված և հասանելի լինեն ձեր հեռախոսում, որպեսզի հավելվածը կարողանա օգտագործել դրանք: Սա կարող է արագացնել մարտկոցի լիցքի սպառումը:"</string>
<string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"օգտագործել մոտավոր տեղադրությունը (ցանցային)"</string>
<string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Այս հավելվածը կարող է ստանալ ձեր տեղադրության տվյալները ցանցային տարբեր աղբյուրներից, օրինակ՝ բջջային աշտարակներից և Wi-Fi ցանցերից: Այս տեղորոշման ծառայությունները պետք է միացված և հասանելի լինեն ձեր պլանշետում, որպեսզի հավելվածը կարողանա օգտագործել դրանք:"</string>
@@ -518,6 +517,37 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Մատնահետքի պատկերակ"</string>
+ <string name="permlab_manageFace" msgid="2137540986007309781">"կառավարել դեմքի ճանաչման սարքը"</string>
+ <string name="permdesc_manageFace" msgid="8919637120670185330">"Հավելվածին թույլ է տալիս ավելացնել և հեռացնել դեմքի նմուշներ:"</string>
+ <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"օգտագործել դեմքի ճանաչման սարքը"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Հավելվածին թույլ է տալիս օգտագործել նույնականացման համար նախատեսված սարքը"</string>
+ <string name="face_acquired_insufficient" msgid="5901287247766106330">"Չհաջողվեց ճանաչել ձեր դեմքը: Նորից փորձեք:"</string>
+ <string name="face_acquired_too_bright" msgid="610606792381297174">"Դեմքը չափազանց վառ է երևում։ Թուլացրեք լույսը։"</string>
+ <string name="face_acquired_too_dark" msgid="7229162716976778371">"Դեմքը չափազանց մուգ է երևում։ Ուժեղացրեք լույսը։"</string>
+ <string name="face_acquired_too_close" msgid="1980310037427755293">"Սարքը մի փոքր հեռացրեք դեմքից։"</string>
+ <string name="face_acquired_too_far" msgid="4494571381828850007">"Սարքը մի փոքր մոտեցրեք դեմքին։"</string>
+ <string name="face_acquired_too_high" msgid="228411096134808372">"Սարքը մի փոքր վերև պահեք։"</string>
+ <string name="face_acquired_too_low" msgid="4539774649296349109">"Սարքը մի փոքր ներքև պահեք։"</string>
+ <string name="face_acquired_too_right" msgid="1650292067226118760">"Սարքը մի փոքր ձախ պահեք։"</string>
+ <string name="face_acquired_too_left" msgid="2712489669456176505">"Սարքը մի փոքր ձախ պահեք։"</string>
+ <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Նայեք սարքի տեսախցիկին։"</string>
+ <string name="face_acquired_not_detected" msgid="5707782294589511391">"Դեմք չի հայտնաբերվել։"</string>
+ <string name="face_acquired_not_steady" msgid="3722829465011040042">"Պահեք սարքը դեմքի դիմաց։"</string>
+ <string-array name="face_acquired_vendor">
+ </string-array>
+ <string name="face_error_hw_not_available" msgid="6255891785768984615">"Դեմքի ճանաչման սարքն անհասանելի է։"</string>
+ <string name="face_error_timeout" msgid="4014326147867150054">"Ժամանակը սպառվել է: Նորից փորձեք:"</string>
+ <string name="face_error_no_space" msgid="8224993703466381314">"Դեմքը հնարավոր չէ պահել։"</string>
+ <string name="face_error_canceled" msgid="283945501061931023">"Դեմքի ճանաչումը չեղարկվել է։"</string>
+ <string name="face_error_lockout" msgid="3407426963155388504">"Չափից շատ փորձեր եք կատարել: Փորձեք ավելի ուշ:"</string>
+ <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Չափից շատ փորձեր եք կատարել: Դեմքի ճանաչման գործառույթն անջատվել է։"</string>
+ <string name="face_error_unable_to_process" msgid="238761109287767270">"Նորից փորձեք:"</string>
+ <string name="face_error_not_enrolled" msgid="9166792142679691323">"Գրանցված դեմք չկա։"</string>
+ <string name="face_error_hw_not_present" msgid="4737289254517095671">"Սարքը չունի դեմքի ճանաչման սկաներ"</string>
+ <string name="face_name_template" msgid="7004562145809595384">"Դեմք <xliff:g id="FACEID">%d</xliff:g>"</string>
+ <string-array name="face_error_vendor">
+ </string-array>
+ <string name="face_icon_content_description" msgid="4024817159806482191">"Դեմքի պատկերակ"</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"կարդալ համաժամեցման կարգավորումները"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Թույլ է տալիս հավելվածին կարդալ համաժամեցման կարգավորումները հաշվի համար: Օրինակ` այն կարող է որոշել, արդյոք Մարդիկ հավելվածը համաժամեցված է հաշվի հետ:"</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"համաժամեցումը փոխարկել միացվածի և անջատվածի"</string>
@@ -1178,6 +1208,9 @@
<skip />
<string name="wifi_no_internet" msgid="8938267198124654938">"Wi-Fi ցանցում ինտերնետ կապ չկա"</string>
<string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Հպեք՝ ընտրանքները տեսնելու համար"</string>
+ <string name="wifi_softap_config_change" msgid="8475911871165857607">"Փոփոխություններ թեժ կետի կարգավորումներում"</string>
+ <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"Ձեր թեժ կետի հաճախականությունը փոխվել է։"</string>
+ <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"Սարքը չի կարող աշխատել միայն 5 ԳՀց հաճախականությամբ։ Այդ հաճախականությունը կօգտագործվի հնարավորության դեպքում։"</string>
<string name="network_switch_metered" msgid="4671730921726992671">"Անցել է <xliff:g id="NETWORK_TYPE">%1$s</xliff:g> ցանցի"</string>
<string name="network_switch_metered_detail" msgid="775163331794506615">"Երբ <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> ցանցում ինտերնետ կապ չի լինում, սարքն անցնում է <xliff:g id="NEW_NETWORK">%1$s</xliff:g> ցանցի: Նման դեպքում կարող են վճարներ գանձվել:"</string>
<string name="network_switch_metered_toast" msgid="5779283181685974304">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> ցանցից անցել է <xliff:g id="NEW_NETWORK">%2$s</xliff:g> ցանցի"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 0f3165e..05f6ba1 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -236,9 +236,6 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Mode pesawat"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Mode pesawat AKTIF"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Mode pesawat MATI"</string>
- <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Penghemat baterai"</string>
- <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Penghemat baterai NONAKTIF"</string>
- <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Penghemat baterai AKTIF"</string>
<string name="global_action_settings" msgid="1756531602592545966">"Setelan"</string>
<string name="global_action_assist" msgid="3892832961594295030">"Bantuan"</string>
<string name="global_action_voice_assist" msgid="7751191495200504480">"Bantuan Suara"</string>
@@ -276,6 +273,9 @@
<string name="permgrouplab_location" msgid="7275582855722310164">"Lokasi"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"mengakses lokasi perangkat ini"</string>
<string name="permgrouprequest_location" msgid="3788275734953323491">"Izinkan <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> mengakses lokasi perangkat ini?"</string>
+ <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"Aplikasi ini hanya akan memiliki akses ke lokasi saat Anda sedang menggunakan aplikasi."</string>
+ <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"Selalu izinkan <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> mengakses lokasi perangkat ini?"</string>
+ <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"Aplikasi ini akan selalu memiliki akses ke lokasi, bahkan saat Anda sedang tidak menggunakan aplikasi."</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Kalender"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"mengakses kalender"</string>
<string name="permgrouprequest_calendar" msgid="289900767793189421">"Izinkan <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> mengakses kalender?"</string>
@@ -402,8 +402,7 @@
<string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Aplikasi ini dapat menambahkan, menghapus, atau mengubah acara kalender di ponsel. Aplikasi ini dapat mengirim pesan yang kelihatannya berasal dari pemilik kalender, atau mengubah acara tanpa memberi tahu pemilik."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"akses perintah penyedia lokasi ekstra"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Memungkinkan aplikasi mengakses perintah penyedia lokasi ekstra. Tindakan ini memungkinkan aplikasi mengganggu pengoperasian GPS atau sumber lokasi lain."</string>
- <!-- no translation found for permlab_accessFineLocation (6265109654698562427) -->
- <skip />
+ <string name="permlab_accessFineLocation" msgid="6265109654698562427">"akses lokasi pasti hanya saat di latar depan"</string>
<string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Aplikasi ini bisa mendapatkan lokasi pasti Anda ketika aplikasi berada di latar depan. Fitur layanan lokasi ini harus diaktifkan dan tersedia di ponsel agar dapat digunakan oleh aplikasi. Fitur ini dapat meningkatkan konsumsi baterai."</string>
<string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"akses perkiraan lokasi (berbasis jaringan)"</string>
<string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Aplikasi ini dapat memperoleh lokasi Anda berdasarkan sumber jaringan seperti menara seluler dan jaringan Wi-Fi. Layanan lokasi ini harus diaktifkan dan tersedia di tablet agar aplikasi dapat menggunakannya."</string>
@@ -518,6 +517,37 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Ikon sidik jari"</string>
+ <string name="permlab_manageFace" msgid="2137540986007309781">"kelola hardware autentikasi wajah"</string>
+ <string name="permdesc_manageFace" msgid="8919637120670185330">"Mengizinkan apl memicu metode untuk menambah & menghapus template wajah untuk digunakan."</string>
+ <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"gunakan hardware autentikasi wajah"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Mengizinkan aplikasi untuk menggunakan hardware autentikasi wajah untuk autentikasi"</string>
+ <string name="face_acquired_insufficient" msgid="5901287247766106330">"Tidak dapat memproses wajah. Harap coba lagi."</string>
+ <string name="face_acquired_too_bright" msgid="610606792381297174">"Wajah terlalu cerah. Coba dengan cahaya lebih redup."</string>
+ <string name="face_acquired_too_dark" msgid="7229162716976778371">"Wajah terlalu gelap. Buka sumber cahaya."</string>
+ <string name="face_acquired_too_close" msgid="1980310037427755293">"Jauhkan sensor dari wajah."</string>
+ <string name="face_acquired_too_far" msgid="4494571381828850007">"Dekatkan sensor ke wajah."</string>
+ <string name="face_acquired_too_high" msgid="228411096134808372">"Naikkan sensor."</string>
+ <string name="face_acquired_too_low" msgid="4539774649296349109">"Turunkan sensor."</string>
+ <string name="face_acquired_too_right" msgid="1650292067226118760">"Gerakkan sensor ke kanan."</string>
+ <string name="face_acquired_too_left" msgid="2712489669456176505">"Gerakkan sensor ke kiri."</string>
+ <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Lihat sensor."</string>
+ <string name="face_acquired_not_detected" msgid="5707782294589511391">"Tidak ada wajah yang terdeteksi"</string>
+ <string name="face_acquired_not_steady" msgid="3722829465011040042">"Pastikan wajah tetap diam di depan perangkat."</string>
+ <string-array name="face_acquired_vendor">
+ </string-array>
+ <string name="face_error_hw_not_available" msgid="6255891785768984615">"Hardware pemrosesan wajah tidak tersedia."</string>
+ <string name="face_error_timeout" msgid="4014326147867150054">"Waktu tunggu wajah habis. Harap coba lagi."</string>
+ <string name="face_error_no_space" msgid="8224993703466381314">"Wajah tidak dapat disimpan."</string>
+ <string name="face_error_canceled" msgid="283945501061931023">"Pemrosesan wajah dibatalkan."</string>
+ <string name="face_error_lockout" msgid="3407426963155388504">"Terlalu banyak percobaan. Coba lagi nanti."</string>
+ <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Terlalu sering dicoba. Autentikasi wajah dinonaktifkan."</string>
+ <string name="face_error_unable_to_process" msgid="238761109287767270">"Coba lagi."</string>
+ <string name="face_error_not_enrolled" msgid="9166792142679691323">"Tidak ada wajah yang didaftarkan."</string>
+ <string name="face_error_hw_not_present" msgid="4737289254517095671">"Perangkat ini tidak memiliki sensor autentikasi wajah"</string>
+ <string name="face_name_template" msgid="7004562145809595384">"<xliff:g id="FACEID">%d</xliff:g> wajah"</string>
+ <string-array name="face_error_vendor">
+ </string-array>
+ <string name="face_icon_content_description" msgid="4024817159806482191">"Ikon wajah"</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"baca setelan sinkron"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Memungkinkan aplikasi membaca setelan sinkronisasi untuk sebuah akun. Misalnya, izin ini dapat menentukan apakah aplikasi Orang disinkronkan dengan sebuah akun."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"nyalakan dan matikan sinkronisasi"</string>
@@ -587,14 +617,14 @@
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="4280246270601044505">"Memantau banyaknya sandi salah yang diketikkan saat membuka kunci layar, dan mengunci tablet atau menghapus semua data pengguna ini jika terlalu banyak sandi salah diketikkan."</string>
<string name="policydesc_watchLogin_secondaryUser" product="TV" msgid="3484832653564483250">"Memantau banyaknya sandi salah yang diketikkan saat membuka kunci layar, dan mengunci TV atau menghapus semua data pengguna ini jika terlalu banyak sandi salah diketikkan."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="2185480427217127147">"Memantau banyaknya sandi salah yang diketikkan saat membuka kunci layar, dan mengunci ponsel atau menghapus semua data pengguna ini jika terlalu banyak sandi salah diketikkan."</string>
- <string name="policylab_resetPassword" msgid="4934707632423915395">"Ubah kunci layar"</string>
+ <string name="policylab_resetPassword" msgid="4934707632423915395">"Mengubah kunci layar"</string>
<string name="policydesc_resetPassword" msgid="1278323891710619128">"Mengubah kunci layar."</string>
- <string name="policylab_forceLock" msgid="2274085384704248431">"Kunci layar"</string>
- <string name="policydesc_forceLock" msgid="1141797588403827138">"Kontrol cara dan kapan layar mengunci."</string>
- <string name="policylab_wipeData" msgid="3910545446758639713">"Hapus semua data"</string>
+ <string name="policylab_forceLock" msgid="2274085384704248431">"Mengunci layar"</string>
+ <string name="policydesc_forceLock" msgid="1141797588403827138">"Mengontrol cara dan kapan layar mengunci."</string>
+ <string name="policylab_wipeData" msgid="3910545446758639713">"Menghapus semua data"</string>
<string name="policydesc_wipeData" product="tablet" msgid="4306184096067756876">"Hapus data tablet tanpa peringatan dengan menyetel ulang data pabrik."</string>
<string name="policydesc_wipeData" product="tv" msgid="5816221315214527028">"Menghapus data TV tanpa peringatan dengan mengembalikan ke setelan pabrik."</string>
- <string name="policydesc_wipeData" product="default" msgid="5096895604574188391">"Hapus data ponsel tanpa peringatan dengan menyetel ulang data pabrik."</string>
+ <string name="policydesc_wipeData" product="default" msgid="5096895604574188391">"Menghapus data ponsel tanpa peringatan dengan melakukan reset ke setelan pabrik."</string>
<string name="policylab_wipeData_secondaryUser" msgid="8362863289455531813">"Menghapus data pengguna"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="6336255514635308054">"Menghapus data pengguna ini di tablet ini tanpa peringatan."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2086473496848351810">"Menghapus data pengguna ini di TV ini tanpa peringatan."</string>
@@ -1178,6 +1208,9 @@
<skip />
<string name="wifi_no_internet" msgid="8938267198124654938">"Wi-Fi tidak memiliki akses internet"</string>
<string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Tap untuk melihat opsi"</string>
+ <string name="wifi_softap_config_change" msgid="8475911871165857607">"Perubahan pada setelan hotspot Anda"</string>
+ <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"Pita hotspot Anda telah berubah."</string>
+ <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"Perangkat ini tidak mendukung preferensi Anda, yaitu hanya 5GHz. Sebagai gantinya, perangkat ini akan menggunakan pita frekuensi 5GHz jika tersedia."</string>
<string name="network_switch_metered" msgid="4671730921726992671">"Dialihkan ke <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
<string name="network_switch_metered_detail" msgid="775163331794506615">"Perangkat menggunakan <xliff:g id="NEW_NETWORK">%1$s</xliff:g> jika <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> tidak memiliki akses internet. Tarif mungkin berlaku."</string>
<string name="network_switch_metered_toast" msgid="5779283181685974304">"Dialihkan dari <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> ke <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index a47d5b6..a915202 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -236,9 +236,6 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Flugstilling"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"KVEIKT er á flugstillingu"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"SLÖKKT er á flugstillingu"</string>
- <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Rafhlöðusparnaður"</string>
- <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Slökkt er á rafhlöðusparnaði"</string>
- <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Kveikt er á rafhlöðusparnaði"</string>
<string name="global_action_settings" msgid="1756531602592545966">"Stillingar"</string>
<string name="global_action_assist" msgid="3892832961594295030">"Aðstoð"</string>
<string name="global_action_voice_assist" msgid="7751191495200504480">"Raddaðstoð"</string>
@@ -276,6 +273,9 @@
<string name="permgrouplab_location" msgid="7275582855722310164">"Staðsetning"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"fá aðgang að staðsetningu þessa tækis"</string>
<string name="permgrouprequest_location" msgid="3788275734953323491">"Viltu veita <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> aðgang að staðsetningu þessa tækis?"</string>
+ <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"Forritið hefur aðeins aðgang að staðsetningunni á meðan þú notar forritið."</string>
+ <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"Viltu alltaf veita <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> aðgang að staðsetningu þessa tækis?"</string>
+ <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"Forritið hefur alltaf aðgang að staðsetningunni, jafnvel þegar þú ert ekki að nota forritið."</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Dagatal"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"fá aðgang að dagatalinu þínu"</string>
<string name="permgrouprequest_calendar" msgid="289900767793189421">"Viltu veita <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> aðgang að dagatalinu þínu?"</string>
@@ -402,8 +402,7 @@
<string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Þetta forrit getur bætt við, fjarlægt eða breytt dagatalsviðburðum í símanum. Þetta forrit getur sent skilaboð sem geta virst koma ffrá eigendum dagatala eða breytt viðburðum án þess að láta eigendurna vita."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"aðgangur að viðbótarskipunum staðsetningarveitu"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Leyfir forriti að fá aðgang að fleiri skipunum staðsetningarveitu. Þetta getur gert forritinu kleift að hafa áhrif á virkni GPS og annars staðsetningarbúnaðar."</string>
- <!-- no translation found for permlab_accessFineLocation (6265109654698562427) -->
- <skip />
+ <string name="permlab_accessFineLocation" msgid="6265109654698562427">"aðgangur að nákvæmri staðsetningu aðeins í forgrunni"</string>
<string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Þetta forrit getur aðeins séð staðsetningu þína þegar það er í forgrunni. Það verður að vera kveikt á þessari staðsetningarþjónustu og hún þarf að vera aðgengileg í símanum til að forritið geti notað hana. Þetta getur aukið rafhlöðunotkun."</string>
<string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"fá aðgang að áætlaðri staðsetningu (frá símkerfi)"</string>
<string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Þetta forrit getur séð staðsetningu þína út frá netkerfum á borð við farsímasenda og Wi-Fi net. Það verður að vera kveikt á slíkri staðsetningarþjónustu og hún þarf að vera aðgengileg spjaldtölvunni til að forritið geti notað hana."</string>
@@ -518,6 +517,37 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Fingrafaratákn"</string>
+ <string name="permlab_manageFace" msgid="2137540986007309781">"stjórna vélbúnaði andlitsgreiningar"</string>
+ <string name="permdesc_manageFace" msgid="8919637120670185330">"Leyfir forritinu að beita aðferðum til að bæta við og eyða andlitssniðmátum til notkunar."</string>
+ <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"nota vélbúnað andlitsgreiningar"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Leyfir forritinu að nota andlitsgreiningarvélbúnað til auðkenningar"</string>
+ <string name="face_acquired_insufficient" msgid="5901287247766106330">"Ekki tókst að vinna úr andlitinu. Reyndu aftur."</string>
+ <string name="face_acquired_too_bright" msgid="610606792381297174">"Andlitið er of bjart. Prófaðu í minni birtu."</string>
+ <string name="face_acquired_too_dark" msgid="7229162716976778371">"Andlitið er of dökkt. Finndu betri lýsingu."</string>
+ <string name="face_acquired_too_close" msgid="1980310037427755293">"Færðu skynjarann lengra frá andlitinu."</string>
+ <string name="face_acquired_too_far" msgid="4494571381828850007">"Færðu skynjarann nær andlitinu."</string>
+ <string name="face_acquired_too_high" msgid="228411096134808372">"Færðu skynjarann ofar."</string>
+ <string name="face_acquired_too_low" msgid="4539774649296349109">"Færðu skynjarann neðar."</string>
+ <string name="face_acquired_too_right" msgid="1650292067226118760">"Færðu skynjarann til hægri."</string>
+ <string name="face_acquired_too_left" msgid="2712489669456176505">"Færðu skynjarann til vinstri."</string>
+ <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Horfðu á skynjarann."</string>
+ <string name="face_acquired_not_detected" msgid="5707782294589511391">"Ekkert andlit fannst."</string>
+ <string name="face_acquired_not_steady" msgid="3722829465011040042">"Haltu andlitinu kyrru fyrir framan tækið."</string>
+ <string-array name="face_acquired_vendor">
+ </string-array>
+ <string name="face_error_hw_not_available" msgid="6255891785768984615">"Andlitsvélbúnaður ekki til staðar."</string>
+ <string name="face_error_timeout" msgid="4014326147867150054">"Tímamörk runnu út fyrir andlit. Reyndu aftur."</string>
+ <string name="face_error_no_space" msgid="8224993703466381314">"Ekki tókst að geyma andlit."</string>
+ <string name="face_error_canceled" msgid="283945501061931023">"Hætt við andlitsgreiningu."</string>
+ <string name="face_error_lockout" msgid="3407426963155388504">"Of margar tilraunir. Reyndu aftur síðar."</string>
+ <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Of margar tilraunir. Slökkt á andlitsgreiningu."</string>
+ <string name="face_error_unable_to_process" msgid="238761109287767270">"Reyndu aftur."</string>
+ <string name="face_error_not_enrolled" msgid="9166792142679691323">"Ekkert andlit skráð."</string>
+ <string name="face_error_hw_not_present" msgid="4737289254517095671">"Þetta tæki er ekki með auðkenningarskynjara fyrir andlit"</string>
+ <string name="face_name_template" msgid="7004562145809595384">"Andlit <xliff:g id="FACEID">%d</xliff:g>"</string>
+ <string-array name="face_error_vendor">
+ </string-array>
+ <string name="face_icon_content_description" msgid="4024817159806482191">"Andlitstákn"</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"lesa samstillingar"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Leyfir forriti að lesa kosti samstillingar fyrir reikning. Þetta er til dæmis hægt að nota til að komast að því hvort forritið Fólk er samstillt við reikning."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"kveikja og slökkva á samstillingu"</string>
@@ -1178,6 +1208,9 @@
<skip />
<string name="wifi_no_internet" msgid="8938267198124654938">"Wi-Fi er ekki með tengingu við internetið"</string>
<string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Ýttu til að sjá valkosti"</string>
+ <string name="wifi_softap_config_change" msgid="8475911871165857607">"Breytingar á stillingum heits reits"</string>
+ <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"Tíðnisvið heita reitsins hefur breyst."</string>
+ <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"Þetta tæki styður ekki val þitt fyrir aðeins 5 GHz. Í staðinn mun þetta tæki nota 5 GHz tíðnisvið þegar það er í boði."</string>
<string name="network_switch_metered" msgid="4671730921726992671">"Skipt yfir á <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
<string name="network_switch_metered_detail" msgid="775163331794506615">"Tækið notar <xliff:g id="NEW_NETWORK">%1$s</xliff:g> þegar <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> er ekki með internetaðgang. Gjöld kunna að eiga við."</string>
<string name="network_switch_metered_toast" msgid="5779283181685974304">"Skipt úr <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> yfir í <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index f7cc8b6..445f3a7 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -236,9 +236,6 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Modalità aereo"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Modalità aereo attiva"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Modalità aereo non attiva"</string>
- <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Risparmio energetico"</string>
- <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Risparmio energetico disattivato"</string>
- <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Risparmio energetico attivo"</string>
<string name="global_action_settings" msgid="1756531602592545966">"Impostazioni"</string>
<string name="global_action_assist" msgid="3892832961594295030">"Assistenza"</string>
<string name="global_action_voice_assist" msgid="7751191495200504480">"Voice Assist"</string>
@@ -276,6 +273,9 @@
<string name="permgrouplab_location" msgid="7275582855722310164">"Geolocalizzazione"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"accedere alla posizione di questo dispositivo"</string>
<string name="permgrouprequest_location" msgid="3788275734953323491">"Consentire all\'app <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> di accedere alla posizione di questo dispositivo?"</string>
+ <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"L\'app avrà accesso alla posizione soltanto quando la usi."</string>
+ <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"Vuoi consentire sempre a <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> di accedere alla posizione di questo dispositivo?"</string>
+ <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"L\'app avrà sempre accesso alla posizione, anche quando non la usi."</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Calendario"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"accedere al calendario"</string>
<string name="permgrouprequest_calendar" msgid="289900767793189421">"Consentire all\'app <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> di accedere al tuo calendario?"</string>
@@ -402,8 +402,7 @@
<string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Questa app può aggiungere, rimuovere o modificare eventi di calendario sul telefono. Può inviare messaggi che possono sembrare inviati dai proprietari del calendario o modificare eventi senza notificare i proprietari."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"accesso a comandi aggiuntivi provider di geolocalizz."</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Consente all\'app di accedere a ulteriori comandi del fornitore di posizione. Ciò potrebbe consentire all\'app di interferire con il funzionamento del GPS o di altre fonti di geolocalizzazione."</string>
- <!-- no translation found for permlab_accessFineLocation (6265109654698562427) -->
- <skip />
+ <string name="permlab_accessFineLocation" msgid="6265109654698562427">"Accesso alla posizione esatta solo in primo piano"</string>
<string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Questa app può recuperare la tua posizione esatta solo quando è in primo piano. Questi servizi di geolocalizzazione devono essere attivi e disponibili sul telefono affinché l\'app possa usarli. Potrebbe aumentare il consumo della batteria."</string>
<string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"accesso alla posizione approssimativa (basata sulla rete)"</string>
<string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Questa app può accedere alla tua posizione tramite fonti di rete come antenne di telefonia mobile e reti Wi-Fi. Tali servizi di geolocalizzazione devono essere attivati e disponibili sul tablet per consentire all\'app di utilizzarli."</string>
@@ -518,6 +517,37 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Icona dell\'impronta digitale"</string>
+ <string name="permlab_manageFace" msgid="2137540986007309781">"gestisci l\'hardware per l\'autenticazione dei volti"</string>
+ <string name="permdesc_manageFace" msgid="8919637120670185330">"Consente all\'app di richiamare i metodi per aggiungere e rimuovere i modelli di volti."</string>
+ <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"utilizza l\'hardware per l\'autenticazione dei volti"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Consente all\'app di utilizzare hardware per l\'autenticazione dei volti"</string>
+ <string name="face_acquired_insufficient" msgid="5901287247766106330">"Impossibile elaborare il volto. Riprova."</string>
+ <string name="face_acquired_too_bright" msgid="610606792381297174">"Volto troppo luminoso. Prova dove c\'è meno luce."</string>
+ <string name="face_acquired_too_dark" msgid="7229162716976778371">"Volto troppo scuro. Non coprire la fonte di luce."</string>
+ <string name="face_acquired_too_close" msgid="1980310037427755293">"Allontana il sensore dal volto."</string>
+ <string name="face_acquired_too_far" msgid="4494571381828850007">"Avvicina il sensore al volto."</string>
+ <string name="face_acquired_too_high" msgid="228411096134808372">"Sposta il sensore verso l\'alto."</string>
+ <string name="face_acquired_too_low" msgid="4539774649296349109">"Sposta il sensore verso il basso."</string>
+ <string name="face_acquired_too_right" msgid="1650292067226118760">"Sposta il sensore verso destra."</string>
+ <string name="face_acquired_too_left" msgid="2712489669456176505">"Sposta il sensore verso sinistra."</string>
+ <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Guarda il sensore."</string>
+ <string name="face_acquired_not_detected" msgid="5707782294589511391">"Nessun volto rilevato."</string>
+ <string name="face_acquired_not_steady" msgid="3722829465011040042">"Tieni il volto fermo davanti al dispositivo."</string>
+ <string-array name="face_acquired_vendor">
+ </string-array>
+ <string name="face_error_hw_not_available" msgid="6255891785768984615">"Hardware per il volto non disponibile."</string>
+ <string name="face_error_timeout" msgid="4014326147867150054">"Timeout operazione associata al volto. Riprova."</string>
+ <string name="face_error_no_space" msgid="8224993703466381314">"Il volto non può essere memorizzato."</string>
+ <string name="face_error_canceled" msgid="283945501061931023">"Operazione associata al volto annullata."</string>
+ <string name="face_error_lockout" msgid="3407426963155388504">"Troppi tentativi. Riprova più tardi."</string>
+ <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Troppi tentativi. Autenticazione disattivata."</string>
+ <string name="face_error_unable_to_process" msgid="238761109287767270">"Riprova."</string>
+ <string name="face_error_not_enrolled" msgid="9166792142679691323">"Nessun volto registrato."</string>
+ <string name="face_error_hw_not_present" msgid="4737289254517095671">"Nessun sensore di autenticazione del volto sul dispositivo."</string>
+ <string name="face_name_template" msgid="7004562145809595384">"Volto <xliff:g id="FACEID">%d</xliff:g>"</string>
+ <string-array name="face_error_vendor">
+ </string-array>
+ <string name="face_icon_content_description" msgid="4024817159806482191">"Icona volto"</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"lettura impostazioni di sincronizz."</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Consente all\'applicazione di leggere le impostazioni di sincronizzazione per un account. Ad esempio, questa autorizzazione può determinare se l\'applicazione Persone è sincronizzata con un account."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"attivazione e disattivazione della sincronizzazione"</string>
@@ -1178,6 +1208,9 @@
<skip />
<string name="wifi_no_internet" msgid="8938267198124654938">"La rete Wi-Fi non ha accesso a Internet"</string>
<string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Tocca per le opzioni"</string>
+ <string name="wifi_softap_config_change" msgid="8475911871165857607">"Modifiche alle tue impostazioni dell\'hotspot"</string>
+ <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"La tua banda di hotspot è cambiata."</string>
+ <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"Questo dispositivo non supporta la tua preferenza esclusiva per 5 GHz. Utilizzerà invece la banda a 5 GHz solo quando è disponibile."</string>
<string name="network_switch_metered" msgid="4671730921726992671">"Passato a <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
<string name="network_switch_metered_detail" msgid="775163331794506615">"Il dispositivo utilizza <xliff:g id="NEW_NETWORK">%1$s</xliff:g> quando <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> non ha accesso a Internet. Potrebbero essere applicati costi."</string>
<string name="network_switch_metered_toast" msgid="5779283181685974304">"Passato da <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> a <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index a8e231a..0c0e0ab 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -242,9 +242,6 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"מצב טיסה"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"מצב טיסה מופעל"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"מצב טיסה כבוי"</string>
- <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"חיסכון בסוללה"</string>
- <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"תכונת החיסכון בסוללה כבויה"</string>
- <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"תכונת החיסכון בסוללה פועלת"</string>
<string name="global_action_settings" msgid="1756531602592545966">"הגדרות"</string>
<string name="global_action_assist" msgid="3892832961594295030">"סיוע"</string>
<string name="global_action_voice_assist" msgid="7751191495200504480">"Voice Assist"</string>
@@ -282,6 +279,9 @@
<string name="permgrouplab_location" msgid="7275582855722310164">"מיקום"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"גישה אל מיקום המכשיר הזה"</string>
<string name="permgrouprequest_location" msgid="3788275734953323491">"לתת לאפליקציה <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> הרשאת גישה למיקום המכשיר?"</string>
+ <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"לאפליקציה תהיה גישה אל נתוני המיקום רק במהלך השימוש באפליקציה."</string>
+ <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"תמיד לתת לאפליקציה <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> הרשאת גישה למיקום המכשיר?"</string>
+ <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"לאפליקציה תמיד תהיה גישה לנתוני המיקום, גם כשהאפליקציה אינה בשימוש."</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"יומן"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"גישה אל היומן"</string>
<string name="permgrouprequest_calendar" msgid="289900767793189421">"לתת לאפליקציה <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> הרשאת גישה ליומן?"</string>
@@ -408,8 +408,7 @@
<string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"אפליקציה זו יכולה להוסיף, להסיר ולשנות אירועי יומן בטלפון. האפליקציה יכולה לשנות אירועים בלי להודיע לבעליהם ולשלוח הודעות שעשויות להיראות כאילו נשלחו מבעלי יומנים."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"גישה לפקודות ספק מיקום נוספות"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"מאפשרת לאפליקציה לגשת לפקודות נוספות של ספק המיקום. הרשאה זו עשויה לאפשר לאפליקציה לשבש את פעולת ה-GPS או מקורות מיקום אחרים."</string>
- <!-- no translation found for permlab_accessFineLocation (6265109654698562427) -->
- <skip />
+ <string name="permlab_accessFineLocation" msgid="6265109654698562427">"קבלת גישה למיקום מדויק בחזית בלבד"</string>
<string name="permdesc_accessFineLocation" msgid="3520508381065331098">"אפליקציה זו יכולה לזהות את המיקום המדויק שלך רק כאשר היא פועלת בחזית. כדי שהאפליקציה תוכל להשתמש בשירותי המיקום, עליהם להיות מופעלים וזמינים בטלפון. ייתכן שפעולה זו תגביר את צריכת הסוללה."</string>
<string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"גישה אל מיקום משוער (מבוסס רשת)"</string>
<string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"אפליקציה זו יכולה לזהות את המיקום שלך על סמך מקורות מיקום ברשת, כגון אנטנות סלולריות ורשתות Wi-Fi. שירותי מיקום אלה חייבים להיות מופעלים וזמינים בטאבלט שלך כדי שהאפליקציה תוכל להשתמש בהם."</string>
@@ -524,6 +523,37 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"סמל טביעת אצבע"</string>
+ <string name="permlab_manageFace" msgid="2137540986007309781">"ניהול של חומרה של זיהוי פנים לצורך אימות"</string>
+ <string name="permdesc_manageFace" msgid="8919637120670185330">"מאפשרת לאפליקציה להפעיל שיטות להוספה ומחיקה של תבניות פנים שבהן ייעשה שימוש."</string>
+ <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"שימוש בחומרה של זיהוי פנים לצורך אימות"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"מאפשרת לאפליקציה להשתמש בחומרה של זיהוי פנים לצורך אימות"</string>
+ <string name="face_acquired_insufficient" msgid="5901287247766106330">"לא ניתן היה לעבד את הפנים. יש לנסות שוב."</string>
+ <string name="face_acquired_too_bright" msgid="610606792381297174">"הפנים בהירים מדי. יש לנסות באור עמום יותר."</string>
+ <string name="face_acquired_too_dark" msgid="7229162716976778371">"הפנים כהים מדי. יש להוסיף מקור אור."</string>
+ <string name="face_acquired_too_close" msgid="1980310037427755293">"יש להרחיק את החיישן מהפנים."</string>
+ <string name="face_acquired_too_far" msgid="4494571381828850007">"יש לקרב את החיישן לפנים."</string>
+ <string name="face_acquired_too_high" msgid="228411096134808372">"יש להזיז את החיישן גבוה יותר."</string>
+ <string name="face_acquired_too_low" msgid="4539774649296349109">"יש להזיז את החיישן נמוך יותר."</string>
+ <string name="face_acquired_too_right" msgid="1650292067226118760">"יש להזיז את החיישן ימינה."</string>
+ <string name="face_acquired_too_left" msgid="2712489669456176505">"יש להזיז את החיישן שמאלה."</string>
+ <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"יש להסתכל על החיישן."</string>
+ <string name="face_acquired_not_detected" msgid="5707782294589511391">"לא זוהו פנים."</string>
+ <string name="face_acquired_not_steady" msgid="3722829465011040042">"יש להחזיק את הפנים באופן יציב מול המכשיר."</string>
+ <string-array name="face_acquired_vendor">
+ </string-array>
+ <string name="face_error_hw_not_available" msgid="6255891785768984615">"החומרה לזיהוי הפנים לא זמינה."</string>
+ <string name="face_error_timeout" msgid="4014326147867150054">"חלף הזמן הקצוב לזיהוי הפנים. יש לנסות שוב."</string>
+ <string name="face_error_no_space" msgid="8224993703466381314">"לא ניתן לשמור את הפנים."</string>
+ <string name="face_error_canceled" msgid="283945501061931023">"פעולת הפנים בוטלה."</string>
+ <string name="face_error_lockout" msgid="3407426963155388504">"יותר מדי ניסיונות. יש לנסות שוב מאוחר יותר."</string>
+ <string name="face_error_lockout_permanent" msgid="8198354656746088890">"יותר מדי ניסיונות. אימות הפנים הושבת."</string>
+ <string name="face_error_unable_to_process" msgid="238761109287767270">"יש לנסות שוב."</string>
+ <string name="face_error_not_enrolled" msgid="9166792142679691323">"לא נרשמו פנים."</string>
+ <string name="face_error_hw_not_present" msgid="4737289254517095671">"במכשיר זה אין חיישן לאימות פנים"</string>
+ <string name="face_name_template" msgid="7004562145809595384">"פנים <xliff:g id="FACEID">%d</xliff:g>"</string>
+ <string-array name="face_error_vendor">
+ </string-array>
+ <string name="face_icon_content_description" msgid="4024817159806482191">"סמל הפנים"</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"קרא את הגדרות הסינכרון"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"מאפשר לאפליקציה לקרוא את הגדרות הסנכרון של חשבון. לדוגמה, ניתן לגלות כך האם האפליקציה \'אנשים\' מסונכרן עם חשבון כלשהו."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"הפעלת וכיבוי סנכרון"</string>
@@ -1222,6 +1252,9 @@
<skip />
<string name="wifi_no_internet" msgid="8938267198124654938">"לרשת ה-Wi-Fi אין גישה לאינטרנט"</string>
<string name="wifi_no_internet_detailed" msgid="8083079241212301741">"הקש לקבלת אפשרויות"</string>
+ <string name="wifi_softap_config_change" msgid="8475911871165857607">"שינויים להגדרות של הנקודה לשיתוף אינטרנט"</string>
+ <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"התדר של הנקודה לשיתוף אינטרנט השתנה."</string>
+ <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"מכשיר זה לא תומך בהעדפות שלך ל-5GHz בלבד. במקום זאת, מכשיר זה ישתמש בתדר 5GHz כשיהיה זמין."</string>
<string name="network_switch_metered" msgid="4671730921726992671">"מעבר אל <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
<string name="network_switch_metered_detail" msgid="775163331794506615">"המכשיר משתמש ברשת <xliff:g id="NEW_NETWORK">%1$s</xliff:g> כשלרשת <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> אין גישה לאינטרנט. עשויים לחול חיובים."</string>
<string name="network_switch_metered_toast" msgid="5779283181685974304">"עבר מרשת <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> לרשת <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 9f8fd74..00d1b5d 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -236,9 +236,6 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"機内モード"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"機内モードON"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"機内モードOFF"</string>
- <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"バッテリー セーバー"</string>
- <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"バッテリー セーバー OFF"</string>
- <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"バッテリー セーバー ON"</string>
<string name="global_action_settings" msgid="1756531602592545966">"設定"</string>
<string name="global_action_assist" msgid="3892832961594295030">"サポート"</string>
<string name="global_action_voice_assist" msgid="7751191495200504480">"音声アシスト"</string>
@@ -276,6 +273,9 @@
<string name="permgrouplab_location" msgid="7275582855722310164">"位置情報"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"この端末の位置情報へのアクセス"</string>
<string name="permgrouprequest_location" msgid="3788275734953323491">"この端末の位置情報へのアクセスを <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> に許可しますか?"</string>
+ <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"このアプリは、使用時のみ、位置情報にアクセスできるようになります。"</string>
+ <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"この端末の位置情報へのアクセスを <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> に常に許可しますか?"</string>
+ <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"このアプリは、未使用時も含め、常に位置情報にアクセスできるようになります。"</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"カレンダー"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"カレンダーへのアクセス"</string>
<string name="permgrouprequest_calendar" msgid="289900767793189421">"カレンダーへのアクセスを <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> に許可しますか?"</string>
@@ -402,8 +402,7 @@
<string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"このアプリは、お使いのスマートフォンでカレンダーの予定を追加、削除、変更できます。また、カレンダーの所有者から発信されたかのように表示されるメッセージを送信したり、所有者に通知することなく予定を変更したりできます。"</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"位置情報提供者の追加コマンドアクセス"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"位置情報提供元の追加のコマンドにアクセスすることをアプリに許可します。許可すると、アプリがGPSなどの位置情報源の動作を妨害する恐れがあります。"</string>
- <!-- no translation found for permlab_accessFineLocation (6265109654698562427) -->
- <skip />
+ <string name="permlab_accessFineLocation" msgid="6265109654698562427">"フォアグラウンドでのみ正確な位置情報にアクセス"</string>
<string name="permdesc_accessFineLocation" msgid="3520508381065331098">"このアプリは、フォアグラウンド状態でのみユーザーの正確な位置情報を取得できます。この位置情報サービスは ON の状態にして、スマートフォンでアプリがサービスを利用できるようにする必要があります。これにより、電池の消費量が増える可能性があります。"</string>
<string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"おおよその位置情報(ネットワーク基地局)へのアクセス"</string>
<string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"このアプリは、ネットワーク位置情報源(携帯基地局や Wi-Fi ネットワークなど)に基づいて、ユーザーの位置情報を取得します。これらの位置情報サービスは ON の状態にして、タブレットでアプリがサービスを利用できるようにする必要があります。"</string>
@@ -518,6 +517,37 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"指紋アイコン"</string>
+ <string name="permlab_manageFace" msgid="2137540986007309781">"顔認証ハードウェアの管理"</string>
+ <string name="permdesc_manageFace" msgid="8919637120670185330">"使用する顔テンプレートの追加や削除を行うメソッドの呼び出しをアプリに許可します。"</string>
+ <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"顔認証ハードウェアの使用"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"顔認証ハードウェアを認証に使用することをアプリに許可します"</string>
+ <string name="face_acquired_insufficient" msgid="5901287247766106330">"顔を認識できませんでした。もう一度お試しください。"</string>
+ <string name="face_acquired_too_bright" msgid="610606792381297174">"顔が明るすぎます。照明を暗くしてみてください。"</string>
+ <string name="face_acquired_too_dark" msgid="7229162716976778371">"顔が暗すぎます。光源のカバーを外してください。"</string>
+ <string name="face_acquired_too_close" msgid="1980310037427755293">"センサーを顔から遠ざけてください。"</string>
+ <string name="face_acquired_too_far" msgid="4494571381828850007">"センサーを顔に近づけてください。"</string>
+ <string name="face_acquired_too_high" msgid="228411096134808372">"センサーを上に動かしてください。"</string>
+ <string name="face_acquired_too_low" msgid="4539774649296349109">"センサーを下に動かしてください。"</string>
+ <string name="face_acquired_too_right" msgid="1650292067226118760">"センサーを右に動かしてください。"</string>
+ <string name="face_acquired_too_left" msgid="2712489669456176505">"センサーを左に動かしてください。"</string>
+ <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"センサーを見てください。"</string>
+ <string name="face_acquired_not_detected" msgid="5707782294589511391">"顔を検出できません。"</string>
+ <string name="face_acquired_not_steady" msgid="3722829465011040042">"端末の前で顔を静止してください。"</string>
+ <string-array name="face_acquired_vendor">
+ </string-array>
+ <string name="face_error_hw_not_available" msgid="6255891785768984615">"顔認証ハードウェアが使用できません。"</string>
+ <string name="face_error_timeout" msgid="4014326147867150054">"読み取りのタイムアウトです。もう一度お試しください。"</string>
+ <string name="face_error_no_space" msgid="8224993703466381314">"顔の情報を保存できません。"</string>
+ <string name="face_error_canceled" msgid="283945501061931023">"顔の操作をキャンセルしました。"</string>
+ <string name="face_error_lockout" msgid="3407426963155388504">"試行回数の上限です。後でもう一度お試しください。"</string>
+ <string name="face_error_lockout_permanent" msgid="8198354656746088890">"試行回数の上限です。顔認証は無効になりました。"</string>
+ <string name="face_error_unable_to_process" msgid="238761109287767270">"もう一度お試しください。"</string>
+ <string name="face_error_not_enrolled" msgid="9166792142679691323">"顔の情報が登録されていません。"</string>
+ <string name="face_error_hw_not_present" msgid="4737289254517095671">"この端末には顔認証センサーがありません"</string>
+ <string name="face_name_template" msgid="7004562145809595384">"顔 <xliff:g id="FACEID">%d</xliff:g>"</string>
+ <string-array name="face_error_vendor">
+ </string-array>
+ <string name="face_icon_content_description" msgid="4024817159806482191">"顔アイコン"</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"同期設定の読み取り"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"アカウントの同期設定の読み取りをアプリに許可します。たとえば、連絡帳アプリがアカウントと同期しているかどうかをアプリから特定できるようになります。"</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"同期のON/OFFの切り替え"</string>
@@ -1178,6 +1208,9 @@
<skip />
<string name="wifi_no_internet" msgid="8938267198124654938">"Wi‑Fi はインターネットに接続していません"</string>
<string name="wifi_no_internet_detailed" msgid="8083079241212301741">"タップしてその他のオプションを表示"</string>
+ <string name="wifi_softap_config_change" msgid="8475911871165857607">"アクセス ポイントの設定の変更"</string>
+ <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"アクセス ポイントの帯域幅が変更されました。"</string>
+ <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"この端末は 5 GHz のみという設定に対応していません。ただし、5 GHz 周波数帯が利用できるときには利用します。"</string>
<string name="network_switch_metered" msgid="4671730921726992671">"「<xliff:g id="NETWORK_TYPE">%1$s</xliff:g>」に切り替えました"</string>
<string name="network_switch_metered_detail" msgid="775163331794506615">"端末で「<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>」によるインターネット接続ができない場合に「<xliff:g id="NEW_NETWORK">%1$s</xliff:g>」を使用します。通信料が発生することがあります。"</string>
<string name="network_switch_metered_toast" msgid="5779283181685974304">"「<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g>」から「<xliff:g id="NEW_NETWORK">%2$s</xliff:g>」に切り替えました"</string>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index deab577..ab65348 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -236,9 +236,6 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"თვითმფრინავის რეჟიმი"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"თვითმფრინავის რეჟიმი ჩართულია."</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"თვითმფრინავის რეჟიმი გამორთულია."</string>
- <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"ბატარეის დამზოგი"</string>
- <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"ბატარეის დამზოგი გამორთულია"</string>
- <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"ბატარეის დამზოგი ჩართულია"</string>
<string name="global_action_settings" msgid="1756531602592545966">"პარამეტრები"</string>
<string name="global_action_assist" msgid="3892832961594295030">"დახმარება"</string>
<string name="global_action_voice_assist" msgid="7751191495200504480">"ხმოვანი ასისტ."</string>
@@ -276,6 +273,9 @@
<string name="permgrouplab_location" msgid="7275582855722310164">"მდებარეობა"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"მოწყობილობის მდებარეობაზე წვდომა"</string>
<string name="permgrouprequest_location" msgid="3788275734953323491">"გსურთ, მიანიჭოთ <b><xliff:g id="APP_NAME">%1$s</xliff:g>-ს</b> ამ მოწყობილობის მდებარეობაზე წვდომის ნებართვა?"</string>
+ <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"ამ აპს მდებარეობაზე წვდომა მხოლოდ მაშინ ექნება, როცა თქვენ მას გამოიყენებთ."</string>
+ <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"გსურთ, მიანიჭოთ <b><xliff:g id="APP_NAME">%1$s</xliff:g>-ს</b> ამ მოწყობილობის მდებარეობაზე წვდომის ნებართვა?"</string>
+ <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"ამ აპს ყოველთვის ექნება მდებარეობაზე წვდომა, მაშინაც კი, როცა თქვენ მას არ იყენებთ."</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"კალენდარი"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"თქვენს კალენდარზე წვდომა"</string>
<string name="permgrouprequest_calendar" msgid="289900767793189421">"გსურთ, მიანიჭოთ <b><xliff:g id="APP_NAME">%1$s</xliff:g>-ს</b> თქვენს კალენდარზე წვდომის ნებართვა?"</string>
@@ -402,8 +402,7 @@
<string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"ამ აპს შეუძლია თქვენს ტელეფონში კალენდრის მოვლენების დამატება, ამოშლა ან შეცვლა. ამ აპს შეუძლია კალენდრების მფლობელების სახელით შეტყობინებების გაგზავნა ან მოვლენების მათი მფლობელების შეტყობინების გარეშე შეცვლა."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"მდებარეობის პროვაიდერის დამატებით ბრძანებებზე წვდომა"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"აპს შეეძლება წვდომა ჰქონდეს მდებარეობის სერვისის დამატებით ბრძანებებზე. შესაძლოა აპმა ეს გამოიყენოს GPS-ისა და მდებარეობის სხვა წყაროების მუშაობის პროცესში ჩარევისთვის."</string>
- <!-- no translation found for permlab_accessFineLocation (6265109654698562427) -->
- <skip />
+ <string name="permlab_accessFineLocation" msgid="6265109654698562427">"ზუსტ მდებარეობაზე წვდომა მხოლოდ წინა პლანზე"</string>
<string name="permdesc_accessFineLocation" msgid="3520508381065331098">"ამ აპს შეუძლია თქვენი ზუსტი მდებარეობის შესახებ ინფორმაციის მიღება მხოლოდ მაშინ, როცა გაშვებულია წინა პლანზე. თქვენს ტელეფონზე ჩართული და ხელმისაწვდომი უნდა იყოს მდებარეობის სერვისები, აპმა მათი გამოყენება რომ შეძლოს. ამან შეიძლება გაზარდოს ბატარეის მოხმარება."</string>
<string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"მიახლოებით მდებარეობაზე წვდომა (ქსელის მეშვეობით)"</string>
<string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"ამ აპს თქვენი მდებარეობის შესახებ ინფორმაციის მიღება ისეთი წყაროების მეშვეობით შეუძლია, როგორიცაა მობილური კავშირგაბმულობის ანძები და Wi-Fi ქსელები. მდებარეობის აღნიშნული სერვისები თქვენს ტაბლეტზე ჩართული და ხელმისაწვდომი უნდა იყოს, რათა აპმა მათი გამოყენება შეძლოს."</string>
@@ -518,6 +517,37 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"თითის ანაბეჭდის ხატულა"</string>
+ <string name="permlab_manageFace" msgid="2137540986007309781">"სახის ამოცნობის აპარატურის მართვა"</string>
+ <string name="permdesc_manageFace" msgid="8919637120670185330">"საშუალებას აძლევს აპს, დაამატოს და წაშალოს სახეების შაბლონები."</string>
+ <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"სახის ამოცნობის აპარატურის გამოყენება"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"საშუალებას აძლევს აპს, ავტორიზაციისთვის გამოიყენოს სახის ამოცნობის აპარატურა"</string>
+ <string name="face_acquired_insufficient" msgid="5901287247766106330">"სახე ვერ მუშავდება. გთხოვთ, ცადოთ ხელახლა."</string>
+ <string name="face_acquired_too_bright" msgid="610606792381297174">"სახე გადანათებულია. დაუკელით განათებას."</string>
+ <string name="face_acquired_too_dark" msgid="7229162716976778371">"სახე ჩაბნელებულია. მოუმატეთ განათებას."</string>
+ <string name="face_acquired_too_close" msgid="1980310037427755293">"დააშორეთ მოწყობილობის სენსორი სახეს."</string>
+ <string name="face_acquired_too_far" msgid="4494571381828850007">"მიუახლოვეთ მოწყობილობის სენსორი სახეს."</string>
+ <string name="face_acquired_too_high" msgid="228411096134808372">"ასწიეთ მოწყობილობის სენსორი ოდნავ ზემოთ."</string>
+ <string name="face_acquired_too_low" msgid="4539774649296349109">"ჩასწიეთ მოწყობილობის სენსორი ოდნავ ქვემოთ."</string>
+ <string name="face_acquired_too_right" msgid="1650292067226118760">"გასწიეთ მოწყობილობის სენსორი ოდნავ მარჯვნივ."</string>
+ <string name="face_acquired_too_left" msgid="2712489669456176505">"გასწიეთ მოწყობილობის სენსორი ოდნავ მარცხნივ."</string>
+ <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"შეხედეთ სენსორს."</string>
+ <string name="face_acquired_not_detected" msgid="5707782294589511391">"სახის ამოცნობა ვერ მოხერხდა."</string>
+ <string name="face_acquired_not_steady" msgid="3722829465011040042">"მოათავსეთ სახე მოწყობილობის წინ უმოძრაოდ."</string>
+ <string-array name="face_acquired_vendor">
+ </string-array>
+ <string name="face_error_hw_not_available" msgid="6255891785768984615">"სახის ამოცნობის აპარატურა მიუწვდომელია."</string>
+ <string name="face_error_timeout" msgid="4014326147867150054">"სახის ამოცნობის დრო ამოიწურა. ცადეთ ხელახლა."</string>
+ <string name="face_error_no_space" msgid="8224993703466381314">"სახის შენახვა ვერ მოხერხდა."</string>
+ <string name="face_error_canceled" msgid="283945501061931023">"სახის ამოცნობა გაუქმდა."</string>
+ <string name="face_error_lockout" msgid="3407426963155388504">"დაფიქსირდა ბევრი მცდელობა. ცადეთ მოგვიანებით."</string>
+ <string name="face_error_lockout_permanent" msgid="8198354656746088890">"დაფიქსირდა ბევრი მცდელობა. სახის ამოცნობა გაითიშა."</string>
+ <string name="face_error_unable_to_process" msgid="238761109287767270">"ცადეთ ხელახლა."</string>
+ <string name="face_error_not_enrolled" msgid="9166792142679691323">"სახე რეგისტრირებული არ არის."</string>
+ <string name="face_error_hw_not_present" msgid="4737289254517095671">"ამ მოწყობილობას არ აქვს სახის ამოცნობის სენსორი"</string>
+ <string name="face_name_template" msgid="7004562145809595384">"სახე <xliff:g id="FACEID">%d</xliff:g>"</string>
+ <string-array name="face_error_vendor">
+ </string-array>
+ <string name="face_icon_content_description" msgid="4024817159806482191">"სახის ხატულა"</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"სინქრონიზაციის პარამეტრების წაკითხვა"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"აპს შეეძლება, წაიკითხოს ანგარიშის სინქრონიზაციის პარამეტრები. მაგალითად, მას შეეძლება განსაზღვროს, არის თუ არა People აპი სინქრონიზებული ანგარიშთან."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"სინქრონიზაციის ჩართვა და გამორთვა"</string>
@@ -1178,6 +1208,9 @@
<skip />
<string name="wifi_no_internet" msgid="8938267198124654938">"Wi‑Fi ქსელს ინტერნეტზე წვდომა არ აქვს"</string>
<string name="wifi_no_internet_detailed" msgid="8083079241212301741">"შეეხეთ ვარიანტების სანახავად"</string>
+ <string name="wifi_softap_config_change" msgid="8475911871165857607">"თქვენი უსადენო ქსელის პარამეტრების ცვლილება"</string>
+ <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"თქვენი უსადენო ქსელის დიაპაზონი შეიცვალა."</string>
+ <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"ამ მოწყობილობას არ შეუძლია მხოლოდ 5 გჰც სიხშირეზე მუშაობა. აღნიშნული სიხშირის გამოყენება მოხდება მაშინ, როცა ეს შესაძლებელია."</string>
<string name="network_switch_metered" msgid="4671730921726992671">"ახლა გამოიყენება <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
<string name="network_switch_metered_detail" msgid="775163331794506615">"თუ <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> ინტერნეტთან კავშირს დაკარგავს, მოწყობილობის მიერ <xliff:g id="NEW_NETWORK">%1$s</xliff:g> იქნება გამოყენებული, რამაც შეიძლება დამატებითი ხარჯები გამოიწვიოს."</string>
<string name="network_switch_metered_toast" msgid="5779283181685974304">"ახლა გამოიყენება <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> (გამოიყენებოდა <xliff:g id="NEW_NETWORK">%2$s</xliff:g>)"</string>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index 38f12c4..3d4749f 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -236,9 +236,6 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Ұшақ режимі"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Ұшақ режимі ҚОСУЛЫ"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Ұшақ режимі ӨШІРУЛІ"</string>
- <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Battery saver"</string>
- <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Battery saver ӨШІРУЛІ"</string>
- <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Battery saver ҚОСУЛЫ"</string>
<string name="global_action_settings" msgid="1756531602592545966">"Параметрлер"</string>
<string name="global_action_assist" msgid="3892832961594295030">"Көмек"</string>
<string name="global_action_voice_assist" msgid="7751191495200504480">"Дауыс көмекшісі"</string>
@@ -276,6 +273,9 @@
<string name="permgrouplab_location" msgid="7275582855722310164">"Орналасу"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"бұл құрылғының орналасқан жерін көру"</string>
<string name="permgrouprequest_location" msgid="3788275734953323491">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> қолданбасына құрылғының орналасқан жері туралы мәліметтерді пайдалануға рұқсат берілсін бе?"</string>
+ <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"Қолданбаны пайдалану кезінде ғана оған геодеректеріңізді көруге рұқсат етіледі."</string>
+ <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"\"<xliff:g id="APP_NAME">%1$s</xliff:g>\" кез келген уақытта құрылғы геодеректерін пайдалансын ба?"</string>
+ <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"Қолданба пайдаланылмаса да, оған геодеректеріңізді көруге рұқсат етіледі."</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Күнтізбе"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"күнтізбеге кіру"</string>
<string name="permgrouprequest_calendar" msgid="289900767793189421">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> қолданбасына күнтізбеге кіруге рұқсат берілсін бе?"</string>
@@ -402,15 +402,14 @@
<string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Бұл қолданба телефондағы күнтізбе оқиғаларын енгізе, өшіре не өзгерте алады. Ол күнтізбе иелерінен келгендей болып көрінетін хабарларды жіберуі немесе иелеріне хабарламай, оқиғаларды өзгертуі мүмкін."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"қосымша аймақ жабдықтаушы пәрмендеріне қол жетімділік"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Қолданбаға орын жеткізушісінің қосымша пәрмендеріне қатынасуға рұқсат береді. Бұл қолданбаға GPS немесе басқа орын көздерінің жұмысына кедергі келтіруге рұқсат беруі мүмкін."</string>
- <!-- no translation found for permlab_accessFineLocation (6265109654698562427) -->
- <skip />
- <string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Бұл қолданба тек фондық режимде нақты орналасқан жеріңіз туралы ақпаратты анықтай алады. Қолданба бұл орынды анықтау қызметтерін пайдалана алуы үшін олар қосулы әрі телефонда қолжетімді болуы керек. Батарея көбірек тұтынылуы мүмкін."</string>
+ <string name="permlab_accessFineLocation" msgid="6265109654698562427">"нақты орналасқан жер туралы ақпаратқа тек ашық экранда кіру"</string>
+ <string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Бұл қолданба нақты орналасқан жеріңіз туралы ақпаратты экранда ашық тұрғанда ғана анықтай алады. Қолданба бұл орынды анықтау қызметтерін пайдалана алуы үшін, олар қосулы әрі телефонда қолжетімді болуы керек. Батарея көбірек тұтынылуы мүмкін."</string>
<string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"шамамен алған орынға қатынасу (желі негізінде)"</string>
<string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Бұл қолданба орналасқан жеріңізді ұялы байланыс мұнаралары мен Wi-Fi желілері сияқты желі көздерінің негізінде анықтайды. Қолданба бұл орынды анықтау қызметтерін пайдалана алуы үшін олар қосулы әрі планшетте қолжетімді болуы керек."</string>
<string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Бұл қолданба орналасқан жеріңізді ұялы байланыс мұнаралары мен Wi-Fi желілері сияқты желі көздерінің негізінде анықтайды. Қолданба бұл орынды анықтау қызметтерін пайдалана алуы үшін олар қосулы әрі теледидарда қолжетімді болуы керек."</string>
<string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Бұл қолданба орналасқан жеріңізді ұялы байланыс мұнаралары мен Wi-Fi желілері сияқты желі көздерінің негізінде анықтайды. Қолданба бұл орынды анықтау қызметтерін пайдалана алуы үшін олар қосулы әрі телефонда қолжетімді болуы керек."</string>
<string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"геодеректерге фондық режимде кіру"</string>
- <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Бұл қолданба кез келген уақытта фондық режимде нақты орналасқан жеріңіз туралы ақпаратты анықтай алады. Қолданба бұл орынды анықтау қызметтерін пайдалана алуы үшін олар қосулы әрі телефонда қолжетімді болуы керек. Батарея көбірек тұтынылуы мүмкін."</string>
+ <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Бұл қолданба нақты орналасқан жеріңіз туралы ақпаратты фондық режимде де анықтай алады. Қолданба бұл орынды анықтау қызметтерін пайдалана алуы үшін, олар қосулы әрі телефонда қолжетімді болуы керек. Батарея көбірек тұтынылуы мүмкін."</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"аудио параметрлерін өзгерту"</string>
<string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Қолданбаға дыбыс қаттылығы және аудио шығыс үндеткішін таңдау сияқты жаһандық аудио параметрлерін өзгерту мүмкіндігін береді."</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"аудио жазу"</string>
@@ -518,6 +517,37 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Саусақ ізі белгішесі"</string>
+ <string name="permlab_manageFace" msgid="2137540986007309781">"бетті тану жабдығын басқару"</string>
+ <string name="permdesc_manageFace" msgid="8919637120670185330">"Қолданбаға пайдаланатын бет үлгілерін енгізу және жою әдістерін шақыруға мүмкіндік береді."</string>
+ <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"бетті тану жабдығын пайдалану"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Қолданбаға бетті тану жабдығын қолдануға рұқсат етеді"</string>
+ <string name="face_acquired_insufficient" msgid="5901287247766106330">"Бет өңделмеді. Әрекетті қайталаңыз."</string>
+ <string name="face_acquired_too_bright" msgid="610606792381297174">"Бет тым ашық түсті болып шықты. Жарықты азайтыңыз."</string>
+ <string name="face_acquired_too_dark" msgid="7229162716976778371">"Бет тым күңгірт түсті. Жарық көзін бөгемеңіз."</string>
+ <string name="face_acquired_too_close" msgid="1980310037427755293">"Датчикті беттен алыстатыңыз."</string>
+ <string name="face_acquired_too_far" msgid="4494571381828850007">"Датчикті бетке жақындатыңыз."</string>
+ <string name="face_acquired_too_high" msgid="228411096134808372">"Датчикті жоғары көтеріңіз."</string>
+ <string name="face_acquired_too_low" msgid="4539774649296349109">"Датчикті төмен қарай жылжытыңыз."</string>
+ <string name="face_acquired_too_right" msgid="1650292067226118760">"Датчикті оңға қарай жылжытыңыз."</string>
+ <string name="face_acquired_too_left" msgid="2712489669456176505">"Датчикті солға қарай жылжытыңыз."</string>
+ <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Датчикке қараңыз."</string>
+ <string name="face_acquired_not_detected" msgid="5707782294589511391">"Бет анықталмады."</string>
+ <string name="face_acquired_not_steady" msgid="3722829465011040042">"Құрылғыға бетіңізді қозғалтпай қарап тұрыңыз."</string>
+ <string-array name="face_acquired_vendor">
+ </string-array>
+ <string name="face_error_hw_not_available" msgid="6255891785768984615">"Бетті тану жабдығы қолжетімді емес."</string>
+ <string name="face_error_timeout" msgid="4014326147867150054">"Күту уақыты бітті. Әрекетті қайталаңыз."</string>
+ <string name="face_error_no_space" msgid="8224993703466381314">"Бетті сақтау мүмкін емес."</string>
+ <string name="face_error_canceled" msgid="283945501061931023">"Бетті танудан бас тартылды."</string>
+ <string name="face_error_lockout" msgid="3407426963155388504">"Тым көп әрекет жасалды. Кейінірек қайталаңыз."</string>
+ <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Тым көп әрекет жасалды. Бетті тану функциясы өшірілді."</string>
+ <string name="face_error_unable_to_process" msgid="238761109287767270">"Қайталап көріңіз."</string>
+ <string name="face_error_not_enrolled" msgid="9166792142679691323">"Ешқандай бет тіркелмеген."</string>
+ <string name="face_error_hw_not_present" msgid="4737289254517095671">"Бұл құрылғыда бетті тану датчигі жоқ"</string>
+ <string name="face_name_template" msgid="7004562145809595384">"<xliff:g id="FACEID">%d</xliff:g> беті"</string>
+ <string-array name="face_error_vendor">
+ </string-array>
+ <string name="face_icon_content_description" msgid="4024817159806482191">"Бет белгішесі"</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"синх параметрлерін оқу"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Қолданбаға есептік жазба синхрондау параметрлерін оқу мүмкіндігін береді. Мысалы, бұл арқылы People қолданбасының есептік жазбамен сихрондалғаны анықталуы мүмкін."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"синх қосу және өшіру арасында ауысу"</string>
@@ -1178,6 +1208,9 @@
<skip />
<string name="wifi_no_internet" msgid="8938267198124654938">"Wi-Fi желісінде интернет байланысы жоқ"</string>
<string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Опциялар үшін түртіңіз"</string>
+ <string name="wifi_softap_config_change" msgid="8475911871165857607">"Хотспот параметрлеріне өзгерістер енгізілді"</string>
+ <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"Хотспот жолағы өзгертілді."</string>
+ <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"Бұл құрылғы тек 5 ГГц жиілікте жұмыс істей алмайды. Бұл жиілік мүмкін болған жағдайда ғана қолданылады."</string>
<string name="network_switch_metered" msgid="4671730921726992671">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> желісіне ауысты"</string>
<string name="network_switch_metered_detail" msgid="775163331794506615">"Құрылғы <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> желісінде интернетпен байланыс жоғалған жағдайда <xliff:g id="NEW_NETWORK">%1$s</xliff:g> желісін пайдаланады. Деректер ақысы алынуы мүмкін."</string>
<string name="network_switch_metered_toast" msgid="5779283181685974304">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> желісінен <xliff:g id="NEW_NETWORK">%2$s</xliff:g> желісіне ауысты"</string>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index 90142dc..22bd6c3 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -236,9 +236,6 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"ពេលជិះយន្តហោះ"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"បានបើករបៀបពេលជិះយន្តហោះ"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"បានបិទរបៀបពេលជិះយន្តហោះ"</string>
- <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"កម្មវិធីសន្សំថ្ម"</string>
- <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"កម្មវិធីសន្សំថ្មបានបិទ"</string>
- <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"កម្មវិធីសន្សំថ្មបានបើក"</string>
<string name="global_action_settings" msgid="1756531602592545966">"ការកំណត់"</string>
<string name="global_action_assist" msgid="3892832961594295030">"ជំនួយ"</string>
<string name="global_action_voice_assist" msgid="7751191495200504480">"ជំនួយសម្លេង"</string>
@@ -276,6 +273,9 @@
<string name="permgrouplab_location" msgid="7275582855722310164">"ទីតាំង"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"ចូលដំណើរការទីតាំងរបស់ឧបករណ៍នេះ"</string>
<string name="permgrouprequest_location" msgid="3788275734953323491">"អនុញ្ញាតឱ្យ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ចូលប្រើទីតាំងរបស់ឧបករណ៍នេះ?"</string>
+ <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"កម្មវិធីនេះនឹងមានសិទ្ធិចូលប្រើទីតាំង នៅពេលអ្នកប្រើកម្មវិធីនេះតែប៉ុណ្ណោះ។"</string>
+ <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"អនុញ្ញាតឱ្យ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ចូលប្រើទីតាំងរបស់ឧបករណ៍នេះជានិច្ចមែនទេ?"</string>
+ <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"កម្មវិធីនេះនឹងមានសិទ្ធិចូលប្រើទីតាំងជានិច្ច ទោះបីជានៅពេលអ្នកមិនប្រើកម្មវិធីនេះក៏ដោយ។"</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"ប្រតិទិន"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"ចូលប្រើប្រិតិទិនរបស់អ្នក"</string>
<string name="permgrouprequest_calendar" msgid="289900767793189421">"អនុញ្ញាតឱ្យ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ចូលប្រើប្រតិទិនរបស់អ្នក?"</string>
@@ -402,8 +402,7 @@
<string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"កម្មវិធីនេះអាចបញ្ចូល លុប ឬប្តូរព្រឹត្តិការណ៍ប្រតិទិននៅលើទូរសព្ទរបស់អ្នក។ កម្មវិធីនេះអាចផ្ញើសារ ដែលអាចបង្ហាញថាសារនោះចេញមកពីម្ចាស់ប្រតិទិន ឬផ្លាស់ប្តូរព្រឹត្តិការណ៍ដោយគ្មានការជូនដំណឹងដល់ម្ចាស់របស់ពួកវាទេ។"</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"ចូលដំណើរការពាក្យបញ្ជាក្រុមហ៊ុនផ្ដល់ទីតាំង"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"ឲ្យកម្មវិធីចូលដំណើរការពាក្យបញ្ជាកម្មវិធីផ្ដល់ទីតាំងបន្ថែម។ វាអាចអនុញ្ញាតឲ្យកម្មវិធីទាក់ទងជាមួយប្រតិបត្តិការជីភីអេស ឬប្រភពទីតាំងផ្សេង។"</string>
- <!-- no translation found for permlab_accessFineLocation (6265109654698562427) -->
- <skip />
+ <string name="permlab_accessFineLocation" msgid="6265109654698562427">"ចូលប្រើទីតាំងជាក់លាក់តែនៅផ្ទៃខាងមុខប៉ុណ្ណោះ"</string>
<string name="permdesc_accessFineLocation" msgid="3520508381065331098">"កម្មវិធីនេះអាចទទួលបានទីតាំងពិតប្រាកដរបស់អ្នកតែនៅពេលវាស្ថិតនៅផ្ទៃខាងមុខប៉ុណ្ណោះ។ សេវាកម្មទីតាំងទាំងនេះត្រូវតែបើក និងមាននៅលើទូរសព្ទរបស់អ្នក ដើម្បីឱ្យកម្មវិធីនេះអាចប្រើពួកវាបាន។ វាអាចប្រើថាមពលច្រើនជាងមុន។"</string>
<string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"ចូលដំណើរការទីតាំងប្រហាក់ប្រហែល (ផ្អែកលើបណ្តាញ)"</string>
<string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"កម្មវិធីនេះអាចទទួលបានទីតាំងរបស់អ្នក ដោយផ្អែកលើប្រភពបណ្តាញ ដូចជា៖ អង់តែន និងបណ្តាញ Wi-Fi ។ សេវាកម្មទីតាំងទាំងនេះត្រូវតែបើក និងត្រូវតែមាននៅលើថេប្លេតរបស់អ្នក ដើម្បីឲ្យកម្មវិធីនេះអាចប្រើវាបាន។"</string>
@@ -518,6 +517,37 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"រូបតំណាងស្នាមម្រាមដៃ"</string>
+ <string name="permlab_manageFace" msgid="2137540986007309781">"គ្រប់គ្រងផ្នែករឹងផ្ទៀងផ្ទាត់ផ្ទៃមុខ"</string>
+ <string name="permdesc_manageFace" msgid="8919637120670185330">"អនុញ្ញាតឱ្យកម្មវិធីប្រើវិធីសាស្ត្រដើម្បីបញ្ចូល និងលុបទម្រង់គំរូផ្ទៃមុខសម្រាប់ប្រើប្រាស់។"</string>
+ <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"ប្រើផ្នែករឹងផ្ទៀងផ្ទាត់ផ្ទៃមុខ"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"អនុញ្ញាតឱ្យកម្មវិធីប្រើផ្នែករឹងផ្ទៀងផ្ទាត់ផ្ទៃមុខសម្រាប់ការផ្ទៀងផ្ទាត់"</string>
+ <string name="face_acquired_insufficient" msgid="5901287247766106330">"មិនអាចដំណើរការផ្ទៃមុខបានទេ។ សូមព្យាយាមម្តងទៀត។"</string>
+ <string name="face_acquired_too_bright" msgid="610606792381297174">"ផ្ទៃមុខចាំងពេក។ សូមសាកល្បងនៅកន្លែងដែលមានពន្លឺទាបជាងនេះ។"</string>
+ <string name="face_acquired_too_dark" msgid="7229162716976778371">"ផ្ទៃមុខងងឹតពេក។ សូមរកកន្លែងដែលមានប្រភពពន្លឺ។"</string>
+ <string name="face_acquired_too_close" msgid="1980310037427755293">"សូមផ្លាស់ទីឧបករណ៍ចាប់សញ្ញាឱ្យឆ្ងាយពីមុខ។"</string>
+ <string name="face_acquired_too_far" msgid="4494571381828850007">"សូមដាក់ឧបករណ៍ចាប់សញ្ញាឱ្យនៅជិតមុខជាងនេះ។"</string>
+ <string name="face_acquired_too_high" msgid="228411096134808372">"សូមផ្លាស់ទីឧបករណ៍ចាប់សញ្ញាឱ្យខ្ពស់ជាងនេះ។"</string>
+ <string name="face_acquired_too_low" msgid="4539774649296349109">"សូមផ្លាស់ទីឧបករណ៍ចាប់សញ្ញាឱ្យទាបជាងនេះ។"</string>
+ <string name="face_acquired_too_right" msgid="1650292067226118760">"សូមផ្លាស់ទីឧបករណ៍ចាប់សញ្ញាទៅស្ដាំ។"</string>
+ <string name="face_acquired_too_left" msgid="2712489669456176505">"សូមផ្លាស់ទីឧបករណ៍ចាប់សញ្ញាទៅឆ្វេង។"</string>
+ <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"សូមមើលទៅឧបករណ៍ចាប់សញ្ញា។"</string>
+ <string name="face_acquired_not_detected" msgid="5707782294589511391">"រកមិនឃើញមុខទេ។"</string>
+ <string name="face_acquired_not_steady" msgid="3722829465011040042">"រក្សាមុខរបស់អ្នកឱ្យនឹងនៅមុខឧបករណ៍។"</string>
+ <string-array name="face_acquired_vendor">
+ </string-array>
+ <string name="face_error_hw_not_available" msgid="6255891785768984615">"មិនអាចប្រើផ្នែករឹងចាប់ផ្ទៃមុខបានទេ។"</string>
+ <string name="face_error_timeout" msgid="4014326147867150054">"ការសម្គាល់ផ្ទៃមុខបានអស់ម៉ោង។ សូមព្យាយាមម្ដងទៀត។"</string>
+ <string name="face_error_no_space" msgid="8224993703466381314">"មិនអាចរក្សាទុកផ្ទៃមុខបានទេ។"</string>
+ <string name="face_error_canceled" msgid="283945501061931023">"បានបោះបង់ប្រតិបត្តិការចាប់ផ្ទៃមុខ។"</string>
+ <string name="face_error_lockout" msgid="3407426963155388504">"ព្យាយាមចូលច្រើនពេកហើយ។ សូមព្យាយាមម្តងទៀតពេលក្រោយ។"</string>
+ <string name="face_error_lockout_permanent" msgid="8198354656746088890">"ព្យាយាមចូលច្រើនពេកហើយ។ បានបិទការផ្ទៀងផ្ទាត់ផ្ទៃមុខ។"</string>
+ <string name="face_error_unable_to_process" msgid="238761109287767270">"សូមព្យាយាមម្ដងទៀត។"</string>
+ <string name="face_error_not_enrolled" msgid="9166792142679691323">"មិនអាចថតបញ្ចូលផ្ទៃមុខបានទេ។"</string>
+ <string name="face_error_hw_not_present" msgid="4737289254517095671">"ឧបករណ៍នេះមិនមានឧបករណ៍ផ្ទៀងផ្ទាត់ផ្ទៃមុខនោះទេ"</string>
+ <string name="face_name_template" msgid="7004562145809595384">"ផ្ទៃមុខទី <xliff:g id="FACEID">%d</xliff:g>"</string>
+ <string-array name="face_error_vendor">
+ </string-array>
+ <string name="face_icon_content_description" msgid="4024817159806482191">"រូបផ្ទៃមុខ"</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"អានការកំណត់ធ្វើសមកាលកម្ម"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"ឲ្យកម្មវិធីអានការកំណត់ធ្វើសមកាលកម្មសម្រាប់គណនី។ ឧទាហរណ៍ វាអាចកំណត់ថាតើកម្មវិធីត្រូវបានបើកជាមួយគណនីដែរឬទេ។"</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"បិទ/បើកការធ្វើសមកាលកម្ម"</string>
@@ -1180,6 +1210,9 @@
<skip />
<string name="wifi_no_internet" msgid="8938267198124654938">"Wi-Fi មិនមានការតភ្ជាប់អ៊ីនធឺណិតទេ"</string>
<string name="wifi_no_internet_detailed" msgid="8083079241212301741">"ប៉ះសម្រាប់ជម្រើស"</string>
+ <string name="wifi_softap_config_change" msgid="8475911871165857607">"ប្ដូរទៅការកំណត់ហតស្ប៉តរបស់អ្នក"</string>
+ <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"រលកសញ្ញាហតស្ប៉តរបស់អ្នកបានផ្លាស់ប្ដូរ។"</string>
+ <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"ឧបករណ៍នេះមិនអាចប្រើចំណូលចិត្តរបស់អ្នកសម្រាប់តែ 5GHz ទេ។ ផ្ទុយមកវិញ ឧបករណ៍នេះនឹងប្រើរលកសញ្ញា 5GHz នៅពេលដែលអាចប្រើបាន។"</string>
<string name="network_switch_metered" msgid="4671730921726992671">"បានប្តូរទៅ <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
<string name="network_switch_metered_detail" msgid="775163331794506615">"ឧបករណ៍ប្រើ <xliff:g id="NEW_NETWORK">%1$s</xliff:g> នៅពេលដែល <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> មិនមានការតភ្ជាប់អ៊ីនធឺណិត។ អាចគិតថ្លៃលើការប្រើប្រាស់ទិន្នន័យ។"</string>
<string name="network_switch_metered_toast" msgid="5779283181685974304">"បានប្តូរពី <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> ទៅ <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index efc650c..58f8d56 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -236,9 +236,6 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"ಏರ್ಪ್ಲೇನ್ ಮೋಡ್"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"ಎರ್ಪ್ಲೇನ್ ಮೋಡ್ ಆನ್ ಆಗಿದೆ"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"ಎರ್ಪ್ಲೇನ್ ಮೋಡ್ ಆಫ್ ಆಗಿದೆ"</string>
- <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"ಬ್ಯಾಟರಿ ಸೇವರ್"</string>
- <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"ಬ್ಯಾಟರಿ ಸೇವರ್ ಆಫ್ ಆಗಿದೆ"</string>
- <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"ಬ್ಯಾಟರಿ ಸೇವರ್ ಆನ್ ಆಗಿದೆ"</string>
<string name="global_action_settings" msgid="1756531602592545966">"ಸೆಟ್ಟಿಂಗ್ಗಳು"</string>
<string name="global_action_assist" msgid="3892832961594295030">"ಸಹಾಯ ಮಾಡು"</string>
<string name="global_action_voice_assist" msgid="7751191495200504480">"ಧ್ವನಿ ಸಹಾಯಕ"</string>
@@ -276,6 +273,9 @@
<string name="permgrouplab_location" msgid="7275582855722310164">"ಸ್ಥಳ"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"ಈ ಸಾಧನದ ಸ್ಥಳವನ್ನು ಪ್ರವೇಶಿಸಿ"</string>
<string name="permgrouprequest_location" msgid="3788275734953323491">"ಈ ಸಾಧನದ ಸ್ಥಳವನ್ನು ಪ್ರವೇಶಿಸಲು <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ಗೆ ಅನುಮತಿಸಬೇಕೇ?"</string>
+ <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"ನೀವು ಅಪ್ಲಿಕೇಶನ್ ಅನ್ನು ಬಳಸುವಾಗ ಈ ಅಪ್ಲಿಕೇಶನ್ ಸ್ಥಳಕ್ಕೆ ಮಾತ್ರ ಪ್ರವೇಶವನ್ನು ಹೊಂದಿರುತ್ತದೆ."</string>
+ <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"ಈ ಸಾಧನದ ಸ್ಥಳವನ್ನು ಪ್ರವೇಶಿಸಲು ಯಾವಾಗಲೂ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ಗೆ ಅನುಮತಿಸಬೇಕೇ?"</string>
+ <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"ನೀವು ಅಪ್ಲಿಕೇಶನ್ ಅನ್ನು ಬಳಸದಿರುವಾಗಲೂ ಅಪ್ಲಿಕೇಶನ್ ಯಾವಾಗಲೂ ಸ್ಥಳಕ್ಕೆ ಪ್ರವೇಶವನ್ನು ಹೊಂದಿರುತ್ತದೆ."</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"ಕ್ಯಾಲೆಂಡರ್"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"ನಿಮ್ಮ ಕ್ಯಾಲೆಂಡರ್ ಪ್ರವೇಶಿಸಲು"</string>
<string name="permgrouprequest_calendar" msgid="289900767793189421">"ನಿಮ್ಮ ಕ್ಯಾಲೆಂಡರ್ ಪ್ರವೇಶಿಸಲು <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ಗೆ ಅನುಮತಿಸಬೇಕೇ?"</string>
@@ -402,8 +402,7 @@
<string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"ಈ ಅಪ್ಲಿಕೇಶನ್ ನಿಮ್ಮ ಫೋನ್ನಲ್ಲಿ ಕ್ಯಾಲೆಂಡರ್ ಈವೆಂಟ್ಗಳನ್ನು ಸೇರಿಸಬಹುದು, ತೆಗೆದುಹಾಕಬಹುದು ಅಥವಾ ಬದಲಾಯಿಸಬಹುದು. ಈ ಅಪ್ಲಿಕೇಶನ್ ಕ್ಯಾಲೆಂಡರ್ನ ಮಾಲೀಕರಿಂದ ಬಂದಿರಬಹುದಾದ ಕಾಣಿಸಿಕೊಳ್ಳುವ ಸಂದೇಶಗಳನ್ನು ಕಳುಹಿಸಬಹುದು ಅಥವಾ ಅವರ ಮಾಲೀಕರಿಗೆ ತಿಳಿಸದಂತೆ ಘಟನೆಗಳು ಬದಲಾಯಿಸುವುದು."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"ಹೆಚ್ಚುವರಿ ಸ್ಥಳ ಪೂರೈಕೆದಾರರ ಆದೇಶಗಳನ್ನು ಪ್ರವೇಶಿಸಿ"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"ಹೆಚ್ಚಿನ ಸ್ಥಳ ಪೂರೈಕೆದಾರ ಆದೇಶಗಳನ್ನು ಪ್ರವೇಶಿಸಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅವಕಾಶ ನೀಡುತ್ತದೆ. ಇದು GPS ಅಥವಾ ಇತರ ಸ್ಥಳ ಮೂಲಗಳ ಕಾರ್ಯಾಚರಣೆಯಲ್ಲಿ ಮಧ್ಯ ಪ್ರವೇಶಿಸಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅನುಮತಿಸಬಹುದು."</string>
- <!-- no translation found for permlab_accessFineLocation (6265109654698562427) -->
- <skip />
+ <string name="permlab_accessFineLocation" msgid="6265109654698562427">"ಮುನ್ನೆಲೆಯಲ್ಲಿ ಮಾತ್ರ ನಿಖರವಾದ ಸ್ಥಳವನ್ನು ಪ್ರವೇಶಿಸಿ"</string>
<string name="permdesc_accessFineLocation" msgid="3520508381065331098">"ಈ ಅಪ್ಲಿಕೇಶನ್ ಮುಂಭಾಗದಲ್ಲಿರುವಾಗ ಮಾತ್ರ ನಿಮ್ಮ ನಿಖರ ಸ್ಥಳವನ್ನು ಪಡೆಯಬಹುದು. ಈ ಸ್ಥಳ ಸೇವೆಗಳನ್ನು ಆನ್ ಮಾಡಿರಬೇಕು ಮತ್ತು ಅವುಗಳನ್ನು ಬಳಸಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಸಾಧ್ಯವಾಗುವಂತೆ ನಿಮ್ಮ ಫೋನ್ನಲ್ಲಿ ಅವುಗಳು ಲಭ್ಯವಿರಬೇಕು."</string>
<string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"ಅಂದಾಜು ಸ್ಥಳವನ್ನು ಪ್ರವೇಶಿಸಿ (ನೆಟ್ವರ್ಕ್-ಆಧಾರಿತ)"</string>
<string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"ಈ ಅಪ್ಲಿಕೇಶನ್ ನಿಮ್ಮ ಸ್ಥಳವನ್ನು ಸೆಲ್ ಟವರ್ಗಳು ಮತ್ತು ವೈ-ಫೈ ನೆಟ್ವರ್ಕ್ನಂತಹ ನೆಟ್ವರ್ಕ್ ಮೂಲಗಳ ಆಧಾರದ ಮೇಲೆ ಪಡೆಯಬಹುದು. ಈ ಸ್ಥಳ ಸೇವೆಗಳನ್ನು ಆನ್ ಮಾಡಿರಬೇಕು ಮತ್ತು ಅವುಗಳನ್ನು ಬಳಸಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಸಾಧ್ಯವಾಗುವಂತೆ ನಿಮ್ಮ ಟ್ಯಾಬ್ಲೆಟ್ನಲ್ಲಿ ಅವುಗಳು ಲಭ್ಯವಿರಬೇಕು."</string>
@@ -518,6 +517,37 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"ಬೆರಳಚ್ಚು ಐಕಾನ್"</string>
+ <string name="permlab_manageFace" msgid="2137540986007309781">"ಮುಖ ದೃಢೀಕರಣ ಹಾರ್ಡ್ವೇರ್ ಅನ್ನು ನಿರ್ವಹಿಸಿ"</string>
+ <string name="permdesc_manageFace" msgid="8919637120670185330">"ಬಳಕೆಗೆ ಮುಖದ ಟೆಂಪ್ಲೇಟ್ಗಳನ್ನು ಸೇರಿಸಲು ಮತ್ತು ಅಳಿಸಲು ವಿಧಾನಗಳನ್ನು ಮನವಿ ಮಾಡಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅನುಮತಿಸುತ್ತದೆ."</string>
+ <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"ಮುಖ ದೃಢೀಕರಣ ಹಾರ್ಡ್ವೇರ್ ಅನ್ನು ಬಳಸಿ"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"ಧೃಡೀಕರಣಕ್ಕಾಗಿ ಮುಖದ ಹಾರ್ಡ್ವೇರ್ ಬಳಸಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅನುಮತಿಸುತ್ತದೆ"</string>
+ <string name="face_acquired_insufficient" msgid="5901287247766106330">"ಮುಖವನ್ನು ಪ್ರಕ್ರಿಯೆಗೊಳಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ. ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string>
+ <string name="face_acquired_too_bright" msgid="610606792381297174">"ಮುಖವು ತುಂಬಾ ಪ್ರಕಾಶಮಾನವಾಗಿದೆ. ಕಡಿಮೆ ಲೈಟ್ನಲ್ಲಿ ಪ್ರಯತ್ನಿಸಿ."</string>
+ <string name="face_acquired_too_dark" msgid="7229162716976778371">"ಮುಖ ತುಂಬಾ ಕಪ್ಪಾಗಿದೆ. ನೇರವಾಗಿ ಬೆಳಕು ಬಿಳುವಂತೆ ಮಾಡಿ."</string>
+ <string name="face_acquired_too_close" msgid="1980310037427755293">"ಮುಖದಿಂದ ಸೆನ್ಸರ್ ಅನ್ನು ದೂರ ಸರಿಸಿ."</string>
+ <string name="face_acquired_too_far" msgid="4494571381828850007">"ಸೆನ್ಸರ್ ಅನ್ನು ಮುಖದ ಹತ್ತಿರಕ್ಕೆ ತನ್ನಿ."</string>
+ <string name="face_acquired_too_high" msgid="228411096134808372">"ಸೆನ್ಸರ್ ಅನ್ನು ಮೇಲಕ್ಕೆ ಸರಿಸಿ."</string>
+ <string name="face_acquired_too_low" msgid="4539774649296349109">"ಸೆನ್ಸರ್ ಅನ್ನು ಕೆಳಕ್ಕೆ ಸರಿಸಿ."</string>
+ <string name="face_acquired_too_right" msgid="1650292067226118760">"ಸೆನ್ಸರ್ ಅನ್ನು ಬಲಕ್ಕೆ ಸರಿಸಿ."</string>
+ <string name="face_acquired_too_left" msgid="2712489669456176505">"ಸೆನ್ಸರ್ ಅನ್ನು ಎಡಕ್ಕೆ ಸರಿಸಿ."</string>
+ <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"ಸೆನ್ಸರ್ ಅನ್ನು ನೋಡಿ."</string>
+ <string name="face_acquired_not_detected" msgid="5707782294589511391">"ಯಾವುದೇ ಮುಖ ಪತ್ತೆಯಾಗಿಲ್ಲ."</string>
+ <string name="face_acquired_not_steady" msgid="3722829465011040042">"ಸಾಧನದ ಮುಂದೆ ಮುಖವನ್ನು ಸ್ಥಿರವಾಗಿ ಇರಿಸಿ."</string>
+ <string-array name="face_acquired_vendor">
+ </string-array>
+ <string name="face_error_hw_not_available" msgid="6255891785768984615">"ಮುಖದ ಹಾರ್ಡ್ವೇರ್ ಲಭ್ಯವಿಲ್ಲ."</string>
+ <string name="face_error_timeout" msgid="4014326147867150054">"ಮುಖ ಸಮಯದ ಅವಧಿಯನ್ನು ತಲುಪಿದೆ. ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string>
+ <string name="face_error_no_space" msgid="8224993703466381314">"ಮುಖವನ್ನು ಸಂಗ್ರಹಿಸಲಾಗುವುದಿಲ್ಲ."</string>
+ <string name="face_error_canceled" msgid="283945501061931023">"ಮುಖದ ಕಾರ್ಯಚರಣೆಯನ್ನು ರದ್ದುಗೊಳಿಸಲಾಗಿದೆ."</string>
+ <string name="face_error_lockout" msgid="3407426963155388504">"ಹಲವು ಬಾರಿ ಪ್ರಯತ್ನಿಸಿದ್ದೀರಿ. ನಂತರ ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string>
+ <string name="face_error_lockout_permanent" msgid="8198354656746088890">"ಹಲವು ಪ್ರಯತ್ನ. ಮುಖದ ದೃಢೀಕರಣ ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ."</string>
+ <string name="face_error_unable_to_process" msgid="238761109287767270">"ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string>
+ <string name="face_error_not_enrolled" msgid="9166792142679691323">"ಯಾವುದೇ ಮುಖವನ್ನು ನೋಂದಣಿ ಮಾಡಿಲ್ಲ."</string>
+ <string name="face_error_hw_not_present" msgid="4737289254517095671">"ಈ ಸಾಧನವು ಮುಖ ದೃಢೀಕರಣ ಸೆನ್ಸರ್ ಅನ್ನು ಹೊಂದಿಲ್ಲ"</string>
+ <string name="face_name_template" msgid="7004562145809595384">"ಮುಖದ <xliff:g id="FACEID">%d</xliff:g>"</string>
+ <string-array name="face_error_vendor">
+ </string-array>
+ <string name="face_icon_content_description" msgid="4024817159806482191">"ಮುಖದ ಐಕಾನ್"</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"ಸಿಂಕ್ ಸೆಟ್ಟಿಂಗ್ಗಳನ್ನು ರೀಡ್ ಮಾಡು"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"ಒಂದು ಖಾತೆಯ ಸಿಂಕ್ ಸೆಟ್ಟಿಂಗ್ಗಳನ್ನು ಓದಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅವಕಾಶ ನೀಡುತ್ತದೆ. ಉದಾಹರಣೆಗೆ, ಖಾತೆಯೊಂದಿಗೆ ಜನರ ಅಪ್ಲಿಕೇಶನ್ ಸಿಂಕ್ ಮಾಡಲಾಗಿದೆಯೇ ಎಂಬುದನ್ನು ಇದು ನಿರ್ಧರಿಸಬಹುದು."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"ಸಿಂಕ್ ಆನ್ ಮತ್ತು ಸಿಂಕ್ ಆಫ್ ಟಾಗಲ್ ಮಾಡಿ"</string>
@@ -1178,6 +1208,9 @@
<skip />
<string name="wifi_no_internet" msgid="8938267198124654938">"ವೈ ಫೈ ಯಾವುದೇ ಇಂಟರ್ನೆಟ್ ಪ್ರವೇಶವನ್ನು ಹೊಂದಿಲ್ಲ"</string>
<string name="wifi_no_internet_detailed" msgid="8083079241212301741">"ಆಯ್ಕೆಗಳಿಗೆ ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
+ <string name="wifi_softap_config_change" msgid="8475911871165857607">"ನಿಮ್ಮ ಹಾಟ್ಸ್ಪಾಟ್ ಸೆಟ್ಟಿಂಗ್ಗಳಿಗೆ ಬದಲಾವಣೆಗಳು"</string>
+ <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"ನಿಮ್ಮ ಹಾಟ್ಸ್ಪಾಟ್ ಬ್ಯಾಂಡ್ ಬದಲಾಗಿದೆ."</string>
+ <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"ಈ ಸಾಧನವು 5GHz ಗೆ ಮಾತ್ರ ನಿಮ್ಮ ಆದ್ಯತೆಯನ್ನು ಬೆಂಬಲಿಸುವುದಿಲ್ಲ. ಬದಲಿಗೆ, ಈ ಸಾಧನವು 5GHz ಬ್ಯಾಂಡ್ ಅನ್ನು ಲಭ್ಯವಿರುವಾಗ ಬಳಸುತ್ತದೆ."</string>
<string name="network_switch_metered" msgid="4671730921726992671">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> ಗೆ ಬದಲಾಯಿಸಲಾಗಿದೆ"</string>
<string name="network_switch_metered_detail" msgid="775163331794506615">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> ಇಂಟರ್ನೆಟ್ ಪ್ರವೇಶ ಹೊಂದಿಲ್ಲದಿರುವಾಗ, ಸಾಧನವು <xliff:g id="NEW_NETWORK">%1$s</xliff:g> ಬಳಸುತ್ತದೆ. ಶುಲ್ಕಗಳು ಅನ್ವಯವಾಗಬಹುದು."</string>
<string name="network_switch_metered_toast" msgid="5779283181685974304">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> ರಿಂದ <xliff:g id="NEW_NETWORK">%2$s</xliff:g> ಗೆ ಬದಲಾಯಿಸಲಾಗಿದೆ"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 881eee20..076b65f 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -236,9 +236,6 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"비행기 모드"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"비행기 모드 사용중"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"비행기 모드 사용중이 아님"</string>
- <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"배터리 세이버"</string>
- <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"배터리 세이버 사용 안함"</string>
- <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"배터리 세이버 사용 중"</string>
<string name="global_action_settings" msgid="1756531602592545966">"설정"</string>
<string name="global_action_assist" msgid="3892832961594295030">"지원"</string>
<string name="global_action_voice_assist" msgid="7751191495200504480">"음성 지원"</string>
@@ -276,6 +273,9 @@
<string name="permgrouplab_location" msgid="7275582855722310164">"위치"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"이 기기의 위치정보에 액세스"</string>
<string name="permgrouprequest_location" msgid="3788275734953323491">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>에서 내 기기 위치에 액세스하도록 허용하시겠습니까?"</string>
+ <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"앱을 사용할 때만 앱에서 위치에 액세스합니다."</string>
+ <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>에서 내 기기 위치에 액세스하도록 허용하시겠습니까?"</string>
+ <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"앱을 사용하지 않을 때에도 앱에서 항상 위치에 액세스합니다."</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"캘린더"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"캘린더에 액세스"</string>
<string name="permgrouprequest_calendar" msgid="289900767793189421">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>에서 내 캘린더에 액세스하도록 허용하시겠습니까?"</string>
@@ -402,8 +402,7 @@
<string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"앱이 일정을 추가, 삭제, 변경할 수 있도록 허용합니다. 앱이 메시지를 전송하거나 사용자에게 별도 표시 없이 일정을 수정할 수도 있습니다."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"추가 위치 제공업체 명령에 접근"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"앱이 추가 위치 정보 제공 기능의 명령에 접근하도록 허용합니다. 이 경우 앱이 GPS 또는 기타 위치 소스의 작동을 방해할 수 있습니다."</string>
- <!-- no translation found for permlab_accessFineLocation (6265109654698562427) -->
- <skip />
+ <string name="permlab_accessFineLocation" msgid="6265109654698562427">"포그라운드에서만 정확한 위치에 액세스"</string>
<string name="permdesc_accessFineLocation" msgid="3520508381065331098">"이 앱은 포그라운드에 있을 때만 나의 정확한 위치를 알 수 있습니다. 앱에서 위치 서비스를 사용하려면 휴대전화에서 위치 서비스가 사용 설정되어 있으며 사용할 수 있어야 합니다. 배터리 사용량이 늘어날 수 있습니다."</string>
<string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"대략적인 위치(네트워크 기반)에 액세스"</string>
<string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"이 앱은 기지국 및 Wi-Fi 네트워크와 같은 네트워크 소스를 통해 내 위치를 알 수 있습니다. 앱에서 위치 서비스를 사용하려면 태블릿에서 위치 서비스가 사용 설정되어 있으며 사용 가능해야 합니다."</string>
@@ -518,6 +517,37 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"지문 아이콘"</string>
+ <string name="permlab_manageFace" msgid="2137540986007309781">"얼굴 인증 하드웨어 관리"</string>
+ <string name="permdesc_manageFace" msgid="8919637120670185330">"사용할 얼굴 템플릿의 추가 및 삭제 메서드를 앱에서 호출하도록 허용합니다."</string>
+ <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"얼굴 인증 하드웨어 사용"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"앱에서 얼굴 인증 하드웨어를 인증에 사용하도록 허용합니다."</string>
+ <string name="face_acquired_insufficient" msgid="5901287247766106330">"얼굴을 인식할 수 없습니다. 다시 시도해 주세요."</string>
+ <string name="face_acquired_too_bright" msgid="610606792381297174">"얼굴이 너무 밝습니다. 조명을 더 어둡게 해 보세요."</string>
+ <string name="face_acquired_too_dark" msgid="7229162716976778371">"얼굴이 너무 어둡습니다. 조명을 더 밝게 해 보세요."</string>
+ <string name="face_acquired_too_close" msgid="1980310037427755293">"센서를 얼굴에서 더 멀리 떨어뜨려 주세요."</string>
+ <string name="face_acquired_too_far" msgid="4494571381828850007">"센서를 얼굴에 더 가까이 대 주세요."</string>
+ <string name="face_acquired_too_high" msgid="228411096134808372">"센서를 위쪽으로 옮겨 주세요."</string>
+ <string name="face_acquired_too_low" msgid="4539774649296349109">"센서를 아래쪽으로 옮겨 주세요."</string>
+ <string name="face_acquired_too_right" msgid="1650292067226118760">"센서를 오른쪽으로 옮겨 주세요."</string>
+ <string name="face_acquired_too_left" msgid="2712489669456176505">"센서를 왼쪽으로 옮겨 주세요."</string>
+ <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"센서를 바라보세요."</string>
+ <string name="face_acquired_not_detected" msgid="5707782294589511391">"얼굴을 감지할 수 없습니다."</string>
+ <string name="face_acquired_not_steady" msgid="3722829465011040042">"얼굴을 움직이지 말고 기기를 마주 보세요."</string>
+ <string-array name="face_acquired_vendor">
+ </string-array>
+ <string name="face_error_hw_not_available" msgid="6255891785768984615">"얼굴 인식 하드웨어를 사용할 수 없습니다."</string>
+ <string name="face_error_timeout" msgid="4014326147867150054">"얼굴 인식 시간이 초과되었습니다. 다시 시도하세요."</string>
+ <string name="face_error_no_space" msgid="8224993703466381314">"얼굴을 저장할 수 없습니다."</string>
+ <string name="face_error_canceled" msgid="283945501061931023">"얼굴 인식 작업이 취소되었습니다."</string>
+ <string name="face_error_lockout" msgid="3407426963155388504">"시도 횟수가 너무 많습니다. 나중에 다시 시도하세요."</string>
+ <string name="face_error_lockout_permanent" msgid="8198354656746088890">"시도 횟수가 너무 많아 얼굴 인증이 사용 중지되었습니다."</string>
+ <string name="face_error_unable_to_process" msgid="238761109287767270">"다시 시도해 보세요."</string>
+ <string name="face_error_not_enrolled" msgid="9166792142679691323">"등록된 얼굴이 없습니다."</string>
+ <string name="face_error_hw_not_present" msgid="4737289254517095671">"이 기기에는 얼굴 인증 센서가 없습니다."</string>
+ <string name="face_name_template" msgid="7004562145809595384">"얼굴 <xliff:g id="FACEID">%d</xliff:g>"</string>
+ <string-array name="face_error_vendor">
+ </string-array>
+ <string name="face_icon_content_description" msgid="4024817159806482191">"얼굴 아이콘"</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"동기화 설정 읽기"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"앱이 계정의 동기화 설정을 읽을 수 있도록 허용합니다. 예를 들어, 계정에서 주소록 앱을 동기화할지 여부를 확인할 수 있습니다."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"동기화 사용 및 사용 중지 전환"</string>
@@ -1178,6 +1208,9 @@
<skip />
<string name="wifi_no_internet" msgid="8938267198124654938">"Wi-Fi가 인터넷에 연결되어 있지 않습니다"</string>
<string name="wifi_no_internet_detailed" msgid="8083079241212301741">"탭하여 옵션 보기"</string>
+ <string name="wifi_softap_config_change" msgid="8475911871165857607">"핫스팟 설정 변경"</string>
+ <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"핫스팟 대역이 변경되었습니다."</string>
+ <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"이 기기에서는 5GHz 전용 환경설정이 지원되지 않습니다. 대신 가능할 때만 기기에서 5GHz 대역이 사용됩니다."</string>
<string name="network_switch_metered" msgid="4671730921726992671">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g>(으)로 전환"</string>
<string name="network_switch_metered_detail" msgid="775163331794506615">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>(으)로 인터넷에 연결할 수 없는 경우 기기에서 <xliff:g id="NEW_NETWORK">%1$s</xliff:g>이(가) 사용됩니다. 요금이 부과될 수 있습니다."</string>
<string name="network_switch_metered_toast" msgid="5779283181685974304">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g>에서 <xliff:g id="NEW_NETWORK">%2$s</xliff:g>(으)로 전환"</string>
@@ -1676,7 +1709,7 @@
<item quantity="one">1초 후에 다시 시도하세요.</item>
</plurals>
<string name="restr_pin_try_later" msgid="973144472490532377">"나중에 다시 시도"</string>
- <string name="immersive_cling_title" msgid="8394201622932303336">"전체 화면 보기 중"</string>
+ <string name="immersive_cling_title" msgid="8394201622932303336">"전체 화면 모드"</string>
<string name="immersive_cling_description" msgid="3482371193207536040">"종료하려면 위에서 아래로 스와이프합니다."</string>
<string name="immersive_cling_positive" msgid="5016839404568297683">"확인"</string>
<string name="done_label" msgid="2093726099505892398">"완료"</string>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index 7d046c6..a39021f 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -236,9 +236,6 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Учак режими"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Учак режими КҮЙҮК"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Учак режими ӨЧҮК"</string>
- <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Батареяны үнөмдөгүч"</string>
- <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Батареяны үнөмдөгүч ӨЧҮК"</string>
- <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Батареяны үнөмдөгүч КҮЙҮК"</string>
<string name="global_action_settings" msgid="1756531602592545966">"Жөндөөлөр"</string>
<string name="global_action_assist" msgid="3892832961594295030">"Жардам"</string>
<string name="global_action_voice_assist" msgid="7751191495200504480">"Үн жардамчысы"</string>
@@ -276,6 +273,9 @@
<string name="permgrouplab_location" msgid="7275582855722310164">"Жайгашкан жер"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"түзмөктүн жайгашкан жерин аныктоого"</string>
<string name="permgrouprequest_location" msgid="3788275734953323491">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> колдонмосу бул түзмөктүн кайда жүргөнүн көрүп турганга уруксат бересизби?"</string>
+ <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"Сиз бул колдонмону пайдаланып жатканда гана ал жайгашкан жериңизди көрө алат."</string>
+ <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> колдонмосуна бул түзмөктүн жайгашкан жерин пайдаланууга дайыма уруксат берилсинби?"</string>
+ <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"Сиз бул колдонмону пайдаланбай турганда да ал жайгашкан жериңизди көрүп турат."</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Жылнаама"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"жылнаамаңызды пайдалануу"</string>
<string name="permgrouprequest_calendar" msgid="289900767793189421">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> колдонмосуна жылнаамаңызды пайдаланууга уруксат берилсинби?"</string>
@@ -402,15 +402,14 @@
<string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Бул колдонмо телефонуңузга жылнаама иш-чараларын кошуп, алып салып же өзгөртүшү мүмкүн. Бул колдонмо жылнаама ээсинин атынан билдирүүлөрдү жөнөтүп же ээсине эскертпестен иш-чараларды өзгөртүшү мүмкүн."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"жайгашкан жерди аныктагычтын кошумча буйруктарын пайдалануу"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Колдонмого жайгашкан жерди табуучу кошумча жабдуучулардын буйруктарын колдонуу мүмкүнчүлүгүн берет. Ушуну менен колдонмо GPS\'тин ишине жана башка жайгашкан жерлерди аныктоо кызматтарына кийлигише алат."</string>
- <!-- no translation found for permlab_accessFineLocation (6265109654698562427) -->
- <skip />
+ <string name="permlab_accessFineLocation" msgid="6265109654698562427">"так аныкталган жайгашкан жерге активдүү режимде гана кирүүгө уруксат берүү"</string>
<string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Бул колдонмонун активдүү режимде гана жайгашкан жериңиздин дайындарын алууга мүмкүнчүлүгү бар. Колдонмо бул кызматтарды пайдаланышы үчүн аларды күйгүзүп, телефонуңузга туташтырып коюшуңуз керек. Ушуну менен батареянын кубаты көбүрөөк сарпталышы мүмкүн."</string>
<string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"болжолдуу жайгашкан жерге (тармактын негизинде) уруксат"</string>
<string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Бул колдонмо байланыш мунаралары жана Wi-Fi сыяктуу тармактык булактар аркылуу жайгашкан жериңизди аныктай алат. Колдонмо бул кызматтарды пайдаланышы үчүн, аларды күйгүзүп, планшетиңизге туташтырып коюшуңуз керек."</string>
<string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Бул колдонмо байланыш мунаралары жана Wi-Fi сыяктуу тармактык булактар аркылуу жайгашкан жериңизди аныктай алат. Колдонмо бул кызматтарды пайдаланышы үчүн, аларды күйгүзүп, сыналгыңызга туташтырып коюшуңуз керек."</string>
<string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Бул колдонмо байланыш мунаралары жана Wi-Fi сыяктуу тармактык булактар аркылуу жайгашкан жериңизди аныктай алат. Колдонмо бул кызматтарды пайдаланышы үчүн, аларды күйгүзүп, телефонуңузга туташтырып коюшуңуз керек."</string>
<string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"так аныкталган жайгашкан жерге фондук режимде кирүүгө уруксат берүү"</string>
- <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Бул колдонмонун фондук режимде жайгашкан жериңиздин дайындарын каалаган убакта алууга мүмкүнчүлүгү бар. Колдонмо бул кызматтарды пайдаланышы үчүн аларды күйгүзүп, телефонуңузга туташтырып коюшуңуз керек. Ушуну менен батареянын кубаты көбүрөөк сарпталышы мүмкүн."</string>
+ <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Бул колдонмо фондук режимде сиздин кайда жүргөнүңүздү көрүп турат. Ал үчүн жайгашкан жерди аныктоо функциясын иштетишиңиз керек. Эскерте кетүүчү нерсе, батареяңыз тез отуруп калышы мүмкүн."</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"аудио жөндөөлөрүңүздү өзгөртүңүз"</string>
<string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Колдонмого үн деңгээли жана кайсы динамик аркылуу үн чыгарылышы керек сыяктуу түзмөктүн аудио тууралоолорун өзгөртүүгө уруксат берет."</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"аудио жаздыруу"</string>
@@ -518,6 +517,37 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Манжа изинин сөлөкөтү"</string>
+ <string name="permlab_manageFace" msgid="2137540986007309781">"жүздүн аныктыгын текшерүүчү аппараттык камсыздоону башкаруу"</string>
+ <string name="permdesc_manageFace" msgid="8919637120670185330">"Колдонмого пайдалануу үчүн жүздүн үлгүлөрүн кошуу жана жок кылуу мүмкүндүгүн берет."</string>
+ <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"жүздүн аныктыгын текшерүүчү аппараттык камсыздоону колдонуу"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Колдонмого аныктыгын текшерүү үчүн жүздүн аныктыгын текшерүүчү аппараттык камсыздоону пайдалануу мүмкүндүгүн берет"</string>
+ <string name="face_acquired_insufficient" msgid="5901287247766106330">"Жүзүңүз таанылбай койду. Кайра аракет кылыңыз."</string>
+ <string name="face_acquired_too_bright" msgid="610606792381297174">"Өтө жарык болуп жатат. Жаракты азайтып көрүңүз."</string>
+ <string name="face_acquired_too_dark" msgid="7229162716976778371">"Өтө караңгы болуп жатат. Жарыкты ачып коюңуз."</string>
+ <string name="face_acquired_too_close" msgid="1980310037427755293">"Сенсорду бетиңизден алысыраак жылдырыңыз."</string>
+ <string name="face_acquired_too_far" msgid="4494571381828850007">"Сенсорду бетиңизге жакыныраак жылдырыңыз."</string>
+ <string name="face_acquired_too_high" msgid="228411096134808372">"Сенсорду жогору жакка жылдырыңыз."</string>
+ <string name="face_acquired_too_low" msgid="4539774649296349109">"Сенсорду төмөн жакка жылдырыңыз."</string>
+ <string name="face_acquired_too_right" msgid="1650292067226118760">"Сенсорду оң жакка жылдырыңыз."</string>
+ <string name="face_acquired_too_left" msgid="2712489669456176505">"Сенсорду сол жакка жылдырыңыз."</string>
+ <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Сенсорду караңыз."</string>
+ <string name="face_acquired_not_detected" msgid="5707782294589511391">"Жүзүңүз табылган жок."</string>
+ <string name="face_acquired_not_steady" msgid="3722829465011040042">"Түзмөктүн алдында жүзүңүздү бир калыпта кармаңыз."</string>
+ <string-array name="face_acquired_vendor">
+ </string-array>
+ <string name="face_error_hw_not_available" msgid="6255891785768984615">"Жүздү аныктоочу аппараттык камсыздоо жеткиликсиз."</string>
+ <string name="face_error_timeout" msgid="4014326147867150054">"Жүздүн аныктыгын текшерүүнү күтүү мөөнөтү бүттү. Кайра аракет кылыңыз."</string>
+ <string name="face_error_no_space" msgid="8224993703466381314">"Жүздү сактоо мүмкүн эмес."</string>
+ <string name="face_error_canceled" msgid="283945501061931023">"Жүздүн аныктыгын текшерүү жокко чыгарылды."</string>
+ <string name="face_error_lockout" msgid="3407426963155388504">"Өтө көп жолу аракет жасадыңыз. Кийинчерээк кайра аракет кылыңыз."</string>
+ <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Өтө көп жолу аракет жасадыңыз. Жүздүн аныктыгын текшерүү сенсору өчүрүлдү."</string>
+ <string name="face_error_unable_to_process" msgid="238761109287767270">"Кайра аракет кылыңыз."</string>
+ <string name="face_error_not_enrolled" msgid="9166792142679691323">"Бир да жүз катталган жок."</string>
+ <string name="face_error_hw_not_present" msgid="4737289254517095671">"Бул түзмөктө жүздүн аныктыгын текшерүү сенсору жок"</string>
+ <string name="face_name_template" msgid="7004562145809595384">"Жүз <xliff:g id="FACEID">%d</xliff:g>"</string>
+ <string-array name="face_error_vendor">
+ </string-array>
+ <string name="face_icon_content_description" msgid="4024817159806482191">"Жүздүн сүрөтчөсү"</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"шайкештирүү жөндөөлөрүн окуу"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Колдонмого эсеп менен синхрондошуу тууралоолорун окуганга уруксат берет. Мисалы, Кишилер колдонмосу эсеп менен синхрондошкондугун аныктай алат."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"синхрондоштурууну өчүрүү/жандыруу"</string>
@@ -1178,6 +1208,9 @@
<skip />
<string name="wifi_no_internet" msgid="8938267198124654938">"Wi-Fi тармагы Интернетке туташпай турат"</string>
<string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Параметрлерди ачуу үчүн таптап коюңуз"</string>
+ <string name="wifi_softap_config_change" msgid="8475911871165857607">"Туташуу түйүнүңүздүн жөндөөлөрүнө өзгөртүүлөр киргизилди"</string>
+ <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"Туташуу түйүнүңүздүн жыштыгы өзгөрдү."</string>
+ <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"Бул түзмөк 5ГГцти гана колдонуу жөндөөсүн колдоого албайт. Анын ордуна, бул түзмөк 5ГГц жыштыгын ал жеткиликтүү болгондо колдонот."</string>
<string name="network_switch_metered" msgid="4671730921726992671">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> тармагына которуштурулду"</string>
<string name="network_switch_metered_detail" msgid="775163331794506615">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> тармагы Интернетке туташпай турганда, түзмөгүңүз <xliff:g id="NEW_NETWORK">%1$s</xliff:g> тармагын колдонот. Акы алынышы мүмкүн."</string>
<string name="network_switch_metered_toast" msgid="5779283181685974304">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> дегенден <xliff:g id="NEW_NETWORK">%2$s</xliff:g> тармагына которуштурулду"</string>
diff --git a/core/res/res/values-land/dimens_package_installer.xml b/core/res/res/values-land/dimens_package_installer.xml
new file mode 100644
index 0000000..2146241
--- /dev/null
+++ b/core/res/res/values-land/dimens_package_installer.xml
@@ -0,0 +1,24 @@
+<?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.
+ -->
+
+<!-- Landscape dimensions for the permission grant dialog. -->
+<resources>
+ <!-- Assuming the dimension of a sailfish, this yields 95% width in splitscreen and 65% in
+ landscape -->
+ <dimen name="permissionGrantDialogWeight">8.6</dimen>
+ <dimen name="permissionGrantDialogWidth">334dp</dimen>
+</resources>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index 50499b0..ef2cede 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -236,9 +236,6 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"ໂໝດໃນຍົນ"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"ເປີດໂໝດຢູ່ໃນຍົນແລ້ວ"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"ປິດໂໝດໃນຍົນແລ້ວ"</string>
- <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"ຕົວປະຢັດແບັດເຕີຣີ"</string>
- <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"ຕົວປະຢັດແບັດເຕີຣີປິດຢູ່"</string>
- <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"ຕົວປະຢັດແບັດເຕີຣີເປີດຢູ່"</string>
<string name="global_action_settings" msgid="1756531602592545966">"ການຕັ້ງຄ່າ"</string>
<string name="global_action_assist" msgid="3892832961594295030">"ຕົວຊ່ວຍ"</string>
<string name="global_action_voice_assist" msgid="7751191495200504480">"ຊ່ວຍເຫຼືອທາງສຽງ"</string>
@@ -276,6 +273,9 @@
<string name="permgrouplab_location" msgid="7275582855722310164">"ສະຖານທີ່"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"ເຂົ້າເຖິງຂໍ້ມູນສະຖານທີ່ຂອງອຸປະກອນນີ້"</string>
<string name="permgrouprequest_location" msgid="3788275734953323491">"ອະນຸຍາດ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ໃຫ້ເຂົ້າເຖິງສະຖານທີ່ຂອງອຸປະກອນບໍ?"</string>
+ <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"ແອັບຈະມີສິດເຂົ້າເຖິງສະຖານທີ່ໃນເວລາທີ່ທ່ານກຳລັງໃຊ້ແອັບຢູ່ເທົ່ານັ້ນ."</string>
+ <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"ອະນຸຍາດໃຫ້ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ເຂົ້າເຖິງສະຖານທີ່ຂອງອຸປະກອນນີ້ບໍ?"</string>
+ <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"ແອັບຈະມີສິດເຂົ້າເຖິງສະຖານທີ່ທຸກເທື່ອ ເຖິງແມ່ນໃນເວລາທ່ານບໍ່ໃຊ້ແອັບຢູ່ກໍຕາມ."</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"ປະຕິທິນ"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"ເຂົ້າຫາປະຕິທິນຂອງທ່ານ"</string>
<string name="permgrouprequest_calendar" msgid="289900767793189421">"ອະນຸຍາດ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ໃຫ້ເຂົ້າເຖິງປະຕິທິນຂອງທ່ານບໍ?"</string>
@@ -402,8 +402,7 @@
<string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"This app can add, remove, or change calendar events on your phone. This app can send messages that may appear to come from calendar owners, or change events without notifying their owners."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"ເຂົ້າເຖິງຄຳສັ່ງຜູ່ໃຫ້ບໍລິການພິກັດສະຖານທີ່"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"ອະນຸຍາດໃຫ້ແອັບຯເຂົ້າເຖິງຄຳສັ່ງເພີ່ມເຕີມຂອງຜູ່ໃຫ້ບໍລິການສະຖານທີ່. ນີ້ອາດຈະເປັນການເຮັດໃຫ້ແອັບຯ ລົບກວນການເຮັດວຽກຂອງ GPS ຫຼືແຫລ່ງຂໍ້ມູນສະຖານທີ່ອື່ນໆໄດ້."</string>
- <!-- no translation found for permlab_accessFineLocation (6265109654698562427) -->
- <skip />
+ <string name="permlab_accessFineLocation" msgid="6265109654698562427">"ເຂົ້າເຖິງສະຖານທີ່ແນ່ນອນໃນພື້ນໜ້າເທົ່ານັ້ນ"</string>
<string name="permdesc_accessFineLocation" msgid="3520508381065331098">"ແອັບນີ້ສາມາດຮັບເອົາສະຖານທີ່ແນ່ນອນຂອງທ່ານໄດ້ທຸກເວລາທີ່ມັນຢູ່ໃນພື້ນໜ້າ. ການບໍລິການສະຖານທີ່ເຫຼົ່ານີ້ຕ້ອງເປີດຢູ່ ແລະ ສາມາດໃຊ້ໄດ້ໃນໂທລະສັບຂອງທ່ານເພື່ອໃຫ້ແອັບສາມາດໃຊ້ພວກມັນໄດ້. ນີ້ອາດຈະເຮັດໃຫ້ການໃຊ້ແບັດເຕີຣີເພີ່ມຂຶ້ນ."</string>
<string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"ເຂົ້າຫາທີ່ຕັ້ງໂດຍປະມານ (ອີງໃສ່ເຄືອຂ່າຍ)"</string>
<string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"This app can get your location based on network sources such as cell towers and Wi-Fi networks. These location services must be turned on and available on your tablet for the app to be able to use them."</string>
@@ -518,6 +517,37 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"ໄອຄອນລາຍນິ້ວມື"</string>
+ <string name="permlab_manageFace" msgid="2137540986007309781">"ຈັດການຮາດແວການກວດສອບຄວາມຖືກຕ້ອງດ້ວຍໃບໜ້າ"</string>
+ <string name="permdesc_manageFace" msgid="8919637120670185330">"ອະນຸຍາດໃຫ້ແອັບເປີດວິທີການຕ່າງໆເພື່ອເພີ່ມ ແລະ ລຶບແມ່ແບບໃບໜ້າສຳລັບການນຳໃຊ້."</string>
+ <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"ໃຊ້ຮາດແວການກວດສອບຄວາມຖືກຕ້ອງດ້ວຍໃບໜ້າ"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"ອະນຸຍາດໃຫ້ແອັບໃຊ້ຮາດແວການກວດສອບຄວາມຖືກຕ້ອງດ້ວຍໃບໜ້າສຳລັບການກວດສອບຄວາມຖືກຕ້ອງ"</string>
+ <string name="face_acquired_insufficient" msgid="5901287247766106330">"ບໍ່ສາມາດປະມວນຜົນຂໍ້ມູນໃບໜ້າໄດ້. ກະລຸນາລອງອີກຄັ້ງ."</string>
+ <string name="face_acquired_too_bright" msgid="610606792381297174">"ໃບໜ້າສະຫວ່າງເກີນໄປ. ກະລຸນາລອງໃນບ່ອນທີ່ແສງໜ້ອຍກວ່າ."</string>
+ <string name="face_acquired_too_dark" msgid="7229162716976778371">"ໃບໜ້າມືດເກີນໄປ. ກະລຸນາເປີດແຫຼ່ງກຳເນີດແສງ."</string>
+ <string name="face_acquired_too_close" msgid="1980310037427755293">"ກະລຸນາຍ້າຍເຊັນເຊີອອກຫ່າງຈາກໃບໜ້າຕື່ມອີກ."</string>
+ <string name="face_acquired_too_far" msgid="4494571381828850007">"ກະລຸນາຍ້າຍເຊັນເຊີເຂົ້າໃກ້ໃບໜ້າຕື່ມອີກ."</string>
+ <string name="face_acquired_too_high" msgid="228411096134808372">"ກະລຸນາຍ້າຍເຊັນເຊີໃຫ້ສູງຂຶ້ນອີກ."</string>
+ <string name="face_acquired_too_low" msgid="4539774649296349109">"ກະລຸນາຍ້າຍເຊັນເຊີໃຫ້ຕໍ່າລົງອີກ."</string>
+ <string name="face_acquired_too_right" msgid="1650292067226118760">"ກະລຸນາຍ້າຍເຊັນເຊີໄປເບື້ອງຂວາ."</string>
+ <string name="face_acquired_too_left" msgid="2712489669456176505">"ກະລຸນາຍ້າຍເຊັນເຊີໄປເບື້ອງຊ້າຍ."</string>
+ <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"ກະລຸນາແນມເບິ່ງເຊັນເຊີ."</string>
+ <string name="face_acquired_not_detected" msgid="5707782294589511391">"ກວດບໍ່ພົບໃບໜ້າ."</string>
+ <string name="face_acquired_not_steady" msgid="3722829465011040042">"ຮັກສາໃບໜ້າໃຫ້ຢູ່ນິ້ງຕໍ່ໜ້າອຸປະກອນ"</string>
+ <string-array name="face_acquired_vendor">
+ </string-array>
+ <string name="face_error_hw_not_available" msgid="6255891785768984615">"ຮາດແວກວດໃບໜ້າບໍ່ສາມາດໃຊ້ໄດ້."</string>
+ <string name="face_error_timeout" msgid="4014326147867150054">"ໝົດເວລາກວດໃບໜ້າແລ້ວ. ກະລຸນາລອງອີກຄັ້ງ."</string>
+ <string name="face_error_no_space" msgid="8224993703466381314">"ບໍ່ສາມາດເກັບຮັກສາໃບໜ້າໄວ້ໄດ້."</string>
+ <string name="face_error_canceled" msgid="283945501061931023">"ຍົກເລີກການດຳເນີນການກັບໃບໜ້າແລ້ວ."</string>
+ <string name="face_error_lockout" msgid="3407426963155388504">"ມີຄວາມພະຍາຍາມຫຼາຍຄັ້ງເກີນໄປ. ກະລຸນາລອງໃໝ່ໃນພາຍຫຼັງ."</string>
+ <string name="face_error_lockout_permanent" msgid="8198354656746088890">"ມີຄວາມພະຍາຍາມຫຼາຍຄັ້ງເກີນໄປ. ປິດນຳໃຊ້ການກວດສອບຄວາມຖືກຕ້ອງດ້ວຍໃບໜ້າແລ້ວ."</string>
+ <string name="face_error_unable_to_process" msgid="238761109287767270">"ລອງອີກຄັ້ງ."</string>
+ <string name="face_error_not_enrolled" msgid="9166792142679691323">"ບໍ່ໄດ້ລົງທະບຽນໃບໜ້າໃດ."</string>
+ <string name="face_error_hw_not_present" msgid="4737289254517095671">"ອຸປະກອນນີ້ບໍ່ມີເຊັນເຊີກວດສອບຄວາມຖືກຕ້ອງດ້ວຍໃບໜ້າ"</string>
+ <string name="face_name_template" msgid="7004562145809595384">"ໃບໜ້າ <xliff:g id="FACEID">%d</xliff:g>"</string>
+ <string-array name="face_error_vendor">
+ </string-array>
+ <string name="face_icon_content_description" msgid="4024817159806482191">"ໄອຄອນໃບໜ້າ"</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"ອ່ານການຕັ້ງຄ່າຊິ້ງຂໍ້ມູນ"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"ອະນຸຍາດໃຫ້ແອັບຯ ອ່ານການຕັ້ງຄ່າການຊິ້ງຂໍ້ມູນຂອງບັນຊີໄດ້. ຕົວຢ່າງເຊັ່ນ: ມັນຈະສາມາດກວດສອບໄດ້ແອັບຯ People ຖືກຊິ້ງຂໍ້ມູນກັບບັນຊີໃດນຶ່ງແລ້ວຫຼືຍັງ."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"ສະລັບການເປີດ ແລະປິດການຊິ້ງຂໍ້ມູນ"</string>
@@ -1178,6 +1208,9 @@
<skip />
<string name="wifi_no_internet" msgid="8938267198124654938">"Wi-Fi ບໍ່ມີສັນຍານອິນເຕີເນັດ"</string>
<string name="wifi_no_internet_detailed" msgid="8083079241212301741">"ແຕະເພື່ອເບິ່ງຕົວເລືອກ"</string>
+ <string name="wifi_softap_config_change" msgid="8475911871165857607">"ການປ່ຽນແປງການຕັ້ງຄ່າຮັອດສະປອດຂອງທ່ານ"</string>
+ <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"ຄື້ນຄວາມຖີ່ຮັອດສະປອດຂອງທ່ານປ່ຽນແປງແລ້ວ."</string>
+ <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"ອຸປະກອນນີ້ບໍ່ຮອງຮັບການຕັ້ງຄ່າຂອງທ່ານສຳລັບ 5GHz ເທົ່ານັ້ນ. ແຕ່ວ່າອຸປະກອນນີ້ຈະໃຊ້ຄື້ນຄວາມຖີ່ 5GHz ເມື່ອສາມາດໃຊ້ໄດ້."</string>
<string name="network_switch_metered" msgid="4671730921726992671">"ສະຫຼັບໄປໃຊ້ <xliff:g id="NETWORK_TYPE">%1$s</xliff:g> ແລ້ວ"</string>
<string name="network_switch_metered_detail" msgid="775163331794506615">"ອຸປະກອນຈະໃຊ້ <xliff:g id="NEW_NETWORK">%1$s</xliff:g> ເມື່ອ <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> ບໍ່ມີການເຊື່ອມຕໍ່ອິນເຕີເນັດ. ອາດມີການຮຽກເກັບຄ່າບໍລິການ."</string>
<string name="network_switch_metered_toast" msgid="5779283181685974304">"ສະຫຼັບຈາກ <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> ໄປໃຊ້ <xliff:g id="NEW_NETWORK">%2$s</xliff:g> ແລ້ວ"</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index bd7a1f0..e72b1a9 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -242,9 +242,6 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Lėktuvo režimas"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"ĮJUNGTAS lėktuvo režimas"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"lėktuvo režimas IŠJUNGTAS"</string>
- <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Akumuliatoriaus tausojimo priemonė"</string>
- <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Akumuliatoriaus tausojimo priemonė IŠJUNGTA"</string>
- <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Akumuliatoriaus tausojimo priemonė ĮJUNGTA"</string>
<string name="global_action_settings" msgid="1756531602592545966">"Nustatymai"</string>
<string name="global_action_assist" msgid="3892832961594295030">"Pagalba"</string>
<string name="global_action_voice_assist" msgid="7751191495200504480">"Voice Assist"</string>
@@ -282,6 +279,9 @@
<string name="permgrouplab_location" msgid="7275582855722310164">"Vietovė"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"pasiekti įrenginio vietovės informaciją"</string>
<string name="permgrouprequest_location" msgid="3788275734953323491">"Suteikti <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> galimybę pasiekti įrenginio vietovę?"</string>
+ <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"Programa galės pasiekti vietovę, tik kai ją naudosite."</string>
+ <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"Visada leisti programai <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> pasiekti šio įrenginio vietovę?"</string>
+ <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"Programa visada galės pasiekti vietovę, net kai jos nenaudosite."</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Kalendorius"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"pasiekti kalendorių"</string>
<string name="permgrouprequest_calendar" msgid="289900767793189421">"Suteikti <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> galimybę pasiekti kalendorių?"</string>
@@ -408,8 +408,7 @@
<string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Ši programa gali pridėti, pašalinti arba pakeisti telefone esančius kalendoriaus įvykius. Ši programa gali siųsti pranešimus, kurie atrodys atsiųsti kalendoriaus savininkų, arba pakeisti įvykius nepranešusi jų savininkams."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"pasiekti papildomas vietos teikimo įrankio komandas"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Programai leidžiama pasiekti papildomas vietovės nustatymo paslaugų teikėjų komandas. Dėl to programa gali trukdyti veikti GPS ar kitiems vietovės nustatymo šaltiniams."</string>
- <!-- no translation found for permlab_accessFineLocation (6265109654698562427) -->
- <skip />
+ <string name="permlab_accessFineLocation" msgid="6265109654698562427">"pasiekti tikslią vietovę, tik kai programa veikia priekiniame plane"</string>
<string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Ši programa gali gauti tikslius jūsų vietovės duomenis bet kuriuo metu, kai veikia priekiniame plane. Šios vietovės paslaugos turi būti įjungtos ir pasiekiamos telefone, kad programa galėtų jas naudoti. Tai gali padidinti akumuliatoriaus energijos suvartojimą."</string>
<string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"pasiekti apytikslę vietą (nustatytą atsižvelgiant į tinklą)"</string>
<string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Ši programa gali gauti jūsų vietos informaciją naudodamasi tinklo šaltinių, pvz., mobiliojo ryšio bokštų ir „Wi-Fi“ tinklų, duomenimis. Šios vietovės paslaugos turi būti įjungtos ir pasiekiamos planšetiniame kompiuteryje, kad programa galėtų jas naudoti."</string>
@@ -524,6 +523,37 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Kontrolinio kodo piktograma"</string>
+ <string name="permlab_manageFace" msgid="2137540986007309781">"tvarkyti veido autentifikavimo aparatinę įrangą"</string>
+ <string name="permdesc_manageFace" msgid="8919637120670185330">"Programai leidžiama aktyv. metodus, norint pridėti ir ištrinti naudojamus veidų šablonus."</string>
+ <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"naudoti veido autentifikavimo aparatinę įrangą"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Programai leidžiama naudoti veido autentifikavimo aparatinę įrangą tapatybei nustatyti"</string>
+ <string name="face_acquired_insufficient" msgid="5901287247766106330">"Nepavyko apdoroti veido. Bandykite dar kartą."</string>
+ <string name="face_acquired_too_bright" msgid="610606792381297174">"Veidas per šviesus. Band. sumažinti apšvietimą."</string>
+ <string name="face_acquired_too_dark" msgid="7229162716976778371">"Veidas per tamsus. Atidenkite apšvietimo šaltinį."</string>
+ <string name="face_acquired_too_close" msgid="1980310037427755293">"Patraukite jutiklį toliau nuo veido."</string>
+ <string name="face_acquired_too_far" msgid="4494571381828850007">"Laikykite jutiklį arčiau veido."</string>
+ <string name="face_acquired_too_high" msgid="228411096134808372">"Pakelkite jutiklį aukščiau."</string>
+ <string name="face_acquired_too_low" msgid="4539774649296349109">"Nuleiskite jutiklį žemiau."</string>
+ <string name="face_acquired_too_right" msgid="1650292067226118760">"Patraukite jutiklį dešinėn."</string>
+ <string name="face_acquired_too_left" msgid="2712489669456176505">"Patraukite jutiklį kairėn."</string>
+ <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Žiūrėkite į jutiklį."</string>
+ <string name="face_acquired_not_detected" msgid="5707782294589511391">"Neaptikta jokių veidų."</string>
+ <string name="face_acquired_not_steady" msgid="3722829465011040042">"Stabiliai laikykite veidą prieš įrenginį."</string>
+ <string-array name="face_acquired_vendor">
+ </string-array>
+ <string name="face_error_hw_not_available" msgid="6255891785768984615">"Veido atpažinimo aparatinė įranga nepasiekiama."</string>
+ <string name="face_error_timeout" msgid="4014326147867150054">"Baigėsi veido atpaž. skirt. laik. Band. dar kartą."</string>
+ <string name="face_error_no_space" msgid="8224993703466381314">"Nepavyko išsaugoti veido duomenų."</string>
+ <string name="face_error_canceled" msgid="283945501061931023">"Veido atpažinimo operacija atšaukta."</string>
+ <string name="face_error_lockout" msgid="3407426963155388504">"Per daug bandymų. Vėliau bandykite dar kartą."</string>
+ <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Per daug bandymų. Veido autentifik. išjungtas."</string>
+ <string name="face_error_unable_to_process" msgid="238761109287767270">"Bandykite dar kartą."</string>
+ <string name="face_error_not_enrolled" msgid="9166792142679691323">"Neužregistruota jokių veidų."</string>
+ <string name="face_error_hw_not_present" msgid="4737289254517095671">"Šiame įrenginyje nėra veido autentifikavimo jutiklio"</string>
+ <string name="face_name_template" msgid="7004562145809595384">"<xliff:g id="FACEID">%d</xliff:g> veidas"</string>
+ <string-array name="face_error_vendor">
+ </string-array>
+ <string name="face_icon_content_description" msgid="4024817159806482191">"Veido pkt."</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"skaityti sinchronizavimo nustatymus"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Leidžiama programai skaityti ir sinchronizuoti paskyros nustatymus. Pvz., taip gali būti nustatoma, ar su paskyra sinchronizuota Žmonių programa."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"įjungti arba išjungti sinchronizavimą"</string>
@@ -1222,6 +1252,9 @@
<skip />
<string name="wifi_no_internet" msgid="8938267198124654938">"„Wi‑Fi“ tinkle nėra interneto ryšio"</string>
<string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Palieskite, kad būtų rodomos parinktys."</string>
+ <string name="wifi_softap_config_change" msgid="8475911871165857607">"Viešosios interneto prieigos taško nustatymų pakeitimai"</string>
+ <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"Viešosios prieigos taško dažnio juosta pasikeitė."</string>
+ <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"Šiame įrenginyje nepalaikoma tik 5 GHz nuostata. Vietoj to šiame įrenginyje bus naudojama 5 GHz dažnio juosta, kai bus pasiekiama."</string>
<string name="network_switch_metered" msgid="4671730921726992671">"Perjungta į tinklą <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
<string name="network_switch_metered_detail" msgid="775163331794506615">"Įrenginyje naudojamas kitas tinklas (<xliff:g id="NEW_NETWORK">%1$s</xliff:g>), kai dabartiniame tinkle (<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>) nėra interneto ryšio. Gali būti taikomi mokesčiai."</string>
<string name="network_switch_metered_toast" msgid="5779283181685974304">"Perjungta iš tinklo <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> į tinklą <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 5f25162..a5116e4 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -239,9 +239,6 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Lidojuma režīms"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Lidojuma režīms ir IESLĒGTS."</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Lidojuma režīms ir IZSLĒGTS."</string>
- <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Akumulatora jaudas taupīšanas režīms"</string>
- <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Akumulatora jaudas taupīšanas režīms ir IZSLĒGTS"</string>
- <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Akumulatora jaudas taupīšanas režīms ir IESLĒGTS"</string>
<string name="global_action_settings" msgid="1756531602592545966">"Iestatījumi"</string>
<string name="global_action_assist" msgid="3892832961594295030">"Palīdzība"</string>
<string name="global_action_voice_assist" msgid="7751191495200504480">"Balss palīgs"</string>
@@ -279,6 +276,9 @@
<string name="permgrouplab_location" msgid="7275582855722310164">"Atrašanās vieta"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"piekļūt ierīces atrašanās vietas informācijai"</string>
<string name="permgrouprequest_location" msgid="3788275734953323491">"Vai atļaut lietotnei <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> piekļūt šīs ierīces atrašanās vietai?"</string>
+ <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"Lietotne varēs piekļūt atrašanās vietai tikai tad, kad izmantosiet šo lietotni."</string>
+ <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"Vienmēr atļaut <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> piekļūt atrašanās vietai?"</string>
+ <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"Lietotne vienmēr varēs piekļūt atrašanās vietai, pat ja neizmantosiet šo lietotni."</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Kalendārs"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"piekļūt jūsu kalendāram"</string>
<string name="permgrouprequest_calendar" msgid="289900767793189421">"Vai atļaut lietotnei <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> piekļūt jūsu kalendāram?"</string>
@@ -405,8 +405,7 @@
<string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Šī lietotne var pievienot, noņemt vai mainīt kalendāra pasākumus jūsu tālrunī. Šī lietotne var sūtīt ziņojumus, ko šķietami sūtījuši kalendāru īpašnieki, vai mainīt pasākumus, neinformējot to īpašniekus."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"piekļūt atrašanās vietas nodrošinātāja papildu komandām"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Ļauj lietotnei piekļūt papildu atrašanās vietas noteikšanas nodrošinātāju komandām. Tas var ļaut lietotnei traucēt GPS vai citu atrašanās vietas noteikšanas avotu darbību."</string>
- <!-- no translation found for permlab_accessFineLocation (6265109654698562427) -->
- <skip />
+ <string name="permlab_accessFineLocation" msgid="6265109654698562427">"piekļuve precīzai atrašanās vietai, tikai darbojoties priekšplānā"</string>
<string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Šī lietotne var iegūt precīzu jūsu atrašanās vietu, tikai darbojoties priekšplānā. Šiem atrašanās vietu pakalpojumiem ir jābūt ieslēgtiem un pieejamiem jūsu tālrunī, lai lietotne varētu tos izmantot. Tādējādi var palielināties akumulatora jaudas patēriņš."</string>
<string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"Piekļūt aptuvenai atrašanās vietai (izmantojot tīklu)"</string>
<string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Šī lietotne var iegūt jūsu atrašanās vietu, pamatojoties uz tīkla avotiem, piemēram, mobilo sakaru torņiem un Wi-Fi tīkliem. Šiem atrašanās vietu pakalpojumiem ir jābūt ieslēgtiem un pieejamiem jūsu planšetdatorā, lai lietotne tos varētu izmantot."</string>
@@ -521,6 +520,37 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Pirksta nospieduma ikona"</string>
+ <string name="permlab_manageFace" msgid="2137540986007309781">"pārvaldīt sejas autentifikācijas aparatūru"</string>
+ <string name="permdesc_manageFace" msgid="8919637120670185330">"Atļauj lietotnei izsaukt metodes izmantojamo sejas veidņu pievienošanai un dzēšanai."</string>
+ <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"izmantot sejas autentifikācijas aparatūru"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Atļauj lietotnei izmantot sejas autentifikācijas aparatūru autentificēšanai"</string>
+ <string name="face_acquired_insufficient" msgid="5901287247766106330">"Nevarēja apstrādāt sejas datus. Mēģiniet vēlreiz."</string>
+ <string name="face_acquired_too_bright" msgid="610606792381297174">"Seja ir pārāk izgaismota. Mēģiniet tumšākā vidē."</string>
+ <string name="face_acquired_too_dark" msgid="7229162716976778371">"Pārāk tumšs sejas attēls. Mēģiniet gaišākā vidē."</string>
+ <string name="face_acquired_too_close" msgid="1980310037427755293">"Pavirziet sensoru tālāk no sejas."</string>
+ <string name="face_acquired_too_far" msgid="4494571381828850007">"Pavirziet sensoru tuvāk sejai."</string>
+ <string name="face_acquired_too_high" msgid="228411096134808372">"Pavirziet sensoru augstāk."</string>
+ <string name="face_acquired_too_low" msgid="4539774649296349109">"Pavirziet sensoru zemāk."</string>
+ <string name="face_acquired_too_right" msgid="1650292067226118760">"Pavirziet sensoru pa labi."</string>
+ <string name="face_acquired_too_left" msgid="2712489669456176505">"Pavirziet sensoru pa kreisi."</string>
+ <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Lūdzu, paskatieties uz sensoru."</string>
+ <string name="face_acquired_not_detected" msgid="5707782294589511391">"Nav atrasta neviena seja."</string>
+ <string name="face_acquired_not_steady" msgid="3722829465011040042">"Turiet ierīci vērstu pret nekustīgu seju."</string>
+ <string-array name="face_acquired_vendor">
+ </string-array>
+ <string name="face_error_hw_not_available" msgid="6255891785768984615">"Sejas autentifikācijas aparatūra nav pieejama."</string>
+ <string name="face_error_timeout" msgid="4014326147867150054">"Sejas datu nolasīšanas noildze. Mēģiniet vēlreiz."</string>
+ <string name="face_error_no_space" msgid="8224993703466381314">"Sejas datus nevar saglabāt."</string>
+ <string name="face_error_canceled" msgid="283945501061931023">"Darbība ar sejas datiem atcelta."</string>
+ <string name="face_error_lockout" msgid="3407426963155388504">"Pārāk daudz mēģinājumu. Vēlāk mēģiniet vēlreiz."</string>
+ <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Par daudz mēģinājumu. Sejas atpazīšana atspējota."</string>
+ <string name="face_error_unable_to_process" msgid="238761109287767270">"Mēģiniet vēlreiz."</string>
+ <string name="face_error_not_enrolled" msgid="9166792142679691323">"Nav reģistrēti sejas dati."</string>
+ <string name="face_error_hw_not_present" msgid="4737289254517095671">"Šai ierīcei nav sejas autentifikācijas sensora."</string>
+ <string name="face_name_template" msgid="7004562145809595384">"Seja <xliff:g id="FACEID">%d</xliff:g>"</string>
+ <string-array name="face_error_vendor">
+ </string-array>
+ <string name="face_icon_content_description" msgid="4024817159806482191">"Sejas ikona"</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"lasīt sinhronizācijas iestatījumus"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Ļauj lietotnei lasīt konta sinhronizācijas iestatījumus. Piemēram, šādi var noteikt, vai lietotne Personas ir sinhronizēta ar kontu."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"ieslēgt un izslēgt sinhronizāciju"</string>
@@ -1200,6 +1230,9 @@
<skip />
<string name="wifi_no_internet" msgid="8938267198124654938">"Wi-Fi tīklā nav piekļuves internetam."</string>
<string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Pieskarieties, lai skatītu iespējas."</string>
+ <string name="wifi_softap_config_change" msgid="8475911871165857607">"Izmaiņas tīklāja iestatījumos"</string>
+ <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"Ir mainīts tīklāja joslas platums."</string>
+ <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"Šajā ierīcē netiek atbalstīta jūsu preference par tikai 5 GHz joslu. 5 GHz josla ierīcē tiks izmantota, kad tā būs pieejama."</string>
<string name="network_switch_metered" msgid="4671730921726992671">"Pārslēdzās uz tīklu <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
<string name="network_switch_metered_detail" msgid="775163331794506615">"Kad vienā tīklā (<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>) nav piekļuves internetam, ierīcē tiek izmantots cits tīkls (<xliff:g id="NEW_NETWORK">%1$s</xliff:g>). Var tikt piemērota maksa."</string>
<string name="network_switch_metered_toast" msgid="5779283181685974304">"Pārslēdzās no tīkla <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> uz tīklu <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
diff --git a/packages/PrintSpooler/res/drawable/print_warning.xml b/core/res/res/values-mcc334-mnc03/config.xml
similarity index 60%
rename from packages/PrintSpooler/res/drawable/print_warning.xml
rename to core/res/res/values-mcc334-mnc03/config.xml
index 35f0fed..c0d2b35 100644
--- a/packages/PrintSpooler/res/drawable/print_warning.xml
+++ b/core/res/res/values-mcc334-mnc03/config.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 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.
@@ -14,12 +14,7 @@
limitations under the License.
-->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="96dp"
- android:height="96dp"
- android:viewportWidth="96.0"
- android:viewportHeight="96.0">
- <path
- android:fillColor="#C8CCCE"
- android:pathData="M4,84H92L48,8 4,84zM52,72h-8v-8h8v8zM52,56H44V40h8v16z"/>
-</vector>
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Show area update info settings in CellBroadcastReceiver and information in SIM status in Settings app -->
+ <bool name="config_showAreaUpdateInfoSettings">true</bool>
+</resources>
diff --git a/packages/PrintSpooler/res/drawable/print_warning.xml b/core/res/res/values-mcc334-mnc030/config.xml
similarity index 60%
copy from packages/PrintSpooler/res/drawable/print_warning.xml
copy to core/res/res/values-mcc334-mnc030/config.xml
index 35f0fed..c0d2b35 100644
--- a/packages/PrintSpooler/res/drawable/print_warning.xml
+++ b/core/res/res/values-mcc334-mnc030/config.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 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.
@@ -14,12 +14,7 @@
limitations under the License.
-->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="96dp"
- android:height="96dp"
- android:viewportWidth="96.0"
- android:viewportHeight="96.0">
- <path
- android:fillColor="#C8CCCE"
- android:pathData="M4,84H92L48,8 4,84zM52,72h-8v-8h8v8zM52,56H44V40h8v16z"/>
-</vector>
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Show area update info settings in CellBroadcastReceiver and information in SIM status in Settings app -->
+ <bool name="config_showAreaUpdateInfoSettings">true</bool>
+</resources>
diff --git a/packages/PrintSpooler/res/drawable/print_warning.xml b/core/res/res/values-mcc704-mnc03/config.xml
similarity index 60%
copy from packages/PrintSpooler/res/drawable/print_warning.xml
copy to core/res/res/values-mcc704-mnc03/config.xml
index 35f0fed..c0d2b35 100644
--- a/packages/PrintSpooler/res/drawable/print_warning.xml
+++ b/core/res/res/values-mcc704-mnc03/config.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 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.
@@ -14,12 +14,7 @@
limitations under the License.
-->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="96dp"
- android:height="96dp"
- android:viewportWidth="96.0"
- android:viewportHeight="96.0">
- <path
- android:fillColor="#C8CCCE"
- android:pathData="M4,84H92L48,8 4,84zM52,72h-8v-8h8v8zM52,56H44V40h8v16z"/>
-</vector>
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Show area update info settings in CellBroadcastReceiver and information in SIM status in Settings app -->
+ <bool name="config_showAreaUpdateInfoSettings">true</bool>
+</resources>
diff --git a/packages/PrintSpooler/res/drawable/print_warning.xml b/core/res/res/values-mcc706-mnc04/config.xml
similarity index 60%
copy from packages/PrintSpooler/res/drawable/print_warning.xml
copy to core/res/res/values-mcc706-mnc04/config.xml
index 35f0fed..c0d2b35 100644
--- a/packages/PrintSpooler/res/drawable/print_warning.xml
+++ b/core/res/res/values-mcc706-mnc04/config.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 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.
@@ -14,12 +14,7 @@
limitations under the License.
-->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="96dp"
- android:height="96dp"
- android:viewportWidth="96.0"
- android:viewportHeight="96.0">
- <path
- android:fillColor="#C8CCCE"
- android:pathData="M4,84H92L48,8 4,84zM52,72h-8v-8h8v8zM52,56H44V40h8v16z"/>
-</vector>
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Show area update info settings in CellBroadcastReceiver and information in SIM status in Settings app -->
+ <bool name="config_showAreaUpdateInfoSettings">true</bool>
+</resources>
diff --git a/packages/PrintSpooler/res/drawable/print_warning.xml b/core/res/res/values-mcc712-mnc04/config.xml
similarity index 60%
copy from packages/PrintSpooler/res/drawable/print_warning.xml
copy to core/res/res/values-mcc712-mnc04/config.xml
index 35f0fed..c0d2b35 100644
--- a/packages/PrintSpooler/res/drawable/print_warning.xml
+++ b/core/res/res/values-mcc712-mnc04/config.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 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.
@@ -14,12 +14,7 @@
limitations under the License.
-->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="96dp"
- android:height="96dp"
- android:viewportWidth="96.0"
- android:viewportHeight="96.0">
- <path
- android:fillColor="#C8CCCE"
- android:pathData="M4,84H92L48,8 4,84zM52,72h-8v-8h8v8zM52,56H44V40h8v16z"/>
-</vector>
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Show area update info settings in CellBroadcastReceiver and information in SIM status in Settings app -->
+ <bool name="config_showAreaUpdateInfoSettings">true</bool>
+</resources>
diff --git a/packages/PrintSpooler/res/drawable/print_warning.xml b/core/res/res/values-mcc716-mnc06/config.xml
similarity index 60%
copy from packages/PrintSpooler/res/drawable/print_warning.xml
copy to core/res/res/values-mcc716-mnc06/config.xml
index 35f0fed..c0d2b35 100644
--- a/packages/PrintSpooler/res/drawable/print_warning.xml
+++ b/core/res/res/values-mcc716-mnc06/config.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 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.
@@ -14,12 +14,7 @@
limitations under the License.
-->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="96dp"
- android:height="96dp"
- android:viewportWidth="96.0"
- android:viewportHeight="96.0">
- <path
- android:fillColor="#C8CCCE"
- android:pathData="M4,84H92L48,8 4,84zM52,72h-8v-8h8v8zM52,56H44V40h8v16z"/>
-</vector>
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Show area update info settings in CellBroadcastReceiver and information in SIM status in Settings app -->
+ <bool name="config_showAreaUpdateInfoSettings">true</bool>
+</resources>
diff --git a/packages/PrintSpooler/res/drawable/print_warning.xml b/core/res/res/values-mcc716-mnc10/config.xml
similarity index 60%
copy from packages/PrintSpooler/res/drawable/print_warning.xml
copy to core/res/res/values-mcc716-mnc10/config.xml
index 35f0fed..c0d2b35 100644
--- a/packages/PrintSpooler/res/drawable/print_warning.xml
+++ b/core/res/res/values-mcc716-mnc10/config.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 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.
@@ -14,12 +14,7 @@
limitations under the License.
-->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="96dp"
- android:height="96dp"
- android:viewportWidth="96.0"
- android:viewportHeight="96.0">
- <path
- android:fillColor="#C8CCCE"
- android:pathData="M4,84H92L48,8 4,84zM52,72h-8v-8h8v8zM52,56H44V40h8v16z"/>
-</vector>
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Show area update info settings in CellBroadcastReceiver and information in SIM status in Settings app -->
+ <bool name="config_showAreaUpdateInfoSettings">true</bool>
+</resources>
diff --git a/packages/PrintSpooler/res/drawable/print_warning.xml b/core/res/res/values-mcc716-mnc17/config.xml
similarity index 60%
copy from packages/PrintSpooler/res/drawable/print_warning.xml
copy to core/res/res/values-mcc716-mnc17/config.xml
index 35f0fed..c0d2b35 100644
--- a/packages/PrintSpooler/res/drawable/print_warning.xml
+++ b/core/res/res/values-mcc716-mnc17/config.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 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.
@@ -14,12 +14,7 @@
limitations under the License.
-->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="96dp"
- android:height="96dp"
- android:viewportWidth="96.0"
- android:viewportHeight="96.0">
- <path
- android:fillColor="#C8CCCE"
- android:pathData="M4,84H92L48,8 4,84zM52,72h-8v-8h8v8zM52,56H44V40h8v16z"/>
-</vector>
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Show area update info settings in CellBroadcastReceiver and information in SIM status in Settings app -->
+ <bool name="config_showAreaUpdateInfoSettings">true</bool>
+</resources>
diff --git a/packages/PrintSpooler/res/drawable/print_warning.xml b/core/res/res/values-mcc722-mnc07/config.xml
similarity index 60%
copy from packages/PrintSpooler/res/drawable/print_warning.xml
copy to core/res/res/values-mcc722-mnc07/config.xml
index 35f0fed..c0d2b35 100644
--- a/packages/PrintSpooler/res/drawable/print_warning.xml
+++ b/core/res/res/values-mcc722-mnc07/config.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 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.
@@ -14,12 +14,7 @@
limitations under the License.
-->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="96dp"
- android:height="96dp"
- android:viewportWidth="96.0"
- android:viewportHeight="96.0">
- <path
- android:fillColor="#C8CCCE"
- android:pathData="M4,84H92L48,8 4,84zM52,72h-8v-8h8v8zM52,56H44V40h8v16z"/>
-</vector>
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Show area update info settings in CellBroadcastReceiver and information in SIM status in Settings app -->
+ <bool name="config_showAreaUpdateInfoSettings">true</bool>
+</resources>
diff --git a/packages/PrintSpooler/res/drawable/print_warning.xml b/core/res/res/values-mcc732-mnc123/config.xml
similarity index 60%
copy from packages/PrintSpooler/res/drawable/print_warning.xml
copy to core/res/res/values-mcc732-mnc123/config.xml
index 35f0fed..c0d2b35 100644
--- a/packages/PrintSpooler/res/drawable/print_warning.xml
+++ b/core/res/res/values-mcc732-mnc123/config.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 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.
@@ -14,12 +14,7 @@
limitations under the License.
-->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="96dp"
- android:height="96dp"
- android:viewportWidth="96.0"
- android:viewportHeight="96.0">
- <path
- android:fillColor="#C8CCCE"
- android:pathData="M4,84H92L48,8 4,84zM52,72h-8v-8h8v8zM52,56H44V40h8v16z"/>
-</vector>
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Show area update info settings in CellBroadcastReceiver and information in SIM status in Settings app -->
+ <bool name="config_showAreaUpdateInfoSettings">true</bool>
+</resources>
diff --git a/packages/PrintSpooler/res/drawable/print_warning.xml b/core/res/res/values-mcc740-mnc00/config.xml
similarity index 60%
copy from packages/PrintSpooler/res/drawable/print_warning.xml
copy to core/res/res/values-mcc740-mnc00/config.xml
index 35f0fed..c0d2b35 100644
--- a/packages/PrintSpooler/res/drawable/print_warning.xml
+++ b/core/res/res/values-mcc740-mnc00/config.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 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.
@@ -14,12 +14,7 @@
limitations under the License.
-->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="96dp"
- android:height="96dp"
- android:viewportWidth="96.0"
- android:viewportHeight="96.0">
- <path
- android:fillColor="#C8CCCE"
- android:pathData="M4,84H92L48,8 4,84zM52,72h-8v-8h8v8zM52,56H44V40h8v16z"/>
-</vector>
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Show area update info settings in CellBroadcastReceiver and information in SIM status in Settings app -->
+ <bool name="config_showAreaUpdateInfoSettings">true</bool>
+</resources>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index 07571efd..47f388d 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -236,9 +236,6 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Режим на работа во авион"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Режимот на работа во авион е вклучен"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Режимот на работа во авион е исклучен"</string>
- <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Штедач на батерија"</string>
- <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Штедачот на батерија е ИСКЛУЧЕН"</string>
- <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Штедачот на батерија е ВКЛУЧЕН"</string>
<string name="global_action_settings" msgid="1756531602592545966">"Поставки"</string>
<string name="global_action_assist" msgid="3892832961594295030">"Асистенција"</string>
<string name="global_action_voice_assist" msgid="7751191495200504480">"Гласовна помош"</string>
@@ -276,6 +273,9 @@
<string name="permgrouplab_location" msgid="7275582855722310164">"Локација"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"пристапува до локацијата на овој уред"</string>
<string name="permgrouprequest_location" msgid="3788275734953323491">"Дали да се дозволи <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> да пристапува до локацијата на уредот?"</string>
+ <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"Апликацијата ќе има пристап до локацијата само додека ја користите."</string>
+ <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"Дозволете <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> секогаш да пристапува до локацијата?"</string>
+ <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"Апликацијата секогаш ќе има пристап до локацијата, дури и кога не ја користите."</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Календар"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"пристапува до календарот"</string>
<string name="permgrouprequest_calendar" msgid="289900767793189421">"Дали да се дозволи <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> да пристапува до календарот?"</string>
@@ -402,8 +402,7 @@
<string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Апликацијава може да додава, отстранува или менува настани во календарот на телефонот. Таа може да испраќа пораки што ќе изгледаат како да се испратени од сопствениците на календарот или да менува настани без да ги извести нивните сопственици."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"пристапи кон наредби на давателот на дополнителна локација"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Овозможува апликацијата да пристапи кон дополнителни наредби на давател на локација. Ова може да овозможи апликацијата да го попечи функционирањето на GPS или други извори на локација."</string>
- <!-- no translation found for permlab_accessFineLocation (6265109654698562427) -->
- <skip />
+ <string name="permlab_accessFineLocation" msgid="6265109654698562427">"пристап до прецизната локација само во преден план"</string>
<string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Апликацијава може да ја добие вашата точна локација само кога е во преден план. Услугиве според локација мора да се вклучени и достапни на телефонот за да може да ги користи апликацијата. Ова може да го зголеми трошењето на батеријата."</string>
<string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"пристап до приближната локација (врз база на мрежа)"</string>
<string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Апликацијава може да ја добие вашата локација од мрежните извори како што се репетиторите за мобилни мрежи и Wi-Fi мрежите. Овие услуги за локација мора да се вклучени и достапни на таблетот за да може да ги користи апликацијата."</string>
@@ -518,6 +517,37 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Икона за отпечатоци"</string>
+ <string name="permlab_manageFace" msgid="2137540986007309781">"управува со хардвер за проверка на лице"</string>
+ <string name="permdesc_manageFace" msgid="8919637120670185330">"Дозволува апликац. да повика начини за додавање и бришење шаблони на лице за користење."</string>
+ <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"користи хардвер за проверка на лице"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Дозволува апликацијата да користи хардвер за лице за проверка"</string>
+ <string name="face_acquired_insufficient" msgid="5901287247766106330">"Лицето не можеше да се обработи. Обидете се пак."</string>
+ <string name="face_acquired_too_bright" msgid="610606792381297174">"Лицето е пресветло. Пробајте на послаба светлина."</string>
+ <string name="face_acquired_too_dark" msgid="7229162716976778371">"Лицето е претемно. Пробајте со поголема светлина."</string>
+ <string name="face_acquired_too_close" msgid="1980310037427755293">"Оддалечете го сензорот од лицето."</string>
+ <string name="face_acquired_too_far" msgid="4494571381828850007">"Доближете го сензорот до лицето."</string>
+ <string name="face_acquired_too_high" msgid="228411096134808372">"Подигнете го сензорот."</string>
+ <string name="face_acquired_too_low" msgid="4539774649296349109">"Спуштете го сензорот."</string>
+ <string name="face_acquired_too_right" msgid="1650292067226118760">"Поместете го сензорот надесно."</string>
+ <string name="face_acquired_too_left" msgid="2712489669456176505">"Поместете го сензорот налево."</string>
+ <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Погледнете во сензорот."</string>
+ <string name="face_acquired_not_detected" msgid="5707782294589511391">"Не е откриено лице."</string>
+ <string name="face_acquired_not_steady" msgid="3722829465011040042">"Држете го лицето стабилно пред уредот."</string>
+ <string-array name="face_acquired_vendor">
+ </string-array>
+ <string name="face_error_hw_not_available" msgid="6255891785768984615">"Хардверот за лице не е достапен."</string>
+ <string name="face_error_timeout" msgid="4014326147867150054">"Истече времето за проверка на лице. Повторен обид."</string>
+ <string name="face_error_no_space" msgid="8224993703466381314">"Лицето не може да се чува."</string>
+ <string name="face_error_canceled" msgid="283945501061931023">"Операцијата со лице се откажа."</string>
+ <string name="face_error_lockout" msgid="3407426963155388504">"Премногу обиди. Обидете се повторно подоцна."</string>
+ <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Премногу обиди. Проверката на лице е оневозможена."</string>
+ <string name="face_error_unable_to_process" msgid="238761109287767270">"Обидете се повторно."</string>
+ <string name="face_error_not_enrolled" msgid="9166792142679691323">"Нема регистрирано лице."</string>
+ <string name="face_error_hw_not_present" msgid="4737289254517095671">"Уредов нема сензор за проверка на лице"</string>
+ <string name="face_name_template" msgid="7004562145809595384">"Лице <xliff:g id="FACEID">%d</xliff:g>"</string>
+ <string-array name="face_error_vendor">
+ </string-array>
+ <string name="face_icon_content_description" msgid="4024817159806482191">"Икона"</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"чита поставки за синхронизација"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Овозможува апликацијата да ги чита поставките за синхронизирање на сметка. На пример, така може да се утврди дали апликацијата „Луѓе“ е синхронизирана со сметка."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"вклучи и исклучи синхронизација"</string>
@@ -1178,6 +1208,9 @@
<skip />
<string name="wifi_no_internet" msgid="8938267198124654938">"Wi-Fi нема пристап до интернет"</string>
<string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Допрете за опции"</string>
+ <string name="wifi_softap_config_change" msgid="8475911871165857607">"Промени на поставките за точка на пристап"</string>
+ <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"Појасот за точка на пристап е променет."</string>
+ <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"Уредов не ги поддржува вашите поставки за само 5 GHz. Наместо тоа, ќе го користи појасот од 5 GHz кога е достапен."</string>
<string name="network_switch_metered" msgid="4671730921726992671">"Префрлено на <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
<string name="network_switch_metered_detail" msgid="775163331794506615">"Уредот користи <xliff:g id="NEW_NETWORK">%1$s</xliff:g> кога <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> нема пристап до интернет. Може да се наплатат трошоци."</string>
<string name="network_switch_metered_toast" msgid="5779283181685974304">"Префрлено од <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> на <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index 5bbddf9..1a3556c 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -236,9 +236,6 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"ഫ്ലൈറ്റ് മോഡ്"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"ഫ്ലൈറ്റ് മോഡ് ഓണാണ്"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"ഫ്ലൈറ്റ് മോഡ് ഓഫാണ്"</string>
- <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"ബാറ്ററി ലാഭിക്കൽ"</string>
- <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"ബാറ്ററി സേവർ ഓഫാണ്"</string>
- <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"ബാറ്ററി ലാഭിക്കൽ ഓണാണ്"</string>
<string name="global_action_settings" msgid="1756531602592545966">"ക്രമീകരണം"</string>
<string name="global_action_assist" msgid="3892832961594295030">"അസിസ്റ്റ്"</string>
<string name="global_action_voice_assist" msgid="7751191495200504480">"വോയ്സ് സഹായം"</string>
@@ -276,6 +273,9 @@
<string name="permgrouplab_location" msgid="7275582855722310164">"ലൊക്കേഷൻ"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"ഈ ഉപകരണത്തിന്റെ ലൊക്കേഷൻ ആക്സസ് ചെയ്യാൻ"</string>
<string name="permgrouprequest_location" msgid="3788275734953323491">"ഈ ഉപകരണത്തിന്റെ ലൊക്കേഷൻ ആക്സസ് ചെയ്യാൻ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ആപ്പിനെ അനുവദിക്കണോ?"</string>
+ <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"നിങ്ങൾ ഉപയോഗിക്കുമ്പോൾ മാത്രം ആപ്പ് ലൊക്കേഷൻ ആക്സസ് ചെയ്യും."</string>
+ <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"ഈ ഉപകരണത്തിന്റെ ലൊക്കേഷൻ ആക്സസ് ചെയ്യാൻ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> എപ്പോഴും ആപ്പിനെ അനുവദിക്കണോ?"</string>
+ <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"നിങ്ങൾ ആപ്പ് ഉപയോഗിക്കാത്തപ്പോൾ പോലും, ഏതുസമയത്തും ആപ്പ് ലൊക്കേഷൻ ആക്സസ് ചെയ്യും."</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"കലണ്ടർ"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"നിങ്ങളുടെ കലണ്ടർ ആക്സസ്സ് ചെയ്യുക"</string>
<string name="permgrouprequest_calendar" msgid="289900767793189421">"നിങ്ങളുടെ കലണ്ടർ ആക്സസ് ചെയ്യാൻ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ആപ്പിനെ അനുവദിക്കണോ?"</string>
@@ -402,8 +402,7 @@
<string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"ഈ ആപ്പിന്, നിങ്ങളുടെ ഫോണിൽ കലണ്ടർ ഇവന്റുകൾ ചേർക്കാനോ നീക്കംചെയ്യാനോ മാറ്റാനോ കഴിയും. കലണ്ടർ ഉടമകളിൽ നിന്നാണ് വരുന്നതെന്ന് തോന്നിപ്പിക്കാവുന്ന സന്ദേശങ്ങൾ അയയ്ക്കാനോ ഉടമകളെ അറിയിക്കാതെ അവരുടെ ഇവന്റുകളെ മാറ്റാനോ ഈ ആപ്പിന് കഴിയും."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"ലൊക്കേഷൻ ദാതാവിന്റെ അധിക കമാൻഡുകൾ ആക്സസ്സുചെയ്യുക"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"ലൊക്കേഷൻ ദാതാവിന്റെ അധിക കമാൻഡുകൾ ആക്സസ്സുചെയ്യാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു. ഇത് GPS-ന്റെയോ മറ്റ് ലൊക്കേഷൻ ഉറവിടങ്ങളുടെയോ പ്രവർത്തനത്തിൽ ഇടപെടാൻ അപ്ലിക്കേഷനെ അനുവദിക്കാനിടയുണ്ട്."</string>
- <!-- no translation found for permlab_accessFineLocation (6265109654698562427) -->
- <skip />
+ <string name="permlab_accessFineLocation" msgid="6265109654698562427">"ഫോർഗ്രൗണ്ടിൽ മാത്രം കൃത്യമായ ലൊക്കേഷൻ ആക്സസ് ചെയ്യുക"</string>
<string name="permdesc_accessFineLocation" msgid="3520508381065331098">"ഫോർഗ്രൗണ്ടിൽ ഉള്ളപ്പോൾ മാത്രമേ ഈ ആപ്പിന് നിങ്ങളുടെ കൃത്യമായ ലൊക്കേഷൻ നേടാനാവൂ. ആപ്പിന് ഉപയോഗിക്കാനായി, ഈ ലൊക്കേഷൻ സേവനങ്ങൾ ഓണായിരിക്കുകയും നിങ്ങളുടെ ഫോണിൽ ലഭ്യമാവുകയും വേണം. ഇതിലൂടെ ബാറ്ററി ഉപഭോഗം വർദ്ധിച്ചേക്കാം."</string>
<string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"ഏകദേശ ലൊക്കേഷൻ (നെറ്റ്വർക്ക് അധിഷ്ഠിതം) ആക്സസ് ചെയ്യുക"</string>
<string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"സെൽ ടവറുകളും വൈഫൈ നെറ്റ്വർക്കുകളും പോലുള്ള നെറ്റ്വർക്ക് ഉറവിടങ്ങളെ അടിസ്ഥാനമാക്കിക്കൊണ്ട് ഈ ആപ്പിന് നിങ്ങളുടെ ലൊക്കേഷൻ അനുമാനിക്കാൻ കഴിയും. നിങ്ങളുടെ ടാബ്ലെറ്റിൽ ഈ ലൊക്കേഷൻ സേവനങ്ങൾ ഓൺ ചെയ്തിട്ടുണ്ടെങ്കിൽ മാത്രമാണ് ആപ്പിന് അവ ഉപയോഗിക്കാൻ കഴിയുക."</string>
@@ -518,6 +517,37 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"വിരലടയാള ഐക്കൺ"</string>
+ <string name="permlab_manageFace" msgid="2137540986007309781">"മുഖം തിരിച്ചറിയൽ ഹാർഡ്വെയർ മാനേജ് ചെയ്യുക"</string>
+ <string name="permdesc_manageFace" msgid="8919637120670185330">"ഉപയോഗിക്കാനായി, മുഖത്തിന്റെ ടെംപ്ലേറ്റുകൾ ചേർക്കാനും ഇല്ലാതാക്കാനുമുള്ള രീതികൾ അഭ്യർത്ഥിക്കാൻ ആപ്പിനെ അനുവദിക്കുന്നു."</string>
+ <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"മുഖം തിരിച്ചറിയൽ ഹാർഡ്വെയർ ഉപയോഗിക്കുക"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"പരിശോധിച്ചുറപ്പിക്കലിനായി മുഖം തിരിച്ചറിയൽ ഹാർഡ്വെയർ ഉപയോഗിക്കാൻ ആപ്പിനെ അനുവദിക്കുന്നു"</string>
+ <string name="face_acquired_insufficient" msgid="5901287247766106330">"മുഖം പ്രോസസ്സ് ചെയ്യാനായില്ല. വീണ്ടും ശ്രമിക്കുക."</string>
+ <string name="face_acquired_too_bright" msgid="610606792381297174">"മുഖം വളരെ തെളിച്ചമുള്ളതാണ്. കുറഞ്ഞ വെളിച്ചത്തിൽ ശ്രമിക്കുക."</string>
+ <string name="face_acquired_too_dark" msgid="7229162716976778371">"മുഖം വളരെ ഇരുണ്ടതാണ്. പ്രകാശ ലഭ്യത ഉറപ്പാക്കി ശ്രമിക്കുക."</string>
+ <string name="face_acquired_too_close" msgid="1980310037427755293">"മുഖത്തിനടുത്തുനിന്ന് സെൻസർ അകലേയ്ക്ക് നീക്കുക."</string>
+ <string name="face_acquired_too_far" msgid="4494571381828850007">"സെൻസർ മുഖത്തിനടുത്തേയ്ക്ക് കൊണ്ടുവരിക."</string>
+ <string name="face_acquired_too_high" msgid="228411096134808372">"സെൻസർ മുകളിലേക്ക് നീക്കുക."</string>
+ <string name="face_acquired_too_low" msgid="4539774649296349109">"സെൻസർ താഴേയ്ക്ക് നീക്കുക."</string>
+ <string name="face_acquired_too_right" msgid="1650292067226118760">"സെൻസർ വലത്തേയ്ക്ക് നീക്കുക."</string>
+ <string name="face_acquired_too_left" msgid="2712489669456176505">"സെൻസർ ഇടത്തേയ്ക്ക് നീക്കുക."</string>
+ <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"സെൻസറിലേക്ക് നോക്കുക."</string>
+ <string name="face_acquired_not_detected" msgid="5707782294589511391">"മുഖം കണ്ടെത്താനായില്ല."</string>
+ <string name="face_acquired_not_steady" msgid="3722829465011040042">"ഉപകരണത്തിന് മുന്നിൽ മുഖം ഇളകാതെ നേരെ നിറുത്തുക."</string>
+ <string-array name="face_acquired_vendor">
+ </string-array>
+ <string name="face_error_hw_not_available" msgid="6255891785768984615">"മുഖത്തിന്റെ ഹാർഡ്വെയർ ലഭ്യമല്ല."</string>
+ <string name="face_error_timeout" msgid="4014326147867150054">"മുഖം നൽകേണ്ട സമയം കഴിഞ്ഞു. വീണ്ടും ശ്രമിക്കുക."</string>
+ <string name="face_error_no_space" msgid="8224993703466381314">"മുഖം സൂക്ഷിക്കാനാവില്ല."</string>
+ <string name="face_error_canceled" msgid="283945501061931023">"മുഖത്തിന്റെ പ്രവർത്തനം റദ്ദാക്കി."</string>
+ <string name="face_error_lockout" msgid="3407426963155388504">"നിരവധി തവണ ശ്രമിച്ചു. പിന്നീട് വീണ്ടും ശ്രമിക്കുക."</string>
+ <string name="face_error_lockout_permanent" msgid="8198354656746088890">"നിരവധി തവണ ശ്രമിച്ചു. മുഖം തിരിച്ചറിയൽ പ്രവർത്തനരഹിതമാക്കി."</string>
+ <string name="face_error_unable_to_process" msgid="238761109287767270">"വീണ്ടും ശ്രമിക്കുക."</string>
+ <string name="face_error_not_enrolled" msgid="9166792142679691323">"ഒരു മുഖവും എൻറോൾ ചെയ്തിട്ടില്ല."</string>
+ <string name="face_error_hw_not_present" msgid="4737289254517095671">"ഈ ഉപകരണത്തിന് മുഖം തിരിച്ചറിയാനാവുന്ന സെൻസർ ഇല്ല"</string>
+ <string name="face_name_template" msgid="7004562145809595384">"മുഖം <xliff:g id="FACEID">%d</xliff:g>"</string>
+ <string-array name="face_error_vendor">
+ </string-array>
+ <string name="face_icon_content_description" msgid="4024817159806482191">"മുഖത്തിന്റെ ഐക്കൺ"</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"സമന്വയ ക്രമീകരണങ്ങൾ റീഡുചെയ്യുക"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"ഒരു അക്കൗണ്ടിനായി സമന്വയ ക്രമീകരണങ്ങൾ റീഡുചെയ്യാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു. ഉദാഹരണത്തിന്, ആളുകൾ അപ്ലിക്കേഷൻ ഒരു അക്കൗണ്ടിൽ സമന്വയിപ്പിച്ചിട്ടുണ്ടോയെന്നത് നിർണ്ണയിക്കാൻ ഇതിനാകും."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"സമന്വയം ഓണാക്കുക, ഓഫാക്കുക ടോഗിൾചെയ്യുക"</string>
@@ -1178,6 +1208,9 @@
<skip />
<string name="wifi_no_internet" msgid="8938267198124654938">"വൈഫൈയ്ക്ക് ഇന്റർനെറ്റ് ആക്സസ് ഇല്ല"</string>
<string name="wifi_no_internet_detailed" msgid="8083079241212301741">"ഓപ്ഷനുകൾക്ക് ടാപ്പുചെയ്യുക"</string>
+ <string name="wifi_softap_config_change" msgid="8475911871165857607">"നിങ്ങളുടെ ഹോട്ട്സ്പോട്ട് ക്രമീകരണത്തിൽ വരുത്തിയ മാറ്റങ്ങൾ"</string>
+ <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"നിങ്ങളുടെ ഹോട്ട്സ്പോട്ട് ബാൻഡ് മാറി."</string>
+ <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"നിങ്ങളുടെ മുൻഗണനയനുസരിച്ചുള്ള, 5GHz മാത്രം എന്നത് ഈ ഉപകരണം പിന്തുണയ്ക്കുന്നില്ല. പകരം, 5GHz ബാൻഡ് ലഭ്യമാകുമ്പോൾ അത് ഉപയോഗിക്കും."</string>
<string name="network_switch_metered" msgid="4671730921726992671">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> എന്നതിലേക്ക് മാറി"</string>
<string name="network_switch_metered_detail" msgid="775163331794506615">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>-ന് ഇന്റർനെറ്റ് ആക്സസ് ഇല്ലാത്തപ്പോൾ ഉപകരണം <xliff:g id="NEW_NETWORK">%1$s</xliff:g> ഉപയോഗിക്കുന്നു. നിരക്കുകൾ ബാധകമായേക്കാം."</string>
<string name="network_switch_metered_toast" msgid="5779283181685974304">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> നെറ്റ്വർക്കിൽ നിന്ന് <xliff:g id="NEW_NETWORK">%2$s</xliff:g> നെറ്റ്വർക്കിലേക്ക് മാറി"</string>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index 96975ca..2a30fcb 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -236,9 +236,6 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Нислэгийн горим"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Нислэгийн горим асав"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Нислэгийн горим унтарсан"</string>
- <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Батарей хэмнэгч"</string>
- <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Тэжээл хэмнэгч УНТРААЛТТАЙ байна"</string>
- <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Тэжээл хэмнэгч АСААЛТТАЙ байна"</string>
<string name="global_action_settings" msgid="1756531602592545966">"Тохиргоо"</string>
<string name="global_action_assist" msgid="3892832961594295030">"Туслах"</string>
<string name="global_action_voice_assist" msgid="7751191495200504480">"Дуут туслах"</string>
@@ -276,6 +273,9 @@
<string name="permgrouplab_location" msgid="7275582855722310164">"Байршил"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"энэ төхөөрөмжийн байршилд хандалт хийх"</string>
<string name="permgrouprequest_location" msgid="3788275734953323491">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>-д энэ төхөөрөмжийн байршилд хандахыг зөвшөөрөх үү?"</string>
+ <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"Та аппыг зөвхөн хэрэглэж байгаа үед апп нь байршилд хандах болно."</string>
+ <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>-д энэ төхөөрөмжийн байршилд хандахыг байнга зөвшөөрөх үү?"</string>
+ <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"Та энэ аппыг хэрэглээгүй байсан ч апп нь байршилд үргэлж хандах болно."</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Хуанли"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"Хуанли руу хандах"</string>
<string name="permgrouprequest_calendar" msgid="289900767793189421">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>-д таны календарьт хандахыг зөвшөөрөх үү?"</string>
@@ -402,8 +402,7 @@
<string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Энэ апп таны утсанд хуанлийн арга хэмжээг нэмэх, устгах, эсвэл өөрчлөх боломжтой. Энэ апп нь хуанли эзэмшигчээс зурвас илгээсэн мэт харагдах, эсвэл эзэмшигчид мэдэгдэлгүйгээр арга хэмжээг өөрчлөх боломжтой."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"байршил нийлүүлэгчийн нэмэлт тушаалд хандах"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Апп нь байршил нийлүүлэгчийн нэмэлт тушаалд хандах боломжтой. Энэ нь апп-д GPS эсвэл бусад байршлын үйлчилгээний ажиллагаанд нөлөөлөх боломжийг олгоно."</string>
- <!-- no translation found for permlab_accessFineLocation (6265109654698562427) -->
- <skip />
+ <string name="permlab_accessFineLocation" msgid="6265109654698562427">"нарийвчилсан байршилд зөвхөн нүүр хэсэгт хандах"</string>
<string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Энэ апп нь зөвхөн нүүр хэсэгт байх үедээ л таны байршлыг нарийн тогтоох боломжтой. Апп эдгээр байршлын үйлчилгээг ашиглахын тулд эдгээрийг таны утсан дээр асааж идэвхтэй байлгах шаардлагатай. Энэ нь батарейны хэрэглээг ихэсгэж болзошгүй."</string>
<string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"ойролцоох байршилд хандах (сүлжээнд суурилсан)"</string>
<string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Энэ апп үүрэн цамхаг, Wi-Fi сүлжээ зэрэг сүлжээний байршлын эх сурвалжийг ашиглан таны байршлыг мэдэх боломжтой. Эдгээр байршлын үйлчилгээ нь таны таблетад асаалттай байх ёстой ба апп ашиглах боломжтой байх ёстой."</string>
@@ -518,6 +517,37 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Хурууны хээний дүрс"</string>
+ <string name="permlab_manageFace" msgid="2137540986007309781">"царай танилтын техник хангамжийг удирдах"</string>
+ <string name="permdesc_manageFace" msgid="8919637120670185330">"Аппад царайны загварыг ашиглахын тулд нэмэх эсвэл устгах аргыг идэвхжүүлэхийг зөвшөөрдөг."</string>
+ <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"царай танилтын техник хангамжийг ашиглах"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Аппад царай танилтын техник хангамжийг баталгаажуулалтад ашиглахыг зөвшөөрдөг"</string>
+ <string name="face_acquired_insufficient" msgid="5901287247766106330">"Царайг таньж чадсангүй. Дахин оролдоно уу."</string>
+ <string name="face_acquired_too_bright" msgid="610606792381297174">"Царай хэт цайвар байна. Гэрэл багатай газар оролдож үзнэ үү."</string>
+ <string name="face_acquired_too_dark" msgid="7229162716976778371">"Царай хэт бараан байна. Гэрэлтэй газар туршиж үзнэ үү."</string>
+ <string name="face_acquired_too_close" msgid="1980310037427755293">"Мэдрэгчийг царайнаас холдуулна уу."</string>
+ <string name="face_acquired_too_far" msgid="4494571381828850007">"Mэдрэгчийг царайтай ойртуулна уу."</string>
+ <string name="face_acquired_too_high" msgid="228411096134808372">"Мэдрэгчийг дээшлүүлнэ үү."</string>
+ <string name="face_acquired_too_low" msgid="4539774649296349109">"Mэдрэгчийг доошлуулна уу."</string>
+ <string name="face_acquired_too_right" msgid="1650292067226118760">"Мэдрэгчийг баруун тийш болгоно уу."</string>
+ <string name="face_acquired_too_left" msgid="2712489669456176505">"Мэдрэгчийг зүүн тийш болгоно уу."</string>
+ <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Мэдрэгч рүү харна уу."</string>
+ <string name="face_acquired_not_detected" msgid="5707782294589511391">"Царай олдсонгүй."</string>
+ <string name="face_acquired_not_steady" msgid="3722829465011040042">"Царайг төхөөрөмжийн урд талд хөдөлгөөнгүй байлгана уу."</string>
+ <string-array name="face_acquired_vendor">
+ </string-array>
+ <string name="face_error_hw_not_available" msgid="6255891785768984615">"Царайны техник хангамж боломжгүй байна."</string>
+ <string name="face_error_timeout" msgid="4014326147867150054">"Царай таниулах хугацаа дууслаа. Дахин оролдоно уу."</string>
+ <string name="face_error_no_space" msgid="8224993703466381314">"Царайг хадгалах боломжгүй байна."</string>
+ <string name="face_error_canceled" msgid="283945501061931023">"Царайны үйл ажиллагааг цуцаллаа."</string>
+ <string name="face_error_lockout" msgid="3407426963155388504">"Хэт олон удаа оролдлоо. Дараа дахин оролдоно уу."</string>
+ <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Хэт олон удаа оролдлоо. Царай танилтыг идэвхгүй болголоо."</string>
+ <string name="face_error_unable_to_process" msgid="238761109287767270">"Дахин оролдоно уу."</string>
+ <string name="face_error_not_enrolled" msgid="9166792142679691323">"Бүртгүүлсэн царай алга."</string>
+ <string name="face_error_hw_not_present" msgid="4737289254517095671">"Энэ төхөөрөмжид царай таних мэдрэгч алга"</string>
+ <string name="face_name_template" msgid="7004562145809595384">"Царай <xliff:g id="FACEID">%d</xliff:g>"</string>
+ <string-array name="face_error_vendor">
+ </string-array>
+ <string name="face_icon_content_description" msgid="4024817159806482191">"Царайны дүрс тэмдэг"</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"синк тохиргоог унших"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Апп нь бүртгэлийн синк тохиргоог унших боломжтой. Жишээ нь энэ нь Хүмүүс апп бүртгэлтэй синк хийгдсэн эсэхийг тодорхойлох боломжтой."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"синкийг унтрааж асаах тохиргоо"</string>
@@ -1178,6 +1208,9 @@
<skip />
<string name="wifi_no_internet" msgid="8938267198124654938">"Wi-Fi-д интернет хандалт алга"</string>
<string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Сонголт хийхийн тулд товшино уу"</string>
+ <string name="wifi_softap_config_change" msgid="8475911871165857607">"Таны сүлжээний цэгийн тохиргооны өөрчлөлт"</string>
+ <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"Таны сүлжээний цэгийн зурвасыг өөрчилсөн."</string>
+ <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"Энэ төхөөрөмж таны \"зөвхөн 5Гц\" гэсэн давуу сонголтыг дэмждэггүй. Үүний оронд энэ төхөөрөмж 5Гц зурвасыг боломжтой үед нь ашиглах болно."</string>
<string name="network_switch_metered" msgid="4671730921726992671">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> руу шилжүүлсэн"</string>
<string name="network_switch_metered_detail" msgid="775163331794506615">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> интернет холболтгүй үед төхөөрөмж <xliff:g id="NEW_NETWORK">%1$s</xliff:g>-г ашигладаг. Төлбөр гарч болзошгүй."</string>
<string name="network_switch_metered_toast" msgid="5779283181685974304">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g>-с <xliff:g id="NEW_NETWORK">%2$s</xliff:g> руу шилжүүлсэн"</string>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index fd7a67cc..27f6477 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -236,9 +236,6 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"विमान मोड"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"विमान मोड चालू आहे"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"विमान मोड बंद आहे"</string>
- <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"बॅटरी बचतकर्ता"</string>
- <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"बॅटरी बचतकर्ता बंद आहे"</string>
- <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"बॅटरी बचतकर्ता सुरू आहे"</string>
<string name="global_action_settings" msgid="1756531602592545966">"सेटिंग्ज"</string>
<string name="global_action_assist" msgid="3892832961594295030">"सहाय्यता"</string>
<string name="global_action_voice_assist" msgid="7751191495200504480">"व्हॉइस सहाय्य"</string>
@@ -276,6 +273,9 @@
<string name="permgrouplab_location" msgid="7275582855722310164">"स्थान"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"या डिव्हाइसच्या स्थानावर प्रवेश"</string>
<string name="permgrouprequest_location" msgid="3788275734953323491">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ला या डिव्हाइसचे स्थान अॅक्सेस करू द्यायचे?"</string>
+ <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"तुम्ही अॅप वापरत असताना, अॅपला फक्त स्थानाचा अॅक्सेस असेल."</string>
+ <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"या डिव्हाइसचे स्थान अॅक्सेस करण्याची <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ला नेहेमी अनुमती द्यायची का?"</string>
+ <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"तुम्ही अॅप वापरत नसलात तरीही, अॅपला स्थानाचा नेहेमी अॅक्सेस असेल."</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"कॅलेंडर"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"आपल्या कॅलेंडरवर प्रवेश"</string>
<string name="permgrouprequest_calendar" msgid="289900767793189421">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ला तुमचे कॅलेंडर अॅक्सेस करू द्यायचे?"</string>
@@ -402,8 +402,7 @@
<string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"हा अॅप आपल्या फोनवर कॅलेंडर इव्हेंट जोडू, काढू किंवा बदलू शकतो. हा अॅप कॅलेंडर मालकांकडून येत आहेत असे वाटणारे मेसेज पाठवू किंवा त्यांच्या मालकांना सूचित केल्याशिवाय इव्हेंट बदलू शकतो."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"अतिरिक्त स्थान प्रदाता आदेश अॅक्सेस करा"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"अॅपला अतिरिक्त स्थान प्रदाता आदेशावर प्रवेश करण्याची अनुमती देते. हे कदाचित अॅपला GPS किंवा इतर स्थान स्त्रोत च्या ऑपरेशनमध्ये हस्तक्षेप करण्याची अनुमती देऊ शकते."</string>
- <!-- no translation found for permlab_accessFineLocation (6265109654698562427) -->
- <skip />
+ <string name="permlab_accessFineLocation" msgid="6265109654698562427">"फक्त फोरग्राउंडमध्ये अचूकपणे अॅक्सेस करा"</string>
<string name="permdesc_accessFineLocation" msgid="3520508381065331098">"हे अॅप फक्त फोरग्राउंडमध्ये असतानाच तुमचे अचूक स्थान मिळवू शकते. या स्थान सेवा सुरू करणे आणि त्या वापरण्यासाठी अॅपसाठी तुमच्या फोनवर उपलब्ध करणे आवश्यक आहे, यामुळे बॅटरी वापर वाढू शकतो."</string>
<string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"अंदाजे स्थानामध्ये (नेटवर्क-आधारित) अॅक्सेस करा"</string>
<string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"हा अॅप सेल टॉवर आणि वाय-फाय नेटवर्क सारख्या नेटवर्क स्रोतांच्या आधारावर तुमचे स्थान मिळवू शकतो. अॅपला या स्थान सेवा वापरण्यास सक्षम असण्यासाठी तुमच्या टॅब्लेटवर त्या चालू केलेल्या आणि उपलब्ध असणे आवश्यक आहे."</string>
@@ -518,6 +517,37 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"फिंगरप्रिंट आयकन"</string>
+ <string name="permlab_manageFace" msgid="2137540986007309781">"चेहरा ऑथेंटिकेशन हार्डवेअर व्यवस्थापित करा"</string>
+ <string name="permdesc_manageFace" msgid="8919637120670185330">"अॅपला वापरासाठी चेहरा टेम्पलेट जोडण्याच्या आणि हटवण्याच्या पद्धती जारी करू देते."</string>
+ <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"चेहरा ऑथेंटिकेशन हार्डवेअर वापरा"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"अॅपला चेहरा ऑथेंटिकेशनसाठी ऑथेंटिकेशन हार्डवेअर वापरू देते"</string>
+ <string name="face_acquired_insufficient" msgid="5901287247766106330">"चेहऱ्यावर प्रक्रिया झाली नाही. पुन्हा प्रयत्न करा."</string>
+ <string name="face_acquired_too_bright" msgid="610606792381297174">"चेहऱ्यावर खूप प्रकाश आहे. कृपया कमी प्रकाशात प्रयत्न करा."</string>
+ <string name="face_acquired_too_dark" msgid="7229162716976778371">"चेहऱ्यावर खूप अंधार आहे. कृपया प्रकाश स्रोत खुला करा."</string>
+ <string name="face_acquired_too_close" msgid="1980310037427755293">"कृपया सेन्सर चेहऱ्यापासून आणखी दूर हलवा."</string>
+ <string name="face_acquired_too_far" msgid="4494571381828850007">"कृपया सेन्सर चेहऱ्याच्या आणखी जवळ आणा."</string>
+ <string name="face_acquired_too_high" msgid="228411096134808372">"कृपया सेन्सर आणखी वर हलवा."</string>
+ <string name="face_acquired_too_low" msgid="4539774649296349109">"कृपया सेन्सर आणखी खाली हलवा."</string>
+ <string name="face_acquired_too_right" msgid="1650292067226118760">"कृपया सेन्सर उजवीकडे हलवा."</string>
+ <string name="face_acquired_too_left" msgid="2712489669456176505">"कृपया सेन्सर डावीकडे हलवा."</string>
+ <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"कृपया सेन्सरकडे पहा."</string>
+ <string name="face_acquired_not_detected" msgid="5707782294589511391">"चेहरा आढळला नाही."</string>
+ <string name="face_acquired_not_steady" msgid="3722829465011040042">"डिव्हाइसच्या समोर चेहरा स्थिर ठेवा."</string>
+ <string-array name="face_acquired_vendor">
+ </string-array>
+ <string name="face_error_hw_not_available" msgid="6255891785768984615">"चेहरा हार्डवेअर उपलब्ध नाही."</string>
+ <string name="face_error_timeout" msgid="4014326147867150054">"चेहरा टाइमआउट झाला. पुन्हा प्रयत्न करा."</string>
+ <string name="face_error_no_space" msgid="8224993703466381314">"चेहरा स्टोअर केला जाऊ शकत नाही."</string>
+ <string name="face_error_canceled" msgid="283945501061931023">"चेहरा ऑपरेशन रद्द केले गेले."</string>
+ <string name="face_error_lockout" msgid="3407426963155388504">"खूप जास्त प्रयत्न केले. नंतर पुन्हा प्रयत्न करा."</string>
+ <string name="face_error_lockout_permanent" msgid="8198354656746088890">"खूप जास्त प्रयत्न केले. चेहरा ऑथेंटिकेशन बंद केले गेले."</string>
+ <string name="face_error_unable_to_process" msgid="238761109287767270">"पुन्हा प्रयत्न करा."</string>
+ <string name="face_error_not_enrolled" msgid="9166792142679691323">"चेहरा नोंदवलेला नाही."</string>
+ <string name="face_error_hw_not_present" msgid="4737289254517095671">"या डिव्हाइसमध्ये चेहरा ऑथेंटिकेशन सेन्सर नाही"</string>
+ <string name="face_name_template" msgid="7004562145809595384">"चेहरा <xliff:g id="FACEID">%d</xliff:g>"</string>
+ <string-array name="face_error_vendor">
+ </string-array>
+ <string name="face_icon_content_description" msgid="4024817159806482191">"चेहरा आयकन"</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"सिंक सेटिंग्ज वाचा"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"खात्याच्या सिंक सेटिंग्ज वाचण्यासाठी अॅप ला अनुमती देते. उदाहरणार्थ, हे खात्यासह लोकांचा अॅप संकालित केला आहे किंवा नाही हे निर्धारित करू शकते."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"सिंक चालू आणि बंद करा टॉगल करा"</string>
@@ -1178,6 +1208,9 @@
<skip />
<string name="wifi_no_internet" msgid="8938267198124654938">"वाय-फाय ला इंटरनेटचा अॅक्सेस नाही"</string>
<string name="wifi_no_internet_detailed" msgid="8083079241212301741">"पर्यायांसाठी टॅप करा"</string>
+ <string name="wifi_softap_config_change" msgid="8475911871165857607">"तुमच्या हॉटस्पॉट सेटिंग्जमधील बदल"</string>
+ <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"तुमचा हॉटस्पॉट बँड बदलला आहे."</string>
+ <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"हे डिव्हाइस तुमच्या फक्त ५GHz साठी प्राधान्याला सपोर्ट करत नाही. त्याऐवजी, हे डिव्हाइस ५GHz बँड उपलब्ध असताना वापरेल."</string>
<string name="network_switch_metered" msgid="4671730921726992671">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> वर स्विच केले"</string>
<string name="network_switch_metered_detail" msgid="775163331794506615">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> कडे इंटरनेटचा अॅक्सेस नसताना डिव्हाइस <xliff:g id="NEW_NETWORK">%1$s</xliff:g> वापरते. शुल्क लागू शकते."</string>
<string name="network_switch_metered_toast" msgid="5779283181685974304">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> वरून <xliff:g id="NEW_NETWORK">%2$s</xliff:g> वर स्विच केले"</string>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index eba3d296..b2f10c1 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -236,9 +236,6 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Mod pesawat"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Mod Pesawat DIHIDUPKAN"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Mod Pesawat DIMATIKAN"</string>
- <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Penjimat bateri"</string>
- <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Penjimat bateri DIMATIKAN"</string>
- <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Penjimat Bateri DIHIDUPKAN"</string>
<string name="global_action_settings" msgid="1756531602592545966">"Tetapan"</string>
<string name="global_action_assist" msgid="3892832961594295030">"Bantu"</string>
<string name="global_action_voice_assist" msgid="7751191495200504480">"Bantuan Suara"</string>
@@ -276,6 +273,9 @@
<string name="permgrouplab_location" msgid="7275582855722310164">"Lokasi"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"mengakses lokasi peranti ini"</string>
<string name="permgrouprequest_location" msgid="3788275734953323491">"Benarkan <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> mengakses lokasi peranti ini?"</string>
+ <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"Apl ini hanya dapat mengakses lokasi semasa anda menggunakan apl tersebut."</string>
+ <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"Sentiasa benarkan <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> mengakses lokasi peranti ini?"</string>
+ <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"Apl ini akan dapat mengakses lokasi pada sepanjang masa, meskipun apabila anda tidak menggunakan apl tersebut."</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Kalendar"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"mengakses kalendar"</string>
<string name="permgrouprequest_calendar" msgid="289900767793189421">"Benarkan <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> mengakses kalendar anda?"</string>
@@ -402,8 +402,7 @@
<string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Apl ini boleh menambah, mengalih keluar atau mengubah acara kalendar pada telefon anda. Apl ini boleh menghantar mesej yang mungkin kelihatan seperti dihantar oleh pemilik kalendar atau mengubah acara tanpa memaklumi pemilik acara itu."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"akses perintah tambahan pembekal lokasi"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Membenarkan apl mengakses arahan pembekal lokasi tambahan. Ini boleh membenarkan apl untuk campur tangan dengan operasi GPS atau sumber lokasi lain."</string>
- <!-- no translation found for permlab_accessFineLocation (6265109654698562427) -->
- <skip />
+ <string name="permlab_accessFineLocation" msgid="6265109654698562427">"akses lokasi tepat hanya di latar depan"</string>
<string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Apl ini boleh mendapatkan lokasi tepat anda hanya apabila apl tersebut berada di latar depan. Perkhidmatan lokasi ini mesti dihidupkan dan tersedia pada telefon anda untuk membolehkan apl menggunakan perkhidmatan tersebut. Tindakan ini mungkin meningkatkan penggunaan bateri."</string>
<string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"akses lokasi anggaran (berasaskan rangkaian)"</string>
<string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Apl ini boleh mendapatkan lokasi anda berdasarkan sumber rangkaian seperti menara selular dan rangkaian Wi-Fi. Perkhidmatan lokasi ini mesti dihidupkan dan tersedia pada tablet anda untuk membolehkan apl menggunakan perkhidmatan tersebut."</string>
@@ -518,6 +517,37 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Ikon cap jari"</string>
+ <string name="permlab_manageFace" msgid="2137540986007309781">"urus perkakasan pengesahan wajah"</string>
+ <string name="permdesc_manageFace" msgid="8919637120670185330">"Membenarkan apl menggunakan kaedah untuk menambahkan dan memadamkan templat wajah untuk digunakan."</string>
+ <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"gunakan perkakasan pengesahan wajah"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Membenarkan apl menggunakan perkakasan pengesahan wajah untuk pengesahan"</string>
+ <string name="face_acquired_insufficient" msgid="5901287247766106330">"Tidak dapat memproses wajah. Sila cuba lagi."</string>
+ <string name="face_acquired_too_bright" msgid="610606792381297174">"Wajah terlalu cerah. Cuba dlm cahaya lebih rendah."</string>
+ <string name="face_acquired_too_dark" msgid="7229162716976778371">"Wajah terlalu gelap. Sila buka sumber cahaya."</string>
+ <string name="face_acquired_too_close" msgid="1980310037427755293">"Sila alihkan penderia lebih jauh dari wajah."</string>
+ <string name="face_acquired_too_far" msgid="4494571381828850007">"Sila dekatkan penderia ke wajah."</string>
+ <string name="face_acquired_too_high" msgid="228411096134808372">"Sila alihkan penderia ke kedudukan lebih tinggi."</string>
+ <string name="face_acquired_too_low" msgid="4539774649296349109">"Sila alihkan penderia ke kedudukan lebih rendah."</string>
+ <string name="face_acquired_too_right" msgid="1650292067226118760">"Sila alihkan penderia ke kanan."</string>
+ <string name="face_acquired_too_left" msgid="2712489669456176505">"Sila alihkan penderia ke kiri."</string>
+ <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Sila lihat penderia."</string>
+ <string name="face_acquired_not_detected" msgid="5707782294589511391">"Tiada wajah dikesan."</string>
+ <string name="face_acquired_not_steady" msgid="3722829465011040042">"Pastikan wajah tidak bergerak di hadapan peranti."</string>
+ <string-array name="face_acquired_vendor">
+ </string-array>
+ <string name="face_error_hw_not_available" msgid="6255891785768984615">"Perkakasan wajah tidak tersedia."</string>
+ <string name="face_error_timeout" msgid="4014326147867150054">"Tamat masa wajah dicapai. Cuba lagi."</string>
+ <string name="face_error_no_space" msgid="8224993703466381314">"Wajah tidak dapat disimpan."</string>
+ <string name="face_error_canceled" msgid="283945501061931023">"Pengendalian wajah dibatalkan."</string>
+ <string name="face_error_lockout" msgid="3407426963155388504">"Terlalu banyak percubaan. Cuba sebentar lagi."</string>
+ <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Terlalu banyak percubaan. Pengesahan wajah dilumpuhkan."</string>
+ <string name="face_error_unable_to_process" msgid="238761109287767270">"Cuba lagi."</string>
+ <string name="face_error_not_enrolled" msgid="9166792142679691323">"Tiada wajah didaftarkan."</string>
+ <string name="face_error_hw_not_present" msgid="4737289254517095671">"Peranti ini tiada penderia pengesahan wajah"</string>
+ <string name="face_name_template" msgid="7004562145809595384">"Wajah <xliff:g id="FACEID">%d</xliff:g>"</string>
+ <string-array name="face_error_vendor">
+ </string-array>
+ <string name="face_icon_content_description" msgid="4024817159806482191">"Ikon wajah"</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"membaca tetapan penyegerakan"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Membenarkan apl membaca tetapan segerak untuk akaun. Sebagai contoh, ini boleh menentukan sama ada apl Orang disegerakkan dengan akaun."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"togol segerak hidup dan mati"</string>
@@ -1178,6 +1208,9 @@
<skip />
<string name="wifi_no_internet" msgid="8938267198124654938">"Wi-Fi tiada akses Internet"</string>
<string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Ketik untuk mendapatkan pilihan"</string>
+ <string name="wifi_softap_config_change" msgid="8475911871165857607">"Perubahan kepada tetapan tempat liputan anda"</string>
+ <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"Jalur tempat liputan anda telah berubah."</string>
+ <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"Peranti ini tidak menyokong pilihan anda untuk 5GHz sahaja. Sebaliknya, peranti ini akan menggunakan jalur 5GHz apabila tersedia."</string>
<string name="network_switch_metered" msgid="4671730921726992671">"Beralih kepada <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
<string name="network_switch_metered_detail" msgid="775163331794506615">"Peranti menggunakan <xliff:g id="NEW_NETWORK">%1$s</xliff:g> apabila <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> tiada akses Internet. Bayaran mungkin dikenakan."</string>
<string name="network_switch_metered_toast" msgid="5779283181685974304">"Beralih daripada <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> kepada <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index 87ef1e5a..f311ab3 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -236,9 +236,6 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"လေယာဥ်ပျံပေါ်အသုံးပြုသောစနစ်"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"လေယဥ်ပျံပေါ်၌အသုံးပြုသောစနစ်ဖွင့်ထားသည်"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"လေယဥ်ပျံပေါ်၌အသုံးပြုသောစနစ်ပိတ်ထားသည်"</string>
- <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"ဘက်ထရီ ချွေတာမှုစနစ်"</string>
- <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"ဘက်ထရီချွေတာခြင်းမုဒ်ကို ပိတ်ထားသည်"</string>
- <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"ဘက်ထရီချွေတာခြင်းမုဒ်ကို ဖွင့်ထားသည်"</string>
<string name="global_action_settings" msgid="1756531602592545966">"ဆက်တင်များ"</string>
<string name="global_action_assist" msgid="3892832961594295030">"အကူအညီ"</string>
<string name="global_action_voice_assist" msgid="7751191495200504480">"အသံ အကူအညီ"</string>
@@ -276,6 +273,9 @@
<string name="permgrouplab_location" msgid="7275582855722310164">"တည်နေရာ"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"ဤစက်ပစ္စည်း၏ တည်နေရာကို ရယူရန်"</string>
<string name="permgrouprequest_location" msgid="3788275734953323491">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> အား ဤစက်ပစ္စည်း၏တည်နေရာကို သုံးခွင့်ပေးလိုပါသလား။"</string>
+ <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"အက်ပ်ကိုအသုံးပြုသည့် အချိန်တွင်သာ ၎င်းကတည်နေရာကို အသုံးပြုခွင့်ရပါမည်။"</string>
+ <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> အား ဤစက်ပစ္စည်း၏တည်နေရာကို အမြဲ အသုံးပြုခွင့်ပေးလိုပါသလား။"</string>
+ <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"သင် အက်ပ်ကိုအသုံးပြုမနေသော်လည်း ၎င်းကတည်နေရာကို အမြဲ အသုံးပြုခွင့်ရနေပါမည်။"</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"ပြက္ခဒိန်"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"သင့်ပြက္ခဒိန်အား ဝင်ရောက်သုံးရန်"</string>
<string name="permgrouprequest_calendar" msgid="289900767793189421">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> အား သင်၏ပြက္ခဒိန်ကို သုံးခွင့်ပေးလိုပါသလား။"</string>
@@ -402,8 +402,7 @@
<string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"ဤအက်ပ်သည် သင့်ဖုန်းပေါ်ရှိ ပြက္ခဒိန်ဖြစ်ရပ်များကို ထည့်ခြင်း၊ ဖယ်ရှားခြင်း သို့မဟုတ် ပြောင်းလဲခြင်းတို့ ပြုလုပ်နိုင်ပါသည်။ ဤအက်ပ်သည် ပြက္ခဒိန်ပိုင်ရှင်များမှ လာခြင်းဖြစ်နိုင်သည့် မက်ဆေ့ဂျ်များကို ပို့နိုင်ပြီး ပိုင်ရှင်များကို အသိမပေးဘဲ ဖြစ်ရပ်များကို ပြောင်းလဲနိုင်ပါသည်။"</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"တည်နေရာပံ့ပိုးမှုညွှန်ကြားချက်အပိုအား ဝင်ရောက်ကြည့်ခြင်း"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"အက်ပ်အား တည်နေရာ စီမံပေးရေး ညွှန်ကြားချက် အပိုများကို ရယူခွင့်ပြုသည်။ သို့ဖြစ်၍ အက်ပ်သည် GPS သို့မဟုတ် အခြား တည်နေရာ ရင်းမြစ်ကို သုံးကြသူတို့၏ လုပ်ငန်းများကို ဝင်စွက်ခွင့် ပြုနိုင်သည်။"</string>
- <!-- no translation found for permlab_accessFineLocation (6265109654698562427) -->
- <skip />
+ <string name="permlab_accessFineLocation" msgid="6265109654698562427">"မျက်နှာစာတွင်သာ တည်နေရာအတိအကျ အသုံးပြုခြင်း"</string>
<string name="permdesc_accessFineLocation" msgid="3520508381065331098">"မျက်နှာစာတွင် ဖွင့်ထားမှသာ ဤအက်ပ်က သင်၏တည်နေရာအတိအကျကို ရယူနိုင်ပါသည်။ သင်၏ဖုန်းတွင် အက်ပ်ကအသုံးပြုရန်အတွက် ဤတည်နေရာဝန်ဆောင်မှုများကို ဖွင့်ထားပြီး အသုံးပြု၍ ရပါမည်။ ၎င်းက ဘက်ထရီ ပိုကုန်နိုင်ပါသည်။"</string>
<string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"အနီးစပ်ဆုံး တည်နေရာ (ကွန်ရက် အခြေခံ)ကို ရယူသုံးရန်"</string>
<string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"ဤအက်ပ်သည် ဆဲလ်တာဝါများနှင့် Wi-Fi ကွန်ရက်များကဲ့သို့ ကွန်ရက်အရင်းအမြစ်ပေါ် အခြေခံပြီး သင့်တည်နေရာအချက်အလက်ကို ရယူနိုင်ပါသည်။ အက်ပ်က အသုံးပြုနိုင်ရန်အတွက် ဤတည်နေရာ ဝန်ဆောင်မှုများကို ဖွင့်ထားရမည် ဖြစ်ပြီး သင့်တက်ဘလက်ပေါ်တွင် ရရှိနိုင်ရပါမည်။"</string>
@@ -518,6 +517,37 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"လက်ဗွေ အိုင်ကွန်"</string>
+ <string name="permlab_manageFace" msgid="2137540986007309781">"မျက်နှာအထောက်အထားစိစစ်ခြင်း စက်ပစ္စည်းကို စီမံပါ"</string>
+ <string name="permdesc_manageFace" msgid="8919637120670185330">"အသုံးပြုရန်အတွက် မျက်နှာပုံစံထည့်ရန် (သို့) ဖျက်ရန်နည်းလမ်းကို အက်ပ်အား သုံးခွင့်ပြုသည်။"</string>
+ <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"မျက်နှာအထောက်အထားစိစစ်ခြင်း စက်ပစ္စည်းကို သုံးပါ"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"အထောက်အထားစိစစ်ရန်အတွက် ဤအက်ပ်အား မျက်နှာအထောက်အထားစိစစ်ခြင်း စက်ပစ္စည်းကိုသုံးခွင့်ပြုသည်"</string>
+ <string name="face_acquired_insufficient" msgid="5901287247766106330">"မျက်နှာကို မမှတ်မိပါ။ ထပ်လုပ်ကြည့်ပါ။"</string>
+ <string name="face_acquired_too_bright" msgid="610606792381297174">"မျက်နှာအလွန်လင်းနေသည်။ အလင်းလျှော့ပြီး စမ်းကြည့်ပါ။"</string>
+ <string name="face_acquired_too_dark" msgid="7229162716976778371">"မျက်နှာအလွန်မှောင်နေသည်။ ပြတင်းပေါက်ဖွင့်လိုက်ပါ။"</string>
+ <string name="face_acquired_too_close" msgid="1980310037427755293">"အာရုံခံကိရိယာကို မျက်နှာနှင့် ခွာလိုက်ပါ။"</string>
+ <string name="face_acquired_too_far" msgid="4494571381828850007">"အာရုံခံကိရိယာကို မျက်နှာအနီး ရွှေ့လိုက်ပါ။"</string>
+ <string name="face_acquired_too_high" msgid="228411096134808372">"အာရုံခံကိရိယာကို ပိုမြင့်အောင်ထားပါ။"</string>
+ <string name="face_acquired_too_low" msgid="4539774649296349109">"အာရုံခံကိရိယာကို ပိုနိမ့်အောင်ထားပါ။"</string>
+ <string name="face_acquired_too_right" msgid="1650292067226118760">"အာရုံခံကိရိယာကို ညာဘက်သို့ ရွှေ့ပါ။"</string>
+ <string name="face_acquired_too_left" msgid="2712489669456176505">"အာရုံခံကိရိယာကို ဘယ်ဘက်သို့ ရွှေ့ပါ။"</string>
+ <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"အာရုံခံကိရိယာကို ကြည့်ပါ။"</string>
+ <string name="face_acquired_not_detected" msgid="5707782294589511391">"မျက်နှာတစ်ခုမျှ မတွေ့ပါ။"</string>
+ <string name="face_acquired_not_steady" msgid="3722829465011040042">"မျက်နှာကို စက်၏ရှေ့တွင် အဆင်သင့်ထားပါ။"</string>
+ <string-array name="face_acquired_vendor">
+ </string-array>
+ <string name="face_error_hw_not_available" msgid="6255891785768984615">"မျက်နှာ စက်ပစ္စည်း မရနိုင်ပါ။"</string>
+ <string name="face_error_timeout" msgid="4014326147867150054">"မျက်နှာ သက်တမ်းကုန်သွားပါပြီ။ ထပ်စမ်းကြည့်ပါ။"</string>
+ <string name="face_error_no_space" msgid="8224993703466381314">"မျက်နှာကို သိမ်း၍မရပါ။"</string>
+ <string name="face_error_canceled" msgid="283945501061931023">"မျက်နှာ ဆောင်ရွက်ခြင်းကို ပယ်ဖျက်လိုက်ပါပြီ။"</string>
+ <string name="face_error_lockout" msgid="3407426963155388504">"အကြိမ်များစွာ စမ်းပြီးပါပြီ။ နောက်မှထပ်စမ်းပါ။"</string>
+ <string name="face_error_lockout_permanent" msgid="8198354656746088890">"အကြိမ်များစွာ စမ်းပြီးပါပြီ။ ပိတ်လိုက်ပါပြီ။"</string>
+ <string name="face_error_unable_to_process" msgid="238761109287767270">"ထပ်စမ်းကြည့်ပါ။"</string>
+ <string name="face_error_not_enrolled" msgid="9166792142679691323">"စာရင်းသွင်းထားသည့် မျက်နှာတစ်ခုမျှ မရှိပါ။"</string>
+ <string name="face_error_hw_not_present" msgid="4737289254517095671">"ဤစက်တွင် မျက်နှာအထောက်အထားစိစစ်ခြင်း အာရုံခံကိရိယာမရှိပါ"</string>
+ <string name="face_name_template" msgid="7004562145809595384">"မျက်နှာ <xliff:g id="FACEID">%d</xliff:g>"</string>
+ <string-array name="face_error_vendor">
+ </string-array>
+ <string name="face_icon_content_description" msgid="4024817159806482191">"မျက်နှာသင်္ကေတ"</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"ထပ်တူပြုအဆင်အပြင်အားဖတ်ခြင်း"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"အပလီကေးရှင်းအား အကောင့်တစ်ခုအတွက် ထပ်တူညီအောင် လုပ်ဆောင်မှု ဆက်တင်အား ကြည့်ခွင့် ပြုပါ။ ဥပမာ People အက်ပ်က အကောင့်တစ်ခုနဲ့ ထပ်တူညီအောင် လုပ်ရန် ဆက်သွယ်ထားမှု ရှိမရှိ သိရှိနိုင်ခြင်း"</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"ထပ်တူညီအောင် လုပ်ခြင်းအား ပြုနိုင်၊ မပြုနိုင် အပြောင်းအလဲလုပ်ခြင်း"</string>
@@ -569,7 +599,7 @@
<string name="permlab_accessDrmCertificates" msgid="7436886640723203615">"DRM လက်မှတ်များကို ရယူသုံးခြင်း"</string>
<string name="permdesc_accessDrmCertificates" msgid="8073288354426159089">"အပလီကေးရှင်းတစ်ခုအား စီမံလုပ်ကိုင်ခွင့် DRM လက်မှတ်များ သုံးခွင့် ပြုသည်။ သာမန် အက်ပ်များ အတွက် ဘယ်တော့မှ မလိုအပ်နိုင်ပါ။"</string>
<string name="permlab_handoverStatus" msgid="7820353257219300883">"Android ရဲ့ အလင်းတန်းထိုး လွှဲပြောင်းမှု အခြေအနေကို ရယူရန်"</string>
- <string name="permdesc_handoverStatus" msgid="4788144087245714948">"ဒီအပလီကေးရှင်းအား အန်ဒရွိုက်၏ လက်ရှိ အလင်းတန်းထိုး လွှဲပြောင်းမှု အကြောင်း အချက်အလက်ကို ရယူခွင့် ပြုသည်"</string>
+ <string name="permdesc_handoverStatus" msgid="4788144087245714948">"ဒီအပလီကေးရှင်းအား Android၏ လက်ရှိ အလင်းတန်းထိုး လွှဲပြောင်းမှု အကြောင်း အချက်အလက်ကို ရယူခွင့် ပြုသည်"</string>
<string name="permlab_removeDrmCertificates" msgid="7044888287209892751">"DRM လက်မှတ်များ ဖယ်ရှားရန်"</string>
<string name="permdesc_removeDrmCertificates" msgid="7272999075113400993">"အပလီကေးရှင်းအား DRM လက်မှတ်များကို ဖယ်ရှားခွင့် ပြုသည်။ သာမန် အက်ပ်များ အတွက် ဘယ်တော့မှ မလိုအပ်နိုင်ပါ။"</string>
<string name="permlab_bindCarrierMessagingService" msgid="1490229371796969158">"စာပို့စာယူ ဆက်သွယ်ရေးဝန်ဆောင်မှုတစ်ခုအား ပူးပေါင်းခွင့်ပြုရန်"</string>
@@ -1178,6 +1208,9 @@
<skip />
<string name="wifi_no_internet" msgid="8938267198124654938">"Wi-Fi တွင် အင်တာနက်ချိတ်ဆက်မှု မရှိပါ"</string>
<string name="wifi_no_internet_detailed" msgid="8083079241212301741">"အခြားရွေးချယ်စရာများကိုကြည့်ရန် တို့ပါ"</string>
+ <string name="wifi_softap_config_change" msgid="8475911871165857607">"သင်၏ဟော့စပေါ့ ဆက်တင်များ ပြောင်းလဲမှု"</string>
+ <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"သင်၏ ဟော့စပေါ့လိုင်း ပြောင်းသွားပါပြီ။"</string>
+ <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"ဤစက်ပစ္စည်းသည် သင်၏ 5GHz သီးသန့်ရွေးချယ်မှုအတွက် ပံ့ပိုးမထားပါ။ ၎င်းအစား ဤစက်ပစ္စည်းသည် ရနိုင်သည့်အခါ 5GHz လိုင်းကို သုံးသွားပါမည်။"</string>
<string name="network_switch_metered" msgid="4671730921726992671">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> သို့ ပြောင်းလိုက်ပြီ"</string>
<string name="network_switch_metered_detail" msgid="775163331794506615">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> ဖြင့် အင်တာနက် အသုံးမပြုနိုင်သည့်အချိန်တွင် စက်ပစ္စည်းသည် <xliff:g id="NEW_NETWORK">%1$s</xliff:g> ကို သုံးပါသည်။ ဒေတာသုံးစွဲခ ကျသင့်နိုင်ပါသည်။"</string>
<string name="network_switch_metered_toast" msgid="5779283181685974304">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> မှ <xliff:g id="NEW_NETWORK">%2$s</xliff:g> သို့ ပြောင်းလိုက်ပြီ"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 759cbaa..a5bd899 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -236,9 +236,6 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Flymodus"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Flymodus er på"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Flymodus er av"</string>
- <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Batterisparing"</string>
- <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Batterisparing er AV"</string>
- <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Batterisparing er PÅ"</string>
<string name="global_action_settings" msgid="1756531602592545966">"Innstillinger"</string>
<string name="global_action_assist" msgid="3892832961594295030">"Hjelp"</string>
<string name="global_action_voice_assist" msgid="7751191495200504480">"Talehjelp"</string>
@@ -276,6 +273,9 @@
<string name="permgrouplab_location" msgid="7275582855722310164">"Posisjon"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"få tilgang til enhetens plassering"</string>
<string name="permgrouprequest_location" msgid="3788275734953323491">"Vil du gi <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> tilgang til denne enhetens posisjon?"</string>
+ <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"Appen får bare tilgang til posisjonen når du bruker appen."</string>
+ <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"Vil du alltid gi <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> tilgang til denne enhetens posisjon?"</string>
+ <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"Appen får alltid tilgang til posisjonen, selv når du ikke bruker appen."</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Kalender"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"åpne kalenderen din"</string>
<string name="permgrouprequest_calendar" msgid="289900767793189421">"Vil du gi <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> tilgang til kalenderen din?"</string>
@@ -402,8 +402,7 @@
<string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Denne appen kan legge til, fjerne eller endre kalenderaktiviteter på telefonen din. Denne appen kan sende meldinger som kan virke som om kommer fra kalendereiere, eller endre aktiviteter uten at eierne blir varslet."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"bruke ekstra posisjonskommandoer"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Appen gis tillatelse til å bruke ekstra kommandoer fra posisjonsleverandører. Dette kan gi appen tillatelse til å påvirke bruken av GPS eller andre posisjonskilder."</string>
- <!-- no translation found for permlab_accessFineLocation (6265109654698562427) -->
- <skip />
+ <string name="permlab_accessFineLocation" msgid="6265109654698562427">"bare tilgang til nøyaktig posisjon i forgrunnen"</string>
<string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Denne appen kan bare få den nøyaktige posisjonen din når den er på i forgrunnen. Disse posisjonstjenestene må være slått på og tilgjengelige på telefonen din for at appen skal kunne bruke dem. Dette kan øke batteriforbruket."</string>
<string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"få tilgang til omtrentlig posisjon (nettverksbasert)"</string>
<string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Denne appen kan få posisjonen din fra nettverkskilder som mobilmaster og Wi-Fi-nettverk. Disse posisjonstjenestene må være slått på og tilgjengelige på nettbrettet ditt for at appen skal kunne bruke dem."</string>
@@ -518,6 +517,37 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Ikon for fingeravtrykk"</string>
+ <string name="permlab_manageFace" msgid="2137540986007309781">"administrere maskinvare for ansiktsautentisering"</string>
+ <string name="permdesc_manageFace" msgid="8919637120670185330">"Lar appen bruke metoder for å legge til og slette ansiktmaler for bruk."</string>
+ <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"bruke maskinvare for ansiktsautentisering"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Lar appen bruke maskinvare for ansiktsautentisering til autentisering"</string>
+ <string name="face_acquired_insufficient" msgid="5901287247766106330">"Kunne ikke behandle ansiktet. Prøv igjen."</string>
+ <string name="face_acquired_too_bright" msgid="610606792381297174">"Ansiktet er for lyst. Prøv med svakere lys."</string>
+ <string name="face_acquired_too_dark" msgid="7229162716976778371">"Ansiktet er for mørkt. Bruk en lyskilde."</string>
+ <string name="face_acquired_too_close" msgid="1980310037427755293">"Flytt sensoren lengre vekk fra ansiktet."</string>
+ <string name="face_acquired_too_far" msgid="4494571381828850007">"Hold sensoren nærmere ansiktet."</string>
+ <string name="face_acquired_too_high" msgid="228411096134808372">"Flytt sensoren opp."</string>
+ <string name="face_acquired_too_low" msgid="4539774649296349109">"Flytt sensoren ned."</string>
+ <string name="face_acquired_too_right" msgid="1650292067226118760">"Flytt sensoren til høyre."</string>
+ <string name="face_acquired_too_left" msgid="2712489669456176505">"Flytt sensoren til venstre."</string>
+ <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Se på sensoren."</string>
+ <string name="face_acquired_not_detected" msgid="5707782294589511391">"Ingen ansikter er oppdaget."</string>
+ <string name="face_acquired_not_steady" msgid="3722829465011040042">"Hold ansiktet stødig foran enheten."</string>
+ <string-array name="face_acquired_vendor">
+ </string-array>
+ <string name="face_error_hw_not_available" msgid="6255891785768984615">"Maskinvare for ansikt er ikke tilgjengelig."</string>
+ <string name="face_error_timeout" msgid="4014326147867150054">"Tidsavbrudd for ansikt er nådd. Prøv igjen."</string>
+ <string name="face_error_no_space" msgid="8224993703466381314">"Ansiktet kan ikke lagres."</string>
+ <string name="face_error_canceled" msgid="283945501061931023">"Ansikt-operasjonen ble avbrutt."</string>
+ <string name="face_error_lockout" msgid="3407426963155388504">"For mange forsøk. Prøv igjen senere."</string>
+ <string name="face_error_lockout_permanent" msgid="8198354656746088890">"For mange forsøk. Ansiktsautentisering er slått av."</string>
+ <string name="face_error_unable_to_process" msgid="238761109287767270">"Prøv igjen."</string>
+ <string name="face_error_not_enrolled" msgid="9166792142679691323">"Ingen ansikt er registrert."</string>
+ <string name="face_error_hw_not_present" msgid="4737289254517095671">"Denne enheten har ikke sensor for ansiktsautentisering"</string>
+ <string name="face_name_template" msgid="7004562145809595384">"Ansikt <xliff:g id="FACEID">%d</xliff:g>"</string>
+ <string-array name="face_error_vendor">
+ </string-array>
+ <string name="face_icon_content_description" msgid="4024817159806482191">"Ansiktikon"</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"lese synkroniseringsinnstillinger"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Lar appen lese synkroniseringsinnstillingene for en konto. For eksempel kan den finne ut om Personer-appen er synkronisert med en konto."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"slå synkronisering av og på"</string>
@@ -587,14 +617,14 @@
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="4280246270601044505">"Overvåker antallet feil passord som er skrevet inn når skjermen låses opp, og låser nettbrettet eller sletter denne brukerens data når for mange feil passord er skrevet inn."</string>
<string name="policydesc_watchLogin_secondaryUser" product="TV" msgid="3484832653564483250">"Overvåker antallet feil passord som er skrevet inn når skjermen låses opp, og låser TV-en eller sletter denne brukerens data når for mange feil passord er skrevet inn."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="2185480427217127147">"Overvåker antallet feil passord som er skrevet inn når skjermen låses opp, og låser telefonen eller sletter denne brukerens data når for mange feil passord er skrevet inn."</string>
- <string name="policylab_resetPassword" msgid="4934707632423915395">"Endre skjermlåsen"</string>
- <string name="policydesc_resetPassword" msgid="1278323891710619128">"Endre skjermlåsen."</string>
- <string name="policylab_forceLock" msgid="2274085384704248431">"Lås skjermen"</string>
- <string name="policydesc_forceLock" msgid="1141797588403827138">"Kontrollér hvordan og når skjermen låses."</string>
- <string name="policylab_wipeData" msgid="3910545446758639713">"Slett alle data"</string>
+ <string name="policylab_resetPassword" msgid="4934707632423915395">"Endring av skjermlåsen"</string>
+ <string name="policydesc_resetPassword" msgid="1278323891710619128">"Kan endre skjermlåsen."</string>
+ <string name="policylab_forceLock" msgid="2274085384704248431">"Låsing av skjermen"</string>
+ <string name="policydesc_forceLock" msgid="1141797588403827138">"Kan kontrollere hvordan og når skjermen låses."</string>
+ <string name="policylab_wipeData" msgid="3910545446758639713">"Sletting av alle data"</string>
<string name="policydesc_wipeData" product="tablet" msgid="4306184096067756876">"Tilbakestill nettbrettets data uten advarsel ved å tilbakestille til fabrikkstandard."</string>
<string name="policydesc_wipeData" product="tv" msgid="5816221315214527028">"Slett TV-ens data uten advarsel ved å tilbakestille til fabrikkstandard."</string>
- <string name="policydesc_wipeData" product="default" msgid="5096895604574188391">"Tilbakestill telefonens data uten advarsel, ved å tilbakestille til fabrikkstandard."</string>
+ <string name="policydesc_wipeData" product="default" msgid="5096895604574188391">"Telefonens data kan slettes uten advarsel ved å tilbakestille til fabrikkstandard."</string>
<string name="policylab_wipeData_secondaryUser" msgid="8362863289455531813">"Slett brukerdataene"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="6336255514635308054">"Sletter denne brukerens data på dette nettbrettet uten advarsel."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2086473496848351810">"Sletter denne brukerens data på denne TV-en uten advarsel."</string>
@@ -607,7 +637,7 @@
<string name="policydesc_encryptedStorage" msgid="2637732115325316992">"Krev at lagrede appdata krypteres."</string>
<string name="policylab_disableCamera" msgid="6395301023152297826">"Deaktiver kameraer"</string>
<string name="policydesc_disableCamera" msgid="2306349042834754597">"Hindre bruk av alle kameraer på enheten."</string>
- <string name="policylab_disableKeyguardFeatures" msgid="8552277871075367771">"Deaktiver enkelte skjermlåsfunksjoner"</string>
+ <string name="policylab_disableKeyguardFeatures" msgid="8552277871075367771">"Deaktivering av skjermlåsfunksjoner"</string>
<string name="policydesc_disableKeyguardFeatures" msgid="2044755691354158439">"Forhindrer bruk av enkelte skjermlåsfunksjoner."</string>
<string-array name="phoneTypes">
<item msgid="8901098336658710359">"Hjemmenummer"</item>
@@ -1178,6 +1208,9 @@
<skip />
<string name="wifi_no_internet" msgid="8938267198124654938">"Wi-Fi har ikke Internett-tilgang"</string>
<string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Trykk for å få alternativer"</string>
+ <string name="wifi_softap_config_change" msgid="8475911871165857607">"Endres til innstillingene dine for Wi-Fi-soner"</string>
+ <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"Båndet ditt for Wi-Fi-sone er endret."</string>
+ <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"Denne enheten støtter ikke innstillingen din for bare 5 GHz. I stedet bruker enheten 5 GHz-båndet når det er tilgjengelig."</string>
<string name="network_switch_metered" msgid="4671730921726992671">"Byttet til <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
<string name="network_switch_metered_detail" msgid="775163331794506615">"Enheten bruker <xliff:g id="NEW_NETWORK">%1$s</xliff:g> når <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> ikke har Internett-tilgang. Avgifter kan påløpe."</string>
<string name="network_switch_metered_toast" msgid="5779283181685974304">"Byttet fra <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> til <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index b8b0256..eac241b 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -236,9 +236,6 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"हवाइजहाज मोड"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"हवाइजहाज मोड खुला छ"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"हवाइजहाज मोड बन्द छ"</string>
- <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"ब्याट्री सेभर"</string>
- <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"ब्याट्री सेभर निष्क्रिय छ"</string>
- <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"ब्याट्री सेभर सक्रिय छ"</string>
<string name="global_action_settings" msgid="1756531602592545966">"सेटिङहरू"</string>
<string name="global_action_assist" msgid="3892832961594295030">"सहायता दिनुहोस्"</string>
<string name="global_action_voice_assist" msgid="7751191495200504480">"आवाज सहायता"</string>
@@ -276,6 +273,9 @@
<string name="permgrouplab_location" msgid="7275582855722310164">"स्थान"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"यस यन्त्रको स्थानमाथि पहुँच"</string>
<string name="permgrouprequest_location" msgid="3788275734953323491">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> लाई यो यन्त्रको स्थानमाथि पहुँच राख्न दिने हो?"</string>
+ <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"तपाईं उक्त अनुप्रयोग प्रयोग गरिरहेका बेलामा त्यससँग स्थानमाथिको पहुँच रहने छ।"</string>
+ <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> लाई यो यन्त्रको स्थानमाथि पहुँच राख्न दिने हो?"</string>
+ <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"तपाईं उक्त अनुप्रयोग प्रयोग नगरिरहेका बेलामा पनि त्यससँग स्थानमाथिको पहुँच रहने छ।"</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"पात्रो"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"तपाईंको पात्रोमाथि पहुँच गर्नुहोस्"</string>
<string name="permgrouprequest_calendar" msgid="289900767793189421">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> लाई आफ्नो पात्रोमाथि पहुँच राख्न दिने हो?"</string>
@@ -402,8 +402,7 @@
<string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"यस अनुप्रयोगले तपाईंको फोनमा पात्रोका कार्यक्रमहरू थप्न, हटाउन वा परिवर्तन गर्न सक्छ। यस अनुप्रयोगले पात्रोका मालिकहरू मार्फत आएको जस्तो लाग्ने सन्देशहरू पठाउन वा तिनीहरूका मालिकहरूलाई सूचित नगरिकन कार्यक्रमहरू परिवर्तन गर्न सक्छ।"</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"अधिक स्थान प्रदायक आदेशहरू पहुँच गर्नुहोस्"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"अनुप्रयोगलाई अतिरिक्त स्थान प्रदायक आदेशहरू पहुँच गर्न अनुमति दिन्छ। यो अनुप्रयोगलाई GPS वा अन्य स्थान स्रोतहरूको संचालन साथै हस्तक्षेप गर्न अनुमति दिन सक्छ।"</string>
- <!-- no translation found for permlab_accessFineLocation (6265109654698562427) -->
- <skip />
+ <string name="permlab_accessFineLocation" msgid="6265109654698562427">"अग्रभूमिमा मात्र सटीक स्थानमाथि पहुँच राख्नुहोस्"</string>
<string name="permdesc_accessFineLocation" msgid="3520508381065331098">"यो अनुप्रयोगले अग्रभागमा चलिरहेको अवस्थामा मात्र तपाईंलाई स्थानको सटिक जानकारी दिन सक्छ। यी स्थानसम्बन्धी सेवाहरू अनिवार्य रूपमा सक्रिय गरिएका हुनु पर्छ र अनुप्रयोगले यिनीहरूको प्रयोग गर्न सकोस् भन्नाका खातिर तपाईंको फोनमै उपलब्ध हुन्छन्। यस कार्यले गर्दा ब्याट्री बढी खर्च हुन सक्छ।"</string>
<string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"अनुमानित स्थान पहुँच गराउनुहोस् (नेटवर्कमा आधारित)"</string>
<string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"यस अनुप्रयोगले सेलका टावर र Wi-Fi नेटवर्कहरू जस्ता नेटवर्कका स्रोतहरूको आधारमा तपाईंको स्थान बारे जानकारी प्राप्त गर्न सक्छ। यो अनुप्रयोग ती स्रोतहरूको प्रयोग गर्न सक्षम होस् भन्नका खातिर यी स्थान सम्बन्धी सेवाहरूलाई अनिवार्य रूपमा सक्रिय पार्नुपर्छ र यी तपाईंको ट्याब्लेटमा उपलब्ध हुनु पर्छ।"</string>
@@ -518,6 +517,37 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"फिंगरप्रिन्ट आइकन"</string>
+ <string name="permlab_manageFace" msgid="2137540986007309781">"अनुहार प्रमाणिकरण हार्डवेयर व्यवस्थापन गर्नुहोस्"</string>
+ <string name="permdesc_manageFace" msgid="8919637120670185330">"अनुप्रयोगलाई प्रयोगका लागि अनुहार टेम्प्लेट थप्न र मेटाउने तरिका आह्वान गर्न अनुमति दिन्छ।"</string>
+ <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"अनुहार प्रमाणिकरण हार्डवेयर प्रयोग गर्नुहोस्"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"अनुप्रयोगलाई प्रमाणीकरणका लागि अनुहार प्रमाणीकरण हार्डवेयर प्रयोग गर्न अनुमति दिन्छ"</string>
+ <string name="face_acquired_insufficient" msgid="5901287247766106330">"अनुहार प्रशोधन गर्न सकिएन। कृपया फेरि प्रयास गर्नुहोस्।"</string>
+ <string name="face_acquired_too_bright" msgid="610606792381297174">"अनुहारमा धेरै उज्यालो छ। कृपया कम प्रकाशमा प्रयास गर्नुहोस्।"</string>
+ <string name="face_acquired_too_dark" msgid="7229162716976778371">"अनुहार धेरै गाढा छ। कृपया प्रकाशको स्रोतको थप्नुहोस्।"</string>
+ <string name="face_acquired_too_close" msgid="1980310037427755293">"कृपया अनुहारबाट सेन्सरलाई अलिक टाढा सार्नुहोस्।"</string>
+ <string name="face_acquired_too_far" msgid="4494571381828850007">"कृपया अनुहारलाई सेन्सरको नजिक ल्याउनुहोस्।"</string>
+ <string name="face_acquired_too_high" msgid="228411096134808372">"कृपया सेन्सरलाई माथि सार्नुहोस्।"</string>
+ <string name="face_acquired_too_low" msgid="4539774649296349109">"कृपया सेन्सरलाई तल सार्नुहोस्।"</string>
+ <string name="face_acquired_too_right" msgid="1650292067226118760">"कृपया सेन्सरलाई दायाँतिर सार्नुहोस्।"</string>
+ <string name="face_acquired_too_left" msgid="2712489669456176505">"कृपया सेन्सरलाई बायाँतिर सार्नुहोस्।"</string>
+ <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"कृपया सेन्सरमा हेर्नुहोस्।"</string>
+ <string name="face_acquired_not_detected" msgid="5707782294589511391">"अनुहार पत्ता लागेन।"</string>
+ <string name="face_acquired_not_steady" msgid="3722829465011040042">"यन्त्रको अगाडि अनुहार स्थिर राख्नुहोस्।"</string>
+ <string-array name="face_acquired_vendor">
+ </string-array>
+ <string name="face_error_hw_not_available" msgid="6255891785768984615">"अनुहार पहिचान गर्ने हार्डवेयर उपलब्ध छैन।"</string>
+ <string name="face_error_timeout" msgid="4014326147867150054">"अनुहारको समय सकिएको छ। फेरि प्रयास गर्नुहोस्।"</string>
+ <string name="face_error_no_space" msgid="8224993703466381314">"अनुहार भण्डारण गर्न सकिँदैन।"</string>
+ <string name="face_error_canceled" msgid="283945501061931023">"अनुहार पहिचान रद्द गरियो।"</string>
+ <string name="face_error_lockout" msgid="3407426963155388504">"धेरैपटक प्रयासहरू भए। पछि फेरि प्रयास गर्नुहोस्।"</string>
+ <string name="face_error_lockout_permanent" msgid="8198354656746088890">"अत्यधिक धेरैपटक गलत प्रयासहरू भए। अनुहार प्रमाणिकरणलाई असक्षम पारियो।"</string>
+ <string name="face_error_unable_to_process" msgid="238761109287767270">"फेरि प्रयास गर्नुहोस्।"</string>
+ <string name="face_error_not_enrolled" msgid="9166792142679691323">"कुनै पनि अनुहार दर्ता गरिएन।"</string>
+ <string name="face_error_hw_not_present" msgid="4737289254517095671">"यो यन्त्रमा कुनै अनुहार प्रमाणिकरण सेन्सर छैन"</string>
+ <string name="face_name_template" msgid="7004562145809595384">"अनुहार <xliff:g id="FACEID">%d</xliff:g>"</string>
+ <string-array name="face_error_vendor">
+ </string-array>
+ <string name="face_icon_content_description" msgid="4024817159806482191">"अनुहारको आइकन"</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"समीकरण सेटिङहरू पढ्नुहोस्"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"अनुप्रयोगलाई खाताको लागि सिंक सेटिङहरू पढ्न अनुमति दिन्छ। उदाहरणको लागि यसले व्यक्तिहरको अनुप्रयोग खातासँग सिंक भएको नभएको निर्धारण गर्न सक्दछ।"</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"टगल सिंक खुला र बन्द"</string>
@@ -1184,6 +1214,9 @@
<skip />
<string name="wifi_no_internet" msgid="8938267198124654938">"Wi-Fi मार्फत इन्टरनेटमाथि पहुँच राख्न सकिँदैन"</string>
<string name="wifi_no_internet_detailed" msgid="8083079241212301741">"विकल्पहरूका लागि ट्याप गर्नुहोस्"</string>
+ <string name="wifi_softap_config_change" msgid="8475911871165857607">"तपाईंको हटस्पट सेटिङहरूमा परिवर्तन हुन्छ"</string>
+ <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"तपाईंको हटस्पट ब्यान्ड परिवर्तन भएको छ।"</string>
+ <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"यो यन्त्रले तपाईंको 5GHz मात्रको प्राथमिकतालाई समर्थन गर्दैन। बरु, उपलब्ध भएको खण्डमा यो यन्त्रले 5GHz ब्यान्ड प्रयोग गर्छ।"</string>
<string name="network_switch_metered" msgid="4671730921726992671">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> मा बदल्नुहोस्"</string>
<string name="network_switch_metered_detail" msgid="775163331794506615">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> मार्फत इन्टरनेटमाथि पहुँच राख्न नसकेको अवस्थामा यन्त्रले <xliff:g id="NEW_NETWORK">%1$s</xliff:g> प्रयोग गर्दछ। शुल्क लाग्न सक्छ।"</string>
<string name="network_switch_metered_toast" msgid="5779283181685974304">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> बाट <xliff:g id="NEW_NETWORK">%2$s</xliff:g> मा परिवर्तन गरियो"</string>
diff --git a/core/res/res/values-night/colors.xml b/core/res/res/values-night/colors.xml
new file mode 100644
index 0000000..688040b
--- /dev/null
+++ b/core/res/res/values-night/colors.xml
@@ -0,0 +1,31 @@
+<?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
+
+ NOTE: You might also want to edit: packages/SystemUI/res/values-night/colors.xml
+ -->
+<resources>
+ <!-- The primary text color if the text is on top of a dark background.
+ This is also affects colorized notifications with dark backgrounds. -->
+ <color name="notification_primary_text_color_dark">#dadada</color>
+
+ <!-- The secondary text color if the text is on top of a dark background. -->
+ <color name="notification_secondary_text_color_dark">#dadada</color>
+
+ <color name="notification_default_color_dark">#dadada</color>
+
+ <!-- The background color of a notification card. -->
+ <color name="notification_material_background_color">@*android:color/material_grey_900</color>
+</resources>
\ No newline at end of file
diff --git a/core/res/res/values-night/themes_device_defaults.xml b/core/res/res/values-night/themes_device_defaults.xml
new file mode 100644
index 0000000..5e3675a
--- /dev/null
+++ b/core/res/res/values-night/themes_device_defaults.xml
@@ -0,0 +1,54 @@
+<?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.
+-->
+
+<!--
+===============================================================
+ PLEASE READ
+===============================================================
+This file contains the themes that are the Device Defaults.
+If you want to edit themes to skin your device, do it here.
+We recommend that you do not edit themes.xml and instead edit
+this file.
+
+Editing this file instead of themes.xml will greatly simplify
+merges for future platform versions and CTS compliance will be
+easier.
+===============================================================
+ PLEASE READ
+===============================================================
+ -->
+<resources>
+
+ <!-- The dark default theme for apps that target API level XX and higher.
+ <p>The DeviceDefault themes are aliases for a specific device’s native look and feel. The
+ DeviceDefault theme family and widget style family offer ways for you to target your app
+ to a device’s native theme with all device customizations intact.</p>
+ <p>For example, when you set your app's {@code targetSdkVersion} to XX or higher, this
+ theme is applied to your application by default. As such, your app might appear with the
+ {@link #Theme_Material Material} styles on one device, but with a different set of styles on
+ another device. This is great if you want your app to fit with the device's native look and
+ feel. If, however, you prefer to keep your UI style the same across all devices, you should
+ apply a specific theme such as {@link #Theme_Material Material} or one of your own design.
+ For more information, read <a
+ href="http://android-developers.blogspot.com/20XX/XX/material-everywhere.html">Material
+ Everywhere</a>.</p>
+ <p>Styles used by the DeviceDefault theme are named using the convention
+ Type.DeviceDefault.Etc (for example, {@code Widget.DeviceDefault.Button} and
+ {@code TextAppearance.DeviceDefault.Widget.PopupMenu.Large}).</p>
+ -->
+ <!-- DeviceDefault theme for a window that should look like the Settings app. -->
+ <style name="Theme.DeviceDefault.Settings" parent="Theme.DeviceDefault"/>
+</resources>
diff --git a/core/res/res/values-night/values.xml b/core/res/res/values-night/values.xml
new file mode 100644
index 0000000..4eb2ff3
--- /dev/null
+++ b/core/res/res/values-night/values.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2018 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<resources>
+ <style name="Theme.DeviceDefault.QuickSettings" parent="android:Theme.DeviceDefault">
+ <!-- Color palette -->
+ <item name="colorPrimaryDark">@color/primary_dark_device_default_settings</item>
+ <item name="colorSecondary">@color/secondary_device_default_settings</item>
+ <item name="colorAccent">@color/accent_device_default_dark</item>
+ <item name="colorError">@color/error_color_device_default_dark</item>
+ <item name="colorControlNormal">?attr/textColorPrimary</item>
+ <item name="alertDialogTheme">@style/Theme.DeviceDefault.Dialog.Alert</item>
+
+ <!-- QS panel background -->
+ <item name="colorBackgroundFloating">@color/material_grey_900</item>
+
+ <!-- volume background -->
+ <item name="panelColorBackground">@color/material_grey_800</item>
+ </style>
+
+ <style name="TextAppearance.Material.Notification">
+ <item name="textColor">@color/notification_secondary_text_color_dark</item>
+ <item name="textSize">@dimen/notification_text_size</item>
+ </style>
+</resources>
\ No newline at end of file
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 05f0f3f..6742d6f 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -236,9 +236,6 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Vliegtuigmodus"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Vliegtuigmodus is AAN"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Vliegtuigmodus is UIT"</string>
- <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Batterijbesparing"</string>
- <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Batterijbesparing is UIT"</string>
- <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Batterijbesparing is AAN"</string>
<string name="global_action_settings" msgid="1756531602592545966">"Instellingen"</string>
<string name="global_action_assist" msgid="3892832961594295030">"Helpen"</string>
<string name="global_action_voice_assist" msgid="7751191495200504480">"Spraakassistent"</string>
@@ -276,6 +273,9 @@
<string name="permgrouplab_location" msgid="7275582855722310164">"Locatie"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"de locatie van dit apparaat openen"</string>
<string name="permgrouprequest_location" msgid="3788275734953323491">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> toegang geven tot de locatie van dit apparaat?"</string>
+ <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"De app heeft alleen toegang tot de locatie wanneer je de app gebruikt."</string>
+ <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> altijd toegang geven tot de locatie van dit apparaat?"</string>
+ <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"De app heeft altijd toegang tot de locatie, ook wanneer je de app niet gebruikt."</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Agenda"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"toegang krijgen tot je agenda"</string>
<string name="permgrouprequest_calendar" msgid="289900767793189421">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> toegang geven tot je agenda?"</string>
@@ -287,13 +287,13 @@
<string name="permgrouprequest_storage" msgid="7885942926944299560">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> toegang geven tot foto\'s, media en bestanden op je apparaat?"</string>
<string name="permgrouplab_microphone" msgid="171539900250043464">"Microfoon"</string>
<string name="permgroupdesc_microphone" msgid="4988812113943554584">"audio opnemen"</string>
- <string name="permgrouprequest_microphone" msgid="9167492350681916038">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> het volgende toestaan: audio opnemen."</string>
+ <string name="permgrouprequest_microphone" msgid="9167492350681916038">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> toestaan om audio op te nemen?"</string>
<string name="permgrouplab_camera" msgid="4820372495894586615">"Camera"</string>
<string name="permgroupdesc_camera" msgid="3250611594678347720">"foto\'s maken en video opnemen"</string>
<string name="permgrouprequest_camera" msgid="1299833592069671756">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> toestaan om foto\'s te maken en video op te nemen?"</string>
<string name="permgrouplab_calllog" msgid="8798646184930388160">"Gesprekkenlijsten"</string>
<string name="permgroupdesc_calllog" msgid="3006237336748283775">"gesprekkenlijst lezen en schrijven"</string>
- <string name="permgrouprequest_calllog" msgid="8487355309583773267">"Wil je <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> toegang tot je gesprekkenlijsten geven?"</string>
+ <string name="permgrouprequest_calllog" msgid="8487355309583773267">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> toegang geven tot je gesprekkenlijsten?"</string>
<string name="permgrouplab_phone" msgid="5229115638567440675">"Telefoon"</string>
<string name="permgroupdesc_phone" msgid="6234224354060641055">"telefoneren en oproepen beheren"</string>
<string name="permgrouprequest_phone" msgid="9166979577750581037">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> toestaan om telefoongesprekken te starten en te beheren?"</string>
@@ -402,8 +402,7 @@
<string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Deze app kan agenda-afspraken toevoegen, verwijderen of wijzigen op je telefoon. Deze app kan berichten verzenden die afkomstig lijken te zijn van agenda-eigenaren of afspraken aanpassen zonder dit aan de eigenaar te melden."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"toegang tot extra opdrachten van locatieaanbieder"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Hiermee kan de app toegang krijgen tot extra opdrachten voor de locatieprovider. De app kan hiermee de werking van gps of andere locatiebronnen te verstoren."</string>
- <!-- no translation found for permlab_accessFineLocation (6265109654698562427) -->
- <skip />
+ <string name="permlab_accessFineLocation" msgid="6265109654698562427">"alleen toegang tot precieze locatie op de voorgrond"</string>
<string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Deze app kan je exacte locatie ophalen wanneer de app op de voorgrond wordt uitgevoerd. De app kan alleen gebruikmaken van deze locatieservices als ze zijn ingeschakeld en beschikbaar zijn op je telefoon. Hierdoor kan het batterijverbruik toenemen."</string>
<string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"toegang tot geschatte locatie (netwerkgebaseerd)"</string>
<string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Deze app kan je locatie ophalen op basis van netwerkbronnen zoals zendmasten en wifi-netwerken. De app kan alleen gebruikmaken van deze locatieservices als ze zijn ingeschakeld en beschikbaar zijn op je tablet."</string>
@@ -518,6 +517,37 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Vingerafdruk-pictogram"</string>
+ <string name="permlab_manageFace" msgid="2137540986007309781">"hardware voor gezichtsherkenning beheren"</string>
+ <string name="permdesc_manageFace" msgid="8919637120670185330">"Hiermee kan de app methoden aanroepen om gezichtstemplates toe te voegen en te verwijderen voor gebruik."</string>
+ <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"hardware voor gezichtsherkenning gebruiken"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Hiermee kan de app hardware voor gezichtsherkenning gebruiken voor verificatie"</string>
+ <string name="face_acquired_insufficient" msgid="5901287247766106330">"Kan gezicht niet verwerken. Probeer het opnieuw."</string>
+ <string name="face_acquired_too_bright" msgid="610606792381297174">"Gezicht is te helder. Probeer bij minder licht."</string>
+ <string name="face_acquired_too_dark" msgid="7229162716976778371">"Gezicht is te donker. Laat meer licht toe."</string>
+ <string name="face_acquired_too_close" msgid="1980310037427755293">"Houd de sensor verder weg van je gezicht."</string>
+ <string name="face_acquired_too_far" msgid="4494571381828850007">"Houd de sensor dichter bij je gezicht."</string>
+ <string name="face_acquired_too_high" msgid="228411096134808372">"Beweeg de sensor omhoog."</string>
+ <string name="face_acquired_too_low" msgid="4539774649296349109">"Beweeg de sensor omlaag."</string>
+ <string name="face_acquired_too_right" msgid="1650292067226118760">"Beweeg de sensor naar rechts."</string>
+ <string name="face_acquired_too_left" msgid="2712489669456176505">"Beweeg de sensor naar links."</string>
+ <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Kijk naar de sensor."</string>
+ <string name="face_acquired_not_detected" msgid="5707782294589511391">"Geen gezicht gedetecteerd."</string>
+ <string name="face_acquired_not_steady" msgid="3722829465011040042">"Houd het gezicht stil voor het apparaat."</string>
+ <string-array name="face_acquired_vendor">
+ </string-array>
+ <string name="face_error_hw_not_available" msgid="6255891785768984615">"Hardware voor gezichtsherkenning niet beschikbaar."</string>
+ <string name="face_error_timeout" msgid="4014326147867150054">"Time-out voor gezicht bereikt. Probeer opnieuw."</string>
+ <string name="face_error_no_space" msgid="8224993703466381314">"Gezicht kan niet worden opgeslagen."</string>
+ <string name="face_error_canceled" msgid="283945501061931023">"Bewerking voor gezichtsherkenning geannuleerd."</string>
+ <string name="face_error_lockout" msgid="3407426963155388504">"Te veel pogingen. Probeer het later opnieuw."</string>
+ <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Te veel pogingen. Gezichtsherkenning inactief."</string>
+ <string name="face_error_unable_to_process" msgid="238761109287767270">"Probeer het opnieuw."</string>
+ <string name="face_error_not_enrolled" msgid="9166792142679691323">"Geen gezicht geregistreerd."</string>
+ <string name="face_error_hw_not_present" msgid="4737289254517095671">"Dit apparaat heeft geen sensor voor gezichtsherkenning"</string>
+ <string name="face_name_template" msgid="7004562145809595384">"Gezicht <xliff:g id="FACEID">%d</xliff:g>"</string>
+ <string-array name="face_error_vendor">
+ </string-array>
+ <string name="face_icon_content_description" msgid="4024817159806482191">"Gezichtspictogram"</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"synchronisatie-instellingen lezen"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Hiermee kan de app de synchronisatie-instellingen voor een account lezen. Dit kan bijvoorbeeld bepalen of de app Personen wordt gesynchroniseerd met een account."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"synchronisatie in- en uitschakelen"</string>
@@ -588,13 +618,13 @@
<string name="policydesc_watchLogin_secondaryUser" product="TV" msgid="3484832653564483250">"Bijhouden hoe vaak onjuiste wachtwoorden worden ingevoerd wanneer het scherm wordt ontgrendeld en de tv vergrendelen of alle gegevens van deze gebruiker wissen als te veel onjuiste wachtwoorden worden ingevoerd."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="2185480427217127147">"Bijhouden hoe vaak onjuiste wachtwoorden worden ingevoerd wanneer het scherm wordt ontgrendeld en de telefoon vergrendelen of alle gegevens van deze gebruiker wissen als te veel onjuiste wachtwoorden worden ingevoerd."</string>
<string name="policylab_resetPassword" msgid="4934707632423915395">"De schermvergrendeling wijzigen"</string>
- <string name="policydesc_resetPassword" msgid="1278323891710619128">"De schermvergrendeling wijzigen."</string>
+ <string name="policydesc_resetPassword" msgid="1278323891710619128">"Wijzig de schermvergrendeling."</string>
<string name="policylab_forceLock" msgid="2274085384704248431">"Het scherm vergrendelen"</string>
- <string name="policydesc_forceLock" msgid="1141797588403827138">"Beheren hoe en wanneer het scherm wordt vergrendeld."</string>
+ <string name="policydesc_forceLock" msgid="1141797588403827138">"Beheer hoe en wanneer het scherm wordt vergrendeld."</string>
<string name="policylab_wipeData" msgid="3910545446758639713">"Alle gegevens wissen"</string>
<string name="policydesc_wipeData" product="tablet" msgid="4306184096067756876">"De gegevens van de tablet zonder waarschuwing wissen door de fabrieksinstellingen te herstellen."</string>
<string name="policydesc_wipeData" product="tv" msgid="5816221315214527028">"De gegevens van de tv zonder waarschuwing wissen door de fabrieksinstellingen te herstellen."</string>
- <string name="policydesc_wipeData" product="default" msgid="5096895604574188391">"De gegevens van de telefoon zonder waarschuwing wissen door de fabrieksinstellingen te herstellen."</string>
+ <string name="policydesc_wipeData" product="default" msgid="5096895604574188391">"Wis de gegevens van de telefoon zonder waarschuwing door de fabrieksinstellingen te herstellen."</string>
<string name="policylab_wipeData_secondaryUser" msgid="8362863289455531813">"Gebruikersgegevens wissen"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="6336255514635308054">"De gegevens van deze gebruiker op deze tablet zonder waarschuwing wissen."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2086473496848351810">"De gegevens van deze gebruiker op deze tv zonder waarschuwing wissen."</string>
@@ -608,7 +638,7 @@
<string name="policylab_disableCamera" msgid="6395301023152297826">"Camera\'s uitschakelen"</string>
<string name="policydesc_disableCamera" msgid="2306349042834754597">"Het gebruik van alle apparaatcamera\'s voorkomen."</string>
<string name="policylab_disableKeyguardFeatures" msgid="8552277871075367771">"Bepaalde functies voor schermvergrendeling uitschakelen"</string>
- <string name="policydesc_disableKeyguardFeatures" msgid="2044755691354158439">"Gebruik van bepaalde functies voor schermvergrendeling voorkomen."</string>
+ <string name="policydesc_disableKeyguardFeatures" msgid="2044755691354158439">"Voorkom het gebruik van bepaalde functies voor schermvergrendeling."</string>
<string-array name="phoneTypes">
<item msgid="8901098336658710359">"Thuis"</item>
<item msgid="869923650527136615">"Mobiel"</item>
@@ -1178,6 +1208,9 @@
<skip />
<string name="wifi_no_internet" msgid="8938267198124654938">"Wifi-netwerk heeft geen internettoegang"</string>
<string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Tik voor opties"</string>
+ <string name="wifi_softap_config_change" msgid="8475911871165857607">"Wijzigingen in je hotspot-instellingen"</string>
+ <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"Je hotspot-band is gewijzigd."</string>
+ <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"Dit apparaat biedt geen ondersteuning voor je voorkeur voor alleen 5 GHz. In plaats daarvan gebruikt dit apparaat de 5-GHz-band wanneer deze beschikbaar is."</string>
<string name="network_switch_metered" msgid="4671730921726992671">"Overgeschakeld naar <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
<string name="network_switch_metered_detail" msgid="775163331794506615">"Apparaat gebruikt <xliff:g id="NEW_NETWORK">%1$s</xliff:g> wanneer <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> geen internetverbinding heeft. Er kunnen kosten in rekening worden gebracht."</string>
<string name="network_switch_metered_toast" msgid="5779283181685974304">"Overgeschakeld van <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> naar <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
@@ -1553,7 +1586,7 @@
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Je hebt je ontgrendelingspatroon <xliff:g id="NUMBER_0">%1$d</xliff:g> keer onjuist getekend. Na nog eens <xliff:g id="NUMBER_1">%2$d</xliff:g> mislukte pogingen wordt u gevraagd je telefoon te ontgrendelen via een e-mailaccount.\n\n Probeer het over <xliff:g id="NUMBER_2">%3$d</xliff:g> seconden opnieuw."</string>
<string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Verwijderen"</string>
- <string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"Volume verhogen tot boven het aanbevolen niveau?\n\nAls u langere tijd op hoog volume naar muziek luistert, raakt je gehoor mogelijk beschadigd."</string>
+ <string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"Volume verhogen tot boven het aanbevolen niveau?\n\nAls je langere tijd op hoog volume naar muziek luistert, raakt je gehoor mogelijk beschadigd."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="8404780875025725199">"Sneltoets voor toegankelijkheid gebruiken?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"Wanneer de snelkoppeling is ingeschakeld, kun je drie seconden op beide volumeknoppen drukken om een toegankelijkheidsfunctie te starten.\n\n Huidige toegankelijkheidsfunctie:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Je kunt de functie wijzigen in Instellingen > Toegankelijkheid."</string>
<string name="disable_accessibility_shortcut" msgid="627625354248453445">"Sneltoets uitschakelen"</string>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index ad9c659..c86fab8 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -236,9 +236,6 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"ଏରୋପ୍ଲେନ୍ ମୋଡ୍"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"ଏରୋପ୍ଲେନ୍ ମୋଡ୍ ଅନ୍ ଅଛି"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"ଏରୋପ୍ଲେନ୍ ମୋଡ୍ ଅଫ୍ ଅଛି"</string>
- <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"ବ୍ୟାଟେରୀ ସେଭର୍"</string>
- <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"ବ୍ୟାଟେରୀ ସେଭର୍ ଅଫ୍ ଅଛି"</string>
- <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"ବ୍ୟାଟେରୀ ସେଭର୍ ଅନ୍ ଅଛି"</string>
<string name="global_action_settings" msgid="1756531602592545966">"ସେଟିଙ୍ଗ"</string>
<string name="global_action_assist" msgid="3892832961594295030">"ସହାୟକ"</string>
<string name="global_action_voice_assist" msgid="7751191495200504480">"ଭଏସ୍ ସହାୟକ"</string>
@@ -276,6 +273,9 @@
<string name="permgrouplab_location" msgid="7275582855722310164">"ଲୋକେଶନ୍"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"ଏହି ଡିଭାଇସ୍ର ଲୋକେଶନ୍ ଆକ୍ସେସ୍ କରେ"</string>
<string name="permgrouprequest_location" msgid="3788275734953323491">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>କୁ ଏହି ଡିଭାଇସ୍ର ଲୋକେଶନ୍ ଆକ୍ସେସ୍ କରିବା ପାଇଁ ଅନୁମତି ଦେବେ କି?"</string>
+ <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"ଆପଣ ଆପ୍ ବ୍ୟବହାର କରୁଥିବା ବେଳେ କେବଳ ଲୋକେସନ୍କୁ ଆପ୍ର ଆକ୍ସେସ୍ ରହିବ।"</string>
+ <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"ସଦାବେଳେ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>କୁ ଏହି ଡିଭାଇସ୍ର ଲୋକେସନ୍ ଆକ୍ସେସ୍ କରିବା ପାଇଁ ଅନୁମତି ଦେବେ କି?"</string>
+ <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"ଆପଣ ଆପ୍ ବ୍ୟବହାର କରୁନଥିଲେ ମଧ୍ୟ, ଲୋକେସନ୍କୁ ସଦାବେଳେ ଆପ୍ର ଆକ୍ସେସ୍ ରହିବ।"</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"କ୍ୟାଲେଣ୍ଡର୍"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"ଆପଣଙ୍କ କ୍ୟାଲେଣ୍ଡର୍ ଆକ୍ସେସ୍ କରେ"</string>
<string name="permgrouprequest_calendar" msgid="289900767793189421">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>କୁ ଆପଣଙ୍କ କ୍ୟାଲେଣ୍ଡର୍କୁ ଆକ୍ସେସ୍ କରିବା ପାଇଁ ଅନୁମତି ଦେବେ କି?"</string>
@@ -402,8 +402,7 @@
<string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"ଏହି ଆପ୍, ଆପଣଙ୍କ ଫୋନ୍ରେ କ୍ୟାଲେଣ୍ଡର୍ ଇଭେଣ୍ଟଗୁଡ଼ିକୁ ଯୋଡ଼ିପାରେ, ବାହାର କରିପାରେ କିମ୍ବା ବଦଳାଇପାରେ। କ୍ୟାଲେଣ୍ଡର୍ ମାଲିକଙ୍କ ପାଖରୁ ଆସିଥିବା ପରି ଜଣାପଡ଼ିବା ମେସେଜ୍କୁ ଏହି ଆପ୍ ପଠାଇପାରେ କିମ୍ବା ମାଲିକଙ୍କୁ ନଜଣାଇ ଇଭେଣ୍ଟ ବଦଳାଇପାରେ।"</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"ଅତିରିକ୍ତ ଲୋକେଶନ୍ ପ୍ରଦାନକାରୀ କମାଣ୍ଡକୁ ଆକ୍ସେସ୍ କରନ୍ତୁ"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"ଅତିରିକ୍ତ ଲୋକେଶନ୍ ପ୍ରଦାନକାରୀ କମାଣ୍ଡ ଆକ୍ସେସ୍ କରିବା ପାଇଁ ଆପ୍କୁ ଅନୁମତି ଦିଏ। GPS କିମ୍ବା ଅନ୍ୟ ଲୋକେଶନ୍ ସୋର୍ସଗୁଡିକରେ ଆପ୍ଟି ପ୍ରଭାବ ପକାଇପାରେ।"</string>
- <!-- no translation found for permlab_accessFineLocation (6265109654698562427) -->
- <skip />
+ <string name="permlab_accessFineLocation" msgid="6265109654698562427">"କେବଳ ସମ୍ମୁଖଭାଗରେ ସଠିକ୍ ଲୋକେଶନ୍ର ଆକ୍ସେସ୍ କରନ୍ତୁ"</string>
<string name="permdesc_accessFineLocation" msgid="3520508381065331098">"ଏହି ଆପ୍ ଯେତେବେଳେ ସମ୍ମୁଖଭାଗରେ ଥିବାବେଳେ ଆପଣଙ୍କର ସଠିକ୍ ଲୋକେଶନ୍ ପ୍ରାପ୍ତ କରିପାରିବ। ଏହି ଲୋକେଶନ୍ ସେବାଗୁଡ଼ିକ ନିଶ୍ଚିତରୂପେ ଅନ୍ ରହିବା ଦରକାର ଏବଂ ଆପ୍ର ବ୍ୟବହାର ପାଇଁ ଫୋନ୍ରେ ଉପଲବ୍ଧ ଥିବା ଦରକାର। ଏହା ବ୍ୟାଟେରୀ ଅଧିକା ଖର୍ଚ୍ଚ କରିପାରେ।"</string>
<string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"ପାଖାପାଖି ଲୋକେଶନ୍ ଆକ୍ସେସ୍ କରେ (ନେଟ୍ୱର୍କ-ଆଧାରିତ)"</string>
<string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"ଏହି ଆପ୍, ନେଟ୍ୱର୍କ ସୋର୍ସ ଉପରେ ଆଧାର କରି ଆପଣଙ୍କ ଲୋକେଶନ୍ ପ୍ରାପ୍ତ କରିପାରେ, ଯେପରିକି ସେଲ୍ ଟାୱାର୍ ଓ ୱାଇ-ଫାଇ ନେଟ୍ୱର୍କ। ଏହି ଲୋକେଶନ୍ ସେବା, ଆପଣଙ୍କ ଟାବଲେଟ୍ରେ ଅନ୍ ରହିଥିବା ଓ ଉପଲବ୍ଧ ଥିବା ଦରକାର, ଯେଉଁଥିରୁ ଆପ୍ ସେଗୁଡ଼ିକର ବ୍ୟବହାର କରିପାରିବ।"</string>
@@ -518,6 +517,37 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"ଆଙ୍ଗୁଠି ଚିହ୍ନ ଆଇକନ୍"</string>
+ <string name="permlab_manageFace" msgid="2137540986007309781">"ଫେସ୍ ପ୍ରମାଣୀକରଣ ହାର୍ଡୱେର୍ର ପରିଚାଳନା କରନ୍ତୁ"</string>
+ <string name="permdesc_manageFace" msgid="8919637120670185330">"ବ୍ୟବହାର ପାଇଁ ଆପ୍କୁ ଫେସିଆଲ୍ ଟେମ୍ପଲେଟ୍ ଯୋଡିବା ଓ ଡିଲିଟ୍ ର ପଦ୍ଧତି ପାଇଁ ଅନୁମତି ଦିଅନ୍ତୁ।"</string>
+ <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"ଫେସ୍ ପ୍ରମାଣୀକରଣ ହାର୍ଡୱେର୍ ବ୍ୟବହାର କରନ୍ତୁ"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"ଫେସ୍ ପ୍ରମାଣୀକରଣ ହାର୍ଡୱେର୍ର ପ୍ରମାଣ ପାଇଁ ଆପ୍କୁ ଅନୁମତି ଦିଅନ୍ତୁ।"</string>
+ <string name="face_acquired_insufficient" msgid="5901287247766106330">"ଫେସ୍ ଚିହ୍ନଟ ହେଲାନାହିଁ ।ଦୟାକରି ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।"</string>
+ <string name="face_acquired_too_bright" msgid="610606792381297174">"ଫେସ୍ଟି ବହୁତ ଉଜ୍ଵଳ ଦେଖାଯାଉଛି। ଦୟାକରି, କମ ଆଲୋକରେ ପୁଣିଥରେ ଚେଷ୍ଟା କରନ୍ତୁ।"</string>
+ <string name="face_acquired_too_dark" msgid="7229162716976778371">"ଫେସ୍ଟି ବହୁତ କଳା ଦେଖାଯାଉଛି। ଦୟାକରି ଆଲୋକକୁ ଅନାବୃତ କରନ୍ତୁ।"</string>
+ <string name="face_acquired_too_close" msgid="1980310037427755293">"ଦୟାକରି ସେନ୍ସର୍କୁ ଫେସ୍ ପାଖରୁ ଦୂରେଇ ରଖନ୍ତୁ"</string>
+ <string name="face_acquired_too_far" msgid="4494571381828850007">"ଦୟାକରି ସେନ୍ସର୍କୁ ଫେସ୍ର ଅତି ନିକଟକୁ ଆଣନ୍ତୁ।"</string>
+ <string name="face_acquired_too_high" msgid="228411096134808372">"ଦୟାକରି ସେନ୍ସର୍କୁ ଆହୁରି ଉପରକୁ ଘୁଞ୍ଚାନ୍ତୁ।"</string>
+ <string name="face_acquired_too_low" msgid="4539774649296349109">"ଦୟାକରି ସେନ୍ସର୍କୁ ତଳକୁ ଘୁଞ୍ଚାନ୍ତୁ।"</string>
+ <string name="face_acquired_too_right" msgid="1650292067226118760">"ଦୟାକରି ସେନ୍ସର୍କୁ ଦକ୍ଷିଣକୁ ଘୁଞ୍ଚାନ୍ତୁ।"</string>
+ <string name="face_acquired_too_left" msgid="2712489669456176505">"ଦୟାକରି ସେନ୍ସର୍କୁ ବାମକୁ ଘୁଞ୍ଚାନ୍ତୁ।"</string>
+ <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"ଦୟାକରି ସେନ୍ସର୍କୁ ଦେଖନ୍ତୁ"</string>
+ <string name="face_acquired_not_detected" msgid="5707782294589511391">"କୌଣସି ଫେସ୍ ଚିହ୍ନଟ ହେଲା ନାହିଁ"</string>
+ <string name="face_acquired_not_steady" msgid="3722829465011040042">"ଡିଭାଇସ୍ର ସାମ୍ନାରେ ଫେସ୍ ସିଧା ରଖନ୍ତୁ।"</string>
+ <string-array name="face_acquired_vendor">
+ </string-array>
+ <string name="face_error_hw_not_available" msgid="6255891785768984615">"ଫେସ୍ର ହାର୍ଡୱେୟାର୍ ଉପଲବ୍ଧ ନାହିଁ।"</string>
+ <string name="face_error_timeout" msgid="4014326147867150054">"ଫେସ୍ର ସମୟସୀମା ସରିଗଲା। ପୁଣିଥରେ ଚେଷ୍ଟା କରନ୍ତୁ।"</string>
+ <string name="face_error_no_space" msgid="8224993703466381314">"ଫେସ୍ ମେମୋରୀରେ ଷ୍ଟୋର୍ କରାଯାଇପାରିବ ନାହିଁ।"</string>
+ <string name="face_error_canceled" msgid="283945501061931023">"ଫେସ୍ର ଅପରେଶନ୍ କ୍ୟାନ୍ସଲ୍ ହୋଇଗଲା"</string>
+ <string name="face_error_lockout" msgid="3407426963155388504">"ବାରମ୍ବାର ଚେଷ୍ଟା। ପରେ ପୁଣିଥରେ ଚେଷ୍ଟା କରନ୍ତୁ।"</string>
+ <string name="face_error_lockout_permanent" msgid="8198354656746088890">"ବାରମ୍ବାର ଚେଷ୍ଟା। ଫେସ୍ ପ୍ରମାଣୀକରଣ ଅକ୍ଷମ କରାଗଲା।"</string>
+ <string name="face_error_unable_to_process" msgid="238761109287767270">"ପୁଣିଥରେ ଚେଷ୍ଟା କରନ୍ତୁ।"</string>
+ <string name="face_error_not_enrolled" msgid="9166792142679691323">"କୌଣସି ଫେସ୍ ପଞ୍ଜୀକୃତ ହୋଇନଥିଲା।"</string>
+ <string name="face_error_hw_not_present" msgid="4737289254517095671">"ଡିଭାଇସ୍ର ଗୋଟିଏ ଫେସ୍ ପ୍ରମାଣୀକରଣ ସେନ୍ସର୍ ନାହିଁ"</string>
+ <string name="face_name_template" msgid="7004562145809595384">"<xliff:g id="FACEID">%d</xliff:g>ଙ୍କ ଫେସ୍"</string>
+ <string-array name="face_error_vendor">
+ </string-array>
+ <string name="face_icon_content_description" msgid="4024817159806482191">"ଫେସ୍ ଆଇକନ୍"</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"ସିଙ୍କ ସେଟିଙ୍ଗକୁ ପଢ଼ନ୍ତୁ"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"ଏକ ଆକାଉଣ୍ଟ ପାଇଁ ସିଙ୍କ ସେଟିଙ୍ଗ ପଢ଼ିବାକୁ ଆପ୍ଟିକୁ ଅନୁମତି ଦିଏ। ଉଦାହରଣସ୍ୱରୂପ, ଲୋକଙ୍କ ଆପ୍ ଏକ ଆକାଉଣ୍ଟରେ ସିଙ୍କ ହୋଇଛି କି ନାହିଁ ଏହା ଜାଣିପାରେ।"</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"ସିଙ୍କ ଅନ୍ ଓ ଅଫ୍ ଟୋଗଲ୍ କରନ୍ତୁ"</string>
@@ -1178,6 +1208,9 @@
<skip />
<string name="wifi_no_internet" msgid="8938267198124654938">"ୱାଇ-ଫାଇର କୌଣସି ଇଣ୍ଟରନେଟ୍ ଆକ୍ସେସ୍ ନାହିଁ"</string>
<string name="wifi_no_internet_detailed" msgid="8083079241212301741">"ବିକଳ୍ପ ପାଇଁ ଟାପ୍ କରନ୍ତୁ"</string>
+ <string name="wifi_softap_config_change" msgid="8475911871165857607">"ଆପଣଙ୍କର ହଟ୍ସ୍ପଟ୍ ସେଟିଙ୍ଗକୁ ବଦଳିଯାଇଛି"</string>
+ <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"ଆପଣଙ୍କର ହଟ୍ସ୍ପଟ୍ ପରିବର୍ତ୍ତନ କରାଯାଇଛି"</string>
+ <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"କେବଳ 5GHz ପାଇଁ, ଏହି ଡିଭାଇସ୍ ଆପଣଙ୍କର ପସନ୍ଦକୁ ସପୋର୍ଟ କରେନାହିଁ। ଏହା ପରିବର୍ତ୍ତେ, ଏହି ଡିଭାଇସ୍ 5GHz ବ୍ୟାଣ୍ଡ ବ୍ୟବହାର କରିବ।"</string>
<string name="network_switch_metered" msgid="4671730921726992671">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g>କୁ ବଦଳାଗଲା"</string>
<string name="network_switch_metered_detail" msgid="775163331794506615">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>ର ଇଣ୍ଟରନେଟ୍ ଆକ୍ସେସ୍ ନଥିବାବେଳେ ଡିଭାଇସ୍ <xliff:g id="NEW_NETWORK">%1$s</xliff:g> ବ୍ୟବହାର କରିଥାଏ। ଶୁଳ୍କ ଲାଗୁ ହୋଇପାରେ।"</string>
<string name="network_switch_metered_toast" msgid="5779283181685974304">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> ରୁ <xliff:g id="NEW_NETWORK">%2$s</xliff:g>କୁ ବଦଳାଗଲା"</string>
@@ -1698,8 +1731,7 @@
<string name="package_updated_device_owner" msgid="1847154566357862089">"ଆପଣଙ୍କ ଆଡମିନ୍ ଅପଡେଟ୍ କରିଛନ୍ତି"</string>
<string name="package_deleted_device_owner" msgid="2307122077550236438">"ଆପଣଙ୍କ ଆଡମିନ୍ ଡିଲିଟ୍ କରିଛନ୍ତି"</string>
<string name="battery_saver_description_with_learn_more" msgid="6323937147992667707">"ବ୍ୟାଟେରୀର କାର୍ଯ୍ୟକାଳକୁ ବଢ଼ାଇବା ପାଇଁ, ବ୍ୟାଟେରୀ ସେଭର୍ କିଛି ଡିଭାଇସ୍ ଫିଚର୍କୁ ବନ୍ଦ କରିବା ସହ କେତେକ ଆପ୍କୁ ଚାଲିବାରୁ ରୋକିଥାଏ। "<annotation id="url">"ଅଧିକ ଜାଣନ୍ତୁ"</annotation></string>
- <!-- no translation found for battery_saver_description (769989536172631582) -->
- <skip />
+ <string name="battery_saver_description" msgid="769989536172631582">"ଆପଣଙ୍କ ବ୍ୟାଟେରୀ ଆୟୁଶ ବଢାଇବାକୁ, ବ୍ୟାଟେରୀ ସେଭର୍ କିଛି ଡିଭାଇସ୍ ଫିଚର୍ଗୁଡିକୁ ବନ୍ଦ କରେ ଏବଂ ଆପ୍ଗୁଡିକୁ ପ୍ରତିବନ୍ଧିତ କରିଥାଏ।"</string>
<string name="data_saver_description" msgid="6015391409098303235">"ଡାଟା ବ୍ୟବହାର କମ୍ କରିବାରେ ସାହାଯ୍ୟ କରିବାକୁ, ଡାଟା ସେଭର୍ ବ୍ୟାକ୍ଗ୍ରାଉଣ୍ଡରେ ଡାଟା ପଠାଇବା କିମ୍ବା ପ୍ରାପ୍ତ କରିବାକୁ କିଛି ଆପ୍କୁ ବ୍ଲକ୍ କରେ। ଆପଣ ବର୍ତ୍ତମାନ ବ୍ୟବହାର କରୁଥିବା ଆପ୍, ଡାଟା ଆକ୍ସେସ୍ କରିପାରେ, କିନ୍ତୁ ଏହା କମ୍ ସମୟରେ କରିପାରେ। ଏହାର ଅର୍ଥ ହୋଇପାରେ, ଯେପରି, ଆପଣ ଟାପ୍ ନକରିବା ପର୍ଯ୍ୟନ୍ତ ଯେଉଁ ଇମେଜ୍ ଦେଖାଯାଏ ନାହିଁ।"</string>
<string name="data_saver_enable_title" msgid="4674073932722787417">"ଡାଟା ସେଭର୍ ଅନ୍ କରିବେ?"</string>
<string name="data_saver_enable_button" msgid="7147735965247211818">"ଅନ୍ କରନ୍ତୁ"</string>
@@ -1878,10 +1910,8 @@
<string name="volume_dialog_ringer_guidance_silent" msgid="2128975224280276122">"କଲ୍ ଓ ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକୁ ନିଃଶବ୍ଦ କରିଦିଆଯିବ"</string>
<string name="notification_channel_system_changes" msgid="5072715579030948646">"ସିଷ୍ଟମ୍ରେ ପରିବର୍ତ୍ତନ"</string>
<string name="notification_channel_do_not_disturb" msgid="6766940333105743037">"ବିରକ୍ତ କରନ୍ତୁ ନାହିଁ"</string>
- <!-- no translation found for zen_upgrade_notification_visd_title (3288313883409759733) -->
- <skip />
- <!-- no translation found for zen_upgrade_notification_visd_content (5533674060311631165) -->
- <skip />
+ <string name="zen_upgrade_notification_visd_title" msgid="3288313883409759733">"ନୂଆ: \"ବିରକ୍ତ କରନ୍ତୁ ନାହିଁ\" ମୋଡ୍ ଅନ୍ ଥିବା ଯୋଗୁଁ ବିଜ୍ଞପ୍ତି ଲୁଚାଇ ଦିଆଯାଉଛି"</string>
+ <string name="zen_upgrade_notification_visd_content" msgid="5533674060311631165">"ଅଧିକ ଜାଣିବାକୁ ଟ୍ୟାପ୍ କରନ୍ତୁ ଏବଂ ବଦଳାନ୍ତୁ।"</string>
<string name="zen_upgrade_notification_title" msgid="3799603322910377294">"’ବିରକ୍ତ କରନ୍ତୁ ନାହିଁ’ ବଦଳିଯାଇଛି"</string>
<string name="zen_upgrade_notification_content" msgid="1794994264692424562">"କ’ଣ ଅବରୋଧ ହୋଇଛି ଯାଞ୍ଚ କରିବା ପାଇଁ ଟାପ୍ କରନ୍ତୁ।"</string>
<string name="notification_app_name_system" msgid="4205032194610042794">"ସିଷ୍ଟମ୍"</string>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index c454a73..48c1a38 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -236,9 +236,6 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"ਏਅਰਪਲੇਨ ਮੋਡ"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"ਏਅਰਪਲੇਨ ਮੋਡ ਚਾਲੂ ਹੈ"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"ਏਅਰਪਲੇਨ ਮੋਡ ਬੰਦ ਹੈ"</string>
- <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"ਬੈਟਰੀ ਸੇਵਰ"</string>
- <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"ਬੈਟਰੀ ਸੇਵਰ ਬੰਦ ਹੈ"</string>
- <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"ਬੈਟਰੀ ਸੇਵਰ ਚਾਲੂ ਹੈ"</string>
<string name="global_action_settings" msgid="1756531602592545966">"ਸੈਟਿੰਗਾਂ"</string>
<string name="global_action_assist" msgid="3892832961594295030">"ਸਹਾਇਤਾ ਕਰੋ"</string>
<string name="global_action_voice_assist" msgid="7751191495200504480">"ਅਵਾਜ਼ੀ ਸਹਾਇਕ"</string>
@@ -276,6 +273,9 @@
<string name="permgrouplab_location" msgid="7275582855722310164">"ਟਿਕਾਣਾ"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"ਇਸ ਡੀਵਾਈਸ ਦੇ ਨਿਰਧਾਰਤ ਟਿਕਾਣੇ ਤੱਕ ਪਹੁੰਚੋ"</string>
<string name="permgrouprequest_location" msgid="3788275734953323491">"ਕੀ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ਨੂੰ ਇਸ ਡੀਵਾਈਸ ਦੇ ਟਿਕਾਣੇ ਤੱਕ ਪਹੁੰਚ ਕਰਨ ਦੇਣੀ ਹੈ?"</string>
+ <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"ਤੁਹਾਡੇ ਵੱਲੋਂ ਐਪ ਦੀ ਵਰਤੋਂ ਕਰਨ ਵੇਲੇ ਹੀ ਐਪ ਕੋਲ ਟਿਕਾਣੇ ਤੱਕ ਪਹੁੰਚ ਹੋਵੇਗੀ।"</string>
+ <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"ਕੀ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ਨੂੰ ਹਮੇਸ਼ਾਂ ਇਸ ਡੀਵਾਈਸ ਦੇ ਟਿਕਾਣੇ ਤੱਕ ਪਹੁੰਚ ਕਰਨ ਦੇਣੀ ਹੈ?"</string>
+ <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"ਐਪ ਕੋਲ ਹਮੇਸ਼ਾਂ ਟਿਕਾਣੇ ਤੱਕ ਪਹੁੰਚ ਹੋਵੇਗੀ, ਭਾਵੇਂ ਤੁਸੀਂ ਐਪ ਦੀ ਵਰਤੋਂ ਨਾ ਕਰ ਰਹੇ ਹੋਵੋ।"</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"ਕੈਲੰਡਰ"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"ਤੁਹਾਡੇ ਕੈਲੰਡਰ ਤੱਕ ਪਹੁੰਚ ਕਰਨ"</string>
<string name="permgrouprequest_calendar" msgid="289900767793189421">"ਕੀ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ਨੂੰ ਤੁਹਾਡੇ ਕੈਲੰਡਰ ਤੱਕ ਪਹੁੰਚ ਕਰਨੀ ਦੇਣੀ ਹੈ?"</string>
@@ -402,8 +402,7 @@
<string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"ਇਹ ਐਪ ਤੁਹਾਡੇ ਫ਼ੋਨ \'ਤੇ ਕੈਲੰਡਰ ਇਵੈਂਟਾਂ ਨੂੰ ਸ਼ਾਮਲ ਕਰ ਸਕਦੀ ਹੈ, ਹਟਾ ਸਕਦੀ ਹੈ, ਜਾਂ ਬਦਲ ਸਕਦੀ ਹੈ। ਇਹ ਐਪ ਉਹਨਾਂ ਸੁਨੇਹਿਆਂ ਨੂੰ ਭੇਜ ਸਕਦੀ ਹੈ ਜੋ ਕੈਲੰਡਰ ਮਾਲਕਾਂ ਤੋਂ ਆਉਂਦੇ ਜਾਪ ਸਕਦੇ ਹਨ, ਜਾਂ ਉਹਨਾਂ ਦੇ ਮਾਲਕਾਂ ਨੂੰ ਸੂਚਿਤ ਕੀਤੇ ਬਿਨਾਂ ਇਵੈਂਟਾਂ ਨੂੰ ਬਦਲ ਸਕਦੀ ਹੈ।"</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"ਵਾਧੂ ਟਿਕਾਣਾ ਪ੍ਰਦਾਤਾ ਕਮਾਂਡਾਂ ਤੱਕ ਪਹੁੰਚ"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"ਐਪ ਨੂੰ ਵਾਧੂ ਟਿਕਾਣਾ ਪ੍ਰਦਾਤਾ ਕਮਾਂਡਾਂ ਤੱਕ ਪਹੁੰਚ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ। ਇਹ ਐਪ ਨੂੰ GPS ਜਾਂ ਹੋਰ ਟਿਕਾਣਾ ਸਰੋਤਾਂ ਦੇ ਓਪਰੇਸ਼ਨ ਵਿੱਚ ਵਿਘਨ ਪਾਉਣ ਦੀ ਆਗਿਆ ਦੇ ਸਕਦਾ ਹੈ।"</string>
- <!-- no translation found for permlab_accessFineLocation (6265109654698562427) -->
- <skip />
+ <string name="permlab_accessFineLocation" msgid="6265109654698562427">"ਸਿਰਫ਼ ਫੋਰਗ੍ਰਾਊਂਡ ਵਿੱਚ ਸਟੀਕ ਟਿਕਾਣੇ ਤੱਕ ਪਹੁੰਚ ਕਰੋ"</string>
<string name="permdesc_accessFineLocation" msgid="3520508381065331098">"ਇਹ ਐਪ ਫੋਰਗ੍ਰਾਉਂਡ ਵਿੱਚ ਹੋਣ \'ਤੇ ਹੀ ਤੁਹਾਡਾ ਸਟੀਕ ਟਿਕਾਣਾ ਪਤਾ ਕਰ ਸਕਦੀ ਹੈ। ਐਪ ਵੱਲੋਂ ਟਿਕਾਣਾ ਸੇਵਾਵਾਂ ਦੀ ਵਰਤੋਂ ਕਰਨ ਲਈ ਇਹ ਸੇਵਾਵਾਂ ਤੁਹਾਡੇ ਫ਼ੋਨ \'ਤੇ ਉਪਲਬਧ ਹੋਣੀਆਂ ਅਤੇ ਚਾਲੂ ਕੀਤੀਆਂ ਹੋਣੀਆਂ ਲਾਜ਼ਮੀ ਹਨ। ਇਸ ਨਾਲ ਬੈਟਰੀ ਦੀ ਖਪਤ ਵਧ ਸਕਦੀ ਹੈ।"</string>
<string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"ਅੰਦਾਜ਼ਨ ਟਿਕਾਣੇ \'ਤੇ ਪਹੁੰਚ ਕਰੋ (ਨੈੱਟਵਰਕ-ਆਧਾਰਿਤ)"</string>
<string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"ਇਹ ਐਪ ਨੈੱਟਵਰਕ ਸਰੋਤਾਂ ਜਿਵੇਂ ਕਿ ਸੈੱਲ ਟਾਵਰਾਂ ਅਤੇ ਵਾਈ-ਫਾਈ ਨੈੱਟਵਰਕਾਂ \'ਤੇ ਆਧਾਰਿਤ ਤੁਹਾਡਾ ਟਿਕਾਣਾ ਪਤਾ ਕਰ ਸਕਦੀ ਹੈ। ਐਪ ਦੁਆਰਾ ਟਿਕਾਣਾ ਸੇਵਾਵਾਂ ਦੀ ਵਰਤੋਂ ਕੀਤੇ ਜਾਣ ਦੇ ਯੋਗ ਹੋਣ ਲਈ ਇਹ ਸੇਵਾਵਾਂ ਤੁਹਾਡੇ ਟੈਬਲੈੱਟ \'ਤੇ ਉਪਲਬਧ ਹੋਣੀਆਂ ਅਤੇ ਚਾਲੂ ਕੀਤੀਆਂ ਹੋਣੀਆਂ ਲਾਜ਼ਮੀ ਹਨ।"</string>
@@ -518,6 +517,37 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਪ੍ਰਤੀਕ"</string>
+ <string name="permlab_manageFace" msgid="2137540986007309781">"ਚਿਹਰਾ ਪ੍ਰਮਾਣੀਕਰਨ ਹਾਰਡਵੇਅਰ ਦਾ ਪ੍ਰਬੰਧਨ ਕਰੋ"</string>
+ <string name="permdesc_manageFace" msgid="8919637120670185330">"ਐਪ ਨੂੰ ਵਰਤਣ ਲਈ ਚਿਹਰਾ ਟੈਮਪਲੇਟ ਸ਼ਾਮਲ ਕਰਨ ਜਾਂ ਮਿਟਾਉਣ ਦੀਆਂ ਵਿਧੀਆਂ ਦੀ ਬੇਨਤੀ ਕਰਨ ਦਿੰਦੀ ਹੈ।"</string>
+ <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"ਚਿਹਰਾ ਪ੍ਰਮਾਣੀਕਰਨ ਹਾਰਡਵੇਅਰ ਵਰਤੋ"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"ਐਪ ਨੂੰ ਪ੍ਰਮਾਣੀਕਰਨ ਲਈ ਚਿਹਰਾ ਪ੍ਰਮਾਣੀਕਰਨ ਹਾਰਡਵੇਅਰ ਵਰਤਣ ਦਿੰਦੀ ਹੈ"</string>
+ <string name="face_acquired_insufficient" msgid="5901287247766106330">"ਚਿਹਰੇ ਦੀ ਪਛਾਣ ਨਹੀਂ ਹੋਈ। ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
+ <string name="face_acquired_too_bright" msgid="610606792381297174">"ਚਿਹਰਾ ਬਹੁਤ ਚਮਕਦਾਰ ਹੈ। ਘੱਟ ਰੋਸ਼ਨੀ ਵਿੱਚ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
+ <string name="face_acquired_too_dark" msgid="7229162716976778371">"ਚਿਹਰਾ ਹਨੇਰੇ ਵਿੱਚ ਹੈ। ਚਾਨਣ ਕਰੋ।"</string>
+ <string name="face_acquired_too_close" msgid="1980310037427755293">"ਚਿਹਰੇ ਨੂੰ ਸੈਂਸਰ ਤੋਂ ਦੂਰ ਲਿਜਾਓ।"</string>
+ <string name="face_acquired_too_far" msgid="4494571381828850007">"ਚਿਹਰੇ ਨੂੰ ਸੈਂਸਰ ਦੇ ਨੇੜੇ ਲਿਆਓ।"</string>
+ <string name="face_acquired_too_high" msgid="228411096134808372">"ਸੈਂਸਰ ਨੂੰ ਉੱਪਰ ਕਰੋ।"</string>
+ <string name="face_acquired_too_low" msgid="4539774649296349109">"ਸੈਂਸਰ ਨੂੰ ਥੱਲੇ ਕਰੋ।"</string>
+ <string name="face_acquired_too_right" msgid="1650292067226118760">"ਸੈਂਸਰ ਨੂੰ ਸੱਜੇ ਪਾਸੇ ਲਿਜਾਓ।"</string>
+ <string name="face_acquired_too_left" msgid="2712489669456176505">"ਸੈਂਸਰ ਨੂੰ ਖੱਬੇ ਪਾਸੇ ਲਿਜਾਓ।"</string>
+ <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"ਕਿਰਪਾ ਕਰਕੇ ਸੈਂਸਰ ਵੱਲ ਦੇਖੋ।"</string>
+ <string name="face_acquired_not_detected" msgid="5707782294589511391">"ਕੋਈ ਚਿਹਰਾ ਨਹੀਂ ਦਿਸਿਆ।"</string>
+ <string name="face_acquired_not_steady" msgid="3722829465011040042">"ਚਿਹਰੇ ਨੂੰ ਡੀਵਾਈਸ ਦੇ ਸਾਹਮਣੇ ਸਥਿਰ ਰੱਖੋ।"</string>
+ <string-array name="face_acquired_vendor">
+ </string-array>
+ <string name="face_error_hw_not_available" msgid="6255891785768984615">"ਚਿਹਰਾ ਹਾਰਡਵੇਅਰ ਉਪਲਬਧ ਨਹੀਂ ਹੈ।"</string>
+ <string name="face_error_timeout" msgid="4014326147867150054">"ਚਿਹਰਾ ਪਛਾਣਨ ਦਾ ਸਮਾਂ ਸਮਾਪਤ ਹੋਇਆ। ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
+ <string name="face_error_no_space" msgid="8224993703466381314">"ਚਿਹਰੇ ਦੀ ਜਾਣਕਾਰੀ ਨੂੰ ਸਟੋਰ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ।"</string>
+ <string name="face_error_canceled" msgid="283945501061931023">"ਚਿਹਰਾ ਪਛਾਣਨ ਦੀ ਪ੍ਰਕਿਰਿਆ ਰੱਦ ਕੀਤੀ ਗਈ।"</string>
+ <string name="face_error_lockout" msgid="3407426963155388504">"ਹੱਦੋਂ ਵੱਧ ਕੋਸ਼ਿਸ਼ਾਂ। ਬਾਅਦ ਵਿੱਚ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
+ <string name="face_error_lockout_permanent" msgid="8198354656746088890">"ਹੱਦੋਂ ਵੱਧ ਕੋਸ਼ਿਸ਼ਾਂ। ਚਿਹਰਾ ਪ੍ਰਮਾਣੀਕਰਨ ਬੰਦ ਹੈ।"</string>
+ <string name="face_error_unable_to_process" msgid="238761109287767270">"ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
+ <string name="face_error_not_enrolled" msgid="9166792142679691323">"ਕੋਈ ਚਿਹਰਾ ਜਾਣਕਾਰੀ ਦਰਜ ਨਹੀਂ ਕੀਤੀ ਗਈ।"</string>
+ <string name="face_error_hw_not_present" msgid="4737289254517095671">"ਇਸ ਡੀਵਾਈਸ ਵਿੱਚ ਕੋਈ ਚਿਹਰਾ ਪ੍ਰਮਾਣੀਕਰਨ ਸੈਂਸਰ ਨਹੀਂ ਹੈ"</string>
+ <string name="face_name_template" msgid="7004562145809595384">"ਚਿਹਰਾ <xliff:g id="FACEID">%d</xliff:g>"</string>
+ <string-array name="face_error_vendor">
+ </string-array>
+ <string name="face_icon_content_description" msgid="4024817159806482191">"ਚਿਹਰਾ ਪ੍ਰਤੀਕ"</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"ਸਿੰਕ ਸੈਟਿੰਗਾਂ ਪੜ੍ਹੋ"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"ਐਪ ਨੂੰ ਕਿਸੇ ਖਾਤੇ ਲਈ ਸਮਕਾਲੀਕਰਨ ਸੈਟਿੰਗਾਂ ਪੜ੍ਹਨ ਦੇ ਯੋਗ ਬਣਾਉਂਦਾ ਹੈ। ਉਦਾਹਰਨ ਲਈ, ਇਹ ਪਤਾ ਕਰ ਸਕਦਾ ਹੈ ਕਿ People ਐਪ ਦਾ ਕਿਸੇ ਖਾਤੇ ਨਾਲ ਸਮਕਾਲੀਕਿਰਤ ਕੀਤਾ ਗਿਆ ਹੈ ਜਾਂ ਨਹੀਂ।"</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"ਸਮਕਾਲੀਕਰਨ ਚਾਲੂ ਅਤੇ ਬੰਦ ਨੂੰ ਟੌਗਲ ਕਰੋ"</string>
@@ -1178,6 +1208,9 @@
<skip />
<string name="wifi_no_internet" msgid="8938267198124654938">"ਵਾਈ-ਫਾਈ ਦੀ ਕੋਈ ਇੰਟਰਨੈੱਟ ਪਹੁੰਚ ਨਹੀਂ ਹੈ"</string>
<string name="wifi_no_internet_detailed" msgid="8083079241212301741">"ਵਿਕਲਪਾਂ ਲਈ ਟੈਪ ਕਰੋ"</string>
+ <string name="wifi_softap_config_change" msgid="8475911871165857607">"ਤੁਹਾਡੀਆਂ ਹੌਟਸਪੌਟ ਸੈਟਿੰਗਾਂ ਵਿੱਚ ਬਦਲਾਅ"</string>
+ <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"ਤੁਹਾਡਾ ਹੌਟਸਪੌਟ ਬੈਂਡ ਬਦਲ ਗਿਆ ਹੈ।"</string>
+ <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"ਇਹ ਡੀਵਾਈਸ ਸਿਰਫ਼ 5GHz ਦੀ ਤੁਹਾਡੀ ਤਰਜੀਹ ਦਾ ਸਮਰਥਨ ਨਹੀਂ ਕਰਦਾ ਹੈ। ਇਸਦੀ ਬਜਾਏ, ਇਹ ਡੀਵਾਈਸ ਉਪਲਬਧ ਹੋਣ \'ਤੇ 5GHz ਬੈਂਡ ਵਰਤੇਗਾ।"</string>
<string name="network_switch_metered" msgid="4671730921726992671">"ਬਦਲਕੇ <xliff:g id="NETWORK_TYPE">%1$s</xliff:g> ਲਿਆਂਦਾ ਗਿਆ"</string>
<string name="network_switch_metered_detail" msgid="775163331794506615">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> ਦੀ ਇੰਟਰਨੈੱਟ \'ਤੇ ਪਹੁੰਚ ਨਾ ਹੋਣ \'ਤੇ ਡੀਵਾਈਸ <xliff:g id="NEW_NETWORK">%1$s</xliff:g> ਦੀ ਵਰਤੋਂ ਕਰਦਾ ਹੈ। ਖਰਚੇ ਲਾਗੂ ਹੋ ਸਕਦੇ ਹਨ।"</string>
<string name="network_switch_metered_toast" msgid="5779283181685974304">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> ਤੋਂ ਬਦਲਕੇ <xliff:g id="NEW_NETWORK">%2$s</xliff:g> \'ਤੇ ਕੀਤਾ ਗਿਆ"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index fa5d2ce..188e3e4 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -242,9 +242,6 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Tryb samolotowy"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Tryb samolotowy jest włączony"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Tryb samolotowy jest wyłączony"</string>
- <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Oszczędzanie baterii"</string>
- <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Oszczędzanie baterii WYŁ."</string>
- <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Oszczędzanie baterii WŁ."</string>
<string name="global_action_settings" msgid="1756531602592545966">"Ustawienia"</string>
<string name="global_action_assist" msgid="3892832961594295030">"Pomoc"</string>
<string name="global_action_voice_assist" msgid="7751191495200504480">"Asystent głosowy"</string>
@@ -282,6 +279,9 @@
<string name="permgrouplab_location" msgid="7275582855722310164">"Lokalizacja"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"dostęp do informacji o lokalizacji tego urządzenia"</string>
<string name="permgrouprequest_location" msgid="3788275734953323491">"Zezwolić aplikacji <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> na dostęp do lokalizacji urządzenia?"</string>
+ <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"Aplikacja będzie mieć dostęp do lokalizacji tylko wtedy, gdy jest używana."</string>
+ <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"Zawsze zezwalać aplikacji <xliff:g id="APP_NAME">%1$s</xliff:g> na dostęp do lokalizacji urządzenia?"</string>
+ <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"Aplikacja będzie zawsze mieć dostęp do lokalizacji, nawet wtedy, gdy nie jest używana."</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Kalendarz"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"dostęp do kalendarza"</string>
<string name="permgrouprequest_calendar" msgid="289900767793189421">"Zezwolić aplikacji <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> na dostęp do kalendarza?"</string>
@@ -408,8 +408,7 @@
<string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Ta aplikacja może dodawać, usuwać i zmieniać wydarzenia z kalendarza na telefonie. Ta aplikacja może wysyłać wiadomości wyglądające jak utworzone przez właścicieli kalendarza lub zmieniać wydarzenia bez wiedzy ich właścicieli."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"dostęp do dodatkowych poleceń dostawcy informacji o lokalizacji"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Pozwala aplikacji na dostęp do dodatkowych poleceń dostawcy informacji o lokalizacji. Aplikacje z tym uprawnieniem mogą wpływać na działanie GPS-a lub innych źródeł lokalizacji."</string>
- <!-- no translation found for permlab_accessFineLocation (6265109654698562427) -->
- <skip />
+ <string name="permlab_accessFineLocation" msgid="6265109654698562427">"dostęp do dokładnej lokalizacji tylko na pierwszym planie"</string>
<string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Ta aplikacja może określić Twoją dokładną lokalizację tylko wtedy, gdy działa na pierwszym planie. Te usługi lokalizacyjne muszą być włączone i dostępne na telefonie, by aplikacja mogła z nich korzystać. Może to zwiększyć zużycie baterii."</string>
<string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"dostęp do przybliżonej lokalizacji (na podstawie sieci)"</string>
<string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Ta aplikacja może określać Twoją lokalizację na podstawie źródeł sieciowych, takich jak stacje bazowe i sieci Wi-Fi. Te usługi lokalizacyjne muszą być włączone i dostępne na tablecie, by aplikacja mogła z nich korzystać."</string>
@@ -524,6 +523,37 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Ikona odcisku palca"</string>
+ <string name="permlab_manageFace" msgid="2137540986007309781">"zarządzanie sprzętem do uwierzytelniania za pomocą twarzy"</string>
+ <string name="permdesc_manageFace" msgid="8919637120670185330">"Zezwala na aktywowanie przez aplikację metody dodawania i usuwania szablonów twarzy."</string>
+ <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"używanie sprzętu do uwierzytelniania za pomocą twarzy"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Zezwala na używanie przez aplikację sprzętu do analizy twarzy na potrzeby uwierzytelniania"</string>
+ <string name="face_acquired_insufficient" msgid="5901287247766106330">"Nie można przeanalizować twarzy. Spróbuj ponownie."</string>
+ <string name="face_acquired_too_bright" msgid="610606792381297174">"Zbyt jasna twarz. Spróbuj w ciemniejszym miejscu."</string>
+ <string name="face_acquired_too_dark" msgid="7229162716976778371">"Zbyt ciemna twarz. Spróbuj w jaśniejszym miejscu."</string>
+ <string name="face_acquired_too_close" msgid="1980310037427755293">"Oddal czujnik od twarzy."</string>
+ <string name="face_acquired_too_far" msgid="4494571381828850007">"Przybliż czujnik do twarzy."</string>
+ <string name="face_acquired_too_high" msgid="228411096134808372">"Przesuń czujnik wyżej."</string>
+ <string name="face_acquired_too_low" msgid="4539774649296349109">"Przesuń czujnik niżej."</string>
+ <string name="face_acquired_too_right" msgid="1650292067226118760">"Przesuń czujnik w prawo."</string>
+ <string name="face_acquired_too_left" msgid="2712489669456176505">"Przesuń czujnik w lewo."</string>
+ <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Spójrz na czujnik."</string>
+ <string name="face_acquired_not_detected" msgid="5707782294589511391">"Nie wykryto twarzy."</string>
+ <string name="face_acquired_not_steady" msgid="3722829465011040042">"Nie ruszaj twarzą, patrząc na urządzenie."</string>
+ <string-array name="face_acquired_vendor">
+ </string-array>
+ <string name="face_error_hw_not_available" msgid="6255891785768984615">"Czujnik twarzy nie jest dostępny."</string>
+ <string name="face_error_timeout" msgid="4014326147867150054">"Upłynął limit czasu analizy twarzy. Spróbuj ponownie."</string>
+ <string name="face_error_no_space" msgid="8224993703466381314">"Nie można zapisać informacji o twarzy."</string>
+ <string name="face_error_canceled" msgid="283945501061931023">"Analiza twarzy została anulowana."</string>
+ <string name="face_error_lockout" msgid="3407426963155388504">"Zbyt wiele prób. Spróbuj ponownie później."</string>
+ <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Zbyt wiele prób. Wyłączono uwierzytelnianie za pomocą twarzy."</string>
+ <string name="face_error_unable_to_process" msgid="238761109287767270">"Spróbuj ponownie."</string>
+ <string name="face_error_not_enrolled" msgid="9166792142679691323">"Nie zarejestrowano twarzy."</string>
+ <string name="face_error_hw_not_present" msgid="4737289254517095671">"To urządzenie nie jest wyposażone w czujnik twarzy"</string>
+ <string name="face_name_template" msgid="7004562145809595384">"Twarz <xliff:g id="FACEID">%d</xliff:g>"</string>
+ <string-array name="face_error_vendor">
+ </string-array>
+ <string name="face_icon_content_description" msgid="4024817159806482191">"Ikona twarzy"</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"czytanie ustawień synchronizacji"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Zezwala aplikacji na odczyt ustawień synchronizacji konta. Pozwala to na przykład określić, czy aplikacja Ludzie jest zsynchronizowana z kontem."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"włączanie i wyłączanie synchronizacji"</string>
@@ -613,7 +643,7 @@
<string name="policydesc_encryptedStorage" msgid="2637732115325316992">"Wymaganie szyfrowania przechowywanych danych aplikacji"</string>
<string name="policylab_disableCamera" msgid="6395301023152297826">"Wyłącz aparaty"</string>
<string name="policydesc_disableCamera" msgid="2306349042834754597">"Zapobieganie używaniu wszystkich aparatów w urządzeniu"</string>
- <string name="policylab_disableKeyguardFeatures" msgid="8552277871075367771">"Wył. niektórych funkcji bl. ekr."</string>
+ <string name="policylab_disableKeyguardFeatures" msgid="8552277871075367771">"Wył. funkcji blokady ekranu"</string>
<string name="policydesc_disableKeyguardFeatures" msgid="2044755691354158439">"Zapobieganie użyciu niektórych funkcji blokady ekranu."</string>
<string-array name="phoneTypes">
<item msgid="8901098336658710359">"Dom"</item>
@@ -1222,6 +1252,9 @@
<skip />
<string name="wifi_no_internet" msgid="8938267198124654938">"Sieć Wi-Fi nie ma dostępu do internetu"</string>
<string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Kliknij, by wyświetlić opcje"</string>
+ <string name="wifi_softap_config_change" msgid="8475911871165857607">"Zmieniono ustawienia hotspotu"</string>
+ <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"Zmieniono pasmo hotspotu."</string>
+ <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"To urządzenie nie może korzystać tylko z częstotliwości 5 GHz. Będzie korzystać z tego pasma, jeśli będzie ono dostępne."</string>
<string name="network_switch_metered" msgid="4671730921726992671">"Zmieniono na połączenie typu <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
<string name="network_switch_metered_detail" msgid="775163331794506615">"Urządzenie korzysta z połączenia typu <xliff:g id="NEW_NETWORK">%1$s</xliff:g>, gdy <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> nie dostępu do internetu. Mogą zostać naliczone opłaty."</string>
<string name="network_switch_metered_toast" msgid="5779283181685974304">"Przełączono z połączenia typu <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> na <xliff:g id="NEW_NETWORK">%2$s</xliff:g>."</string>
@@ -1453,7 +1486,7 @@
<string name="add_account_button_label" msgid="3611982894853435874">"Dodaj konto"</string>
<string name="number_picker_increment_button" msgid="2412072272832284313">"Zwiększ"</string>
<string name="number_picker_decrement_button" msgid="476050778386779067">"Zmniejsz"</string>
- <string name="number_picker_increment_scroll_mode" msgid="5259126567490114216">"<xliff:g id="VALUE">%s</xliff:g> dotknij i przytrzymaj."</string>
+ <string name="number_picker_increment_scroll_mode" msgid="5259126567490114216">"<xliff:g id="VALUE">%s</xliff:g> naciśnij i przytrzymaj."</string>
<string name="number_picker_increment_scroll_action" msgid="9101473045891835490">"Przesuń w górę, by zwiększyć, i w dół, by zmniejszyć."</string>
<string name="time_picker_increment_minute_button" msgid="8865885114028614321">"Zmień minutę na późniejszą"</string>
<string name="time_picker_decrement_minute_button" msgid="6246834937080684791">"Zmień minutę na wcześniejszą"</string>
@@ -1480,7 +1513,7 @@
<string name="activitychooserview_choose_application_error" msgid="8624618365481126668">"Nie udało się uruchomić aplikacji <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
<string name="shareactionprovider_share_with" msgid="806688056141131819">"Udostępnij przez:"</string>
<string name="shareactionprovider_share_with_application" msgid="5627411384638389738">"Udostępnij przez <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
- <string name="content_description_sliding_handle" msgid="415975056159262248">"Uchwyt przesuwny. Dotknij i przytrzymaj."</string>
+ <string name="content_description_sliding_handle" msgid="415975056159262248">"Uchwyt przesuwny. Naciśnij i przytrzymaj."</string>
<string name="description_target_unlock_tablet" msgid="3833195335629795055">"Przesuń, aby odblokować."</string>
<string name="action_bar_home_description" msgid="5293600496601490216">"Przejdź do strony głównej"</string>
<string name="action_bar_up_description" msgid="2237496562952152589">"Przejdź wyżej"</string>
diff --git a/core/res/res/values-port/dimens_package_installer.xml b/core/res/res/values-port/dimens_package_installer.xml
new file mode 100644
index 0000000..af28713
--- /dev/null
+++ b/core/res/res/values-port/dimens_package_installer.xml
@@ -0,0 +1,23 @@
+<?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.
+ -->
+
+<!-- portrait dimensions for the permission grant dialog. -->
+<resources>
+ <!-- This yields 95% width -->
+ <dimen name="permissionGrantDialogWeight">380</dimen>
+ <dimen name="permissionGrantDialogWidth">0dp</dimen>
+</resources>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index 2d846c1..6c0391c 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -236,9 +236,6 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Modo avião"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Modo avião ATIVADO"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Modo avião DESATIVADO"</string>
- <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Economia de bateria"</string>
- <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"A Economia de bateria está DESATIVADA"</string>
- <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"A Economia de bateria está ATIVADA"</string>
<string name="global_action_settings" msgid="1756531602592545966">"Configurações"</string>
<string name="global_action_assist" msgid="3892832961594295030">"Assistência"</string>
<string name="global_action_voice_assist" msgid="7751191495200504480">"Ajuda de voz"</string>
@@ -276,6 +273,9 @@
<string name="permgrouplab_location" msgid="7275582855722310164">"Local"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"acesse o local do dispositivo"</string>
<string name="permgrouprequest_location" msgid="3788275734953323491">"Permitir que <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> acesse a localização deste dispositivo?"</string>
+ <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"O app só terá acesso ao local enquanto estiver sendo usado."</string>
+ <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"Sempre permitir que <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> acesse o local deste disp.?"</string>
+ <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"O app sempre terá acesso ao local, mesmo quando não estiver sendo usado."</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Agenda"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"acesse sua agenda"</string>
<string name="permgrouprequest_calendar" msgid="289900767793189421">"Permitir que <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> acesse sua agenda?"</string>
@@ -402,8 +402,7 @@
<string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Este app pode adicionar, remover ou alterar eventos da agenda no seu smartphone. Ele também pode enviar mensagens que aparentem ser de autoria do proprietário da agenda ou alterar eventos sem notificar o proprietário."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"acessar comandos extras do provedor de localização"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Permite que o app acesse comandos do provedor não relacionados à localização. Isso pode permitir que o app interfira no funcionamento do GPS ou de outras fontes de localização."</string>
- <!-- no translation found for permlab_accessFineLocation (6265109654698562427) -->
- <skip />
+ <string name="permlab_accessFineLocation" msgid="6265109654698562427">"acessar localização precisa apenas em primeiro plano"</string>
<string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Este app pode ver sua localização exata a qualquer momento apenas quando está em primeiro plano. Esses serviços de localização precisam estar ativados e disponíveis no seu smartphone para que o app possa usá-los. Isso pode aumentar o consumo de bateria."</string>
<string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"acessar localização aproximada (com base na rede)"</string>
<string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Este app pode ver seu local com base nas fontes de rede, como torres de celular e redes Wi-Fi. Esses serviços de localização precisam estar ativados e disponíveis no seu tablet para que o app possa usá-los."</string>
@@ -518,6 +517,37 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Ícone de impressão digital"</string>
+ <string name="permlab_manageFace" msgid="2137540986007309781">"gerenciar hardware de autenticação facial"</string>
+ <string name="permdesc_manageFace" msgid="8919637120670185330">"Permite que o app execute métodos para adicionar e excluir modelos de rosto para uso."</string>
+ <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"usar hardware de autenticação facial"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Permite que o app use o hardware de autenticação facial para autenticação"</string>
+ <string name="face_acquired_insufficient" msgid="5901287247766106330">"Falha ao processar o rosto. Tente novamente."</string>
+ <string name="face_acquired_too_bright" msgid="610606792381297174">"Rosto muito iluminado. Tente com menos iluminação."</string>
+ <string name="face_acquired_too_dark" msgid="7229162716976778371">"Rosto muito escuro. Acrescente uma fonte de luz."</string>
+ <string name="face_acquired_too_close" msgid="1980310037427755293">"Afaste o sensor do rosto."</string>
+ <string name="face_acquired_too_far" msgid="4494571381828850007">"Aproxime o sensor do rosto."</string>
+ <string name="face_acquired_too_high" msgid="228411096134808372">"Mova o sensor para cima."</string>
+ <string name="face_acquired_too_low" msgid="4539774649296349109">"Mova o sensor para baixo."</string>
+ <string name="face_acquired_too_right" msgid="1650292067226118760">"Mova o sensor para a direita."</string>
+ <string name="face_acquired_too_left" msgid="2712489669456176505">"Mova o sensor para a esquerda."</string>
+ <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Olhe para o sensor."</string>
+ <string name="face_acquired_not_detected" msgid="5707782294589511391">"Nenhum rosto detectado."</string>
+ <string name="face_acquired_not_steady" msgid="3722829465011040042">"Mantenha o rosto parado na frente do dispositivo."</string>
+ <string-array name="face_acquired_vendor">
+ </string-array>
+ <string name="face_error_hw_not_available" msgid="6255891785768984615">"Hardware de rosto não disponível."</string>
+ <string name="face_error_timeout" msgid="4014326147867150054">"Tempo máx. p/ captura facial atingido. Tente novamente."</string>
+ <string name="face_error_no_space" msgid="8224993703466381314">"Não é possível armazenar um rosto."</string>
+ <string name="face_error_canceled" msgid="283945501061931023">"Operação facial cancelada."</string>
+ <string name="face_error_lockout" msgid="3407426963155388504">"Excesso de tentativas. Tente novamente mais tarde."</string>
+ <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Excesso de tentativas. Autenticação facial desat."</string>
+ <string name="face_error_unable_to_process" msgid="238761109287767270">"Tente novamente."</string>
+ <string name="face_error_not_enrolled" msgid="9166792142679691323">"Nenhum rosto registrado."</string>
+ <string name="face_error_hw_not_present" msgid="4737289254517095671">"Este dispositivo não tem um sensor de autenticação facial"</string>
+ <string name="face_name_template" msgid="7004562145809595384">"Rosto <xliff:g id="FACEID">%d</xliff:g>"</string>
+ <string-array name="face_error_vendor">
+ </string-array>
+ <string name="face_icon_content_description" msgid="4024817159806482191">"Ícone facial"</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"ler as configurações de sincronização"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Permite que o app leia as configurações de sincronização de uma conta. Por exemplo, pode determinar se o app People está sincronizado com uma conta."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"ativar e desativar sincronização"</string>
@@ -1178,6 +1208,9 @@
<skip />
<string name="wifi_no_internet" msgid="8938267198124654938">"O Wi‑Fi não tem acesso à Internet"</string>
<string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Toque para ver opções"</string>
+ <string name="wifi_softap_config_change" msgid="8475911871165857607">"Mudanças nas suas configurações de ponto de acesso"</string>
+ <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"Sua banda de ponto de acesso foi alterada."</string>
+ <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"Este dispositivo não é compatível com sua preferência apenas por 5 GHz. Em vez disso, o dispositivo usará a banda de 5 GHz quando ela estiver disponível."</string>
<string name="network_switch_metered" msgid="4671730921726992671">"Alternado para <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
<string name="network_switch_metered_detail" msgid="775163331794506615">"O dispositivo usa <xliff:g id="NEW_NETWORK">%1$s</xliff:g> quando <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> não tem acesso à Internet. Esse serviço pode ser cobrado."</string>
<string name="network_switch_metered_toast" msgid="5779283181685974304">"Alternado de <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> para <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
@@ -1398,7 +1431,7 @@
<string name="gpsVerifYes" msgid="2346566072867213563">"Sim"</string>
<string name="gpsVerifNo" msgid="1146564937346454865">"Não"</string>
<string name="sync_too_many_deletes" msgid="5296321850662746890">"Limite de exclusão excedido"</string>
- <string name="sync_too_many_deletes_desc" msgid="496551671008694245">"Há <xliff:g id="NUMBER_OF_DELETED_ITEMS">%1$d</xliff:g> itens excluídos para <xliff:g id="TYPE_OF_SYNC">%2$s</xliff:g>, conta <xliff:g id="ACCOUNT_NAME">%3$s</xliff:g>. O que você deseja fazer?"</string>
+ <string name="sync_too_many_deletes_desc" msgid="496551671008694245">"Há <xliff:g id="NUMBER_OF_DELETED_ITEMS">%1$d</xliff:g> itens excluídos para <xliff:g id="TYPE_OF_SYNC">%2$s</xliff:g>, conta <xliff:g id="ACCOUNT_NAME">%3$s</xliff:g>. O que você quer fazer?"</string>
<string name="sync_really_delete" msgid="2572600103122596243">"Excluir os itens"</string>
<string name="sync_undo_deletes" msgid="2941317360600338602">"Desfazer as exclusões"</string>
<string name="sync_do_nothing" msgid="3743764740430821845">"Não fazer nada por enquanto"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 1f5704a..597be12 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -236,9 +236,6 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Modo de avião"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"O modo de voo está ativado"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"O modo de voo está desativado"</string>
- <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Poupança de bateria"</string>
- <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Poupança de bateria DeSATIVADA"</string>
- <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Poupança de bateria ATIVADA"</string>
<string name="global_action_settings" msgid="1756531602592545966">"Definições"</string>
<string name="global_action_assist" msgid="3892832961594295030">"Assistência"</string>
<string name="global_action_voice_assist" msgid="7751191495200504480">"Assist. de voz"</string>
@@ -276,6 +273,9 @@
<string name="permgrouplab_location" msgid="7275582855722310164">"Localização"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"aceder à localização do seu dispositivo"</string>
<string name="permgrouprequest_location" msgid="3788275734953323491">"Pretende permitir que a aplicação <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> aceda à localização deste dispositivo?"</string>
+ <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"A aplicação tem acesso à localização apenas quando a estiver a utilizar."</string>
+ <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"Pretende permitir que a aplicação <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> aceda sempre à localização deste dispositivo?"</string>
+ <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"A aplicação terá sempre acesso à localização, mesmo quando não está a utilizá-la."</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Calendário"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"aceder ao calendário"</string>
<string name="permgrouprequest_calendar" msgid="289900767793189421">"Pretende permitir que a aplicação <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> aceda ao calendário?"</string>
@@ -402,8 +402,7 @@
<string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Esta aplicação pode adicionar, remover ou alterar eventos do calendário no seu telemóvel. Esta aplicação pode enviar mensagens que parecem vir de proprietários do calendário ou alterar eventos sem notificar os respetivos proprietários."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"aceder a comandos adicionais do fornecedor de localização"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Permite que a aplicação aceda a comandos adicionais do fornecedor de localização. Esta opção pode permitir que a aplicação interfira com o funcionamento do GPS ou de outras fontes de localização."</string>
- <!-- no translation found for permlab_accessFineLocation (6265109654698562427) -->
- <skip />
+ <string name="permlab_accessFineLocation" msgid="6265109654698562427">"apenas aceder à localização exata em primeiro plano"</string>
<string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Esta aplicação apenas pode obter a sua localização exata quando estiver em primeiro plano. É necessário que estes Serviços de localização estejam ativados e disponíveis no seu telemóvel para que a aplicação os possa utilizar. Esta ação pode aumentar o consumo da bateria."</string>
<string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"aceder à localização aproximada (baseada na rede)"</string>
<string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Esta aplicação pode obter a sua localização com base em fontes de rede, tais como torres de redes móveis e redes Wi-Fi. É necessário que estes serviços de localização estejam ativados e disponíveis no seu tablet para que a aplicação os possa utilizar."</string>
@@ -518,6 +517,37 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Ícone de impressão digital"</string>
+ <string name="permlab_manageFace" msgid="2137540986007309781">"gerir hardware de autenticação facial"</string>
+ <string name="permdesc_manageFace" msgid="8919637120670185330">"Permite à aplicação invocar métodos para adicionar e eliminar modelos faciais para uso."</string>
+ <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"utilizar hardware de autenticação facial"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Permite que a aplicação utilize hardware de autenticação facial para autenticação."</string>
+ <string name="face_acquired_insufficient" msgid="5901287247766106330">"Não foi possível processar o rosto. Tente de novo."</string>
+ <string name="face_acquired_too_bright" msgid="610606792381297174">"Rosto demasiado claro. Experimente com menos luz."</string>
+ <string name="face_acquired_too_dark" msgid="7229162716976778371">"Rosto demasiado escuro. Destape a fonte de luz."</string>
+ <string name="face_acquired_too_close" msgid="1980310037427755293">"Afaste o sensor do rosto."</string>
+ <string name="face_acquired_too_far" msgid="4494571381828850007">"Aproxime o sensor do rosto."</string>
+ <string name="face_acquired_too_high" msgid="228411096134808372">"Mova o sensor para cima."</string>
+ <string name="face_acquired_too_low" msgid="4539774649296349109">"Mova o sensor para baixo."</string>
+ <string name="face_acquired_too_right" msgid="1650292067226118760">"Mova o sensor para a direita."</string>
+ <string name="face_acquired_too_left" msgid="2712489669456176505">"Mova o sensor para a esquerda."</string>
+ <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Olhe para o sensor."</string>
+ <string name="face_acquired_not_detected" msgid="5707782294589511391">"Nenhum rosto detetado."</string>
+ <string name="face_acquired_not_steady" msgid="3722829465011040042">"Mantenha o rosto parado em frente ao dispositivo."</string>
+ <string-array name="face_acquired_vendor">
+ </string-array>
+ <string name="face_error_hw_not_available" msgid="6255891785768984615">"O hardware de rosto não está disponível."</string>
+ <string name="face_error_timeout" msgid="4014326147867150054">"Limite de tempo de rosto atingido. Tente novamente."</string>
+ <string name="face_error_no_space" msgid="8224993703466381314">"Não é possível armazenar o rosto."</string>
+ <string name="face_error_canceled" msgid="283945501061931023">"Operação de rosto cancelada."</string>
+ <string name="face_error_lockout" msgid="3407426963155388504">"Demasiadas tentativas. Tente novamente mais tarde."</string>
+ <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Demasiadas tentativas. Autenticação facial desativada."</string>
+ <string name="face_error_unable_to_process" msgid="238761109287767270">"Tente novamente."</string>
+ <string name="face_error_not_enrolled" msgid="9166792142679691323">"Nenhum rosto inscrito."</string>
+ <string name="face_error_hw_not_present" msgid="4737289254517095671">"Este dispositivo não tem sensor de autenticação facial."</string>
+ <string name="face_name_template" msgid="7004562145809595384">"Rosto <xliff:g id="FACEID">%d</xliff:g>"</string>
+ <string-array name="face_error_vendor">
+ </string-array>
+ <string name="face_icon_content_description" msgid="4024817159806482191">"Ícone de rosto"</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"ler definições de sincronização"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Permite que a aplicação leia as definições de sincronização de uma conta. Por exemplo, pode determinar se a aplicação Pessoas está sincronizada com uma conta."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"ativar e desativar a sincronização"</string>
@@ -588,13 +618,13 @@
<string name="policydesc_watchLogin_secondaryUser" product="TV" msgid="3484832653564483250">"Monitorizar o número de palavras-passe incorretas introduzidas ao desbloquear o ecrã e bloquear a TV ou apagar todos os dados deste utilizador se forem introduzidas demasiadas palavras-passe incorretas."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="2185480427217127147">"Monitorizar o número de palavras-passe incorretas introduzidas ao desbloquear o ecrã e bloquear o telemóvel ou apagar todos os dados deste utilizador se forem introduzidas demasiadas palavras-passe incorretas."</string>
<string name="policylab_resetPassword" msgid="4934707632423915395">"Alterar o bloqueio de ecrã"</string>
- <string name="policydesc_resetPassword" msgid="1278323891710619128">"Alterar o bloqueio de ecrã."</string>
+ <string name="policydesc_resetPassword" msgid="1278323891710619128">"Altera o bloqueio de ecrã."</string>
<string name="policylab_forceLock" msgid="2274085384704248431">"Bloquear o ecrã"</string>
- <string name="policydesc_forceLock" msgid="1141797588403827138">"Controlar como e quando ocorre o bloqueio do ecrã."</string>
+ <string name="policydesc_forceLock" msgid="1141797588403827138">"Controla como e quando ocorre o bloqueio do ecrã."</string>
<string name="policylab_wipeData" msgid="3910545446758639713">"Apagar todos os dados"</string>
<string name="policydesc_wipeData" product="tablet" msgid="4306184096067756876">"Apagar os dados do tablet sem avisar através de uma reposição de dados de fábrica."</string>
<string name="policydesc_wipeData" product="tv" msgid="5816221315214527028">"Apagar os dados da TV sem aviso prévio ao executar uma reposição de dados de fábrica."</string>
- <string name="policydesc_wipeData" product="default" msgid="5096895604574188391">"Apagar os dados do telemóvel sem avisar através de uma reposição de dados de fábrica."</string>
+ <string name="policydesc_wipeData" product="default" msgid="5096895604574188391">"Apaga os dados do telemóvel sem avisar ao efetuar uma reposição de dados de fábrica."</string>
<string name="policylab_wipeData_secondaryUser" msgid="8362863289455531813">"Apagar os dados do utilizador"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="6336255514635308054">"Apagar os dados deste utilizador neste tablet sem aviso."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2086473496848351810">"Apagar os dados deste utilizador nesta TV sem aviso."</string>
@@ -608,7 +638,7 @@
<string name="policylab_disableCamera" msgid="6395301023152297826">"Desativar câmaras"</string>
<string name="policydesc_disableCamera" msgid="2306349042834754597">"Evitar a utilização de todas as câmaras do dispositivo."</string>
<string name="policylab_disableKeyguardFeatures" msgid="8552277871075367771">"Desat. funcionalid. bloq. ecrã"</string>
- <string name="policydesc_disableKeyguardFeatures" msgid="2044755691354158439">"Impeça a utilização de algumas funcionalidades de bloqueio de ecrã."</string>
+ <string name="policydesc_disableKeyguardFeatures" msgid="2044755691354158439">"Impede a utilização de algumas funcionalidades de bloqueio de ecrã."</string>
<string-array name="phoneTypes">
<item msgid="8901098336658710359">"Casa"</item>
<item msgid="869923650527136615">"Móvel"</item>
@@ -1178,6 +1208,9 @@
<skip />
<string name="wifi_no_internet" msgid="8938267198124654938">"O Wi-Fi não tem acesso à Internet."</string>
<string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Toque para obter mais opções"</string>
+ <string name="wifi_softap_config_change" msgid="8475911871165857607">"Alterações às definições de zona Wi-Fi"</string>
+ <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"A banda da sua zona Wi-Fi foi alterada."</string>
+ <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"Este dispositivo não suporta a sua preferência apenas para 5 GHz. Em alternativa, este dispositivo vai utilizar a banda de 5 GHz quando estiver disponível."</string>
<string name="network_switch_metered" msgid="4671730921726992671">"Mudou para <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
<string name="network_switch_metered_detail" msgid="775163331794506615">"O dispositivo utiliza <xliff:g id="NEW_NETWORK">%1$s</xliff:g> quando <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> não tem acesso à Internet. Podem aplicar-se custos."</string>
<string name="network_switch_metered_toast" msgid="5779283181685974304">"Mudou de <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> para <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 2d846c1..6c0391c 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -236,9 +236,6 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Modo avião"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Modo avião ATIVADO"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Modo avião DESATIVADO"</string>
- <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Economia de bateria"</string>
- <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"A Economia de bateria está DESATIVADA"</string>
- <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"A Economia de bateria está ATIVADA"</string>
<string name="global_action_settings" msgid="1756531602592545966">"Configurações"</string>
<string name="global_action_assist" msgid="3892832961594295030">"Assistência"</string>
<string name="global_action_voice_assist" msgid="7751191495200504480">"Ajuda de voz"</string>
@@ -276,6 +273,9 @@
<string name="permgrouplab_location" msgid="7275582855722310164">"Local"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"acesse o local do dispositivo"</string>
<string name="permgrouprequest_location" msgid="3788275734953323491">"Permitir que <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> acesse a localização deste dispositivo?"</string>
+ <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"O app só terá acesso ao local enquanto estiver sendo usado."</string>
+ <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"Sempre permitir que <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> acesse o local deste disp.?"</string>
+ <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"O app sempre terá acesso ao local, mesmo quando não estiver sendo usado."</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Agenda"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"acesse sua agenda"</string>
<string name="permgrouprequest_calendar" msgid="289900767793189421">"Permitir que <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> acesse sua agenda?"</string>
@@ -402,8 +402,7 @@
<string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Este app pode adicionar, remover ou alterar eventos da agenda no seu smartphone. Ele também pode enviar mensagens que aparentem ser de autoria do proprietário da agenda ou alterar eventos sem notificar o proprietário."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"acessar comandos extras do provedor de localização"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Permite que o app acesse comandos do provedor não relacionados à localização. Isso pode permitir que o app interfira no funcionamento do GPS ou de outras fontes de localização."</string>
- <!-- no translation found for permlab_accessFineLocation (6265109654698562427) -->
- <skip />
+ <string name="permlab_accessFineLocation" msgid="6265109654698562427">"acessar localização precisa apenas em primeiro plano"</string>
<string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Este app pode ver sua localização exata a qualquer momento apenas quando está em primeiro plano. Esses serviços de localização precisam estar ativados e disponíveis no seu smartphone para que o app possa usá-los. Isso pode aumentar o consumo de bateria."</string>
<string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"acessar localização aproximada (com base na rede)"</string>
<string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Este app pode ver seu local com base nas fontes de rede, como torres de celular e redes Wi-Fi. Esses serviços de localização precisam estar ativados e disponíveis no seu tablet para que o app possa usá-los."</string>
@@ -518,6 +517,37 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Ícone de impressão digital"</string>
+ <string name="permlab_manageFace" msgid="2137540986007309781">"gerenciar hardware de autenticação facial"</string>
+ <string name="permdesc_manageFace" msgid="8919637120670185330">"Permite que o app execute métodos para adicionar e excluir modelos de rosto para uso."</string>
+ <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"usar hardware de autenticação facial"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Permite que o app use o hardware de autenticação facial para autenticação"</string>
+ <string name="face_acquired_insufficient" msgid="5901287247766106330">"Falha ao processar o rosto. Tente novamente."</string>
+ <string name="face_acquired_too_bright" msgid="610606792381297174">"Rosto muito iluminado. Tente com menos iluminação."</string>
+ <string name="face_acquired_too_dark" msgid="7229162716976778371">"Rosto muito escuro. Acrescente uma fonte de luz."</string>
+ <string name="face_acquired_too_close" msgid="1980310037427755293">"Afaste o sensor do rosto."</string>
+ <string name="face_acquired_too_far" msgid="4494571381828850007">"Aproxime o sensor do rosto."</string>
+ <string name="face_acquired_too_high" msgid="228411096134808372">"Mova o sensor para cima."</string>
+ <string name="face_acquired_too_low" msgid="4539774649296349109">"Mova o sensor para baixo."</string>
+ <string name="face_acquired_too_right" msgid="1650292067226118760">"Mova o sensor para a direita."</string>
+ <string name="face_acquired_too_left" msgid="2712489669456176505">"Mova o sensor para a esquerda."</string>
+ <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Olhe para o sensor."</string>
+ <string name="face_acquired_not_detected" msgid="5707782294589511391">"Nenhum rosto detectado."</string>
+ <string name="face_acquired_not_steady" msgid="3722829465011040042">"Mantenha o rosto parado na frente do dispositivo."</string>
+ <string-array name="face_acquired_vendor">
+ </string-array>
+ <string name="face_error_hw_not_available" msgid="6255891785768984615">"Hardware de rosto não disponível."</string>
+ <string name="face_error_timeout" msgid="4014326147867150054">"Tempo máx. p/ captura facial atingido. Tente novamente."</string>
+ <string name="face_error_no_space" msgid="8224993703466381314">"Não é possível armazenar um rosto."</string>
+ <string name="face_error_canceled" msgid="283945501061931023">"Operação facial cancelada."</string>
+ <string name="face_error_lockout" msgid="3407426963155388504">"Excesso de tentativas. Tente novamente mais tarde."</string>
+ <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Excesso de tentativas. Autenticação facial desat."</string>
+ <string name="face_error_unable_to_process" msgid="238761109287767270">"Tente novamente."</string>
+ <string name="face_error_not_enrolled" msgid="9166792142679691323">"Nenhum rosto registrado."</string>
+ <string name="face_error_hw_not_present" msgid="4737289254517095671">"Este dispositivo não tem um sensor de autenticação facial"</string>
+ <string name="face_name_template" msgid="7004562145809595384">"Rosto <xliff:g id="FACEID">%d</xliff:g>"</string>
+ <string-array name="face_error_vendor">
+ </string-array>
+ <string name="face_icon_content_description" msgid="4024817159806482191">"Ícone facial"</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"ler as configurações de sincronização"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Permite que o app leia as configurações de sincronização de uma conta. Por exemplo, pode determinar se o app People está sincronizado com uma conta."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"ativar e desativar sincronização"</string>
@@ -1178,6 +1208,9 @@
<skip />
<string name="wifi_no_internet" msgid="8938267198124654938">"O Wi‑Fi não tem acesso à Internet"</string>
<string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Toque para ver opções"</string>
+ <string name="wifi_softap_config_change" msgid="8475911871165857607">"Mudanças nas suas configurações de ponto de acesso"</string>
+ <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"Sua banda de ponto de acesso foi alterada."</string>
+ <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"Este dispositivo não é compatível com sua preferência apenas por 5 GHz. Em vez disso, o dispositivo usará a banda de 5 GHz quando ela estiver disponível."</string>
<string name="network_switch_metered" msgid="4671730921726992671">"Alternado para <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
<string name="network_switch_metered_detail" msgid="775163331794506615">"O dispositivo usa <xliff:g id="NEW_NETWORK">%1$s</xliff:g> quando <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> não tem acesso à Internet. Esse serviço pode ser cobrado."</string>
<string name="network_switch_metered_toast" msgid="5779283181685974304">"Alternado de <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> para <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
@@ -1398,7 +1431,7 @@
<string name="gpsVerifYes" msgid="2346566072867213563">"Sim"</string>
<string name="gpsVerifNo" msgid="1146564937346454865">"Não"</string>
<string name="sync_too_many_deletes" msgid="5296321850662746890">"Limite de exclusão excedido"</string>
- <string name="sync_too_many_deletes_desc" msgid="496551671008694245">"Há <xliff:g id="NUMBER_OF_DELETED_ITEMS">%1$d</xliff:g> itens excluídos para <xliff:g id="TYPE_OF_SYNC">%2$s</xliff:g>, conta <xliff:g id="ACCOUNT_NAME">%3$s</xliff:g>. O que você deseja fazer?"</string>
+ <string name="sync_too_many_deletes_desc" msgid="496551671008694245">"Há <xliff:g id="NUMBER_OF_DELETED_ITEMS">%1$d</xliff:g> itens excluídos para <xliff:g id="TYPE_OF_SYNC">%2$s</xliff:g>, conta <xliff:g id="ACCOUNT_NAME">%3$s</xliff:g>. O que você quer fazer?"</string>
<string name="sync_really_delete" msgid="2572600103122596243">"Excluir os itens"</string>
<string name="sync_undo_deletes" msgid="2941317360600338602">"Desfazer as exclusões"</string>
<string name="sync_do_nothing" msgid="3743764740430821845">"Não fazer nada por enquanto"</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index af4d3f4..05aed44 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -239,9 +239,6 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Mod Avion"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Modul Avion este ACTIVAT"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Modul avion este DEZACTIVAT"</string>
- <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Economisirea energiei"</string>
- <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Economisirea bateriei este dezactivată"</string>
- <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Economisirea bateriei este activată"</string>
<string name="global_action_settings" msgid="1756531602592545966">"Setări"</string>
<string name="global_action_assist" msgid="3892832961594295030">"Asistență"</string>
<string name="global_action_voice_assist" msgid="7751191495200504480">"Asistent vocal"</string>
@@ -279,6 +276,9 @@
<string name="permgrouplab_location" msgid="7275582855722310164">"Locație"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"acceseze locația acestui dispozitiv"</string>
<string name="permgrouprequest_location" msgid="3788275734953323491">"Permiteți <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> să acceseze locația acestui dispozitiv?"</string>
+ <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"Aplicația va avea acces la locație doar atunci când o folosiți."</string>
+ <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"Permiteți <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> să acceseze locația dispozitivului?"</string>
+ <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"Aplicația va avea în permanență acces la locație, chiar și atunci când nu o folosiți."</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Calendar"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"acceseze calendarul"</string>
<string name="permgrouprequest_calendar" msgid="289900767793189421">"Permiteți <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> să vă acceseze calendarul?"</string>
@@ -405,8 +405,7 @@
<string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Această aplicație poate să adauge, să elimine sau să modifice evenimente din calendarul de pe telefon. Această aplicație poate să trimită mesaje care par trimise de proprietarii calendarului sau să modifice evenimentele fără notificarea acestora."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"accesare comenzi suplimentare ale furnizorului locației"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Permite aplicației să acceseze comenzi suplimentare pentru furnizorul locației. Aplicația ar putea să utilizeze această permisiune pentru a influența operațiile GPS sau ale altor surse de locații."</string>
- <!-- no translation found for permlab_accessFineLocation (6265109654698562427) -->
- <skip />
+ <string name="permlab_accessFineLocation" msgid="6265109654698562427">"să acceseze locația exactă în prim-plan"</string>
<string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Aplicația vă poate obține locația exactă numai când rulează în prim-plan. Serviciile de localizare trebuie să fie activate și disponibile pe telefon pentru ca aplicația să le poată folosi. Acest lucru poate accelera descărcarea bateriei."</string>
<string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"să acceseze locația aproximativă (bazată pe rețea)"</string>
<string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Această aplicație vă poate obține locația cu ajutorul surselor rețelei, cum ar fi turnurile de telefonie mobilă și rețelele Wi-Fi. Aceste servicii de localizare trebuie să fie activate și disponibile pe tabletă pentru ca aplicația să le poată folosi."</string>
@@ -521,6 +520,37 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Pictograma amprentă"</string>
+ <string name="permlab_manageFace" msgid="2137540986007309781">"să gestioneze hardware-ul de autentificare facială"</string>
+ <string name="permdesc_manageFace" msgid="8919637120670185330">"Permite aplicației să invoce metode pentru a adăuga și a șterge șabloane faciale pentru utilizare."</string>
+ <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"să folosească hardware de autentificare facială"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Permite aplicației să folosească hardware de autentificare facială pentru autentificare"</string>
+ <string name="face_acquired_insufficient" msgid="5901287247766106330">"Chipul nu a putut fi procesat. Încercați din nou."</string>
+ <string name="face_acquired_too_bright" msgid="610606792381297174">"Chip prea luminat. Încercați cu mai puțină lumină."</string>
+ <string name="face_acquired_too_dark" msgid="7229162716976778371">"Chip prea întunecat. Măriți sursa de lumină."</string>
+ <string name="face_acquired_too_close" msgid="1980310037427755293">"Deplasați senzorul mai departe de chip."</string>
+ <string name="face_acquired_too_far" msgid="4494571381828850007">"Aduceți senzorul mai aproape de chip."</string>
+ <string name="face_acquired_too_high" msgid="228411096134808372">"Deplasați senzorul mai sus."</string>
+ <string name="face_acquired_too_low" msgid="4539774649296349109">"Deplasați senzorul mai jos."</string>
+ <string name="face_acquired_too_right" msgid="1650292067226118760">"Deplasați senzorul spre dreapta."</string>
+ <string name="face_acquired_too_left" msgid="2712489669456176505">"Deplasați senzorul spre stânga."</string>
+ <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Priviți spre senzor."</string>
+ <string name="face_acquired_not_detected" msgid="5707782294589511391">"Nu s-a detectat niciun chip."</string>
+ <string name="face_acquired_not_steady" msgid="3722829465011040042">"Țineți chipul nemișcat în fața dispozitivului."</string>
+ <string-array name="face_acquired_vendor">
+ </string-array>
+ <string name="face_error_hw_not_available" msgid="6255891785768984615">"Hardware-ul pentru chip nu este disponibil."</string>
+ <string name="face_error_timeout" msgid="4014326147867150054">"Timpul pentru reunoaștere facială a expirat. Încercați din nou."</string>
+ <string name="face_error_no_space" msgid="8224993703466381314">"Chipul nu poate fi stocat."</string>
+ <string name="face_error_canceled" msgid="283945501061931023">"Operațiunea privind chipul a fost anulată."</string>
+ <string name="face_error_lockout" msgid="3407426963155388504">"Prea multe încercări. Reîncercați mai târziu."</string>
+ <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Prea multe încercări. Autentificarea facială este dezactivată."</string>
+ <string name="face_error_unable_to_process" msgid="238761109287767270">"Încercați din nou."</string>
+ <string name="face_error_not_enrolled" msgid="9166792142679691323">"Nu au fost înregistrate chipuri."</string>
+ <string name="face_error_hw_not_present" msgid="4737289254517095671">"Dispozitivul nu are un senzor de autentificare a chipului"</string>
+ <string name="face_name_template" msgid="7004562145809595384">"Chip <xliff:g id="FACEID">%d</xliff:g>"</string>
+ <string-array name="face_error_vendor">
+ </string-array>
+ <string name="face_icon_content_description" msgid="4024817159806482191">"Pictograma chip"</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"citire setări sincronizare"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Permite aplicației să citească setările de sincronizare ale unui cont. De exemplu, cu această permisiune aplicația poate determina dacă aplicația Persoane este sincronizată cu un anumit cont."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"activează/dezactivează sincronizarea"</string>
@@ -1200,6 +1230,9 @@
<skip />
<string name="wifi_no_internet" msgid="8938267198124654938">"Rețeaua Wi-Fi nu are acces la internet"</string>
<string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Atingeți pentru opțiuni"</string>
+ <string name="wifi_softap_config_change" msgid="8475911871165857607">"Modificări aduse setărilor pentru hotspot"</string>
+ <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"S-a schimbat banda de frecvență a hotspotului."</string>
+ <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"Dispozitivul nu acceptă doar preferința pentru 5 GHz. Dispozitivul va folosi banda de 5 GHz când este disponibilă."</string>
<string name="network_switch_metered" msgid="4671730921726992671">"S-a comutat la <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
<string name="network_switch_metered_detail" msgid="775163331794506615">"Dispozitivul folosește <xliff:g id="NEW_NETWORK">%1$s</xliff:g> când <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> nu are acces la internet. Se pot aplica taxe."</string>
<string name="network_switch_metered_toast" msgid="5779283181685974304">"S-a comutat de la <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> la <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 9365c4f..63a8574 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -242,9 +242,6 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Режим полета"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Выключить"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Включить"</string>
- <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Режим энергосбережения"</string>
- <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Выключить"</string>
- <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Включить"</string>
<string name="global_action_settings" msgid="1756531602592545966">"Настройки"</string>
<string name="global_action_assist" msgid="3892832961594295030">"Помощник"</string>
<string name="global_action_voice_assist" msgid="7751191495200504480">"Аудиоподсказки"</string>
@@ -278,22 +275,25 @@
<string name="managed_profile_label" msgid="8947929265267690522">"Переключиться на рабочий профиль"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Контакты"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"доступ к контактам"</string>
- <string name="permgrouprequest_contacts" msgid="6032805601881764300">"Разрешить приложению <b>\"<xliff:g id="APP_NAME">%1$s</xliff:g>\"</b> доступ к контактам?"</string>
+ <string name="permgrouprequest_contacts" msgid="6032805601881764300">"Разрешить приложению <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> доступ к контактам?"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"Местоположение"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"доступ к данным о местоположении устройства"</string>
- <string name="permgrouprequest_location" msgid="3788275734953323491">"Разрешить приложению <b>\"<xliff:g id="APP_NAME">%1$s</xliff:g>\"</b> доступ к данным о местоположении устройства?"</string>
+ <string name="permgrouprequest_location" msgid="3788275734953323491">"Разрешить приложению <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> доступ к данным о местоположении устройства?"</string>
+ <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"Доступ будет открыт, только когда вы пользуетесь приложением."</string>
+ <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"Всегда разрешать приложению <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> доступ к геоданным устройства?"</string>
+ <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"Доступ будет открыт, даже когда вы не пользуетесь приложением."</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Календарь"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"доступ к календарю"</string>
- <string name="permgrouprequest_calendar" msgid="289900767793189421">"Разрешить приложению <b>\"<xliff:g id="APP_NAME">%1$s</xliff:g>\"</b> доступ к календарю?"</string>
+ <string name="permgrouprequest_calendar" msgid="289900767793189421">"Разрешить приложению <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> доступ к календарю?"</string>
<string name="permgrouplab_sms" msgid="228308803364967808">"SMS"</string>
<string name="permgroupdesc_sms" msgid="4656988620100940350">"отправлять и просматривать SMS-сообщения"</string>
- <string name="permgrouprequest_sms" msgid="7168124215838204719">"Разрешить приложению <b>\"<xliff:g id="APP_NAME">%1$s</xliff:g>\"</b> отправлять и просматривать SMS?"</string>
+ <string name="permgrouprequest_sms" msgid="7168124215838204719">"Разрешить приложению <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> отправлять и просматривать SMS?"</string>
<string name="permgrouplab_storage" msgid="1971118770546336966">"Хранилище"</string>
<string name="permgroupdesc_storage" msgid="637758554581589203">"доступ к фото, мультимедиа и файлам на вашем устройстве"</string>
<string name="permgrouprequest_storage" msgid="7885942926944299560">"Разрешить приложению <b>\"<xliff:g id="APP_NAME">%1$s</xliff:g>\"</b> доступ к фото, мультимедиа и файлам на устройстве?"</string>
<string name="permgrouplab_microphone" msgid="171539900250043464">"Микрофон"</string>
<string name="permgroupdesc_microphone" msgid="4988812113943554584">"записывать аудио"</string>
- <string name="permgrouprequest_microphone" msgid="9167492350681916038">"Разрешить приложению <b>\"<xliff:g id="APP_NAME">%1$s</xliff:g>\"</b> записывать аудио?"</string>
+ <string name="permgrouprequest_microphone" msgid="9167492350681916038">"Разрешить приложению <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> записывать аудио?"</string>
<string name="permgrouplab_camera" msgid="4820372495894586615">"Камера"</string>
<string name="permgroupdesc_camera" msgid="3250611594678347720">"снимать фото и видео"</string>
<string name="permgrouprequest_camera" msgid="1299833592069671756">"Разрешить приложению <b>\"<xliff:g id="APP_NAME">%1$s</xliff:g>\"</b> снимать фото и видео?"</string>
@@ -302,7 +302,7 @@
<string name="permgrouprequest_calllog" msgid="8487355309583773267">"Разрешить приложению <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> доступ к списку вызовов?"</string>
<string name="permgrouplab_phone" msgid="5229115638567440675">"Телефон"</string>
<string name="permgroupdesc_phone" msgid="6234224354060641055">"осуществлять вызовы и управлять ими"</string>
- <string name="permgrouprequest_phone" msgid="9166979577750581037">"Разрешить приложению <b>\"<xliff:g id="APP_NAME">%1$s</xliff:g>\"</b> совершать звонки и управлять ими?"</string>
+ <string name="permgrouprequest_phone" msgid="9166979577750581037">"Разрешить приложению <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> совершать звонки и управлять ими?"</string>
<string name="permgrouplab_sensors" msgid="416037179223226722">"Нательные датчики"</string>
<string name="permgroupdesc_sensors" msgid="7147968539346634043">"доступ к данным датчиков о состоянии организма"</string>
<string name="permgrouprequest_sensors" msgid="6349806962814556786">"Разрешить приложению <b>\"<xliff:g id="APP_NAME">%1$s</xliff:g>\"</b> доступ к данным датчиков о состоянии организма?"</string>
@@ -408,8 +408,7 @@
<string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Приложение может добавлять, удалять или изменять мероприятия в календаре на телефоне. Оно также может отправлять сообщения от имени владельцев календаря, а также изменять мероприятия без ведома владельца."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"Доступ к дополнительным командам управления источниками геоданных"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Доступ к дополнительным командам управления источниками геоданных и вмешательство в работу системы GPS или других источников геоданных."</string>
- <!-- no translation found for permlab_accessFineLocation (6265109654698562427) -->
- <skip />
+ <string name="permlab_accessFineLocation" msgid="6265109654698562427">"Доступ к точному местоположению только в фоновом режиме"</string>
<string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Приложение может получать сведения о вашем точном местоположении только в фоновом режиме. Для этого необходимо включить соответствующие параметры на телефоне и разрешить использовать геоданные. Может увеличиться расход заряда батареи."</string>
<string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"Доступ к примерному местоположению (по координатам сети)"</string>
<string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Приложение может получать сведения о вашем местоположении от сетевых источников, таких как вышки сотовой связи и точки доступа Wi-Fi. Необходимо включить соответствующие параметры на планшете и разрешить приложению использовать геоданные."</string>
@@ -524,6 +523,37 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Значок отпечатка пальца"</string>
+ <string name="permlab_manageFace" msgid="2137540986007309781">"Управлять оборудованием для распознавания лиц"</string>
+ <string name="permdesc_manageFace" msgid="8919637120670185330">"Приложение сможет добавлять и удалять шаблоны лиц."</string>
+ <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"Использовать оборудование для распознавания лиц"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Приложение сможет использовать распознающее оборудование для аутентификации."</string>
+ <string name="face_acquired_insufficient" msgid="5901287247766106330">"Не удалось распознать лицо. Повторите попытку."</string>
+ <string name="face_acquired_too_bright" msgid="610606792381297174">"Лицо слишком яркое. Притушите свет."</string>
+ <string name="face_acquired_too_dark" msgid="7229162716976778371">"Лицо слишком темное. Сделайте свет ярче."</string>
+ <string name="face_acquired_too_close" msgid="1980310037427755293">"Уберите устройство дальше от лица"</string>
+ <string name="face_acquired_too_far" msgid="4494571381828850007">"Поднесите устройство ближе к лицу"</string>
+ <string name="face_acquired_too_high" msgid="228411096134808372">"Поднимите устройство выше"</string>
+ <string name="face_acquired_too_low" msgid="4539774649296349109">"Опустите устройство ниже"</string>
+ <string name="face_acquired_too_right" msgid="1650292067226118760">"Сдвиньте устройство правее"</string>
+ <string name="face_acquired_too_left" msgid="2712489669456176505">"Сдвиньте устройство левее"</string>
+ <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Посмотрите в камеру устройства"</string>
+ <string name="face_acquired_not_detected" msgid="5707782294589511391">"Лица не обнаружены"</string>
+ <string name="face_acquired_not_steady" msgid="3722829465011040042">"Расположите лицо напротив устройства"</string>
+ <string-array name="face_acquired_vendor">
+ </string-array>
+ <string name="face_error_hw_not_available" msgid="6255891785768984615">"Оборудование для распознавания лица недоступно"</string>
+ <string name="face_error_timeout" msgid="4014326147867150054">"Превышено время ожидания. Повторите попытку."</string>
+ <string name="face_error_no_space" msgid="8224993703466381314">"Невозможно сохранить распознанное лицо"</string>
+ <string name="face_error_canceled" msgid="283945501061931023">"Распознавание отменено"</string>
+ <string name="face_error_lockout" msgid="3407426963155388504">"Слишком много попыток. Повторите позже."</string>
+ <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Слишком много попыток. Сканер отключен."</string>
+ <string name="face_error_unable_to_process" msgid="238761109287767270">"Попробуйте ещё раз"</string>
+ <string name="face_error_not_enrolled" msgid="9166792142679691323">"Нет зарегистрированных лиц"</string>
+ <string name="face_error_hw_not_present" msgid="4737289254517095671">"На этом устройстве нет сканера для распознавания лица"</string>
+ <string name="face_name_template" msgid="7004562145809595384">"Лицо <xliff:g id="FACEID">%d</xliff:g>"</string>
+ <string-array name="face_error_vendor">
+ </string-array>
+ <string name="face_icon_content_description" msgid="4024817159806482191">"Значок лица"</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"Просмотр настроек синхронизации"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Приложение сможет просматривать настройки синхронизации аккаунта, например определять, включена ли синхронизация для приложения \"Контакты\"."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"Включение/выключение синхронизации"</string>
@@ -1222,6 +1252,9 @@
<skip />
<string name="wifi_no_internet" msgid="8938267198124654938">"Сеть Wi-Fi не подключена к Интернету"</string>
<string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Нажмите, чтобы показать варианты."</string>
+ <string name="wifi_softap_config_change" msgid="8475911871165857607">"Изменения в настройках точки доступа"</string>
+ <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"Частота точки доступа изменена."</string>
+ <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"Устройство не может работать только на частоте 5 ГГц. Эта частота будет использоваться, когда это возможно."</string>
<string name="network_switch_metered" msgid="4671730921726992671">"Новое подключение: <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
<string name="network_switch_metered_detail" msgid="775163331794506615">"Устройство использует <xliff:g id="NEW_NETWORK">%1$s</xliff:g>, если подключение к сети <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> недоступно. Может взиматься плата за передачу данных."</string>
<string name="network_switch_metered_toast" msgid="5779283181685974304">"Устройство отключено от сети <xliff:g id="NEW_NETWORK">%2$s</xliff:g> и теперь использует <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index b382e67..a3e3ea0 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -236,9 +236,6 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"අහස්යානා ආකාරය"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"අහස්යානා ආකාරය සක්රීයයි."</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"අහස්යානා අකාරය අක්රියයි"</string>
- <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"බැටරි සුරැකුම"</string>
- <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"බැටරි සුරැකුම ක්රියාවිරහිතයි"</string>
- <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"බැටරි සුරැකුම ක්රියාත්මකයි"</string>
<string name="global_action_settings" msgid="1756531602592545966">"සැකසීම්"</string>
<string name="global_action_assist" msgid="3892832961594295030">"සහාය දීම"</string>
<string name="global_action_voice_assist" msgid="7751191495200504480">"හඬ සහායක"</string>
@@ -276,6 +273,9 @@
<string name="permgrouplab_location" msgid="7275582855722310164">"ස්ථානය"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"මෙම උපාංගයේ ස්ථානයට ප්රවේශ කරන්න"</string>
<string name="permgrouprequest_location" msgid="3788275734953323491">"<b><xliff:g id="APP_NAME">%1$s</xliff:g><b> වෙත මෙම උපාංගයේ ස්ථානය ලබා ගැනීමට ඉඩ දෙන්නද?"</string>
+ <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"ඔබ යෙදුම භාවිතා විට පමණක් යෙදුමට ස්ථානයට ප්රවේශය ඇත."</string>
+ <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"<b><xliff:g id="APP_NAME">%1$s</xliff:g><b> වෙත මෙම උපාංගයේ ස්ථානය ලබා ගැනීමට සැම විට ඉඩ දෙන්නද?"</string>
+ <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"ඔබ යෙදුම භාවිතා නොකරන විට පවා යෙදුමට සැම විටම ස්ථානයට ප්රවේශය ඇත."</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"දින දර්ශනය"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"ඔබේ දින දර්ශනයට පිවිසෙන්න"</string>
<string name="permgrouprequest_calendar" msgid="289900767793189421">"<b><xliff:g id="APP_NAME">%1$s</xliff:g><b> වෙත ඔබගේ දින දර්ශනය ප්රවේශ කිරීමට ඉඩ දෙන්නද?"</string>
@@ -402,8 +402,7 @@
<string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"මෙම යෙදුමට ඔබේ දුරකථනය මත දින දර්ශන සිදුවීම් එක් කිරීමට, ඉවත් කිරීමට, හෝ වෙනස් කිරීමට හැකිය. මෙම යෙදුමට දින දර්ශන හිමිකරුවන් වෙතින් වන ඒවා ලෙස දිස් විය හැකි පණිවිඩ එවීමට, හෝ සිදුවීම්වල හිමිකරුවන්ට දැනුම් දීමෙන් තොරව ඒවා වෙනස් කිරීමට හැකිය."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"අමතර ස්ථාන සැපයුම්කරු විධාන වෙත ප්රවේශ වීම"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"ස්ථානය සපයන අමතර අණ වලට ප්රවේශය කිරීමට යෙදුමට අවසර දෙන්න. GPS ක්රියාවන් හෝ වෙනත් ස්ථාන මූලාශ්ර සමඟ මැදිහත් වීමට මෙයින් යෙදුමට ඉඩ ලැබේ."</string>
- <!-- no translation found for permlab_accessFineLocation (6265109654698562427) -->
- <skip />
+ <string name="permlab_accessFineLocation" msgid="6265109654698562427">"පෙරබිම තුළ පමණක් නිශ්චිත ස්ථානය වෙත පිවිසෙන්න"</string>
<string name="permdesc_accessFineLocation" msgid="3520508381065331098">"මෙම යෙදුම පෙරබිම තුළ ඇති විට පමණක් එයට ඔබේ නිශ්චිත ස්ථානය ලබා ගත හැකිය. යෙදුමට ඒවා භාවිත කිරීමට හැකි වීමට මෙම ස්ථාන සේවා ක්රියාත්මක කර සහ ඔබේ දුරකථනය මත ලබා ගත හැකිව තිබිය යුතුය. මෙය බැටරි පරිභෝජනය වැඩි කළ හැකිය."</string>
<string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"ආසන්නතම ස්ථානයට (ජාලය-පාදක වූ) පිවිසීම"</string>
<string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"මෙම යෙදුමට ජංගම දුරකථන කුළුණු සහ Wi-Fi ජාල වැනි ජාල මූලාශ්ර පදනම්ව ඔබගේ ස්ථානය ලබා ගත හැකිය. යෙදුමට ඒවා භාවිත කිරීමට හැකි වීමට මෙම ස්ථාන සේවා ක්රියාත්මක කර සහ ඔබේ ටැබ්ලට් පරිගණකය මත ලබා ගත හැකිව තිබිය යුතුය."</string>
@@ -518,6 +517,37 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"ඇඟිලි සලකුණු නිරූපකය"</string>
+ <string name="permlab_manageFace" msgid="2137540986007309781">"මුහුණු සත්යාපක දෘඪාංග කළමනාකරණය කරන්න"</string>
+ <string name="permdesc_manageFace" msgid="8919637120670185330">"මුහුණු අච්චු එකතු කිරීමට සහ ඉවත් කිරීමට අදාළ ක්රම භාවිතය සඳහා මෙම යෙදුමට ඉඩ දෙයි."</string>
+ <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"මුහුණු සත්යාපක දෘඪාංග භාවිතා කරන්න"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"සත්යාපනය සඳහා සත්යාපක දෘඪාංග භාවිත කිරීමට යෙදුමට ඉඩ දෙයි"</string>
+ <string name="face_acquired_insufficient" msgid="5901287247766106330">"මුහුණ සැකසීමට නොහැකි විය. කරුණාකර නැවත උත්සාහ කර."</string>
+ <string name="face_acquired_too_bright" msgid="610606792381297174">"මුහුණ දීප්තිමත් වැඩිය. කරුණාකර අඩු ආලෝකය උත්සාහ කරන්න."</string>
+ <string name="face_acquired_too_dark" msgid="7229162716976778371">"මුහුණ අඳුරු වැඩිය. කරුණාර ආලෝක ප්රභවය නිරාවරණය කරන්න."</string>
+ <string name="face_acquired_too_close" msgid="1980310037427755293">"කරුණාකර සංවේදකය මුහුණෙන් වඩා ඈත් කරන්න."</string>
+ <string name="face_acquired_too_far" msgid="4494571381828850007">"කරුණාකර සංවේදකය මුහුණ සමීපයට ගෙන එන්න."</string>
+ <string name="face_acquired_too_high" msgid="228411096134808372">"කරුණාකර සංවේදකය ඉහළට ගෙන යන්න."</string>
+ <string name="face_acquired_too_low" msgid="4539774649296349109">"කරුණාකර සංවේදකය පහළට ගෙන යන්න."</string>
+ <string name="face_acquired_too_right" msgid="1650292067226118760">"කරුණාකර සංවේදකය දකුණට ගෙන යන්න."</string>
+ <string name="face_acquired_too_left" msgid="2712489669456176505">"කරුණාකර සංවේදකය වමට ගෙන යන්න."</string>
+ <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"කරුණාකර සංවේදකය දෙස බලන්න."</string>
+ <string name="face_acquired_not_detected" msgid="5707782294589511391">"මුහුණ අනාවරණය කර නොගන්නා ලදී."</string>
+ <string name="face_acquired_not_steady" msgid="3722829465011040042">"උපාංගය ඉදිරියේ මුහුණ ස්ථාවරව තබන්න."</string>
+ <string-array name="face_acquired_vendor">
+ </string-array>
+ <string name="face_error_hw_not_available" msgid="6255891785768984615">"මුහුණු දෘඪාංගය ලද නොහැකිය."</string>
+ <string name="face_error_timeout" msgid="4014326147867150054">"මුහුණු කාල නිමාව ළඟා විය. නැවත උත්සාහ කරන්න."</string>
+ <string name="face_error_no_space" msgid="8224993703466381314">"මුහුණ ගබඩා කළ නොහැක."</string>
+ <string name="face_error_canceled" msgid="283945501061931023">"මුහුණු මෙහෙයුම අවලංගු කරන ලදී."</string>
+ <string name="face_error_lockout" msgid="3407426963155388504">"උත්සාහයන් ඉතා වැඩි ගණනකි. පසුව නැවත උත්සාහ කරන්න."</string>
+ <string name="face_error_lockout_permanent" msgid="8198354656746088890">"උත්සාහයන් ඉතා වැඩි ගණනකි. මුහුණු සත්යාපනය අබල කරන ලදී."</string>
+ <string name="face_error_unable_to_process" msgid="238761109287767270">"නැවත උත්සාහ කරන්න."</string>
+ <string name="face_error_not_enrolled" msgid="9166792142679691323">"මුහුණක් ඇතුළත් කර නොමැත."</string>
+ <string name="face_error_hw_not_present" msgid="4737289254517095671">"මෙම උපාංගයේ මුහුණු සත්යාපක සංවේදකයක් නොමැත"</string>
+ <string name="face_name_template" msgid="7004562145809595384">"මුහුණු <xliff:g id="FACEID">%d</xliff:g>"</string>
+ <string-array name="face_error_vendor">
+ </string-array>
+ <string name="face_icon_content_description" msgid="4024817159806482191">"මුහුණ නිරූපකය"</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"සමමුහුර්ත සැකසීම් කියවන්න"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"ගිණුම සඳහා සමමුහුර්ත සැකසීම් කියවීමට යෙදුමට අවසර දෙන්න. උදාහරණයක් ලෙස, ගිණුමක් සමඟ පුද්ගල යෙදුම සමමුහුර්ත දැයි මෙයට හඳුනා ගත හැක."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"සමමුහුර්ත කිරීම සක්රිය කරන්න සහ අක්රිය කරන්න"</string>
@@ -1180,6 +1210,9 @@
<skip />
<string name="wifi_no_internet" msgid="8938267198124654938">"Wi-Fi සඳහා අන්තර්ජාල ප්රවේශය නැත"</string>
<string name="wifi_no_internet_detailed" msgid="8083079241212301741">"විකල්ප සඳහා තට්ටු කරන්න"</string>
+ <string name="wifi_softap_config_change" msgid="8475911871165857607">"ඔබගේ හොට්ස්පොට් සැකසීම්වලට වෙනස් කිරීම්"</string>
+ <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"ඔබගේ හොට්ස්පොට් කලාපය වෙනස් වී ඇත."</string>
+ <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"මෙම උපාංගය 5GHz සඳහා ඔබේ මනාපවලට සහාය නොදක්වයි. ඒ වෙනුවට, මෙම උපාංගය ලබා ගත හැකි විට 5GHz කලාපය භාවිතා කරනු ඇත."</string>
<string name="network_switch_metered" msgid="4671730921726992671">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> වෙත මාරු විය"</string>
<string name="network_switch_metered_detail" msgid="775163331794506615">"උපාංගය <xliff:g id="NEW_NETWORK">%1$s</xliff:g> <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> සඳහා අන්තර්ජාල ප්රවේශය නැති විට භාවිත කරයි. ගාස්තු අදාළ විය හැකිය."</string>
<string name="network_switch_metered_toast" msgid="5779283181685974304">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> සිට <xliff:g id="NEW_NETWORK">%2$s</xliff:g> වෙත මාරු විය"</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index 041cece..51ddff6 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -242,9 +242,6 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Režim v lietadle"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Režim v lietadle je ZAPNUTÝ"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Režim v lietadle je VYPNUTÝ"</string>
- <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Šetrič batérie"</string>
- <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Šetrič batérie je VYPNUTÝ"</string>
- <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Šetrič batérie je ZAPNUTÝ"</string>
<string name="global_action_settings" msgid="1756531602592545966">"Nastavenia"</string>
<string name="global_action_assist" msgid="3892832961594295030">"Pomôcť"</string>
<string name="global_action_voice_assist" msgid="7751191495200504480">"Hlasový asistent"</string>
@@ -282,6 +279,9 @@
<string name="permgrouplab_location" msgid="7275582855722310164">"Poloha"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"prístup k polohe tohto zariadenia"</string>
<string name="permgrouprequest_location" msgid="3788275734953323491">"Povoliť aplikácii <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> prístup k polohe tohto zariadenia?"</string>
+ <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"Aplikácia bude mať prístup k polohe iba vtedy, keď ju budete používať."</string>
+ <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"Chcete vždy povoliť aplikácii <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> prístup k polohe tohto zariadenia?"</string>
+ <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"Aplikácia bude mať vždy prístup k polohe, aj keď ju nebudete používať."</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Kalendár"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"prístup ku kalendáru"</string>
<string name="permgrouprequest_calendar" msgid="289900767793189421">"Povoliť aplikácii <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> prístup ku kalendáru?"</string>
@@ -408,8 +408,7 @@
<string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Táto aplikácia môže pridávať, odstraňovať alebo meniť udalosti kalendára vo vašom telefóne. Táto aplikácia môže odosielať správy od vlastníkov kalendára alebo meniť udalosti bez upozornenia ich vlastníkov."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"prístup k ďalším príkazom poskytovateľa polohy"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Umožňuje aplikácii pristupovať k ďalším príkazom poskytovateľa informácií o polohe. Aplikácii to môže umožniť zasahovať do činnosti systému GPS alebo iných zdrojov informácií o polohe."</string>
- <!-- no translation found for permlab_accessFineLocation (6265109654698562427) -->
- <skip />
+ <string name="permlab_accessFineLocation" msgid="6265109654698562427">"prístup k presnej polohe iba v popredí"</string>
<string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Táto aplikácia dokáže získať vašu presnú polohu iba vtedy, keď je spustená v popredí. Na to, aby mohla používať služby určovania polohy, musia byť tieto služby zapnuté a k dispozícii v telefóne. Môže to zvýšiť spotebu batérie."</string>
<string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"prístup k približnej polohe (pomocou siete)"</string>
<string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Táto aplikácia môže získať údaje o vašej polohe na základe sieťových zdrojov, ako sú mobilné veže a siete Wi‑Fi. Na to, aby aplikácia mohla používať služby určovania polohy, musia byť tieto služby zapnuté a k dispozícii na tablete."</string>
@@ -524,6 +523,37 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Ikona odtlačku prsta"</string>
+ <string name="permlab_manageFace" msgid="2137540986007309781">"správa hardvéru na overenie tváre"</string>
+ <string name="permdesc_manageFace" msgid="8919637120670185330">"Umožňuje aplikácii vyvolať metódy, ktoré pridávajú a odstraňujú šablóny tvárí."</string>
+ <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"používanie hardvéru na overenie tváre"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Umožňuje aplikácii používať na overenie totožnosti hardvér na overenie tváre"</string>
+ <string name="face_acquired_insufficient" msgid="5901287247766106330">"Tvár sa nepodarilo spracovať. Skúste to znova."</string>
+ <string name="face_acquired_too_bright" msgid="610606792381297174">"Tvár je príliš svetlá. Skúste to pri nižšom osvetlení."</string>
+ <string name="face_acquired_too_dark" msgid="7229162716976778371">"Tvár je príliš tmavá. Zvýšte osvetlenie."</string>
+ <string name="face_acquired_too_close" msgid="1980310037427755293">"Oddiaľte senzor od tváre."</string>
+ <string name="face_acquired_too_far" msgid="4494571381828850007">"Priblížte senzor k tvári."</string>
+ <string name="face_acquired_too_high" msgid="228411096134808372">"Pohnite senzor vyššie."</string>
+ <string name="face_acquired_too_low" msgid="4539774649296349109">"Pohnite senzor nižšie."</string>
+ <string name="face_acquired_too_right" msgid="1650292067226118760">"Pohnite senzor doprava."</string>
+ <string name="face_acquired_too_left" msgid="2712489669456176505">"Pohnite senzor doľava."</string>
+ <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Pozerajte sa na senzor."</string>
+ <string name="face_acquired_not_detected" msgid="5707782294589511391">"Nebola rozpoznaná žiadna tvár."</string>
+ <string name="face_acquired_not_steady" msgid="3722829465011040042">"Udržte tvár nehybne oproti zariadeniu."</string>
+ <string-array name="face_acquired_vendor">
+ </string-array>
+ <string name="face_error_hw_not_available" msgid="6255891785768984615">"Hardvér na snímanie tváre nie je k dispozícii"</string>
+ <string name="face_error_timeout" msgid="4014326147867150054">"Limit rozpoznania tváre vypršal. Skúste to znova."</string>
+ <string name="face_error_no_space" msgid="8224993703466381314">"Tvár sa nedá uchovať."</string>
+ <string name="face_error_canceled" msgid="283945501061931023">"Operácia týkajúca sa tváre bola zrušená"</string>
+ <string name="face_error_lockout" msgid="3407426963155388504">"Príliš veľa pokusov. Skúste to znova neskôr."</string>
+ <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Príliš veľa pokusov. Overenie tváre je zakázané."</string>
+ <string name="face_error_unable_to_process" msgid="238761109287767270">"Skúste to znova."</string>
+ <string name="face_error_not_enrolled" msgid="9166792142679691323">"Nemáte zaregistrovanú žiadnu tvár."</string>
+ <string name="face_error_hw_not_present" msgid="4737289254517095671">"Toto zariadenie nemá senzor na overenie tváre."</string>
+ <string name="face_name_template" msgid="7004562145809595384">"Tvár <xliff:g id="FACEID">%d</xliff:g>"</string>
+ <string-array name="face_error_vendor">
+ </string-array>
+ <string name="face_icon_content_description" msgid="4024817159806482191">"Ikona tváre"</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"čítať nastavenia synchronizácie"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Umožňuje aplikácii čítať nastavenia synchronizácie v účte. Môže napríklad určiť, či je s účtom synchronizovaná aplikácia Ľudia."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"zapnúť alebo vypnúť synchronizáciu"</string>
@@ -1222,6 +1252,9 @@
<skip />
<string name="wifi_no_internet" msgid="8938267198124654938">"Sieť Wi‑Fi nemá prístup k internetu"</string>
<string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Klepnutím získate možnosti"</string>
+ <string name="wifi_softap_config_change" msgid="8475911871165857607">"Zmeny nastavení hotspotu"</string>
+ <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"Pásmo vášho hotspotu sa zmenilo."</string>
+ <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"Toto zariadenie nepodporuje vašu predvoľbu používať iba 5 GHz. Namiesto toho bude pásmo 5 GHz používať vtedy, keď bude k dispozícii."</string>
<string name="network_switch_metered" msgid="4671730921726992671">"Prepnuté na sieť: <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
<string name="network_switch_metered_detail" msgid="775163331794506615">"Keď <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> nemá prístup k internetu, zariadenie používa <xliff:g id="NEW_NETWORK">%1$s</xliff:g>. Môžu sa účtovať poplatky."</string>
<string name="network_switch_metered_toast" msgid="5779283181685974304">"Prepnuté zo siete <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> na sieť <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 9809ee8..3e035bf 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -242,9 +242,6 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Način za letalo"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Način za letalo je VKLOPLJEN"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Način za letalo je IZKLOPLJEN"</string>
- <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Varčevanje z energijo akumulatorja"</string>
- <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Varčevanje z energijo akumulatorja je izklopljeno"</string>
- <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Varčevanje z energijo akumulatorja je vklopljeno"</string>
<string name="global_action_settings" msgid="1756531602592545966">"Nastavitve"</string>
<string name="global_action_assist" msgid="3892832961594295030">"Pomoč"</string>
<string name="global_action_voice_assist" msgid="7751191495200504480">"Glas. pomočnik"</string>
@@ -282,6 +279,9 @@
<string name="permgrouplab_location" msgid="7275582855722310164">"Lokacija"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"dostop do lokacije te naprave"</string>
<string name="permgrouprequest_location" msgid="3788275734953323491">"Želite aplikaciji <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> omogočiti dostop do lokacije te naprave?"</string>
+ <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"Aplikacija bo imela dostop do lokacije samo, ko aplikacijo uporabljate."</string>
+ <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"Želite aplikaciji <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> vedno dovoliti dostop do lokacije te naprave?"</string>
+ <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"Aplikacija bo vedno imela dostop do lokacije, tudi ko aplikacije ne uporabljate."</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Koledar"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"dostop do koledarja"</string>
<string name="permgrouprequest_calendar" msgid="289900767793189421">"Želite aplikaciji <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> omogočiti dostop do koledarja?"</string>
@@ -408,8 +408,7 @@
<string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Ta aplikacija lahko dodaja, odstranjuje in spreminja dogodke v koledarju, ki so shranjeni v telefonu. Ta aplikacija lahko pošilja sporočila, ki bodo morda videti, kot da prihajajo od lastnikov koledarjev, ali spreminja dogodke brez vednosti lastnikov."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"dostopanje do ukazov ponudnika dodatnih lokacij"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Aplikaciji omogoča dostop do dodatnih ukazov ponudnika lokacij. S tem lahko aplikacija moti delovanje sistema GPS ali drugih virov lokacije."</string>
- <!-- no translation found for permlab_accessFineLocation (6265109654698562427) -->
- <skip />
+ <string name="permlab_accessFineLocation" msgid="6265109654698562427">"dostop do točne lokacije samo, ko deluje v ospredju"</string>
<string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Ta aplikacija lahko pridobi vašo točno lokacijo samo, ko deluje v ospredju. Če želite aplikaciji omogočiti uporabo teh lokacijskih storitev, morajo biti te vklopljene in na voljo v telefonu. Poraba energije akumulatorja bo morda večja."</string>
<string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"dostop do približne lokacije (na podlagi podatkov omrežja)"</string>
<string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Ta aplikacija lahko pridobi vašo lokacijo na podlagi omrežnih virov, kot so bazne postaje in omrežja Wi-Fi. Če želite aplikaciji omogočiti uporabo teh lokacijskih storitev, morajo biti te vklopljene in na voljo v tabličnem računalniku."</string>
@@ -524,6 +523,37 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Ikona prstnih odtisov"</string>
+ <string name="permlab_manageFace" msgid="2137540986007309781">"upravljanje strojne opreme za preverjanje pristnosti obraza"</string>
+ <string name="permdesc_manageFace" msgid="8919637120670185330">"Aplikaciji omogoča sprožanje načinov za dodajanje in brisanje predlog z obrazi za uporabo."</string>
+ <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"uporaba strojne opreme za preverjanje pristnosti obraza"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Aplikaciji omogoča uporabo strojne opreme za preverjanje pristnosti obraza"</string>
+ <string name="face_acquired_insufficient" msgid="5901287247766106330">"Obraza ni bilo mogoče obdelati. Poskusite znova."</string>
+ <string name="face_acquired_too_bright" msgid="610606792381297174">"Obraz je presvetel. Poskusite pri manj svetlobe."</string>
+ <string name="face_acquired_too_dark" msgid="7229162716976778371">"Obraz je pretemen. Odkrijte vir svetlobe."</string>
+ <string name="face_acquired_too_close" msgid="1980310037427755293">"Tipalo premaknite dlje od obraza."</string>
+ <string name="face_acquired_too_far" msgid="4494571381828850007">"Tipalo premaknite bliže obrazu."</string>
+ <string name="face_acquired_too_high" msgid="228411096134808372">"Tipalo premaknite višje."</string>
+ <string name="face_acquired_too_low" msgid="4539774649296349109">"Tipalo premaknite nižje."</string>
+ <string name="face_acquired_too_right" msgid="1650292067226118760">"Tipalo premaknite v desno."</string>
+ <string name="face_acquired_too_left" msgid="2712489669456176505">"Tipalo premaknite v levo."</string>
+ <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Poglejte v tipalo."</string>
+ <string name="face_acquired_not_detected" msgid="5707782294589511391">"Ni zaznanih obrazov."</string>
+ <string name="face_acquired_not_steady" msgid="3722829465011040042">"Obraz naj bo pri miru in pred napravo."</string>
+ <string-array name="face_acquired_vendor">
+ </string-array>
+ <string name="face_error_hw_not_available" msgid="6255891785768984615">"Strojna oprema za prepoznavo obraza ni na voljo."</string>
+ <string name="face_error_timeout" msgid="4014326147867150054">"Dosežena časovna omejitev za obraz. Poskusite znova."</string>
+ <string name="face_error_no_space" msgid="8224993703466381314">"Obraza ni mogoče shraniti."</string>
+ <string name="face_error_canceled" msgid="283945501061931023">"Dejanje z obrazom je bilo preklicano."</string>
+ <string name="face_error_lockout" msgid="3407426963155388504">"Preveč poskusov. Poskusite znova pozneje."</string>
+ <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Preveč poskusov. Preverjanje pristnosti obraza je onemogočeno."</string>
+ <string name="face_error_unable_to_process" msgid="238761109287767270">"Poskusite znova."</string>
+ <string name="face_error_not_enrolled" msgid="9166792142679691323">"Ni prijavljenih obrazov."</string>
+ <string name="face_error_hw_not_present" msgid="4737289254517095671">"Ta naprava nima tipala za preverjanje pristnosti obraza"</string>
+ <string name="face_name_template" msgid="7004562145809595384">"Obraz <xliff:g id="FACEID">%d</xliff:g>"</string>
+ <string-array name="face_error_vendor">
+ </string-array>
+ <string name="face_icon_content_description" msgid="4024817159806482191">"Ikona obraza"</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"branje nastavitev sinhronizacije"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Aplikaciji omogoča branje nastavitev sinhronizacije za račun. S tem lahko aplikacija na primer ugotovi, ali je aplikacija Ljudje sinhronizirana z računom."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"vklop in izklop sinhronizacije"</string>
@@ -1222,6 +1252,9 @@
<skip />
<string name="wifi_no_internet" msgid="8938267198124654938">"Omrežje Wi-Fi nima dostopa do interneta"</string>
<string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Dotaknite se za možnosti"</string>
+ <string name="wifi_softap_config_change" msgid="8475911871165857607">"Spremembe nastavitev dostopne točke"</string>
+ <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"Pas dostopne točke je spremenjen."</string>
+ <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"Ta naprava ne podpira prednostne nastavitve samo za 5-GHz pas. Namesto tega bo ta naprava uporabljala 5-GHz pas, ko bo na voljo."</string>
<string name="network_switch_metered" msgid="4671730921726992671">"Preklopljeno na omrežje vrste <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
<string name="network_switch_metered_detail" msgid="775163331794506615">"Naprava uporabi omrežje vrste <xliff:g id="NEW_NETWORK">%1$s</xliff:g>, ko omrežje vrste <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> nima dostopa do interneta. Prenos podatkov se lahko zaračuna."</string>
<string name="network_switch_metered_toast" msgid="5779283181685974304">"Preklopljeno z omrežja vrste <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> na omrežje vrste <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index bc92b50..bb42da4 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -236,9 +236,6 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"modaliteti i aeroplanit"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Modaliteti i aeroplanit është i AKTIVIZUAR"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Modaliteti i aeroplanit është i ÇAKTIVIZUAR"</string>
- <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Kursyesi i baterisë"</string>
- <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"\"Kursyesi i baterisë\" është JOAKTIV."</string>
- <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"\"Kursyesi i baterisë\" është AKTIV"</string>
<string name="global_action_settings" msgid="1756531602592545966">"Cilësimet"</string>
<string name="global_action_assist" msgid="3892832961594295030">"Ndihma"</string>
<string name="global_action_voice_assist" msgid="7751191495200504480">"Ndihma zanore"</string>
@@ -276,6 +273,9 @@
<string name="permgrouplab_location" msgid="7275582855722310164">"Vendndodhja"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"qaset te vendndodhja e kësaj pajisjeje"</string>
<string name="permgrouprequest_location" msgid="3788275734953323491">"Të lejohet që <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> të ketë qasje te vendndodhja e kësaj pajisjeje?"</string>
+ <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"Aplikacioni do të ketë qasje te vendndodhja vetëm kur po e përdor aplikacionin."</string>
+ <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"Të lejohet gjithmonë që <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> të ketë qasje te vendndodhja?"</string>
+ <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"Aplikacioni do të ketë gjithmonë qasje te vendndodhja, edhe kur nuk po e përdor aplikacionin."</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Kalendari"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"qasje te kalendari yt"</string>
<string name="permgrouprequest_calendar" msgid="289900767793189421">"Të lejohet që <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> të ketë qasje te kalendari yt?"</string>
@@ -402,8 +402,7 @@
<string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Ky aplikacion mund të shtojë, të heqë ose të ndryshojë ngjarjet e kalendarit në telefonin tënd. Ky aplikacion mund të dërgojë mesazhe që mund të duket se vijnë nga zotëruesit e kalendarit ose të ndryshojë ngjarjet pa i njoftuar zotëruesit e tyre."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"qasje në komandat shtesë të ofruesit të vendndodhjes"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Lejon aplikacionin të ketë qasje në komandat shtesë të ofruesit për vendndodhjen. Kjo mund ta lejojë aplikacionin të ndërhyjë në operacionin e GPS-së apo të burimeve të tjera për vendndodhjen."</string>
- <!-- no translation found for permlab_accessFineLocation (6265109654698562427) -->
- <skip />
+ <string name="permlab_accessFineLocation" msgid="6265109654698562427">"qasu në vendndodhjen e saktë vetëm në plan të parë"</string>
<string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Ky aplikacion mund të marrë vendndodhjen tënde të saktë në çdo kohë kur është në plan të parë. Këto shërbime të vendndodhjes duhet të jenë të aktivizuara dhe në dispozicion në telefonin tënd që aplikacioni të mund t\'i përdorë. Kjo gjë mund të rritë konsumin e baterisë."</string>
<string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"qasu te vendndodhja e përafërt (bazuar në rrjet)"</string>
<string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Ky aplikacion mund të marrë vendndodhjen tënde bazuar në burimet e rrjetit si antenat e operatorëve celulare dhe rrjetet Wi-Fi. Këto shërbime të vendndodhjes duhet të jenë të aktivizuara dhe në dispozicion në tabletin tënd që aplikacioni të mund t\'i përdorë."</string>
@@ -518,6 +517,37 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Ikona e gjurmës së gishtit"</string>
+ <string name="permlab_manageFace" msgid="2137540986007309781">"menaxho harduerin për vërtetimin e fytyrës"</string>
+ <string name="permdesc_manageFace" msgid="8919637120670185330">"Lejon aplikacionin të aktivizojë mënyra për shtim e fshirje të shablloneve të përdorura."</string>
+ <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"përdor harduerin për vërtetimin e fytyrës"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Lejon aplikacionin të përdorë harduer vërtetimi të fytyrës për procesin e vërtetimit"</string>
+ <string name="face_acquired_insufficient" msgid="5901287247766106330">"Fytyra nuk mund të përpunohej. Provo sërish."</string>
+ <string name="face_acquired_too_bright" msgid="610606792381297174">"Fytyra ka shumë ndriçim. Provo me më pak ndriçim."</string>
+ <string name="face_acquired_too_dark" msgid="7229162716976778371">"Fytyra shumë e errët. Zbulo burimin e dritës."</string>
+ <string name="face_acquired_too_close" msgid="1980310037427755293">"Lëvize sensorin pak më larg fytyrës."</string>
+ <string name="face_acquired_too_far" msgid="4494571381828850007">"Sille sensorin më pranë fytyrës."</string>
+ <string name="face_acquired_too_high" msgid="228411096134808372">"Lëvize sensorin më lart."</string>
+ <string name="face_acquired_too_low" msgid="4539774649296349109">"Lëvize sensorin më poshtë."</string>
+ <string name="face_acquired_too_right" msgid="1650292067226118760">"Lëvize sensorin djathtas."</string>
+ <string name="face_acquired_too_left" msgid="2712489669456176505">"Lëvize sensorin majtas."</string>
+ <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Shiko nga sensori"</string>
+ <string name="face_acquired_not_detected" msgid="5707782294589511391">"Nuk u diktua fytyrë"</string>
+ <string name="face_acquired_not_steady" msgid="3722829465011040042">"Mbaje fytyrën pa lëvizur përpara pajisjes."</string>
+ <string-array name="face_acquired_vendor">
+ </string-array>
+ <string name="face_error_hw_not_available" msgid="6255891785768984615">"Nuk ka harduer për fytyrën."</string>
+ <string name="face_error_timeout" msgid="4014326147867150054">"Mbaroi afati për fytyrën. Provo sërish."</string>
+ <string name="face_error_no_space" msgid="8224993703466381314">"Fytyra nuk mund të ruhet."</string>
+ <string name="face_error_canceled" msgid="283945501061931023">"Veprimi me fytyrën u anulua."</string>
+ <string name="face_error_lockout" msgid="3407426963155388504">"Shumë përpjekje. Provo sërish më vonë."</string>
+ <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Shumë përpjekje. Vërtetimi për fytyrën joaktiv."</string>
+ <string name="face_error_unable_to_process" msgid="238761109287767270">"Provo sërish."</string>
+ <string name="face_error_not_enrolled" msgid="9166792142679691323">"Nuk ka fytyrë të regjistruar."</string>
+ <string name="face_error_hw_not_present" msgid="4737289254517095671">"Kjo pajisje nuk ka sensor vërtetimi për fytyrën"</string>
+ <string name="face_name_template" msgid="7004562145809595384">"Fytyra <xliff:g id="FACEID">%d</xliff:g>"</string>
+ <string-array name="face_error_vendor">
+ </string-array>
+ <string name="face_icon_content_description" msgid="4024817159806482191">"Ikona e fytyrës"</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"lexo cilësimet e sinkronizimit"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Lejon aplikacionin të lexojë cilësimet e sinkronizimit për një llogari. Për shembull, kjo mund të përcaktojë nëse aplikacioni \"Kontaktet\" është i sinkronizuar me një llogari."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"ndiz ose fik sinkronizimin"</string>
@@ -1178,6 +1208,9 @@
<skip />
<string name="wifi_no_internet" msgid="8938267198124654938">"Wi-Fi nuk ka qasje në internet"</string>
<string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Trokit për opsionet"</string>
+ <string name="wifi_softap_config_change" msgid="8475911871165857607">"Ndryshimet në cilësimet e zonës së qasjes për internet"</string>
+ <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"Brezi yt i zonës së qasjes për internet ka ndryshuar."</string>
+ <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"Kjo pajisje nuk e mbështet preferencën për vetëm 5 GHz. Përkundrazi, pajisja do të përdorë brezin 5 GHz nëse ka."</string>
<string name="network_switch_metered" msgid="4671730921726992671">"Kaloi te <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
<string name="network_switch_metered_detail" msgid="775163331794506615">"Pajisja përdor <xliff:g id="NEW_NETWORK">%1$s</xliff:g> kur <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> nuk ka qasje në internet. Mund të zbatohen tarifa."</string>
<string name="network_switch_metered_toast" msgid="5779283181685974304">"Kaloi nga <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> te <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 2001cf7..fa1dc59 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -239,9 +239,6 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Режим рада у авиону"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Режим рада у авиону је УКЉУЧЕН"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Режим рада у авиону је ИСКЉУЧЕН"</string>
- <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Уштеда батерије"</string>
- <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Уштеда батерије је ИСКЉУЧЕНА"</string>
- <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Уштеда батерије је УКЉУЧЕНА"</string>
<string name="global_action_settings" msgid="1756531602592545966">"Подешавања"</string>
<string name="global_action_assist" msgid="3892832961594295030">"Помоћ"</string>
<string name="global_action_voice_assist" msgid="7751191495200504480">"Гласовна помоћ"</string>
@@ -279,6 +276,9 @@
<string name="permgrouplab_location" msgid="7275582855722310164">"Локација"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"приступи локацији овог уређаја"</string>
<string name="permgrouprequest_location" msgid="3788275734953323491">"Желите ли да омогућите да <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> приступа локацији овог уређаја?"</string>
+ <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"Апликација ће имати приступ локацији само док користите апликацију."</string>
+ <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"Желите ли да омогућите да <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> приступа локацији овог уређаја?"</string>
+ <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"Апликација ће увек имати приступ локацији, чак и када не користите апликацију."</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Календар"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"приступи календару"</string>
<string name="permgrouprequest_calendar" msgid="289900767793189421">"Желите ли да омогућите да <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> приступа календару?"</string>
@@ -405,8 +405,7 @@
<string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Ова апликацији може да додаје, уклања или мења догађаје из календара на телефону. Ова апликација може да шаље поруке које изгледају као да их шаљу власници календара или да мења догађаје без знања власника."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"приступ додатним командама добављача локације"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Омогућава апликацији да приступа додатним командама даваоца услуга локације. То може да омогући апликацији да утиче на рад GPS-а или других извора локације."</string>
- <!-- no translation found for permlab_accessFineLocation (6265109654698562427) -->
- <skip />
+ <string name="permlab_accessFineLocation" msgid="6265109654698562427">"приступ прецизној локацији само у првом плану"</string>
<string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Ова апликација може да одреди вашу тачну локацију само када ради у првом плану. Ове услуге локације морају да буду укључене и доступне на телефону да би апликација могла да их користи. То може да повећа потрошњу батерије."</string>
<string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"приступ приближној локацији (утврђена преко мреже)"</string>
<string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Ова апликација може да приступи вашој локацији помоћу извора мреже, као што су мобилни предајници и Wi-Fi мреже. Ове услуге локације морају да буду укључене и доступне на таблету да би апликација могла да их користи."</string>
@@ -521,6 +520,37 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Икона отиска прста"</string>
+ <string name="permlab_manageFace" msgid="2137540986007309781">"управљање хардв. за потврду идентитета помоћу лица"</string>
+ <string name="permdesc_manageFace" msgid="8919637120670185330">"Дозвољава да апликација активира методе за додавање и брисање шаблона лица ради коришћења."</string>
+ <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"коришћење хардв. за потврду идентитета помоћу лица"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Дозвољава да апликација користи хардвер за потврду идентитета помоћу лица"</string>
+ <string name="face_acquired_insufficient" msgid="5901287247766106330">"Обрада лица није успела. Пробајте поново."</string>
+ <string name="face_acquired_too_bright" msgid="610606792381297174">"Лице је пресветло. Пробајте са слабијим осветљењем."</string>
+ <string name="face_acquired_too_dark" msgid="7229162716976778371">"Лице је исувише тамно. Откријте извор светла."</string>
+ <string name="face_acquired_too_close" msgid="1980310037427755293">"Удаљите сензор од лица."</string>
+ <string name="face_acquired_too_far" msgid="4494571381828850007">"Приближите сензор лицу."</string>
+ <string name="face_acquired_too_high" msgid="228411096134808372">"Померите сензор навише."</string>
+ <string name="face_acquired_too_low" msgid="4539774649296349109">"Померите сензор наниже."</string>
+ <string name="face_acquired_too_right" msgid="1650292067226118760">"Померите сензор удесно."</string>
+ <string name="face_acquired_too_left" msgid="2712489669456176505">"Померите сензор улево."</string>
+ <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Гледајте у сензор."</string>
+ <string name="face_acquired_not_detected" msgid="5707782294589511391">"Није откривено ниједно лице."</string>
+ <string name="face_acquired_not_steady" msgid="3722829465011040042">"Задржите лице испред уређаја без померања."</string>
+ <string-array name="face_acquired_vendor">
+ </string-array>
+ <string name="face_error_hw_not_available" msgid="6255891785768984615">"Харвдер за лице није доступан."</string>
+ <string name="face_error_timeout" msgid="4014326147867150054">"Истекло је време за проверу лица. Пробајте поново."</string>
+ <string name="face_error_no_space" msgid="8224993703466381314">"Није могуће сачувати лице."</string>
+ <string name="face_error_canceled" msgid="283945501061931023">"Обрада лица је отказана."</string>
+ <string name="face_error_lockout" msgid="3407426963155388504">"Превише покушаја. Пробајте поново касније."</string>
+ <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Више покушаја. Потврда идентитета је онемогућена."</string>
+ <string name="face_error_unable_to_process" msgid="238761109287767270">"Пробајте поново."</string>
+ <string name="face_error_not_enrolled" msgid="9166792142679691323">"Није регистровано ниједно лице."</string>
+ <string name="face_error_hw_not_present" msgid="4737289254517095671">"Овај уређај нема сензор за потврду идентитета помоћу лица"</string>
+ <string name="face_name_template" msgid="7004562145809595384">"Лице <xliff:g id="FACEID">%d</xliff:g>"</string>
+ <string-array name="face_error_vendor">
+ </string-array>
+ <string name="face_icon_content_description" msgid="4024817159806482191">"Икона лица"</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"читање подешавања синхронизације"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Дозвољава апликацији да чита подешавања синхронизације за налог. На пример, овако може да се утврди да ли је апликација Људи синхронизована са налогом."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"укључивање и искључивање синхронизације"</string>
@@ -1200,6 +1230,9 @@
<skip />
<string name="wifi_no_internet" msgid="8938267198124654938">"Wi-Fi нема приступ интернету"</string>
<string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Додирните за опције"</string>
+ <string name="wifi_softap_config_change" msgid="8475911871165857607">"Промене подешавања за хотспот"</string>
+ <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"Опсег хотспота је промењен."</string>
+ <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"Овај уређај не подржава подешавање само за 5 GHz. Уређај ће користити опсег од 5 GHz када буде доступан."</string>
<string name="network_switch_metered" msgid="4671730921726992671">"Прешли сте на тип мреже <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
<string name="network_switch_metered_detail" msgid="775163331794506615">"Уређај користи тип мреже <xliff:g id="NEW_NETWORK">%1$s</xliff:g> када тип мреже <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> нема приступ интернету. Можда ће се наплаћивати трошкови."</string>
<string name="network_switch_metered_toast" msgid="5779283181685974304">"Прешли сте са типа мреже <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> на тип мреже <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 223e7f3..7b9005c50 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -236,9 +236,6 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Flygplansläge"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Flygplansläge är AKTIVERAT"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Flygplansläge är INAKTIVERAT"</string>
- <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Batterisparläge"</string>
- <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Batterisparläget har inaktiverats"</string>
- <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Batterisparläget har aktiverats"</string>
<string name="global_action_settings" msgid="1756531602592545966">"Inställningar"</string>
<string name="global_action_assist" msgid="3892832961594295030">"Hjälp"</string>
<string name="global_action_voice_assist" msgid="7751191495200504480">"Voice Assist"</string>
@@ -272,19 +269,22 @@
<string name="managed_profile_label" msgid="8947929265267690522">"Byt till jobbprofilen"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Kontakter"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"få tillgång till dina kontakter"</string>
- <string name="permgrouprequest_contacts" msgid="6032805601881764300">"Vill du ge <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> åtkomstbehörighet till dina kontakter?"</string>
+ <string name="permgrouprequest_contacts" msgid="6032805601881764300">"Vill du ge <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> åtkomst till dina kontakter?"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"Plats"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"komma åt enhetens platsuppgifter"</string>
- <string name="permgrouprequest_location" msgid="3788275734953323491">"Vill du ge <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> åtkomstbehörighet till enhetens plats?"</string>
+ <string name="permgrouprequest_location" msgid="3788275734953323491">"Vill du ge <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> åtkomst till enhetens plats?"</string>
+ <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"Appen har endast åtkomst till platsen när du använder den."</string>
+ <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"Vill du ge <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> åtkomst till enhetens plats?"</string>
+ <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"Appen har alltid åtkomst till platsen, även om du inte använder den."</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Kalender"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"få tillgång till din kalender"</string>
- <string name="permgrouprequest_calendar" msgid="289900767793189421">"Vill du ge <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> åtkomstbehörighet till din kalender?"</string>
+ <string name="permgrouprequest_calendar" msgid="289900767793189421">"Vill du ge <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> åtkomst till din kalender?"</string>
<string name="permgrouplab_sms" msgid="228308803364967808">"Sms"</string>
<string name="permgroupdesc_sms" msgid="4656988620100940350">"skicka och visa sms"</string>
<string name="permgrouprequest_sms" msgid="7168124215838204719">"Vill du ge <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> behörighet att skicka och visa sms?"</string>
<string name="permgrouplab_storage" msgid="1971118770546336966">"Lagring"</string>
<string name="permgroupdesc_storage" msgid="637758554581589203">"få åtkomst till foton, media och filer på din enhet"</string>
- <string name="permgrouprequest_storage" msgid="7885942926944299560">"Vill du ge <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> åtkomstbehörighet till foton, mediafiler och andra filer på enheten?"</string>
+ <string name="permgrouprequest_storage" msgid="7885942926944299560">"Vill du ge <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> åtkomst till foton, mediefiler och andra filer på enheten?"</string>
<string name="permgrouplab_microphone" msgid="171539900250043464">"Mikrofon"</string>
<string name="permgroupdesc_microphone" msgid="4988812113943554584">"spela in ljud"</string>
<string name="permgrouprequest_microphone" msgid="9167492350681916038">"Vill du ge <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> behörighet att spela in ljud?"</string>
@@ -299,7 +299,7 @@
<string name="permgrouprequest_phone" msgid="9166979577750581037">"Vill du ge <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> behörighet att ringa och hantera telefonsamtal?"</string>
<string name="permgrouplab_sensors" msgid="416037179223226722">"Kroppssensorer"</string>
<string name="permgroupdesc_sensors" msgid="7147968539346634043">"få åtkomst till sensordata om dina vitalparametrar"</string>
- <string name="permgrouprequest_sensors" msgid="6349806962814556786">"Vill du ge <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> åtkomstbehörighet till sensordata om vitalparametrar?"</string>
+ <string name="permgrouprequest_sensors" msgid="6349806962814556786">"Vill du ge <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> åtkomst till sensordata om vitalparametrar?"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="3901717936930170320">"Hämta fönsterinnehåll"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="3772225008605310672">"Granska innehållet i ett fönster som du interagerar med."</string>
<string name="capability_title_canRequestTouchExploration" msgid="3108723364676667320">"Aktivera Explore by touch"</string>
@@ -402,8 +402,7 @@
<string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Appen kan lägga till, ta bort eller ändra kalenderhändelser på mobilen. Appen kan skicka meddelanden som ser ut att komma från kalenderns ägare eller ändra uppgifter utan ägarens vetskap."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"få åtkomst till extra kommandon för platsleverantör"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Tillåter att appen får åtkomst till extra kommandon för platsleverantör. Detta kan innebära att appen tillåts störa funktionen för GPS eller andra platskällor."</string>
- <!-- no translation found for permlab_accessFineLocation (6265109654698562427) -->
- <skip />
+ <string name="permlab_accessFineLocation" msgid="6265109654698562427">"endast åtkomst till exakt plats i förgrunden"</string>
<string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Den här appen kan endast få information om din exakta plats när den körs i förgrunden. Platstjänsterna måste ha aktiverats och finnas på mobilen om appen ska kunna använda dem. Detta kan leda till ökad batteriförbrukning."</string>
<string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"få åtkomst till din ungefärliga position (nätverksbaserad)"</string>
<string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Appen kan få information om din plats från källor i nätverket, som mobilmaster och Wi-Fi-nätverk. De platstjänsterna måste ha aktiverats och finnas på surfplattan om appen ska kunna använda dem."</string>
@@ -518,6 +517,37 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Ikon för fingeravtryck"</string>
+ <string name="permlab_manageFace" msgid="2137540986007309781">"hantera maskinvara för ansiktsautentisering"</string>
+ <string name="permdesc_manageFace" msgid="8919637120670185330">"Tillåter att appen anropar metoder för att lägga till och radera ansiktsmallar."</string>
+ <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"använda maskinvara för ansiktsautentisering"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Tillåter att appen använder maskinvara för ansiktsigenkänning vid autentisering"</string>
+ <string name="face_acquired_insufficient" msgid="5901287247766106330">"Det gick inte att läsa av ansiktet. Försök igen."</string>
+ <string name="face_acquired_too_bright" msgid="610606792381297174">"Ansiktet är för ljust. Testa i svagare belysning."</string>
+ <string name="face_acquired_too_dark" msgid="7229162716976778371">"Ansiktet är för mörkt. Testa i starkare belysning."</string>
+ <string name="face_acquired_too_close" msgid="1980310037427755293">"Flytta sensorn längre ifrån ansiktet."</string>
+ <string name="face_acquired_too_far" msgid="4494571381828850007">"Ha sensorn närmare ansiktet."</string>
+ <string name="face_acquired_too_high" msgid="228411096134808372">"Flytta sensorn uppåt."</string>
+ <string name="face_acquired_too_low" msgid="4539774649296349109">"Flytta sensorn nedåt."</string>
+ <string name="face_acquired_too_right" msgid="1650292067226118760">"Flytta sensorn åt höger."</string>
+ <string name="face_acquired_too_left" msgid="2712489669456176505">"Flytta sensorn åt vänster."</string>
+ <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Titta på sensorn."</string>
+ <string name="face_acquired_not_detected" msgid="5707782294589511391">"Inget ansikte hittades."</string>
+ <string name="face_acquired_not_steady" msgid="3722829465011040042">"Håll ansiktet stilla framför enheten."</string>
+ <string-array name="face_acquired_vendor">
+ </string-array>
+ <string name="face_error_hw_not_available" msgid="6255891785768984615">"Maskinvara för ansiktsigenkänning saknas."</string>
+ <string name="face_error_timeout" msgid="4014326147867150054">"Tidsgränsen för ansikte har nåtts. Försök igen."</string>
+ <string name="face_error_no_space" msgid="8224993703466381314">"Det gick inte att lagra ansiktet."</string>
+ <string name="face_error_canceled" msgid="283945501061931023">"Ansiktsåtgärden har avbrutits."</string>
+ <string name="face_error_lockout" msgid="3407426963155388504">"Du har gjort för många försök. Försök igen senare."</string>
+ <string name="face_error_lockout_permanent" msgid="8198354656746088890">"För många försök. Ansiktsautentisering inaktiverad"</string>
+ <string name="face_error_unable_to_process" msgid="238761109287767270">"Försök igen."</string>
+ <string name="face_error_not_enrolled" msgid="9166792142679691323">"Inget ansikte har registrerats."</string>
+ <string name="face_error_hw_not_present" msgid="4737289254517095671">"Enheten har ingen sensor för ansiktsautentisering"</string>
+ <string name="face_name_template" msgid="7004562145809595384">"Ansikte <xliff:g id="FACEID">%d</xliff:g>"</string>
+ <string-array name="face_error_vendor">
+ </string-array>
+ <string name="face_icon_content_description" msgid="4024817159806482191">"Ansikte"</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"läsa synkroniseringsinställningar"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Tillåter att appen läser synkroniseringsinställningarna för ett konto. Detta kan användas till exempel för att avgöra om appen Personer är synkroniserad med ett konto."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"aktivera/inaktivera synkronisering"</string>
@@ -1178,6 +1208,9 @@
<skip />
<string name="wifi_no_internet" msgid="8938267198124654938">"Wi-Fi-nätverket är inte anslutet till internet"</string>
<string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Tryck för alternativ"</string>
+ <string name="wifi_softap_config_change" msgid="8475911871165857607">"Ändringar i inställningarna för surfzon"</string>
+ <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"Frekvensbandet för surfzonen har ändrats."</string>
+ <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"Den här enheten har inte stöd för inställningen för att endast använda 5 GHz. I stället används 5 GHz när det är möjligt."</string>
<string name="network_switch_metered" msgid="4671730921726992671">"Byte av nätverk till <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
<string name="network_switch_metered_detail" msgid="775163331794506615">"<xliff:g id="NEW_NETWORK">%1$s</xliff:g> används på enheten när det inte finns internetåtkomst via <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>. Avgifter kan tillkomma."</string>
<string name="network_switch_metered_toast" msgid="5779283181685974304">"Byte av nätverk från <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> till <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 995ed14..9d8790d 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -71,10 +71,10 @@
<string name="RuacMmi" msgid="7827887459138308886">"Ukataaji wa simu zinazokera zisizohitajika"</string>
<string name="CndMmi" msgid="3116446237081575808">"Kuonyeshwa kwa nambari inayopiga"</string>
<string name="DndMmi" msgid="1265478932418334331">"Usinisumbue"</string>
- <string name="CLIRDefaultOnNextCallOn" msgid="429415409145781923">"Chaguo msingi za ID ya mpigaji simu za kutozuia. Simu ifuatayo: Imezuiliwa"</string>
- <string name="CLIRDefaultOnNextCallOff" msgid="3092918006077864624">"Chaguo msingi za kitambulisho cha mpigaji simu huwa kuzuiwa. Simu ifuatayo: Haijazuiliwa"</string>
- <string name="CLIRDefaultOffNextCallOn" msgid="6179425182856418465">"Chaguo msingi za ID ya mpigaji simu za kutozuia. Simu ifuatayo:Imezuiliwa"</string>
- <string name="CLIRDefaultOffNextCallOff" msgid="2567998633124408552">"Chaguo msingi za ID ya mpigaji simu za kutozuia. Simu ifuatayo: Haijazuiliwa"</string>
+ <string name="CLIRDefaultOnNextCallOn" msgid="429415409145781923">"Chaguomsingi za ID ya mpigaji simu za kutozuia. Simu ifuatayo: Imezuiliwa"</string>
+ <string name="CLIRDefaultOnNextCallOff" msgid="3092918006077864624">"Chaguomsingi za kitambulisho cha mpigaji simu huwa kuzuiwa. Simu ifuatayo: Haijazuiliwa"</string>
+ <string name="CLIRDefaultOffNextCallOn" msgid="6179425182856418465">"Chaguomsingi za ID ya mpigaji simu za kutozuia. Simu ifuatayo:Imezuiliwa"</string>
+ <string name="CLIRDefaultOffNextCallOff" msgid="2567998633124408552">"Chaguomsingi za ID ya mpigaji simu za kutozuia. Simu ifuatayo: Haijazuiliwa"</string>
<string name="serviceNotProvisioned" msgid="8614830180508686666">"Huduma haitathminiwi."</string>
<string name="CLIRPermanent" msgid="3377371145926835671">"Hauwezi kubadilisha mpangilio wa kitambulisho cha anayepiga."</string>
<string name="RestrictedOnDataTitle" msgid="5221736429761078014">"Hakuna huduma ya data kwa vifaa vya mkononi"</string>
@@ -234,9 +234,6 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Hali ya ndegeni"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Hali ya ndegeni IMEWASHWA"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Hali ya ndegeni IMEZIMWA"</string>
- <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Kiokoa betri"</string>
- <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Kiokoa betri KIMEZIMWA"</string>
- <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Kiokoa betri KIMEWASHWA"</string>
<string name="global_action_settings" msgid="1756531602592545966">"Mipangilio"</string>
<string name="global_action_assist" msgid="3892832961594295030">"Mapendekezo"</string>
<string name="global_action_voice_assist" msgid="7751191495200504480">"Usaidizi wa Sauti"</string>
@@ -274,6 +271,9 @@
<string name="permgrouplab_location" msgid="7275582855722310164">"Mahali"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"itambue mahali kifaa hiki kilipo"</string>
<string name="permgrouprequest_location" msgid="3788275734953323491">"Ungependa kuiruhusu <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> itambue mahali kifaa kilipo?"</string>
+ <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"Programu itafikia tu data ya mahali ulipo unaipotumia."</string>
+ <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"Ungependa kuruhusu <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ifikie data ya mahali kifaa hiki kilipo kila wakati?"</string>
+ <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"Programu itaweza kufikia data ya mahali kifaa kilipo hata wakati huitumii."</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Kalenda"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"ifikie kalenda yako"</string>
<string name="permgrouprequest_calendar" msgid="289900767793189421">"Ungependa kuiruhusu <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ifikie kalenda yako?"</string>
@@ -400,8 +400,7 @@
<string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Programu hii inaweza kuongeza, kuondoa au kubadilisha matukio kwenye simu yako. Pogramu hii inaweza kutuma ujumbe unaoonekana kuwa umetoka kwa wamiliki wa kalenda au kurekebisha matukio bila kuwataarifu wamiliki."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"fikia amri za ziada za mtoa huduma ya mahali"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Ruhusu programu kufikia amri za ziada za mtoa huduma za mahali. Hii huenda ikaruhusu programu ikatize matumizi ya GPS au vyanzo vingine vya eneo."</string>
- <!-- no translation found for permlab_accessFineLocation (6265109654698562427) -->
- <skip />
+ <string name="permlab_accessFineLocation" msgid="6265109654698562427">"kufikia mahali mahususi ikiwa tu programu imefunguliwa kwenye skrini"</string>
<string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Programu hii inaweza kupata mahali halisi ikiwa tu umeifungua kwenye skrini. Ni lazima uwashe huduma hizi za mahali na zipatikane kwenye simu yako ili programu iweze kuzitumia. Hatua hii inaweza kuongeza utumiaji wa betri."</string>
<string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"fikia mahali karibu na hapo (inategemea mtandao)"</string>
<string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Programu hii inaweza kupata eneo lako kulingana na vyanzo vya mtandao kama vile minara ya simu na mitandao ya Wi-Fi. Huduma hizi za mahali lazima ziwashwe na zipatikane kwenye kompyuta yako kibao ili programu iweze kuzitumia."</string>
@@ -516,6 +515,37 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Aikoni ya kitambulisho"</string>
+ <string name="permlab_manageFace" msgid="2137540986007309781">"dhibiti maunzi ya kuthibitisha uso"</string>
+ <string name="permdesc_manageFace" msgid="8919637120670185330">"Huruhusu programu iombe njia za kuongeza na kufuta violezo vya uso vitakavyotumiwa."</string>
+ <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"tumia maunzi ya kuthibistiha uso"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Huruhusu programu ithibitishe uso kwa kutumia maunzi ya kuthibitisha"</string>
+ <string name="face_acquired_insufficient" msgid="5901287247766106330">"Imeshindwa kuchakata uso. Tafadhali jaribu tena."</string>
+ <string name="face_acquired_too_bright" msgid="610606792381297174">"Uso unang\'aa sana. Tafadhalia punguza mwangaza."</string>
+ <string name="face_acquired_too_dark" msgid="7229162716976778371">"Uso hauna mwangaza wa kutosha. Tafadhali ongeza mwangaza."</string>
+ <string name="face_acquired_too_close" msgid="1980310037427755293">"Tafadhali sogeza kitambuzi mbali na uso."</string>
+ <string name="face_acquired_too_far" msgid="4494571381828850007">"Tafadhali sogeza kitambuzi karibu na uso."</string>
+ <string name="face_acquired_too_high" msgid="228411096134808372">"Tafadhali sogeza kitambuzi juu."</string>
+ <string name="face_acquired_too_low" msgid="4539774649296349109">"Tafadhali sogeza kitambuzi chini."</string>
+ <string name="face_acquired_too_right" msgid="1650292067226118760">"Tafadhali sogeza kitambuzi kulia."</string>
+ <string name="face_acquired_too_left" msgid="2712489669456176505">"Tafadhali sogeza kitambuzi kushoto."</string>
+ <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Tafadhali angalia kitambuzi."</string>
+ <string name="face_acquired_not_detected" msgid="5707782294589511391">"Haikutambua uso wowote"</string>
+ <string name="face_acquired_not_steady" msgid="3722829465011040042">"Weka uso wima mbele ya kifaa."</string>
+ <string-array name="face_acquired_vendor">
+ </string-array>
+ <string name="face_error_hw_not_available" msgid="6255891785768984615">"Maunzi ya uso hayapatikani."</string>
+ <string name="face_error_timeout" msgid="4014326147867150054">"Muda wa kutambua uso umeisha. Jaribu tena."</string>
+ <string name="face_error_no_space" msgid="8224993703466381314">"Huwezi kuhifadhi uso."</string>
+ <string name="face_error_canceled" msgid="283945501061931023">"Utendaji wa kitambulisho umeghairiwa."</string>
+ <string name="face_error_lockout" msgid="3407426963155388504">"Umejaribu mara nyingi mno. Jaribu tena baadaye."</string>
+ <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Umejaribu mara nyingi mno. Kitambuzi cha uso kimezimwa."</string>
+ <string name="face_error_unable_to_process" msgid="238761109287767270">"Jaribu tena."</string>
+ <string name="face_error_not_enrolled" msgid="9166792142679691323">"Hujasajili uso wowote."</string>
+ <string name="face_error_hw_not_present" msgid="4737289254517095671">"Kifaa hiki hakina kitambuzi kinachothibitisha uso"</string>
+ <string name="face_name_template" msgid="7004562145809595384">"Uso wa <xliff:g id="FACEID">%d</xliff:g>"</string>
+ <string-array name="face_error_vendor">
+ </string-array>
+ <string name="face_icon_content_description" msgid="4024817159806482191">"Aikoni ya uso"</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"kusoma mipangilio ya usawazishaji"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Inaruhusu programu kusoma mipangilio ya upatanishi wa akaunti. Kwa mfano, huku kunaweza kuamua kama programu ya Watu imepatanishwa na akaunti."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"kuwasha na kuzima usawazishaji"</string>
@@ -773,9 +803,9 @@
<string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Umejaribu kufungua kompyuta kibao kwa njia isiyo sahihi mara <xliff:g id="NUMBER_0">%1$d</xliff:g>. Ukijaribu tena mara <xliff:g id="NUMBER_1">%2$d</xliff:g> bila mafanikio, kompyuta ndogo itarejeshwa kwenye mipangilio iliyotoka nayo kiwandani na data yote iliyomo itafutwa."</string>
<string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Umekosea majaribio ya kufungua runinga mara <xliff:g id="NUMBER_0">%1$d</xliff:g>. Ukikosea majaribio <xliff:g id="NUMBER_1">%2$d</xliff:g> zaidi, runinga itarejeshwa katika hali iliyotoka nayo kiwandani na data yote ya watumiaji itafutwa."</string>
<string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Umejaribu kufungua simu kwa njia isiyo sahihi mara <xliff:g id="NUMBER_0">%1$d</xliff:g>. Ukijaribu tena mara <xliff:g id="NUMBER_1">%2$d</xliff:g> bila mafanikio, simu itarejeshwa kwenye mipangilio iliyotoka nayo kiwandani na data yote iliyomo itafutwa."</string>
- <string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"Umejaribu kufungua kompyuta ndogo kwa njia isiyo sahihi mara <xliff:g id="NUMBER">%d</xliff:g>. Kompyuta ndogo haitaweza kuwekwa upya kwa kiwanda chaguo msingi."</string>
+ <string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"Umejaribu kufungua kompyuta ndogo kwa njia isiyo sahihi mara <xliff:g id="NUMBER">%d</xliff:g>. Kompyuta ndogo haitaweza kuwekwa upya kwa kiwanda chaguomsingi."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="3195755534096192191">"Umekosea majaribio ya kufungua runinga mara <xliff:g id="NUMBER">%d</xliff:g>. Sasa runinga itarejeshwa katika mipangilio iliyotoka nayo kiwandani."</string>
- <string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"Umejaribu kufungua kompyuta ndogo kwa njia isiyo sahihi mara <xliff:g id="NUMBER">%d</xliff:g>. Kompyuta ndogo haitaweza kuwekwa upya kwa kiwanda chaguo msingi."</string>
+ <string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"Umejaribu kufungua kompyuta ndogo kwa njia isiyo sahihi mara <xliff:g id="NUMBER">%d</xliff:g>. Kompyuta ndogo haitaweza kuwekwa upya kwa kiwanda chaguomsingi."</string>
<string name="lockscreen_too_many_failed_attempts_countdown" msgid="6251480343394389665">"Jaribu tena kwa sekunde <xliff:g id="NUMBER">%d</xliff:g>."</string>
<string name="lockscreen_forgot_pattern_button_text" msgid="2626999449610695930">"Umesahau mchoro?"</string>
<string name="lockscreen_glogin_forgot_pattern" msgid="2588521501166032747">"Fungua akaunti"</string>
@@ -1065,9 +1095,9 @@
<string name="whichImageCaptureApplication" msgid="3680261417470652882">"Piga picha ukitumia"</string>
<string name="whichImageCaptureApplicationNamed" msgid="8619384150737825003">"Piga picha ukitumia %1$s"</string>
<string name="whichImageCaptureApplicationLabel" msgid="6390303445371527066">"Piga picha"</string>
- <string name="alwaysUse" msgid="4583018368000610438">"Tumia kama chaguo msingi la kitendo hiki."</string>
+ <string name="alwaysUse" msgid="4583018368000610438">"Tumia kama chaguomsingi la kitendo hiki."</string>
<string name="use_a_different_app" msgid="8134926230585710243">"Tumia programu tofauti"</string>
- <string name="clearDefaultHintMsg" msgid="3252584689512077257">"Futa chaguo msingi katika mipangilio ya Mfumo > Apps > iliyopakuliwa."</string>
+ <string name="clearDefaultHintMsg" msgid="3252584689512077257">"Futa chaguomsingi katika mipangilio ya Mfumo > Apps > iliyopakuliwa."</string>
<string name="chooseActivity" msgid="7486876147751803333">"Chagua kitendo"</string>
<string name="chooseUsbActivity" msgid="6894748416073583509">"Chagua programu ya kifaa cha USB"</string>
<string name="noApplications" msgid="2991814273936504689">"Hakuna programu zinazoweza kufanya tendo hili."</string>
@@ -1142,8 +1172,8 @@
<string name="volume_icon_description_incall" msgid="8890073218154543397">"Sauti ya simu"</string>
<string name="volume_icon_description_media" msgid="4217311719665194215">"Sauti ya faili"</string>
<string name="volume_icon_description_notification" msgid="7044986546477282274">"Sauti ya arifa"</string>
- <string name="ringtone_default" msgid="3789758980357696936">"Mlio chaguo msingi"</string>
- <string name="ringtone_default_with_actual" msgid="1767304850491060581">"Chaguo msingi (<xliff:g id="ACTUAL_RINGTONE">%1$s</xliff:g>)"</string>
+ <string name="ringtone_default" msgid="3789758980357696936">"Mlio chaguomsingi"</string>
+ <string name="ringtone_default_with_actual" msgid="1767304850491060581">"Chaguomsingi (<xliff:g id="ACTUAL_RINGTONE">%1$s</xliff:g>)"</string>
<string name="ringtone_silent" msgid="7937634392408977062">"Hamna"</string>
<string name="ringtone_picker_title" msgid="3515143939175119094">"Toni za mlio"</string>
<string name="ringtone_picker_title_alarm" msgid="6473325356070549702">"Sauti za kengele"</string>
@@ -1176,6 +1206,9 @@
<skip />
<string name="wifi_no_internet" msgid="8938267198124654938">"Wi-Fi haina muunganisho wa intaneti"</string>
<string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Gusa ili upate chaguo"</string>
+ <string name="wifi_softap_config_change" msgid="8475911871165857607">"Mabadiliko kwenye mipangilio ya mtandao pepe"</string>
+ <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"Bendi ya mtandao pepe wako imebadilika."</string>
+ <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"Kifaa hiki hakitumii mapendeleo yako ya GHz 5 pekee. Badala yake, kifaa hiki kitatumia bendi ya GHz 5 itakapopatikana."</string>
<string name="network_switch_metered" msgid="4671730921726992671">"Sasa inatumia <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
<string name="network_switch_metered_detail" msgid="775163331794506615">"Kifaa hutumia <xliff:g id="NEW_NETWORK">%1$s</xliff:g> wakati <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> haina intaneti. Huenda ukalipishwa."</string>
<string name="network_switch_metered_toast" msgid="5779283181685974304">"Imebadilisha mtandao kutoka <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> na sasa inatumia <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
@@ -1540,12 +1573,12 @@
<string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Umeingiza nenosiri lako kwa makosa mara <xliff:g id="NUMBER_0">%1$d</xliff:g>. \n\n Jaribu tena baada ya sekunde <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Umeingiza nenosiri lako kwa makosa mara <xliff:g id="NUMBER_0">%1$d</xliff:g>. \n\n Jaribu tena baada ya sekunde <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Umechora ruwaza yako ya kufunga kwa makosa mara <xliff:g id="NUMBER_0">%1$d</xliff:g>. \n\n Jaribu tena baada ya sekunde <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Umejaribu kufungua kompyuta ndogo kwa njia isiyo sahihi mara <xliff:g id="NUMBER_0">%1$d</xliff:g>. Baada ya majaribio <xliff:g id="NUMBER_1">%2$d</xliff:g> zaidi yasiyofaulu, kompyuta ndogo itarejeshwa katika mfumo chaguo msingi ilivyotoka kiwandani data yote ya mtumiaji itapotea."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Umejaribu kufungua kompyuta ndogo kwa njia isiyo sahihi mara <xliff:g id="NUMBER_0">%1$d</xliff:g>. Baada ya majaribio <xliff:g id="NUMBER_1">%2$d</xliff:g> zaidi yasiyofaulu, kompyuta ndogo itarejeshwa katika mfumo chaguomsingi ilivyotoka kiwandani data yote ya mtumiaji itapotea."</string>
<string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Umekosea majaribio ya kufungua runinga mara <xliff:g id="NUMBER_0">%1$d</xliff:g>. Ukikosea majaribio mengine <xliff:g id="NUMBER_1">%2$d</xliff:g>, runinga itarejeshwa katika hali iliyotoka nayo kiwandani na data yote ya watumiaji itafutwa."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Umejaribu kufungua simu kwa njia isiyo sahihi mara <xliff:g id="NUMBER_0">%1$d</xliff:g>. Baada ya majaribio <xliff:g id="NUMBER_1">%2$d</xliff:g> zaidi yasiyofaulu, simu itarejeshwa katika mfumo chaguo msingi ilivyotoka kiwandani na data yote ya mtumiaji itapotea."</string>
- <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Umejaribu kufungua kompyuta ndogo kwa njia isiyo sahihi mara <xliff:g id="NUMBER">%d</xliff:g>. Sasa kompyuta ndogo itarejeshwa katika mfumo chaguo msingi ilivyotoka kiwandani."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Umejaribu kufungua simu kwa njia isiyo sahihi mara <xliff:g id="NUMBER_0">%1$d</xliff:g>. Baada ya majaribio <xliff:g id="NUMBER_1">%2$d</xliff:g> zaidi yasiyofaulu, simu itarejeshwa katika mfumo chaguomsingi ilivyotoka kiwandani na data yote ya mtumiaji itapotea."</string>
+ <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Umejaribu kufungua kompyuta ndogo kwa njia isiyo sahihi mara <xliff:g id="NUMBER">%d</xliff:g>. Sasa kompyuta ndogo itarejeshwa katika mfumo chaguomsingi ilivyotoka kiwandani."</string>
<string name="kg_failed_attempts_now_wiping" product="tv" msgid="4987878286750741463">"Umekosea majaribio ya kufungua runinga mara <xliff:g id="NUMBER">%d</xliff:g>. Sasa runinga itarejeshwa katika hali iliyotoka nayo kiwandani."</string>
- <string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Umejaribu kufungua simu kwa njia isiyo sahihi mara <xliff:g id="NUMBER">%d</xliff:g>. Sasa simu itarejeshwa katika mfumo chaguo msingi ilivyotoka kiwandani."</string>
+ <string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Umejaribu kufungua simu kwa njia isiyo sahihi mara <xliff:g id="NUMBER">%d</xliff:g>. Sasa simu itarejeshwa katika mfumo chaguomsingi ilivyotoka kiwandani."</string>
<string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Umekosea katika kuweka mchoro wako wa kufungua mara <xliff:g id="NUMBER_0">%1$d</xliff:g>. Baada ya majaribio <xliff:g id="NUMBER_1">%2$d</xliff:g> bila kufaulu, utaombwa kufungua kompyuta yako ndogo kwa kutumia akaunti yako ya barua pepe.\n\n Jaribu tena baada ya sekunde <xliff:g id="NUMBER_2">%3$d</xliff:g>."</string>
<string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Umekosea kuchora mchoro wa kufungua mara <xliff:g id="NUMBER_0">%1$d</xliff:g>. Ukikosea majaribio mengine <xliff:g id="NUMBER_1">%2$d</xliff:g>, utaombwa ufungue runinga yako ukitumia akaunti ya barua pepe.\n\n Jaribu tena baada ya sekunde <xliff:g id="NUMBER_2">%3$d</xliff:g>."</string>
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Umekosea kuchora mchoro wako wa kufungua mara <xliff:g id="NUMBER_0">%1$d</xliff:g>. Baada ya majaribio <xliff:g id="NUMBER_1">%2$d</xliff:g> yasiyofaulu, utaombwa kufungua simu yako kwa kutumia akaunti ya barua pepe.\n\n Jaribu tena baada ya sekunde <xliff:g id="NUMBER_2">%3$d</xliff:g>."</string>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index 34a7798..0edf58c 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -236,9 +236,6 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"விமானப் பயன்முறை"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"விமானப் பயன்முறை இயக்கத்தில் உள்ளது"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"விமானப் பயன்முறை முடக்கத்தில் உள்ளது"</string>
- <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"பேட்டரி சேமிப்பான்"</string>
- <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"பேட்டரி சேமிப்பான் ஆஃப் செய்யப்பட்டுள்ளது"</string>
- <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"பேட்டரி சேமிப்பான் ஆன் செய்யப்பட்டுள்ளது"</string>
<string name="global_action_settings" msgid="1756531602592545966">"அமைப்பு"</string>
<string name="global_action_assist" msgid="3892832961594295030">"உதவி"</string>
<string name="global_action_voice_assist" msgid="7751191495200504480">"குரல் உதவி"</string>
@@ -276,6 +273,9 @@
<string name="permgrouplab_location" msgid="7275582855722310164">"இருப்பிடம்"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"இந்தச் சாதனத்தின் இருப்பிடத்தை அறிந்து கொள்ள"</string>
<string name="permgrouprequest_location" msgid="3788275734953323491">"இந்தச் சாதனத்தின் இருப்பிடத்தை அணுகுவதற்கு <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> பயன்பாட்டை அனுமதிக்கவா?"</string>
+ <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"இந்த ஆப்ஸைப் பயன்படுத்தும் சமயத்தில் மட்டுமே, இது உங்கள் இருப்பிடத்தை அணுகும்."</string>
+ <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"இதன் இருப்பிடத்தை எப்போதுமே <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> அணுக அனுமதிக்கவா?"</string>
+ <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"இந்த ஆப்ஸைப் பயன்படுத்தாத சமயங்களில் கூட, இது உங்கள் இருப்பிடத்தை அணுகும்."</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"கேலெண்டர்"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"கேலெண்டரை அணுகலாம்"</string>
<string name="permgrouprequest_calendar" msgid="289900767793189421">"கேலெண்டரை அணுகுவதற்கு <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> பயன்பாட்டை அனுமதிக்கவா?"</string>
@@ -402,8 +402,7 @@
<string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"இந்தப் பயன்பாடு உங்கள் மொபைலில் கேலெண்டர் நிகழ்வுகளைச் சேர்க்கலாம், அகற்றலாம் அல்லது மாற்றலாம். இந்தப் பயன்பாடு கேலெண்டர் உரிமையாளர்கள் அனுப்பியது போல் தோன்றும் செய்திகளை அனுப்பலாம் அல்லது உரிமையாளர்களிடம் தெரிவிக்காமலே கேலெண்டரில் நிகழ்வுகளை மாற்றலாம்."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"கூடுதல் இட வழங்குநரின் கட்டளைகளின் அணுகல்"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"கூடுதல் இட வழங்குநர் கட்டளைகளை அணுகப் பயன்பாட்டை அனுமதிக்கிறது. இது, GPS அல்லது பிற இருப்பிட மூலங்களின் செயல்பாட்டை இடைமறிக்க பயன்பாட்டை அனுமதிக்கலாம்."</string>
- <!-- no translation found for permlab_accessFineLocation (6265109654698562427) -->
- <skip />
+ <string name="permlab_accessFineLocation" msgid="6265109654698562427">"முன்புலத்தில் இயங்கும்போது மட்டும் துல்லியமான இருப்பிடத்தைக் கண்டறிதல்"</string>
<string name="permdesc_accessFineLocation" msgid="3520508381065331098">"இந்த ஆப்ஸ் முன்புலத்தில் இயங்கும்போது மட்டுமே நீங்கள் இருக்கும் இடத்தைத் துல்லியமாகக் கண்டறியும். உங்கள் மொபைலில், இருப்பிடச் சேவைகளை ஆப்ஸ் பயன்படுத்துவதற்கு வசதியாக, அவை ஆன் செய்யப்பட்டிருக்க வேண்டும். இதனால் பேட்டரி அதிகம் பயன்படுத்தப்படலாம்."</string>
<string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"தோராயமான இருப்பிடத்தை அணுகுதல் (நெட்வொர்க் அடிப்படையில்)"</string>
<string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"இந்தப் பயன்பாடு நெட்வொர்க் மூலங்களின் (செல் கோபுரங்கள், வைஃபை நெட்வொர்க்குகள் போன்றவை) அடிப்படையில் உங்கள் இருப்பிடத்தைப் பெறலாம். பயன்பாடு பயன்படுத்தும் வகையில், உங்கள் டேப்லெட்டில் இந்த இருப்பிடச் சேவைகள் இயக்கப்பட்டு, கிடைக்கும்படி இருக்க வேண்டும்."</string>
@@ -518,6 +517,37 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"கைரேகை ஐகான்"</string>
+ <string name="permlab_manageFace" msgid="2137540986007309781">"முக அங்கீகாரத்திற்கான வன்பொருளை நிர்வகித்தல்"</string>
+ <string name="permdesc_manageFace" msgid="8919637120670185330">"உபயோகிப்பதற்காக முக டெம்ப்ளேட்டுகளை சேர்க்கும்/நீக்கும் முறைகளை இயக்க, ஆப்ஸை அனுமதிக்கும்."</string>
+ <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"முக அங்கீகாரத்திற்கான வன்பொருளைப் பயன்படுத்துதல்"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"அடையாளம் காண்பதற்கு, முக அங்கீகார வன்பொருளைப் பயன்படுத்த ஆப்ஸை அனுமதிக்கிறது"</string>
+ <string name="face_acquired_insufficient" msgid="5901287247766106330">"அடையாளம் காண முடியவில்லை. மீண்டும் முயலவும்."</string>
+ <string name="face_acquired_too_bright" msgid="610606792381297174">"முகம் பிரகாசமாக உள்ளது. குறைந்த ஒளியில் முயலவும்."</string>
+ <string name="face_acquired_too_dark" msgid="7229162716976778371">"முகம் தெரியவில்லை, வெளிச்சமான இடத்தில் முயலவும்."</string>
+ <string name="face_acquired_too_close" msgid="1980310037427755293">"சென்சாரை முகத்திலிருந்து சற்று தொலைவில் நகர்த்துக."</string>
+ <string name="face_acquired_too_far" msgid="4494571381828850007">"சென்சாரை முகத்திற்கு அருகில் கொண்டு வரவும்."</string>
+ <string name="face_acquired_too_high" msgid="228411096134808372">"சென்சாரை மேலே உயர்த்தவும்."</string>
+ <string name="face_acquired_too_low" msgid="4539774649296349109">"சென்சாரைக் கீழே நகர்த்தவும்."</string>
+ <string name="face_acquired_too_right" msgid="1650292067226118760">"சென்சாரை வலது புறமாக நகர்த்தவும்."</string>
+ <string name="face_acquired_too_left" msgid="2712489669456176505">"சென்சாரை இடது புறமாக நகர்த்தவும்."</string>
+ <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"சென்சாரைப் பார்க்கவும்."</string>
+ <string name="face_acquired_not_detected" msgid="5707782294589511391">"முகம் தெரியவில்லை."</string>
+ <string name="face_acquired_not_steady" msgid="3722829465011040042">"சாதனத்தின் முன், முகத்தை அசையாமல் காண்பிக்கவும்."</string>
+ <string-array name="face_acquired_vendor">
+ </string-array>
+ <string name="face_error_hw_not_available" msgid="6255891785768984615">"முக அங்கீகாரத்திற்கான வன்பொருள் இல்லை."</string>
+ <string name="face_error_timeout" msgid="4014326147867150054">"முகப் பதிவிற்கான நேரம் முடிந்தது. மீண்டும் முயல்க."</string>
+ <string name="face_error_no_space" msgid="8224993703466381314">"முகத்தைச் சேமிக்க இயலாது."</string>
+ <string name="face_error_canceled" msgid="283945501061931023">"முக அங்கீகாரச் செயல்பாடு ரத்துசெய்யப்பட்டது."</string>
+ <string name="face_error_lockout" msgid="3407426963155388504">"பலமுறை முயன்றுவிட்டீர்கள். பிறகு முயலவும்."</string>
+ <string name="face_error_lockout_permanent" msgid="8198354656746088890">"பலமுறை தோல்வி. முக அங்கீகாரம் முடக்கப்பட்டது."</string>
+ <string name="face_error_unable_to_process" msgid="238761109287767270">"மீண்டும் முயலவும்."</string>
+ <string name="face_error_not_enrolled" msgid="9166792142679691323">"முகம் எதுவும் பதிவு செய்யப்படவில்லை."</string>
+ <string name="face_error_hw_not_present" msgid="4737289254517095671">"இந்தச் சாதனத்தில், முக அங்கீகாரத்திற்கான சென்சார் இல்லை"</string>
+ <string name="face_name_template" msgid="7004562145809595384">"முகம் <xliff:g id="FACEID">%d</xliff:g>"</string>
+ <string-array name="face_error_vendor">
+ </string-array>
+ <string name="face_icon_content_description" msgid="4024817159806482191">"முக ஐகான்"</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"ஒத்திசைவு அமைப்புகளைப் படித்தல்"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"கணக்கிற்கான ஒத்திசைவு அமைப்புகளைப் படிக்க பயன்பாட்டை அனுமதிக்கிறது. எடுத்துக்காட்டாக, பீப்பிள் பயன்பாடு கணக்குடன் ஒத்திசைக்கப்பட்டுள்ளதா என்பதை இது தீர்மானிக்கலாம்."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"ஒத்திசைவை இயக்குவதையும், முடக்குவதையும் மாற்றுதல்"</string>
@@ -1178,6 +1208,9 @@
<skip />
<string name="wifi_no_internet" msgid="8938267198124654938">"வைஃபையில் இண்டர்நெட் அணுகல் இல்லை"</string>
<string name="wifi_no_internet_detailed" msgid="8083079241212301741">"விருப்பங்களுக்கு, தட்டவும்"</string>
+ <string name="wifi_softap_config_change" msgid="8475911871165857607">"உங்கள் ஹாட்ஸ்பாட் அமைப்புகளில் செய்யப்பட்டுள்ள மாற்றங்கள்"</string>
+ <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"உங்கள் ஹாட்ஸ்பாட்டின் அலைவரிசை மாறிவிட்டது."</string>
+ <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"இந்தச் சாதனத்தில், ’5GHz மட்டும்’ எனும் முன்னுரிமைத் தேர்வு ஆதரிக்கப்படவில்லை. எனினும் 5GHz அலைவரிசை கிடைக்கும்போது, சாதனம் அதைப் பயன்படுத்திக்கொள்ளும்."</string>
<string name="network_switch_metered" msgid="4671730921726992671">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g>க்கு மாற்றப்பட்டது"</string>
<string name="network_switch_metered_detail" msgid="775163331794506615">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> நெட்வொர்க்கில் இண்டர்நெட் அணுகல் இல்லாததால், சாதனமானது <xliff:g id="NEW_NETWORK">%1$s</xliff:g> நெட்வொர்க்கைப் பயன்படுத்துகிறது. கட்டணங்கள் விதிக்கப்படலாம்."</string>
<string name="network_switch_metered_toast" msgid="5779283181685974304">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> இலிருந்து <xliff:g id="NEW_NETWORK">%2$s</xliff:g>க்கு மாற்றப்பட்டது"</string>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index d45be72..98b737f 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -236,9 +236,6 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"ఎయిర్ప్లేన్ మోడ్"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"ఎయిర్ప్లేన్ మోడ్ ఆన్లో ఉంది"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"ఎయిర్ప్లేన్ మోడ్ ఆఫ్లో ఉంది"</string>
- <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"బ్యాటరీ సేవర్"</string>
- <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"బ్యాటరీ సేవర్ ఆఫ్లో ఉంది"</string>
- <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"బ్యాటరీ సేవర్ ఆన్లో ఉంది"</string>
<string name="global_action_settings" msgid="1756531602592545966">"సెట్టింగ్లు"</string>
<string name="global_action_assist" msgid="3892832961594295030">"సహాయం"</string>
<string name="global_action_voice_assist" msgid="7751191495200504480">"వాయిస్ అసిస్టెంట్"</string>
@@ -276,6 +273,9 @@
<string name="permgrouplab_location" msgid="7275582855722310164">"స్థానం"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"ఈ పరికర స్థానాన్ని యాక్సెస్ చేయడానికి"</string>
<string name="permgrouprequest_location" msgid="3788275734953323491">"ఈ పరికర స్థానాన్ని యాక్సెస్ చేయడానికి <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>ని అనుమతించాలా?"</string>
+ <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"మీరు యాప్ని ఉపయోగిస్తున్నప్పుడు మాత్రమే యాప్ స్థానానికి యాక్సెస్ కలిగి ఉంటుంది."</string>
+ <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"ఈ పరికర స్థానాన్ని యాక్సెస్ చేయడానికి <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>ని ఎల్లప్పుడూ అనుమతించాలా?"</string>
+ <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"ఈ యాప్ ఎల్లప్పుడూ స్థానానికి యాక్సెస్ కలిగి ఉంటుంది, మీరు యాప్ని ఉపయోగించనప్పుడు కూడా."</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"క్యాలెండర్"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"మీ క్యాలెండర్ను యాక్సెస్ చేయడానికి"</string>
<string name="permgrouprequest_calendar" msgid="289900767793189421">"మీ క్యాలెండర్ని యాక్సెస్ చేయడానికి <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>ని అనుమతించాలా?"</string>
@@ -402,8 +402,7 @@
<string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"ఈ యాప్ మీ ఫోన్లో క్యాలెండర్ ఈవెంట్లను జోడించగలదు, తీసివేయగలదు లేదా మార్చగలదు. ఈ యాప్ క్యాలెండర్ యజమానుల నుండి వచ్చినట్లుగా సందేశాలను పంపగలదు లేదా ఈవెంట్లను వాటి యజమానులకు తెలియకుండానే మార్చగలదు."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"అదనపు స్థాన ప్రదాత ఆదేశాలను యాక్సెస్ చేయడం"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"అదనపు స్థాన ప్రదాత ఆదేశాలను యాక్సెస్ చేయడానికి యాప్ను అనుమతిస్తుంది. ఇది GPS లేదా ఇతర స్థాన మూలాల నిర్వహణలో యాప్ ప్రమేయం ఉండేలా అనుమతించవచ్చు."</string>
- <!-- no translation found for permlab_accessFineLocation (6265109654698562427) -->
- <skip />
+ <string name="permlab_accessFineLocation" msgid="6265109654698562427">"స్క్రీన్పై ఉన్నప్పుడు మాత్రమే ఖచ్చితమైన స్థానాన్ని యాక్సెస్ చేయండి"</string>
<string name="permdesc_accessFineLocation" msgid="3520508381065331098">"ఈ యాప్ స్క్రీన్పై ఉన్నప్పుడు మాత్రమే అది మీ ఖచ్చితమైన స్థానాన్ని తెలుసుకోగలదు. యాప్ ఉపయోగించడానికి మీ ఫోన్లో ఈ స్థాన సేవలను తప్పనిసరిగా ఆన్ చేయాలి మరియు అందుబాటులో ఉండాలి. ఇది బ్యాటరీ వినియోగాన్ని పెంచవచ్చు."</string>
<string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"ఇంచుమించు స్థానాన్ని (నెట్వర్క్-ఆధారితం) యాక్సెస్ చేయడం"</string>
<string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"ఈ యాప్ సెల్ టవర్లు మరియు Wi-Fi నెట్వర్క్ల వంటి నెట్వర్క్ మూలాధారాల ఆధారంగా మీ స్థానాన్ని తెలుసుకోగలదు. యాప్ ఉపయోగించడానికి మీ టాబ్లెట్లో ఈ స్థాన సేవలను తప్పనిసరిగా ఆన్ చేయాలి మరియు అందుబాటులో ఉండాలి."</string>
@@ -518,6 +517,37 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"వేలిముద్ర చిహ్నం"</string>
+ <string name="permlab_manageFace" msgid="2137540986007309781">"ముఖ ప్రమాణీకరణ హార్డ్వేర్ను నిర్వహించండి"</string>
+ <string name="permdesc_manageFace" msgid="8919637120670185330">"వినియోగం కోసం ముఖ టెంప్లేట్లను జోడించే మరియు తొలగించే పద్ధతులను అమలు చేయడానికి యాప్ను అనుమతిస్తుంది."</string>
+ <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"ముఖ ప్రమాణీకరణ హార్డ్వేర్ను వాడండి"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"ప్రమాణీకరణ కోసం ముఖ ప్రామాణీకరణ హార్డ్వేర్ను ఉపయోగించడానికి యాప్ని అనుమతిస్తుంది"</string>
+ <string name="face_acquired_insufficient" msgid="5901287247766106330">"ముఖాన్ని ప్రాసెస్ చేయడం సాధ్యపడలేదు. దయచేసి మళ్లీ ప్రయత్నించండి."</string>
+ <string name="face_acquired_too_bright" msgid="610606792381297174">"ముఖం చాలా ప్రకాశవంతంగా ఉంటుంది. దయచేసి తక్కువ కాంతిలో ప్రయత్నించండి."</string>
+ <string name="face_acquired_too_dark" msgid="7229162716976778371">"ముఖం చాలా చీకటిగా ఉంది. దయచేసి కాంతి మూలాన్ని వెతకండి."</string>
+ <string name="face_acquired_too_close" msgid="1980310037427755293">"దయచేసి ముఖం నుండి దూరంగా సెన్సార్ను జరపండి."</string>
+ <string name="face_acquired_too_far" msgid="4494571381828850007">"దయచేసి సెన్సార్ను ముఖానికి మరింత దగ్గరగా తీసుకురండి."</string>
+ <string name="face_acquired_too_high" msgid="228411096134808372">"దయచేసి సెన్సార్ను ఎక్కువకు జరపండి."</string>
+ <string name="face_acquired_too_low" msgid="4539774649296349109">"దయచేసి సెన్సార్ను తక్కువకు జరపండి."</string>
+ <string name="face_acquired_too_right" msgid="1650292067226118760">"దయచేసి సెన్సార్ను కుడికి జరపండి."</string>
+ <string name="face_acquired_too_left" msgid="2712489669456176505">"దయచేసి సెన్సార్ను ఎడమవైపుకు జరపండి."</string>
+ <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"దయచేసి సెన్సార్ను చూడండి."</string>
+ <string name="face_acquired_not_detected" msgid="5707782294589511391">"ఎలాంటి ముఖం గుర్తించబడలేదు."</string>
+ <string name="face_acquired_not_steady" msgid="3722829465011040042">"పరికరానికి నేరుగా ముఖాన్ని స్థిరంగా ఉంచండి."</string>
+ <string-array name="face_acquired_vendor">
+ </string-array>
+ <string name="face_error_hw_not_available" msgid="6255891785768984615">"ముఖ హార్డ్వేర్ అందుబాటులో లేదు."</string>
+ <string name="face_error_timeout" msgid="4014326147867150054">"ముఖ గడువు సమయం చేరుకుంది. మళ్లీ ప్రయత్నించండి."</string>
+ <string name="face_error_no_space" msgid="8224993703466381314">"ముఖం నిల్వ చేయబడదు."</string>
+ <string name="face_error_canceled" msgid="283945501061931023">"ముఖ కార్యకలాపం రద్దయింది."</string>
+ <string name="face_error_lockout" msgid="3407426963155388504">"చాలా ఎక్కువ ప్రయత్నాలు చేసారు. తర్వాత మళ్లీ ప్రయత్నించండి."</string>
+ <string name="face_error_lockout_permanent" msgid="8198354656746088890">"చాలా ఎక్కువ ప్రయత్నాలు చేసారు. ముఖ ప్రమాణీకరణ నిలిపివేయబడింది."</string>
+ <string name="face_error_unable_to_process" msgid="238761109287767270">"మళ్లీ ప్రయత్నించండి."</string>
+ <string name="face_error_not_enrolled" msgid="9166792142679691323">"ముఖం నమోదు చేయబడలేదు."</string>
+ <string name="face_error_hw_not_present" msgid="4737289254517095671">"ఈ పరికరంలో ముఖ ప్రామాణీకరణ సెన్సార్ లేదు"</string>
+ <string name="face_name_template" msgid="7004562145809595384">"ముఖ <xliff:g id="FACEID">%d</xliff:g>"</string>
+ <string-array name="face_error_vendor">
+ </string-array>
+ <string name="face_icon_content_description" msgid="4024817159806482191">"ముఖ చిహ్నం"</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"సమకాలీకరణ సెట్టింగ్లను చదవడం"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"ఖాతా యొక్క సమకాలీకరణ సెట్టింగ్లను చదవడానికి యాప్ను అనుమతిస్తుంది. ఉదాహరణకు, వ్యక్తుల యాప్ ఖాతాతో సమకాలీకరించబడాలా లేదా అనే విషయాన్ని ఇది నిశ్చయించవచ్చు."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"సమకాలీకరణను ఆన్ మరియు ఆఫ్కు టోగుల్ చేయడం"</string>
@@ -1178,6 +1208,9 @@
<skip />
<string name="wifi_no_internet" msgid="8938267198124654938">"Wi-Fiకి ఇంటర్నెట్ యాక్సెస్ లేదు"</string>
<string name="wifi_no_internet_detailed" msgid="8083079241212301741">"ఎంపికల కోసం నొక్కండి"</string>
+ <string name="wifi_softap_config_change" msgid="8475911871165857607">"మీ హాట్స్పాట్ సెట్టింగ్లకు మార్పులు"</string>
+ <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"మీ హాట్స్పాట్ బ్యాండ్ మార్చబడింది."</string>
+ <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"ఈ పరికరం 5GHz కోసం మాత్రమే మీ ప్రాధాన్యతకు మద్దతు ఇవ్వదు. బదులుగా, ఈ పరికరం అందుబాటులో ఉన్నప్పుడు 5GHz బ్యాండ్ను ఉపయోగిస్తుంది."</string>
<string name="network_switch_metered" msgid="4671730921726992671">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g>కి మార్చబడింది"</string>
<string name="network_switch_metered_detail" msgid="775163331794506615">"పరికరం <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>కి ఇంటర్నెట్ యాక్సెస్ లేనప్పుడు <xliff:g id="NEW_NETWORK">%1$s</xliff:g>ని ఉపయోగిస్తుంది. ఛార్జీలు వర్తించవచ్చు."</string>
<string name="network_switch_metered_toast" msgid="5779283181685974304">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> నుండి <xliff:g id="NEW_NETWORK">%2$s</xliff:g>కి మార్చబడింది"</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 884ee5c..89c2e44 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -236,9 +236,6 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"โหมดใช้งานบนเครื่องบิน"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"เปิดโหมดใช้งานบนเครื่องบิน"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"โหมดใช้งานบนเครื่องบินปิดทำงานอยู่"</string>
- <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"โหมดประหยัดแบตเตอรี่"</string>
- <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"ปิดโหมดประหยัดแบตเตอรี่อยู่"</string>
- <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"เปิดโหมดประหยัดแบตเตอรี่อยู่"</string>
<string name="global_action_settings" msgid="1756531602592545966">"การตั้งค่า"</string>
<string name="global_action_assist" msgid="3892832961594295030">"ผู้ช่วย"</string>
<string name="global_action_voice_assist" msgid="7751191495200504480">"ตัวช่วยเสียง"</string>
@@ -276,6 +273,9 @@
<string name="permgrouplab_location" msgid="7275582855722310164">"ตำแหน่ง"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"เข้าถึงตำแหน่งของอุปกรณ์นี้"</string>
<string name="permgrouprequest_location" msgid="3788275734953323491">"อนุญาตให้ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> เข้าถึงตำแหน่งของอุปกรณ์นี้ไหม"</string>
+ <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"แอปจะมีสิทธิ์เข้าถึงตำแหน่งได้ในขณะที่คุณกำลังใช้แอปเท่านั้น"</string>
+ <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"อนุญาตให้ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> เข้าถึงตำแหน่งของอุปกรณ์นี้ทุกครั้งไหม"</string>
+ <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"แอปจะมีสิทธิ์เข้าถึงตำแหน่งเสมอแม้ว่าคุณจะไม่ได้ใช้แอป"</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"ปฏิทิน"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"เข้าถึงปฏิทิน"</string>
<string name="permgrouprequest_calendar" msgid="289900767793189421">"อนุญาตให้ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> เข้าถึงปฏิทินไหม"</string>
@@ -402,8 +402,7 @@
<string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"แอปนี้สามารถเพิ่ม นำออก หรือเปลี่ยนกิจกรรมในปฏิทินบนโทรศัพท์ของคุณ แอปนี้สามารถส่งข้อความที่ดูเหมือนว่ามาจากเจ้าของปฏิทิน หรือเปลี่ยนแปลงกิจกรรมโดยไม่แจ้งให้เจ้าของทราบ"</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"เข้าถึงคำสั่งของโปรแกรมแจ้งตำแหน่งพิเศษ"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"อนุญาตให้แอปเข้าถึงคำสั่งของผู้ให้บริการตำแหน่งเพิ่มเติม ซึ่งอาจทำให้แอปสามารถแทรกแซงการทำงานของ GPS หรือต้นทางของตำแหน่งอื่นๆ ได้"</string>
- <!-- no translation found for permlab_accessFineLocation (6265109654698562427) -->
- <skip />
+ <string name="permlab_accessFineLocation" msgid="6265109654698562427">"เข้าถึงตำแหน่งที่แม่นยำเมื่ออยู่เบื้องหน้าเท่านั้น"</string>
<string name="permdesc_accessFineLocation" msgid="3520508381065331098">"แอปนี้รับตำแหน่งที่แม่นยำของคุณได้เมื่อทำงานอยู่เบื้องหน้าเท่านั้น แอปจะใช้บริการตำแหน่งเหล่านี้ได้ต่อเมื่อคุณเปิดบริการและบริการพร้อมใช้งานในโทรศัพท์ของคุณ ซึ่งอาจทำให้มีการใช้แบตเตอรี่มากขึ้น"</string>
<string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"เข้าถึงตำแหน่งโดยประมาณ (อิงจากเครือข่าย)"</string>
<string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"แอปนี้สามารถรับตำแหน่งของคุณโดยอิงจากแหล่งข้อมูลเครือข่าย เช่น เสาสัญญาณมือถือและเครือข่าย Wi-Fi แอปจะใช้บริการตำแหน่งเหล่านี้ได้ต่อเมื่อคุณเปิดบริการและบริการพร้อมใช้งานในแท็บเล็ตของคุณ"</string>
@@ -518,6 +517,37 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"ไอคอนลายนิ้วมือ"</string>
+ <string name="permlab_manageFace" msgid="2137540986007309781">"จัดการฮาร์ดแวร์ตรวจสอบสิทธิ์ด้วยใบหน้า"</string>
+ <string name="permdesc_manageFace" msgid="8919637120670185330">"อนุญาตให้แอปเรียกใช้วิธีเพิ่มและลบเทมเพลตใบหน้าสำหรับการใช้งาน"</string>
+ <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"ใช้ฮาร์ดแวร์ตรวจสอบสิทธิ์ด้วยใบหน้า"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"อนุญาตให้แอปใช้ฮาร์ดแวร์ตรวจสอบสิทธิ์ด้วยใบหน้าเพื่อตรวจสอบสิทธิ์"</string>
+ <string name="face_acquired_insufficient" msgid="5901287247766106330">"ประมวลผลใบหน้าไม่ได้ โปรดลองอีกครั้ง"</string>
+ <string name="face_acquired_too_bright" msgid="610606792381297174">"ใบหน้าสว่างเกินไป โปรดลดแสง"</string>
+ <string name="face_acquired_too_dark" msgid="7229162716976778371">"ใบหน้ามืดเกินไป โปรดเปิดแหล่งกำเนิดแสง"</string>
+ <string name="face_acquired_too_close" msgid="1980310037427755293">"โปรดขยับเซ็นเซอร์ให้ห่างจากใบหน้ามากขึ้น"</string>
+ <string name="face_acquired_too_far" msgid="4494571381828850007">"โปรดขยับเซ็นเซอร์ให้ใกล้ใบหน้ามากขึ้น"</string>
+ <string name="face_acquired_too_high" msgid="228411096134808372">"โปรดขยับเซ็นเซอร์ให้สูงขึ้น"</string>
+ <string name="face_acquired_too_low" msgid="4539774649296349109">"โปรดขยับเซ็นเซอร์ให้ต่ำลง"</string>
+ <string name="face_acquired_too_right" msgid="1650292067226118760">"โปรดขยับเซ็นเซอร์ไปทางขวา"</string>
+ <string name="face_acquired_too_left" msgid="2712489669456176505">"โปรดขยับเซ็นเซอร์ไปทางซ้าย"</string>
+ <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"โปรดมองที่เซ็นเซอร์"</string>
+ <string name="face_acquired_not_detected" msgid="5707782294589511391">"ไม่พบใบหน้า"</string>
+ <string name="face_acquired_not_steady" msgid="3722829465011040042">"ให้ใบหน้าอยู่นิ่งๆ หน้าอุปกรณ์"</string>
+ <string-array name="face_acquired_vendor">
+ </string-array>
+ <string name="face_error_hw_not_available" msgid="6255891785768984615">"ฮาร์ดแวร์รู้จำใบหน้าไม่พร้อมใช้งาน"</string>
+ <string name="face_error_timeout" msgid="4014326147867150054">"หมดเวลาใช้ใบหน้าแล้ว โปรดลองอีกครั้ง"</string>
+ <string name="face_error_no_space" msgid="8224993703466381314">"จัดเก็บข้อมูลใบหน้าไม่ได้"</string>
+ <string name="face_error_canceled" msgid="283945501061931023">"ยกเลิกการดำเนินการกับใบหน้าแล้ว"</string>
+ <string name="face_error_lockout" msgid="3407426963155388504">"ดำเนินการหลายครั้งเกินไป ลองอีกครั้งในภายหลัง"</string>
+ <string name="face_error_lockout_permanent" msgid="8198354656746088890">"ดำเนินการหลายครั้งเกินไป ปิดใช้การตรวจสอบสิทธิ์ด้วยใบหน้าแล้ว"</string>
+ <string name="face_error_unable_to_process" msgid="238761109287767270">"ลองอีกครั้ง"</string>
+ <string name="face_error_not_enrolled" msgid="9166792142679691323">"ไม่ได้ลงทะเบียนใบหน้าไว้"</string>
+ <string name="face_error_hw_not_present" msgid="4737289254517095671">"อุปกรณ์นี้ไม่มีเซ็นเซอร์ตรวจสอบสิทธิ์ด้วยใบหน้า"</string>
+ <string name="face_name_template" msgid="7004562145809595384">"ใบหน้า <xliff:g id="FACEID">%d</xliff:g>"</string>
+ <string-array name="face_error_vendor">
+ </string-array>
+ <string name="face_icon_content_description" msgid="4024817159806482191">"ไอคอนใบหน้า"</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"อ่านการตั้งค่าการซิงค์แล้ว"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"อนุญาตให้แอปพลิเคชันอ่านการตั้งค่าการซิงค์ของบัญชี ตัวอย่างเช่น การอนุญาตนี้สามารถระบุได้ว่าแอปพลิเคชัน People ซิงค์กับบัญชีหรือไม่"</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"สลับระหว่างเปิดและปิดการซิงค์"</string>
@@ -1178,6 +1208,9 @@
<skip />
<string name="wifi_no_internet" msgid="8938267198124654938">"Wi-Fi เชื่อมต่ออินเทอร์เน็ตไม่ได้"</string>
<string name="wifi_no_internet_detailed" msgid="8083079241212301741">"แตะเพื่อดูตัวเลือก"</string>
+ <string name="wifi_softap_config_change" msgid="8475911871165857607">"มีการเปลี่ยนแปลงการตั้งค่าฮอตสปอต"</string>
+ <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"ย่านความถี่ฮอตสปอตมีการเปลี่ยนแปลง"</string>
+ <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"อุปกรณ์นี้ไม่รองรับค่ากำหนดของคุณเฉพาะสำหรับ 5 GHz เท่านั้น และจะใช้ย่านความถี่ 5 GHz แทน เมื่อใช้ได้"</string>
<string name="network_switch_metered" msgid="4671730921726992671">"เปลี่ยนเป็น <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
<string name="network_switch_metered_detail" msgid="775163331794506615">"อุปกรณ์จะใช้ <xliff:g id="NEW_NETWORK">%1$s</xliff:g> เมื่อ <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> เข้าถึงอินเทอร์เน็ตไม่ได้ โดยอาจมีค่าบริการ"</string>
<string name="network_switch_metered_toast" msgid="5779283181685974304">"เปลี่ยนจาก <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> เป็น <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
@@ -1255,7 +1288,7 @@
<string name="usb_notification_message" msgid="3370903770828407960">"แตะเพื่อดูตัวเลือกเพิ่มเติม"</string>
<string name="usb_power_notification_message" msgid="4647527153291917218">"กำลังชาร์จอุปกรณ์ที่เชื่อมต่อ แตะเพื่อดูตัวเลือกเพิ่มเติม"</string>
<string name="usb_unsupported_audio_accessory_title" msgid="3529881374464628084">"ตรวจพบอุปกรณ์เสริมสำหรับเสียงแบบแอนะล็อก"</string>
- <string name="usb_unsupported_audio_accessory_message" msgid="6309553946441565215">"อุปกรณ์ที่พ่วงไม่สามารถใช้งานร่วมกับโทรศัพท์นี้ แตะเพื่อเรียนรู้เพิ่มเติม"</string>
+ <string name="usb_unsupported_audio_accessory_message" msgid="6309553946441565215">"อุปกรณ์ที่พ่วงไม่สามารถใช้งานร่วมกับโทรศัพท์นี้ แตะเพื่อดูข้อมูลเพิ่มเติม"</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"เชื่อมต่อการแก้ไขข้อบกพร่องผ่าน USB แล้ว"</string>
<string name="adb_active_notification_message" msgid="7463062450474107752">"แตะเพื่อปิดการแก้ไขข้อบกพร่อง USB"</string>
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"เลือกเพื่อปิดใช้งานการแก้ไขข้อบกพร่อง USB"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 8e037b5..4453309 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -236,9 +236,6 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Airplane mode"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Naka-ON ang airplane mode"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Naka-OFF ang airplane mode"</string>
- <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Pangtipid sa baterya"</string>
- <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"NAKA-OFF ang Pangtipid sa baterya"</string>
- <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"NAKA-ON ang Pangtipid sa baterya"</string>
<string name="global_action_settings" msgid="1756531602592545966">"Mga Setting"</string>
<string name="global_action_assist" msgid="3892832961594295030">"Tulong"</string>
<string name="global_action_voice_assist" msgid="7751191495200504480">"Voice Assist"</string>
@@ -276,6 +273,9 @@
<string name="permgrouplab_location" msgid="7275582855722310164">"Lokasyon"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"i-access ang lokasyon ng device na ito"</string>
<string name="permgrouprequest_location" msgid="3788275734953323491">"Payagan ang <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> na i-access ang lokasyon ng device na ito?"</string>
+ <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"Maa-access lang ng app ang lokasyon habang ginagamit mo ang app."</string>
+ <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"Laging payagan ang <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> na i-access ang lokasyon?"</string>
+ <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"Palaging maa-access ng app ang lokasyon, kahit na hindi mo ginagamit ang app."</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Kalendaryo"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"i-access ang iyong kalendaryo"</string>
<string name="permgrouprequest_calendar" msgid="289900767793189421">"Payagan ang <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> na i-access ang iyong kalendaryo?"</string>
@@ -402,8 +402,7 @@
<string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Makakapagdagdag, makakapag-alis o makakapagbago ang app na ito ng mga event sa kalendaryo sa iyong telepono. Magagawa ng app na ito na magpadala ng mga mensahe na maaaring mukhang mula sa mga may-ari ng kalendaryo o magbago ng mga event nang hindi inaabisuhan ang mga may-ari nito."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"i-access ang mga dagdag na command ng provider ng lokasyon"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Nagbibigay-daan sa app na mag-access ng mga karagdagang command ng provider ng lokasyon. Maaari nitong bigyang-daan ang app na gambalain ang pagpapatakbo ng GPS o ng iba pang mga pinagmulan ng lokasyon."</string>
- <!-- no translation found for permlab_accessFineLocation (6265109654698562427) -->
- <skip />
+ <string name="permlab_accessFineLocation" msgid="6265109654698562427">"i-access lang ang tumpak na lokasyon sa foreground"</string>
<string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Makukuha lang ng app na ito ang iyong eksaktong lokasyon kapag nasa foreground ito. Ang mga serbisyo ng lokasyon na ito ay dapat naka-on at available sa iyong telepono para magamit ng app ang mga ito. Maaaring lumakas ang pagkonsumo ng baterya dahil dito."</string>
<string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"i-access ang tinatantyang lokasyon (batay sa network)"</string>
<string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Makukuha ng app na ito ang iyong lokasyon batay sa mga pinagmulan ng network gaya ng mga cell tower at Wi-Fi network. Ang mga serbisyo ng lokasyon na ito ay dapat naka-on at available sa iyong tablet para sa app upang magamit ang mga ito."</string>
@@ -518,6 +517,37 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Icon ng fingerprint"</string>
+ <string name="permlab_manageFace" msgid="2137540986007309781">"pamahalaan ang hardware sa authentication ng mukha"</string>
+ <string name="permdesc_manageFace" msgid="8919637120670185330">"Pumapayag na mag-invoke ang app ng paraang magdagdag at mag-delete ng template ng mukha."</string>
+ <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"gumamit ng hardware sa pag-authenticate ng mukha"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Pumapayag na gumamit ng face authentication hardware ang app para sa pag-authenticate"</string>
+ <string name="face_acquired_insufficient" msgid="5901287247766106330">"Hindi maiproseso ang mukha. Pakisubukang muli."</string>
+ <string name="face_acquired_too_bright" msgid="610606792381297174">"Masyadong maliwanag. Pakisubukan sa mas madilim."</string>
+ <string name="face_acquired_too_dark" msgid="7229162716976778371">"Masyadong madilim. Huwag takpan ang ilaw."</string>
+ <string name="face_acquired_too_close" msgid="1980310037427755293">"Pakilayo sa mukha ang sensor."</string>
+ <string name="face_acquired_too_far" msgid="4494571381828850007">"Pakilapit sa mukha ang sensor."</string>
+ <string name="face_acquired_too_high" msgid="228411096134808372">"Pakitaas ang sensor."</string>
+ <string name="face_acquired_too_low" msgid="4539774649296349109">"Pakibaba ang sensor."</string>
+ <string name="face_acquired_too_right" msgid="1650292067226118760">"Pakiusog pakanan ang sensor."</string>
+ <string name="face_acquired_too_left" msgid="2712489669456176505">"Pakiusog pakaliwa ang sensor."</string>
+ <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Tumingin sa sensor."</string>
+ <string name="face_acquired_not_detected" msgid="5707782294589511391">"Walang natukoy na mukha."</string>
+ <string name="face_acquired_not_steady" msgid="3722829465011040042">"Panatilihin sa harap ng device ang mukha."</string>
+ <string-array name="face_acquired_vendor">
+ </string-array>
+ <string name="face_error_hw_not_available" msgid="6255891785768984615">"Hindi available ang hardware para sa mukha."</string>
+ <string name="face_error_timeout" msgid="4014326147867150054">"Nag-time out ang mukha. Subukang muli."</string>
+ <string name="face_error_no_space" msgid="8224993703466381314">"Hindi ma-store ang mukha."</string>
+ <string name="face_error_canceled" msgid="283945501061931023">"Nakansela ang operation kaugnay ng mukha."</string>
+ <string name="face_error_lockout" msgid="3407426963155388504">"Masyadong maraming pagsubok. Subukang muli mamaya."</string>
+ <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Sobrang pagsubok. Bawal na: facial authentication."</string>
+ <string name="face_error_unable_to_process" msgid="238761109287767270">"Subukang muli."</string>
+ <string name="face_error_not_enrolled" msgid="9166792142679691323">"Walang naka-enroll na mukha."</string>
+ <string name="face_error_hw_not_present" msgid="4737289254517095671">"Walang sensor ng pag-authenticate ng mukha ang device na ito"</string>
+ <string name="face_name_template" msgid="7004562145809595384">"Mukha <xliff:g id="FACEID">%d</xliff:g>"</string>
+ <string-array name="face_error_vendor">
+ </string-array>
+ <string name="face_icon_content_description" msgid="4024817159806482191">"Face icon"</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"basahin ang mga setting ng sync"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Pinapayagan ang app na basahin ang mga setting ng pag-sync para sa isang account. Halimbawa, matutukoy nito kung naka-sync ang app na Mga Tao sa isang account."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"I-toggle on at off ang pag-sync"</string>
@@ -1178,6 +1208,9 @@
<skip />
<string name="wifi_no_internet" msgid="8938267198124654938">"Walang access sa internet ang Wi-Fi"</string>
<string name="wifi_no_internet_detailed" msgid="8083079241212301741">"I-tap para sa mga opsyon"</string>
+ <string name="wifi_softap_config_change" msgid="8475911871165857607">"Mga pagbabago sa mga setting ng iyong hotspot"</string>
+ <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"Nagbago ang band ng iyong hotspot."</string>
+ <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"Hindi sinusuportahan ng device na ito ang kagustuhan mong gumamit lang ng 5GHz. Sa halip, gagamitin ng device na ito ang 5GHz na band kapag available."</string>
<string name="network_switch_metered" msgid="4671730921726992671">"Lumipat sa <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
<string name="network_switch_metered_detail" msgid="775163331794506615">"Ginagamit ng device ang <xliff:g id="NEW_NETWORK">%1$s</xliff:g> kapag walang access sa internet ang <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>. Maaaring may mga malapat na singilin."</string>
<string name="network_switch_metered_toast" msgid="5779283181685974304">"Lumipat sa <xliff:g id="NEW_NETWORK">%2$s</xliff:g> mula sa <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 34fc23e..a71bc4a 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -236,9 +236,6 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Uçak modu"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Uçak modu AÇIK"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Uçak modu KAPALI"</string>
- <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Pil tasarrufu"</string>
- <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Pil tasarrufu KAPALI"</string>
- <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Pil tasarrufu AÇIK"</string>
<string name="global_action_settings" msgid="1756531602592545966">"Ayarlar"</string>
<string name="global_action_assist" msgid="3892832961594295030">"Asist"</string>
<string name="global_action_voice_assist" msgid="7751191495200504480">"Sesli Yardım"</string>
@@ -276,6 +273,9 @@
<string name="permgrouplab_location" msgid="7275582855722310164">"Konum"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"bu cihazın konumuna erişme"</string>
<string name="permgrouprequest_location" msgid="3788275734953323491">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> uygulamasının bu cihazın konumuna erişmesine izin verilsin mi?"</string>
+ <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"Bu uygulama konum bilgisine yalnızca kullanıldığı sırada erişebilecektir."</string>
+ <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> uygulamasının bu cihazın konumuna erişmesine her zaman izin verilsin mi?"</string>
+ <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"Bu uygulama, kullanılmadığında bile konum bilgisine her zaman erişebilecektir."</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Takvim"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"takviminize erişme"</string>
<string name="permgrouprequest_calendar" msgid="289900767793189421">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> uygulamasının takviminize erişmesine izin verilsin mi?"</string>
@@ -402,8 +402,7 @@
<string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Bu uygulama, telefonunuza takvim etkinlikleri ekleyebilir, mevcut etkinlikleri kaldırabilir ve değiştirebilir. Bu uygulama, takvim sahiplerinden gelmiş gibi görünen iletiler gönderebilir veya takvim sahiplerinin bilgisi olmadan etkinlikleri değiştirebilir."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"ek konum sağlayıcı komutlarına eriş"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Uygulamanın, ekstra konum sağlayıcı komutlarına erişmesine izin verir. Bu izin, uygulamanın GPS veya diğer konum kaynaklarının çalışmasını kesmesine olanak sağlayabilir."</string>
- <!-- no translation found for permlab_accessFineLocation (6265109654698562427) -->
- <skip />
+ <string name="permlab_accessFineLocation" msgid="6265109654698562427">"yalnızca ön planda kesin konuma erişme"</string>
<string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Bu uygulama yalnızca ön plandayken kesin konumunuzu alabilir. Uygulamanın bu hizmetleri kullanabilmesi için telefonunuzda bu konum hizmetleri açık ve kullanılabilir olmalıdır. Bu, pil tüketimini artırabilir."</string>
<string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"konum bilgilerine yaklaşık olarak erişme (ağ tabanlı)"</string>
<string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Bu uygulama baz istasyonu ve kablosuz ağ gibi ağ kaynaklarını kullanarak konumunuzu belirleyebilir. Uygulamanın bu hizmetleri kullanabilmesi için tabletinizde bu konum hizmetleri açık ve kullanılabilir olmalıdır."</string>
@@ -518,6 +517,37 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Parmak izi simgesi"</string>
+ <string name="permlab_manageFace" msgid="2137540986007309781">"yüz kimlik doğrulaması donanımını yönetme"</string>
+ <string name="permdesc_manageFace" msgid="8919637120670185330">"Uygulamanın, kullanılacak yüz şablonlarını ekleme ve silme yöntemlerini başlatmasına izin verir."</string>
+ <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"yüz kimlik doğrulaması donanımını kullanma"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Uygulamanın yüz kimlik doğrulaması donanımı kullanmasına izin verir"</string>
+ <string name="face_acquired_insufficient" msgid="5901287247766106330">"Yüz işlenemedi. Lütfen tekrar deneyin."</string>
+ <string name="face_acquired_too_bright" msgid="610606792381297174">"Yüzünüz çok parlak. Lütfen ışığı kısmayı deneyin."</string>
+ <string name="face_acquired_too_dark" msgid="7229162716976778371">"Yüzünüz çok karanlık. Lütfen ışığı artırın."</string>
+ <string name="face_acquired_too_close" msgid="1980310037427755293">"Lütfen sensörü yüzünüzden biraz uzaklaştırın."</string>
+ <string name="face_acquired_too_far" msgid="4494571381828850007">"Lütfen sensörü yüzünüze yaklaştırın."</string>
+ <string name="face_acquired_too_high" msgid="228411096134808372">"Lütfen sensörü daha yukarı kaldırın."</string>
+ <string name="face_acquired_too_low" msgid="4539774649296349109">"Lütfen sensörü daha aşağı indirin."</string>
+ <string name="face_acquired_too_right" msgid="1650292067226118760">"Lütfen sensörü sağa kaydırın."</string>
+ <string name="face_acquired_too_left" msgid="2712489669456176505">"Lütfen sensörü sola kaydırın."</string>
+ <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Lütfen sensöre bakın."</string>
+ <string name="face_acquired_not_detected" msgid="5707782294589511391">"Yüz algılanmadı."</string>
+ <string name="face_acquired_not_steady" msgid="3722829465011040042">"Yüzünüzü cihazın önünde sabit bir şekilde tutun."</string>
+ <string-array name="face_acquired_vendor">
+ </string-array>
+ <string name="face_error_hw_not_available" msgid="6255891785768984615">"Yüz donanımı kullanılamıyor."</string>
+ <string name="face_error_timeout" msgid="4014326147867150054">"Yüz için zaman aşımı oluştu. Tekrar deneyin."</string>
+ <string name="face_error_no_space" msgid="8224993703466381314">"Yüz kaydedilemiyor."</string>
+ <string name="face_error_canceled" msgid="283945501061931023">"Yüz işlemi iptal edildi."</string>
+ <string name="face_error_lockout" msgid="3407426963155388504">"Çok fazla deneme yapıldı. Daha sonra tekrar deneyin."</string>
+ <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Çok fazla deneme yapıldı. Yüz kimlik doğrulaması devre dışı bırakıldı."</string>
+ <string name="face_error_unable_to_process" msgid="238761109287767270">"Tekrar deneyin."</string>
+ <string name="face_error_not_enrolled" msgid="9166792142679691323">"Herhangi bir yüz kaydedilmedi."</string>
+ <string name="face_error_hw_not_present" msgid="4737289254517095671">"Bu cihazda yüz kimlik doğrulaması sensörü yok"</string>
+ <string name="face_name_template" msgid="7004562145809595384">"Yüz <xliff:g id="FACEID">%d</xliff:g>"</string>
+ <string-array name="face_error_vendor">
+ </string-array>
+ <string name="face_icon_content_description" msgid="4024817159806482191">"Yüz simgesi"</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"senk. ayarlarını okuma"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Uygulamaya bir hesaba ait senkronizasyon ayarlarını okuma izni verir. Örneğin, bu izne sahip bir uygulama Kişiler uygulamasının bir hesapla senkronize olup olmadığını belirleyebilir."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"senkronizasyonu açma/kapatma"</string>
@@ -608,7 +638,7 @@
<string name="policylab_disableCamera" msgid="6395301023152297826">"Kameraları devre dışı bırak"</string>
<string name="policydesc_disableCamera" msgid="2306349042834754597">"Tüm cihaz kameralarının kullanımını engelleme."</string>
<string name="policylab_disableKeyguardFeatures" msgid="8552277871075367771">"Ekran kilidinin bazı özelliklerini devre dışı bırakma"</string>
- <string name="policydesc_disableKeyguardFeatures" msgid="2044755691354158439">"Ekran kilidinin bazı özelliklerinin kullanılmasını önleyin."</string>
+ <string name="policydesc_disableKeyguardFeatures" msgid="2044755691354158439">"Ekran kilidinin bazı özelliklerinin kullanılmasını önleme."</string>
<string-array name="phoneTypes">
<item msgid="8901098336658710359">"Ev"</item>
<item msgid="869923650527136615">"Mobil"</item>
@@ -1178,6 +1208,9 @@
<skip />
<string name="wifi_no_internet" msgid="8938267198124654938">"Kablosuz bağlantının internet erişimi yok"</string>
<string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Seçenekler için dokunun"</string>
+ <string name="wifi_softap_config_change" msgid="8475911871165857607">"Hotspot ayarlarınız değişti"</string>
+ <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"Hotspot bandı değişti."</string>
+ <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"Bu cihaz yalnızca 5 GHz bandının kullanılmasına yönelik tercihinizi desteklemiyor. Bunun yerine, bu cihaz 5 GHz bandını mevcut olduğunda kullanacak."</string>
<string name="network_switch_metered" msgid="4671730921726992671">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> ağına geçildi"</string>
<string name="network_switch_metered_detail" msgid="775163331794506615">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> ağının internet erişimi olmadığında cihaz <xliff:g id="NEW_NETWORK">%1$s</xliff:g> ağını kullanır. Bunun için ödeme alınabilir."</string>
<string name="network_switch_metered_toast" msgid="5779283181685974304">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> ağından <xliff:g id="NEW_NETWORK">%2$s</xliff:g> ağına geçildi"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 8729d67..6476134 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -242,9 +242,6 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Режим польоту"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Режим польоту ВВІМК."</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Режим польоту ВИМК."</string>
- <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Режим енергозбереження"</string>
- <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Режим економії заряду акумулятора ВИМКНЕНО"</string>
- <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Режим економії заряду акумулятора ВВІМКНЕНО"</string>
<string name="global_action_settings" msgid="1756531602592545966">"Налаштування"</string>
<string name="global_action_assist" msgid="3892832961594295030">"Підказки"</string>
<string name="global_action_voice_assist" msgid="7751191495200504480">"Голос. підказки"</string>
@@ -282,6 +279,9 @@
<string name="permgrouplab_location" msgid="7275582855722310164">"Геодані"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"доступ до геоданих пристрою"</string>
<string name="permgrouprequest_location" msgid="3788275734953323491">"Надати додатку <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> доступ до геоданих пристрою?"</string>
+ <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"Додаток матиме доступ до геоданих, лише коли ви ним користуєтеся."</string>
+ <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"Завжди надавати додатку <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> доступ до геоданих цього пристрою?"</string>
+ <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"Додаток матиме доступ до геоданих, навіть коли ви ним не користуєтеся."</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Календар"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"отримувати доступ до календаря"</string>
<string name="permgrouprequest_calendar" msgid="289900767793189421">"Надати додатку <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> доступ до календаря?"</string>
@@ -408,8 +408,7 @@
<string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Цей додаток може додавати, вилучати та змінювати події календаря на вашому телефоні. Він також може надсилати повідомлення від імені власників календаря або редагувати події без відома власників."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"отр. дост. до додат. команд пров. місцезн."</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Додаток отримуватиме доступ до додаткових команд постачальника геоданих. Можливе втручання додатка в роботу GPS чи інших джерел геоданих."</string>
- <!-- no translation found for permlab_accessFineLocation (6265109654698562427) -->
- <skip />
+ <string name="permlab_accessFineLocation" msgid="6265109654698562427">"отримувати доступ до даних про точне місцезнаходження лише в активному режимі"</string>
<string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Цей додаток може отримувати дані про ваше точне місцезнаходження лише в активному режимі. Щоб додаток користувався службами локації, вони мають бути наявні й увімкнені на вашому телефоні. Через це може швидше розряджатись акумулятор."</string>
<string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"отримувати дані про приблизне місцезнаходження (на основі мережі)"</string>
<string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Цей додаток може отримувати дані про ваше місцезнаходження на основі джерел мережі, як-от антен мобільного зв’язку та мереж Wi-Fi. Щоб додаток міг користуватися цими службами локації, вони мають бути доступними й увімкненими на вашому планшеті."</string>
@@ -524,6 +523,37 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Значок відбитка пальця"</string>
+ <string name="permlab_manageFace" msgid="2137540986007309781">"керувати обладнанням для автентифікації облич"</string>
+ <string name="permdesc_manageFace" msgid="8919637120670185330">"Додаток може активувати способи додавання й видалення шаблонів облич."</string>
+ <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"застосовувати обладнання для автентифікації облич"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Додаток може застосовувати обладнання для автентифікації облич"</string>
+ <string name="face_acquired_insufficient" msgid="5901287247766106330">"Не вдалося розпізнати обличчя. Повторіть спробу."</string>
+ <string name="face_acquired_too_bright" msgid="610606792381297174">"Обличчя засвітле. Спробуйте знизити яскравість."</string>
+ <string name="face_acquired_too_dark" msgid="7229162716976778371">"Обличчя затемне. Не заступайте джерело світла."</string>
+ <string name="face_acquired_too_close" msgid="1980310037427755293">"Перемістіть сканер далі від обличчя."</string>
+ <string name="face_acquired_too_far" msgid="4494571381828850007">"Перемістіть сканер ближче до обличчя."</string>
+ <string name="face_acquired_too_high" msgid="228411096134808372">"Перемістіть сканер вище."</string>
+ <string name="face_acquired_too_low" msgid="4539774649296349109">"Перемістіть сканер нижче."</string>
+ <string name="face_acquired_too_right" msgid="1650292067226118760">"Перемістіть сканер праворуч."</string>
+ <string name="face_acquired_too_left" msgid="2712489669456176505">"Перемістіть сканер ліворуч."</string>
+ <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Дивіться на сканер."</string>
+ <string name="face_acquired_not_detected" msgid="5707782294589511391">"Обличчя не виявлено."</string>
+ <string name="face_acquired_not_steady" msgid="3722829465011040042">"Тримайте обличчя нерухомо перед пристроєм."</string>
+ <string-array name="face_acquired_vendor">
+ </string-array>
+ <string name="face_error_hw_not_available" msgid="6255891785768984615">"Обладнання для сканування облич недоступне."</string>
+ <string name="face_error_timeout" msgid="4014326147867150054">"Час очікування обличчя минув. Повторіть спробу."</string>
+ <string name="face_error_no_space" msgid="8224993703466381314">"Не вдається зберегти обличчя."</string>
+ <string name="face_error_canceled" msgid="283945501061931023">"Дію з обличчям скасовано."</string>
+ <string name="face_error_lockout" msgid="3407426963155388504">"Забагато спроб. Повторіть пізніше."</string>
+ <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Забагато спроб. Автентифікацію обличчя вимкнено."</string>
+ <string name="face_error_unable_to_process" msgid="238761109287767270">"Повторіть спробу."</string>
+ <string name="face_error_not_enrolled" msgid="9166792142679691323">"Немає зареєстрованих облич."</string>
+ <string name="face_error_hw_not_present" msgid="4737289254517095671">"На цьому пристрої немає сканера для автентифікації облич"</string>
+ <string name="face_name_template" msgid="7004562145809595384">"Обличчя <xliff:g id="FACEID">%d</xliff:g>"</string>
+ <string-array name="face_error_vendor">
+ </string-array>
+ <string name="face_icon_content_description" msgid="4024817159806482191">"Значок обличчя"</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"читати налаштування синхронізації"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Дозволяє програмі читати налаштування синхронізації для облікового запису, наприклад, визначати, чи програма Люди синхронізується з обліковим записом."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"вмикати й вимикати синхронізацію"</string>
@@ -1222,6 +1252,9 @@
<skip />
<string name="wifi_no_internet" msgid="8938267198124654938">"Мережа Wi-Fi не має доступу до Інтернету"</string>
<string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Торкніться, щоб відкрити опції"</string>
+ <string name="wifi_softap_config_change" msgid="8475911871165857607">"Зміни в налаштуваннях точки доступу"</string>
+ <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"Діапазон точки доступу змінено."</string>
+ <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"На цьому пристрої не підтримується налаштування лише 5 ГГц. Замість цього буде використовуватися діапазон 5 ГГц, якщо доступно."</string>
<string name="network_switch_metered" msgid="4671730921726992671">"Пристрій перейшов на мережу <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
<string name="network_switch_metered_detail" msgid="775163331794506615">"Коли мережа <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> не має доступу до Інтернету, використовується <xliff:g id="NEW_NETWORK">%1$s</xliff:g>. Може стягуватися плата."</string>
<string name="network_switch_metered_toast" msgid="5779283181685974304">"Пристрій перейшов з мережі <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> на мережу <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index 1fc3e2c..2ed0e6c 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -236,9 +236,6 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"ہوائی جہاز وضع"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"ہوائی جہاز وضع آن ہے"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"ہوائی جہاز وضع آف ہے"</string>
- <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"بیٹری سیور"</string>
- <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"بیٹری سیور آف ہے"</string>
- <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"بیٹری سیور آن ہے"</string>
<string name="global_action_settings" msgid="1756531602592545966">"ترتیبات"</string>
<string name="global_action_assist" msgid="3892832961594295030">"اسسٹ"</string>
<string name="global_action_voice_assist" msgid="7751191495200504480">"Voice Assist"</string>
@@ -276,6 +273,9 @@
<string name="permgrouplab_location" msgid="7275582855722310164">"مقام"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"اس آلہ کے مقام تک رسائی حاصل کریں"</string>
<string name="permgrouprequest_location" msgid="3788275734953323491">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> کو اس آلہ کے مقام تک رسائی کی اجازت دیں؟"</string>
+ <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"جب آپ ایپ استعمال کریں گے تبھی ایپ کو مقام تک رسائی حاصل ہوگی۔"</string>
+ <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> کو ہمیشہ اس آلہ کے مقام تک رسائی کرنے کی اجازت دیں؟"</string>
+ <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"اگرچہ آپ ایپ استعمال نہ کر رہے ہوں تب بھی ایپ کو ہمیشہ مقام تک رسائی حاصل ہوگی۔"</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"کیلنڈر"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"اپنے کیلنڈر تک رسائی حاصل کریں"</string>
<string name="permgrouprequest_calendar" msgid="289900767793189421">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> کو آپ کے کیلنڈر تک رسائی کی اجازت دیں؟"</string>
@@ -402,8 +402,7 @@
<string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"یہ اپپ آپ کے فون پر کیلنڈر ایونٹس کو شامل، ہٹا یا ترمیم کر سکتی ہے۔ یہ ایپ وہ پیغامات بھیج سکتی ہے جو کیلنڈر کے مالکان کی جانب سے آنے والے معلوم ہو سکتے ہیں یا ان مالکان کو اطلاع دیے بغیر ایونٹس تبدیل کر سکتی ہے۔"</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"اضافی مقام فراہم کنندہ کی کمانڈز تک رسائی حاصل کریں"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"ایپ کو اضافی مقام فراہم کنندہ کی کمانڈز تک رسائی حاصل کرنے کی اجازت دیتی ہے۔ یہ ایپ کو GPS یا دوسرے مقام کے مآخذ کے عمل کے ساتھ مداخلت کرنے کی اجازت دے سکتی ہے۔"</string>
- <!-- no translation found for permlab_accessFineLocation (6265109654698562427) -->
- <skip />
+ <string name="permlab_accessFineLocation" msgid="6265109654698562427">"صرف پیش منظر میں درست مقام تک رسائی حاصل کریں"</string>
<string name="permdesc_accessFineLocation" msgid="3520508381065331098">"یہ ایپ جب پس منظر میں ہوتی ہے تبھی یہ آپ کا صحیح مقام حاصل کر سکتی ہے۔ ایپ کو ان مقام کی سروسز کو استعمال کر سکنے کیلئے ان کا آن ہونا اور آپ کے فون پر دستیاب ہونا ضروری ہے۔"</string>
<string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"تخمینی مقام تک رسائی حاصل کریں (نیٹ ورک پر مبنی)"</string>
<string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"نیٹ ورک مآخذات جیسے کہ سیل ٹاورز اور Wi-Fi نیٹ ورکس کی بنیاد پر یہ ایپ آپ کا مقام حاصل کر سکتی ہے۔ ایپ کو ان مقام کی سروسز کو استعمال کرنے کیلئے ان کا آن ہونا اور آپ کے ٹیبلیٹ پر دستیاب ہونا ضروری ہے۔"</string>
@@ -518,6 +517,37 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"فنگر پرنٹ آئیکن"</string>
+ <string name="permlab_manageFace" msgid="2137540986007309781">"چہرے کی توثیق کے ہارڈویئر کا نظم کریں"</string>
+ <string name="permdesc_manageFace" msgid="8919637120670185330">"ایپ کو چہرے کی تمثیلات شامل اور حذف کرنے کے طریقوں کو کالعدم قرار دینے کی اجازت دیتا ہے۔"</string>
+ <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"چہرے کی توثیق کا ہارڈویئر استعمال کریں"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"ایپ کو توثیق کیلئے چہرے کا ہارڈ ویئر استعمال کرنے کی اجازت دیتا ہے"</string>
+ <string name="face_acquired_insufficient" msgid="5901287247766106330">"چہرے پر کارروائی نہیں ہو سکی۔ دوبارہ کوشش کریں۔"</string>
+ <string name="face_acquired_too_bright" msgid="610606792381297174">"چہرے پر بہت زیادہ روشنی ہے۔ براہ کرم کم روشنی میں کوشش کریں۔"</string>
+ <string name="face_acquired_too_dark" msgid="7229162716976778371">"چہرے پر روشنی بہت کم ہے۔ براہ کرم روشنی بڑھائیں۔"</string>
+ <string name="face_acquired_too_close" msgid="1980310037427755293">"براہ کرم سینسر کو چہرے سے تھوڑا دور رکھیں۔"</string>
+ <string name="face_acquired_too_far" msgid="4494571381828850007">"براہ کرم سینسر کو چہرے سے تھوڑا قریب رکھیں۔"</string>
+ <string name="face_acquired_too_high" msgid="228411096134808372">"براہ کرم سینسر کو تھوڑا اوپر کریں۔"</string>
+ <string name="face_acquired_too_low" msgid="4539774649296349109">"براہ کرم سینسر کو تھوڑا نیچے کریں۔"</string>
+ <string name="face_acquired_too_right" msgid="1650292067226118760">"براہ کرم سینسر کو دائیں طرف کریں۔"</string>
+ <string name="face_acquired_too_left" msgid="2712489669456176505">"براہ کرم سینسر کو بائیں طرف کریں۔"</string>
+ <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"براہ کرم سینسر کی طرف دیکھیں۔"</string>
+ <string name="face_acquired_not_detected" msgid="5707782294589511391">"کسی چہرے کا پتہ نہیں چلا۔"</string>
+ <string name="face_acquired_not_steady" msgid="3722829465011040042">"آلہ کے سامنے چہرہ سیدھا رکھیں۔"</string>
+ <string-array name="face_acquired_vendor">
+ </string-array>
+ <string name="face_error_hw_not_available" msgid="6255891785768984615">"چہرے کا ہارڈویئر دستیاب نہیں ہے۔"</string>
+ <string name="face_error_timeout" msgid="4014326147867150054">"چہرہ پہچانے کی میعاد ختم ہو گئی۔ دوبارہ کوشش کریں۔"</string>
+ <string name="face_error_no_space" msgid="8224993703466381314">"چہرے کو اسٹور نہیں کیا جا سکتا۔"</string>
+ <string name="face_error_canceled" msgid="283945501061931023">"چہرے پر ہونے والی کارروائی منسوخ ہو گئی۔"</string>
+ <string name="face_error_lockout" msgid="3407426963155388504">"کافی زیادہ کوششیں کی گئیں۔ دوبارہ کوشش کریں۔"</string>
+ <string name="face_error_lockout_permanent" msgid="8198354656746088890">"کافی زیادہ کوششیں کی گئیں۔ چہرے کی توثیق منسوخ ہو گئی۔"</string>
+ <string name="face_error_unable_to_process" msgid="238761109287767270">"دوبارہ کوشش کریں۔"</string>
+ <string name="face_error_not_enrolled" msgid="9166792142679691323">"کوئی بھی چہرہ مندرج نہیں ہے۔"</string>
+ <string name="face_error_hw_not_present" msgid="4737289254517095671">"اس آلہ میں چہرے کی تصدیق کرنے والا سینسر نہیں ہے۔"</string>
+ <string name="face_name_template" msgid="7004562145809595384">"چہرہ <xliff:g id="FACEID">%d</xliff:g>"</string>
+ <string-array name="face_error_vendor">
+ </string-array>
+ <string name="face_icon_content_description" msgid="4024817159806482191">"چہرے کا آئیکن"</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"مطابقت پذیری کی ترتیبات پڑھیں"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"ایپ کو کسی اکاؤنٹ کیلئے مطابقت پذیری کی ترتیبات پڑھنے کی اجازت دیتا ہے۔ مثلا، یہ تعین کرسکتا ہے کہ آیا People ایپ کسی اکاؤنٹ کے ساتھ مطابقت پذیر ہے۔"</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"مطابقت پذیری آن اور آف ٹوگل کریں"</string>
@@ -1178,6 +1208,9 @@
<skip />
<string name="wifi_no_internet" msgid="8938267198124654938">"Wi-Fi کو انٹرنیٹ تک رسائی نہیں ہے"</string>
<string name="wifi_no_internet_detailed" msgid="8083079241212301741">"اختیارات کیلئے تھپتھپائیں"</string>
+ <string name="wifi_softap_config_change" msgid="8475911871165857607">"اپنے ہاٹ اسپاٹ کی ترتیبات میں تبدیلیاں"</string>
+ <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"آپ کا ہاٹ اسپات بینڈ تبدیل ہو گیا۔"</string>
+ <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"یہ آلہ صرف 5GHz کے لئے آپ کی ترجیح کا تعاون نہیں کرے گا۔ بلکہ 5GHz بینڈ کے دستیاب ہونے پر اس کا استعمال کرے گا۔"</string>
<string name="network_switch_metered" msgid="4671730921726992671">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> پر سوئچ ہو گیا"</string>
<string name="network_switch_metered_detail" msgid="775163331794506615">"جب <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> کو انٹرنیٹ تک رسائی نہیں ہوتی ہے تو آلہ <xliff:g id="NEW_NETWORK">%1$s</xliff:g> کا استعمال کرتا ہے۔ چارجز لاگو ہو سکتے ہیں۔"</string>
<string name="network_switch_metered_toast" msgid="5779283181685974304">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> سے <xliff:g id="NEW_NETWORK">%2$s</xliff:g> پر سوئچ ہو گیا"</string>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index c9df779..577e6b9 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -236,9 +236,6 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Parvoz rejimi"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Parvoz usuli yoqilgan"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Parvoz rejimi o‘chirilgan"</string>
- <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Quvvat tejash rejimi"</string>
- <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Quvvat tejash rejimi YOQILMAGAN"</string>
- <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Quvvat tejash rejimi YONIQ"</string>
<string name="global_action_settings" msgid="1756531602592545966">"Sozlamalar"</string>
<string name="global_action_assist" msgid="3892832961594295030">"Yordam"</string>
<string name="global_action_voice_assist" msgid="7751191495200504480">"Ovozli yordam"</string>
@@ -276,6 +273,9 @@
<string name="permgrouplab_location" msgid="7275582855722310164">"Joylashuv"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"shu qurilmaning joylashuvi haqidagi axborotga kirish"</string>
<string name="permgrouprequest_location" msgid="3788275734953323491">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> uchun bu qurilmaning joylashuvi haqidagi axborotdan foydalanishiga ruxsat berilsinmi?"</string>
+ <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"Bu ilovadan foydalanilayotdangina u joylashuv axborotidan foydalana oladi."</string>
+ <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> uchun bu qurilmaning joylashuvi haqidagi axborotdan foydalanishiga ruxsat berilsinmi?"</string>
+ <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"Bu ilovadan foydalanilmaganda ham u doim joylashuv axborotidan foydalana oladi."</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Taqvim"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"taqvimingizga kirish"</string>
<string name="permgrouprequest_calendar" msgid="289900767793189421">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> uchun taqvimingizga ruxsat berilsinmi?"</string>
@@ -402,15 +402,14 @@
<string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Bu ilova telefonga taqvim tadbirlarini qo‘shishi, olib tashlashi yoki o‘zgartirishi mumkin. Bu ilova taqvim egalari nomidan ko‘rinadigan SMS yuborishi yoki egalarini ogohlantirmasdan tadbirlarni o‘zgartirishi mumkin."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"qo‘shimcha manzillarga kirish buyruqlari"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Ilovaga qo‘shimcha joylashuv xizmati buyruqlaridan foydalanishga ruxsat beradi. Uning yordamida ilova GPS yoki boshqa joylashuv ma’lumoti manbalarining ishlashiga xalaqit qilishi mumkin."</string>
- <!-- no translation found for permlab_accessFineLocation (6265109654698562427) -->
- <skip />
- <string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Bu ilova faqat fon rejimida aniq joylashuv axborotingizdan foydalanishi mumkin. Ilova ushbu joylashuv xizmatlaridan foydalana olishi uchun ular telefoningizda yoniq bo‘lishi va ishlashi kerak. Bunda batareya sarfi oshishi mumkin."</string>
+ <string name="permlab_accessFineLocation" msgid="6265109654698562427">"aniq joylashuv axborotini olishga faqat old fonda ruxsat"</string>
+ <string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Bu ilova faqat fon rejimida aniq joylashuv axborotingizdan foydalanishi mumkin. Ilova ushbu joylashuv xizmatlaridan foydalana olishi uchun ular telefoningizda yoniq turishi va ishlashi kerak. Bunda batareya sarfi oshishi mumkin."</string>
<string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"taxminiy joylashuv (tarmoq asosida) ma’lumotlaridan foydalanishga ruxsat"</string>
<string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Bu ilova Wi-Fi va uyali tarmoq antennalari kabi tarmoq manbalari asosida joylashuvingiz axborotini olishi mumkin. Ilova ushbu joylashuv xizmatlaridan foydalana olishi uchun ular planshetda yoniq bo‘lishi va ishlashi kerak."</string>
<string name="permdesc_accessCoarseLocation" product="tv" msgid="1884022719818788511">"Bu ilova Wi-Fi va uyali tarmoq antennalari kabi tarmoq manbalari asosida joylashuvingiz axborotini olishi mumkin. Ilova ushbu joylashuv xizmatlaridan foydalana olishi uchun ular televizorda yoniq bo‘lishi va ishlashi kerak."</string>
<string name="permdesc_accessCoarseLocation" product="default" msgid="7788009094906196995">"Bu ilova Wi-Fi va uyali tarmoq antennalari kabi tarmoq manbalari asosida joylashuvingiz axborotini olishi mumkin. Ilova ushbu joylashuv xizmatlaridan foydalana olishi uchun ular telefoningizda yoniq bo‘lishi va ishlashi kerak."</string>
- <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"joylashuv axborotiga ruxsat fon rejimida"</string>
- <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Bu ilova fon rejimida aniq joylashuv axborotingizdan foydalanishi mumkin. Ilova ushbu joylashuv xizmatlaridan foydalana olishi uchun ular telefoningizda yoniq bo‘lishi va ishlashi kerak. Bunda batareya sarfi oshishi mumkin."</string>
+ <string name="permlab_accessBackgroundLocation" msgid="5742466381902568536">"fonda joylashuv axborotini olishga ruxsat"</string>
+ <string name="permdesc_accessBackgroundLocation" msgid="6371533283380774135">"Bu ilova fon rejimida aniq joylashuv axborotingizdan foydalanishi mumkin. Ilova ushbu joylashuv xizmatlaridan foydalana olishi uchun ular telefoningizda yoniq turishi va ishlashi kerak. Bunda batareya sarfi oshishi mumkin."</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"audio sozlamalaringizni o‘zgartirish"</string>
<string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Ilovalarga tovush va ovoz chiqarish uchun foydalaniladigan karnay kabi global audio sozlamalarini o‘zgartirish uchun ruxsat beradi."</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"ovoz yozib olish"</string>
@@ -518,6 +517,37 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Barmoq izi belgisi"</string>
+ <string name="permlab_manageFace" msgid="2137540986007309781">"yuzni aniqlash qurilmasini boshqarish"</string>
+ <string name="permdesc_manageFace" msgid="8919637120670185330">"Ilova foydalanish uchun yuz namunalarini qo‘shish va o‘chirish usullarini tatbiq qilishi mumkin."</string>
+ <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"yuzni aniqlash qurilmasidan foydalanish"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Haqiqiylikni tekshirish uchun skanerdan foydalanish imkonini beradi"</string>
+ <string name="face_acquired_insufficient" msgid="5901287247766106330">"Yuz aniqlanmadi. Qaytadan urining."</string>
+ <string name="face_acquired_too_bright" msgid="610606792381297174">"Yuz juda yorqin. Yorug‘likni kamaytiring."</string>
+ <string name="face_acquired_too_dark" msgid="7229162716976778371">"Yuz juda xira. Yorug‘likni oshiring."</string>
+ <string name="face_acquired_too_close" msgid="1980310037427755293">"Qurilmani yuzingizdan uzoqroq tuting."</string>
+ <string name="face_acquired_too_far" msgid="4494571381828850007">"Qurilmani yuzga yaqin tuting."</string>
+ <string name="face_acquired_too_high" msgid="228411096134808372">"Qurilmani teparoq ko‘taring."</string>
+ <string name="face_acquired_too_low" msgid="4539774649296349109">"Qurilmani pastroq tushiring."</string>
+ <string name="face_acquired_too_right" msgid="1650292067226118760">"Qurilmani o‘ngroq suring."</string>
+ <string name="face_acquired_too_left" msgid="2712489669456176505">"Qurilmani chaproq suring."</string>
+ <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Qurilma kamerasiga qarang."</string>
+ <string name="face_acquired_not_detected" msgid="5707782294589511391">"Yuz aniqlanmadi."</string>
+ <string name="face_acquired_not_steady" msgid="3722829465011040042">"Yuzingizni qurilmaga qarating."</string>
+ <string-array name="face_acquired_vendor">
+ </string-array>
+ <string name="face_error_hw_not_available" msgid="6255891785768984615">"Yuzni aniqlash uchun qurilma mavjud emas."</string>
+ <string name="face_error_timeout" msgid="4014326147867150054">"Yuzni aniqlash vaqti tugadi. Qaytadan urining."</string>
+ <string name="face_error_no_space" msgid="8224993703466381314">"Aniqlangan yuz saqlanmadi."</string>
+ <string name="face_error_canceled" msgid="283945501061931023">"Yuzni aniqlash bekor qilindi."</string>
+ <string name="face_error_lockout" msgid="3407426963155388504">"Juda ko‘p urinildi. Keyinroq qaytadan urining."</string>
+ <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Juda ko‘p urinildi. Skaner faolsizlantirildi."</string>
+ <string name="face_error_unable_to_process" msgid="238761109287767270">"Qaytadan urining."</string>
+ <string name="face_error_not_enrolled" msgid="9166792142679691323">"Hech qanday yuz qayd qilinmagan."</string>
+ <string name="face_error_hw_not_present" msgid="4737289254517095671">"Bu qurilmada yuzni aniqlash uchun skaner mavjud emas"</string>
+ <string name="face_name_template" msgid="7004562145809595384">"Yuz <xliff:g id="FACEID">%d</xliff:g>"</string>
+ <string-array name="face_error_vendor">
+ </string-array>
+ <string name="face_icon_content_description" msgid="4024817159806482191">"Yuz belgisi"</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"sinx-sh sozlamalarini o‘qish"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Ilovaga hisobning sinxronlash sozlamalarini o‘qish uchun ruxsat beradi. Masalan, bu \"Odamlar\" ilovasi hisob bilan sinxronlangan yoki aksini aniqlay oladi."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"sinx.ni yoqish/o‘chirish"</string>
@@ -1178,6 +1208,9 @@
<skip />
<string name="wifi_no_internet" msgid="8938267198124654938">"Wi-Fi tarmoqda internet aloqasi yo‘q"</string>
<string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Variantlarni ko‘rsatish uchun bosing"</string>
+ <string name="wifi_softap_config_change" msgid="8475911871165857607">"Hotspot sozlamalari o‘zgartirildi"</string>
+ <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"Hotspot chastotasi o‘zgartirildi."</string>
+ <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"Qurilma faqat 5 GGs chastotada ishlay olmaydi. Bu chastotadan imkoniyatga qarab foydalaniladi."</string>
<string name="network_switch_metered" msgid="4671730921726992671">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> tarmog‘iga ulanildi"</string>
<string name="network_switch_metered_detail" msgid="775163331794506615">"Agar <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> tarmoqda internet uzilsa, qurilma <xliff:g id="NEW_NETWORK">%1$s</xliff:g>ga ulanadi. Sarflangan trafik uchun haq olinishi mumkin."</string>
<string name="network_switch_metered_toast" msgid="5779283181685974304">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> tarmog‘idan <xliff:g id="NEW_NETWORK">%2$s</xliff:g> tarmog‘iga o‘tildi"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index f0ee98c..7b603fb 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -236,9 +236,6 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Chế độ trên máy bay"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Chế độ trên máy bay BẬT"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Chế độ trên máy bay TẮT"</string>
- <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Trình tiết kiệm pin"</string>
- <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Trình tiết kiệm pin đang TẮT"</string>
- <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Trình tiết kiệm pin đang BẬT"</string>
<string name="global_action_settings" msgid="1756531602592545966">"Cài đặt"</string>
<string name="global_action_assist" msgid="3892832961594295030">"Hỗ trợ"</string>
<string name="global_action_voice_assist" msgid="7751191495200504480">"Trợ lý thoại"</string>
@@ -276,6 +273,9 @@
<string name="permgrouplab_location" msgid="7275582855722310164">"Vị trí"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"truy cập vị trí của thiết bị này"</string>
<string name="permgrouprequest_location" msgid="3788275734953323491">"Cho phép <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> truy cập vào vị trí của thiết bị này?"</string>
+ <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"Ứng dụng sẽ chỉ có quyền truy cập vào vị trí khi bạn sử dụng."</string>
+ <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"Luôn cho phép <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> truy cập vị trí thiết bị?"</string>
+ <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"Ứng dụng sẽ luôn có quyền truy cập vào vị trí, ngay cả khi bạn không sử dụng."</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Lịch"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"truy cập lịch của bạn"</string>
<string name="permgrouprequest_calendar" msgid="289900767793189421">"Cho phép <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> truy cập vào lịch của bạn?"</string>
@@ -402,8 +402,7 @@
<string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Ứng dụng này có thể thêm, xóa hoặc thay đổi sự kiện lịch trên điện thoại của bạn. Ứng dụng này có thể gửi các tin nhắn trông có vẻ như được gửi từ các chủ sở hữu lịch hoặc thay đổi sự kiện mà không thông báo cho chủ sở hữu."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"truy cập vào các lệnh của nhà cung cấp vị trí bổ sung"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Cho phép ứng dụng truy cập vào các lệnh của nhà cung cấp vị trí bổ sung. Điều này có thể cho phép ứng dụng can thiệp vào hoạt động của Hệ thống định vị toàn cầu (GPS) hoặc các nguồn vị trí khác."</string>
- <!-- no translation found for permlab_accessFineLocation (6265109654698562427) -->
- <skip />
+ <string name="permlab_accessFineLocation" msgid="6265109654698562427">"chỉ truy cập vị trí chính xác trong nền trước"</string>
<string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Bất cứ khi nào chạy trong nền trước, ứng dụng này có thể nhận thông tin vị trí chính xác của bạn. Để ứng dụng có thể sử các dụng dịch vụ vị trí, điện thoại của bạn phải có các dịch vụ này và dịch vụ ở trạng thái bật. Hoạt động này có thể tăng mức tiêu thụ pin."</string>
<string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"truy cập vị trí gần đúng (dựa vào mạng)"</string>
<string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Ứng dụng này có thể nhận thông tin vị trí của bạn dựa trên các nguồn mạng như tháp phát sóng di động và mạng Wi-Fi. Các dịch vụ vị trí này phải được bật và khả dụng trên máy tính bảng của bạn để ứng dụng có thể sử dụng chúng."</string>
@@ -518,6 +517,37 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Biểu tượng vân tay"</string>
+ <string name="permlab_manageFace" msgid="2137540986007309781">"quản lý phần cứng xác thực khuôn mặt"</string>
+ <string name="permdesc_manageFace" msgid="8919637120670185330">"Cho phép ứng dụng gọi ra các phương pháp để thêm và xóa mẫu khuôn mặt sử dụng."</string>
+ <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"sử dụng phần cứng xác thực khuôn mặt"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Cho phép ứng dụng sử dụng phần cứng xác thực khuôn mặt để tiến hành xác thực"</string>
+ <string name="face_acquired_insufficient" msgid="5901287247766106330">"Không thể xử lý khuôn mặt. Vui lòng thử lại."</string>
+ <string name="face_acquired_too_bright" msgid="610606792381297174">"Khuôn mặt quá sáng. Hãy thử dưới ánh sáng yếu hơn."</string>
+ <string name="face_acquired_too_dark" msgid="7229162716976778371">"Khuôn mặt quá tối. Hãy tìm nguồn sáng tốt hơn."</string>
+ <string name="face_acquired_too_close" msgid="1980310037427755293">"Hãy đưa cảm biến ra xa khuôn mặt hơn."</string>
+ <string name="face_acquired_too_far" msgid="4494571381828850007">"Hãy đưa cảm biến lại gần khuôn mặt hơn."</string>
+ <string name="face_acquired_too_high" msgid="228411096134808372">"Hãy đưa cảm biến lên cao hơn."</string>
+ <string name="face_acquired_too_low" msgid="4539774649296349109">"Hãy đưa cảm biến xuống thấp hơn."</string>
+ <string name="face_acquired_too_right" msgid="1650292067226118760">"Hãy đưa cảm biến sang bên phải."</string>
+ <string name="face_acquired_too_left" msgid="2712489669456176505">"Hay đưa cảm biến sang bên trái."</string>
+ <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Hãy nhìn vào cảm biến."</string>
+ <string name="face_acquired_not_detected" msgid="5707782294589511391">"Không phát hiện được khuôn mặt nào."</string>
+ <string name="face_acquired_not_steady" msgid="3722829465011040042">"Hãy giữ yên khuôn mặt trước thiết bị."</string>
+ <string-array name="face_acquired_vendor">
+ </string-array>
+ <string name="face_error_hw_not_available" msgid="6255891785768984615">"Không truy cập được phần cứng nhận dạng khuôn mặt."</string>
+ <string name="face_error_timeout" msgid="4014326147867150054">"Đã hết thời gian chờ khuôn mặt. Hãy thử lại."</string>
+ <string name="face_error_no_space" msgid="8224993703466381314">"Không thể lưu khuôn mặt."</string>
+ <string name="face_error_canceled" msgid="283945501061931023">"Đã hủy thao tác dùng khuôn mặt."</string>
+ <string name="face_error_lockout" msgid="3407426963155388504">"Bạn đã thử quá nhiều lần. Hãy thử lại sau."</string>
+ <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Đã thử quá nhiều lần. Đã tắt xác thực khuôn mặt."</string>
+ <string name="face_error_unable_to_process" msgid="238761109287767270">"Hãy thử lại."</string>
+ <string name="face_error_not_enrolled" msgid="9166792142679691323">"Bạn chưa đăng ký khuôn mặt."</string>
+ <string name="face_error_hw_not_present" msgid="4737289254517095671">"Thiết bị này không có cảm biến xác thực khuôn mặt"</string>
+ <string name="face_name_template" msgid="7004562145809595384">"Khuôn mặt <xliff:g id="FACEID">%d</xliff:g>"</string>
+ <string-array name="face_error_vendor">
+ </string-array>
+ <string name="face_icon_content_description" msgid="4024817159806482191">"Biểu tượng khuôn mặt"</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"đọc cài đặt đồng bộ hóa"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Cho phép ứng dụng đọc cài đặt đồng bộ hóa cho tài khoản. Ví dụ: việc này có thể xác định liệu ứng dụng Mọi người đã được đồng bộ hóa với tài khoản chưa."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"chuyển đổi bật và tắt đồng bộ hóa"</string>
@@ -1178,6 +1208,9 @@
<skip />
<string name="wifi_no_internet" msgid="8938267198124654938">"Wi-Fi không có quyền truy cập Internet"</string>
<string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Nhấn để biết tùy chọn"</string>
+ <string name="wifi_softap_config_change" msgid="8475911871165857607">"Những thay đổi trong mục cài đặt điểm phát sóng của bạn"</string>
+ <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"Bằng tần của điểm phát sóng đã thay đổi."</string>
+ <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"Thiết bị này không hỗ trợ tùy chọn chỉ sử dụng băng tần 5 GHz. Thay vào đó, thiết bị này sẽ sử dụng băng tần 5 GHz khi có thể."</string>
<string name="network_switch_metered" msgid="4671730921726992671">"Đã chuyển sang <xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
<string name="network_switch_metered_detail" msgid="775163331794506615">"Thiết bị sử dụng <xliff:g id="NEW_NETWORK">%1$s</xliff:g> khi <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> không có quyền truy cập Internet. Bạn có thể phải trả phí."</string>
<string name="network_switch_metered_toast" msgid="5779283181685974304">"Đã chuyển từ <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> sang <xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index a97553f..8a55d01 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -236,9 +236,6 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"飞行模式"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"已开启飞行模式"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"未开启飞行模式"</string>
- <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"省电模式"</string>
- <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"省电模式已关闭"</string>
- <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"省电模式已开启"</string>
<string name="global_action_settings" msgid="1756531602592545966">"设置"</string>
<string name="global_action_assist" msgid="3892832961594295030">"助手应用"</string>
<string name="global_action_voice_assist" msgid="7751191495200504480">"语音助理"</string>
@@ -276,6 +273,9 @@
<string name="permgrouplab_location" msgid="7275582855722310164">"位置信息"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"获取此设备的位置信息"</string>
<string name="permgrouprequest_location" msgid="3788275734953323491">"允许<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>获取此设备的位置信息吗?"</string>
+ <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"只有当您使用该应用时,该应用才有权获取位置信息。"</string>
+ <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"一律允许<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>获取此设备的位置信息吗?"</string>
+ <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"即使您并未使用该应用,该应用也将始终有权获取位置信息。"</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"日历"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"访问您的日历"</string>
<string name="permgrouprequest_calendar" msgid="289900767793189421">"允许<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>访问您的日历吗?"</string>
@@ -402,8 +402,7 @@
<string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"此应用可在手机上添加、移除或更改日历活动。此应用可能会以日历所有者的身份发送消息,或在不通知所有者的情况下更改活动。"</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"获取额外的位置信息提供程序命令"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"允许该应用使用其他的位置信息提供程序命令。此权限使该应用可以干扰GPS或其他位置信息源的运作。"</string>
- <!-- no translation found for permlab_accessFineLocation (6265109654698562427) -->
- <skip />
+ <string name="permlab_accessFineLocation" msgid="6265109654698562427">"只能在前台获取精确的位置信息"</string>
<string name="permdesc_accessFineLocation" msgid="3520508381065331098">"此应用只有在前台运行时才能获取您的精确位置信息。您的手机必须支持并开启这些位置信息服务,此应用才能使用这些服务。这可能会增加耗电量。"</string>
<string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"访问大致位置信息(以网络为依据)"</string>
<string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"此应用可根据网络来源(例如基站和 WLAN 网络)获取您的位置信息。您的平板电脑必须支持并开启这些位置信息服务,此应用才能使用这些服务。"</string>
@@ -518,6 +517,37 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"指纹图标"</string>
+ <string name="permlab_manageFace" msgid="2137540986007309781">"管理人脸身份验证硬件"</string>
+ <string name="permdesc_manageFace" msgid="8919637120670185330">"允许该应用调用方法来添加和删除可用的人脸模板。"</string>
+ <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"使用人脸身份验证硬件"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"允许该应用使用人脸身份验证硬件进行身份验证"</string>
+ <string name="face_acquired_insufficient" msgid="5901287247766106330">"无法识别此面孔,请重试。"</string>
+ <string name="face_acquired_too_bright" msgid="610606792381297174">"面部太亮,请在光线适中的环境下重试。"</string>
+ <string name="face_acquired_too_dark" msgid="7229162716976778371">"面部太暗,请不要挡住光线。"</string>
+ <string name="face_acquired_too_close" msgid="1980310037427755293">"请将传感器移到距离面孔较远处。"</string>
+ <string name="face_acquired_too_far" msgid="4494571381828850007">"请将传感器靠近面孔。"</string>
+ <string name="face_acquired_too_high" msgid="228411096134808372">"请上移传感器。"</string>
+ <string name="face_acquired_too_low" msgid="4539774649296349109">"请下移传感器。"</string>
+ <string name="face_acquired_too_right" msgid="1650292067226118760">"请右移传感器。"</string>
+ <string name="face_acquired_too_left" msgid="2712489669456176505">"请左移传感器。"</string>
+ <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"请目视传感器。"</string>
+ <string name="face_acquired_not_detected" msgid="5707782294589511391">"未检测到任何面孔。"</string>
+ <string name="face_acquired_not_steady" msgid="3722829465011040042">"请将面孔保持在设备前不动。"</string>
+ <string-array name="face_acquired_vendor">
+ </string-array>
+ <string name="face_error_hw_not_available" msgid="6255891785768984615">"无法使用面孔识别硬件"</string>
+ <string name="face_error_timeout" msgid="4014326147867150054">"面孔处理操作超时,请重试。"</string>
+ <string name="face_error_no_space" msgid="8224993703466381314">"无法存储面孔。"</string>
+ <string name="face_error_canceled" msgid="283945501061931023">"面孔处理操作已取消。"</string>
+ <string name="face_error_lockout" msgid="3407426963155388504">"尝试次数过多,请稍后重试。"</string>
+ <string name="face_error_lockout_permanent" msgid="8198354656746088890">"尝试次数过多,系统已停用人脸身份验证功能。"</string>
+ <string name="face_error_unable_to_process" msgid="238761109287767270">"请重试。"</string>
+ <string name="face_error_not_enrolled" msgid="9166792142679691323">"未注册任何面孔。"</string>
+ <string name="face_error_hw_not_present" msgid="4737289254517095671">"此设备没有人脸身份验证传感器"</string>
+ <string name="face_name_template" msgid="7004562145809595384">"面孔 <xliff:g id="FACEID">%d</xliff:g>"</string>
+ <string-array name="face_error_vendor">
+ </string-array>
+ <string name="face_icon_content_description" msgid="4024817159806482191">"面孔图标"</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"读取同步设置"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"允许该应用读取某个帐号的同步设置。例如,此权限可确定“联系人”应用是否与某个帐号同步。"</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"启用和停用同步"</string>
@@ -1178,6 +1208,9 @@
<skip />
<string name="wifi_no_internet" msgid="8938267198124654938">"此 WLAN 网络无法访问互联网"</string>
<string name="wifi_no_internet_detailed" msgid="8083079241212301741">"点按即可查看相关选项"</string>
+ <string name="wifi_softap_config_change" msgid="8475911871165857607">"您的热点设置已变更"</string>
+ <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"您的热点频段已变更。"</string>
+ <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"此设备不支持您的偏好设置(仅限 5GHz),而且会在 5GHz 频段可用时使用该频段。"</string>
<string name="network_switch_metered" msgid="4671730921726992671">"已切换至<xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
<string name="network_switch_metered_detail" msgid="775163331794506615">"设备会在<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>无法访问互联网时使用<xliff:g id="NEW_NETWORK">%1$s</xliff:g>(可能需要支付相应的费用)。"</string>
<string name="network_switch_metered_toast" msgid="5779283181685974304">"已从<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g>切换至<xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index 8d94421..18fc2c9 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -236,9 +236,6 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"飛行模式"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"飛航模式為 [開啟]"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"飛行模式為 [關閉]"</string>
- <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"省電模式"</string>
- <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"省電模式已關閉"</string>
- <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"省電模式已開啟"</string>
<string name="global_action_settings" msgid="1756531602592545966">"設定"</string>
<string name="global_action_assist" msgid="3892832961594295030">"小幫手"</string>
<string name="global_action_voice_assist" msgid="7751191495200504480">"語音助手"</string>
@@ -276,6 +273,9 @@
<string name="permgrouplab_location" msgid="7275582855722310164">"位置資訊"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"存取此裝置的位置"</string>
<string name="permgrouprequest_location" msgid="3788275734953323491">"允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」<b></b>存取此裝置的位置資訊嗎?"</string>
+ <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"只有在您使用此應用程式時,應用程式才能存取位置資訊。"</string>
+ <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"要一律允許「<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>」存取此裝置的位置資訊嗎?"</string>
+ <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"即使您沒有使用此應用程式,應用程式亦一直能夠存取位置資訊。"</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"日曆"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"存取您的日曆"</string>
<string name="permgrouprequest_calendar" msgid="289900767793189421">"允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」<b></b>存取您的日曆嗎?"</string>
@@ -402,8 +402,7 @@
<string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"此應用程式可以加入、移除或變更您的手機中的日曆活動。此應用程式可以傳送看似來自日曆擁有者的訊息,或變更活動而不通知其擁有者。"</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"接收額外的位置提供者指令"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"允許應用程式存取額外的位置提供者指令。這項設定可能會使應用程式干擾 GPS 或其他位置來源的運作。"</string>
- <!-- no translation found for permlab_accessFineLocation (6265109654698562427) -->
- <skip />
+ <string name="permlab_accessFineLocation" msgid="6265109654698562427">"只在前景存取精確位置"</string>
<string name="permdesc_accessFineLocation" msgid="3520508381065331098">"此應用程式只可在前台運行時獲取您的確實位置資訊。您的手機必須支援並啟用這些位置資訊服務,應用程式方可使用這項功能,但這樣做可能會增加耗電量。"</string>
<string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"存取約略位置 (根據網絡)"</string>
<string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"此應用程式可以根據您的網絡來源 (例如手機訊號塔和 Wi-Fi 網絡) 取得您的位置。您的平板電腦必須支援並啟用這些位置資訊服務,應用程式方可使用這項功能。"</string>
@@ -518,6 +517,37 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"指紋圖示"</string>
+ <string name="permlab_manageFace" msgid="2137540986007309781">"管理臉孔驗證硬件"</string>
+ <string name="permdesc_manageFace" msgid="8919637120670185330">"允許應用程式調用方法,以加入和刪除可用的臉孔範本。"</string>
+ <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"使用臉孔驗證硬件"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"允許應用程式使用臉孔驗證硬件來驗證"</string>
+ <string name="face_acquired_insufficient" msgid="5901287247766106330">"無法識別臉孔,請再試一次。"</string>
+ <string name="face_acquired_too_bright" msgid="610606792381297174">"臉孔太光亮,請在較暗的光線下嘗試。"</string>
+ <string name="face_acquired_too_dark" msgid="7229162716976778371">"臉孔太暗,請尋找更佳光源。"</string>
+ <string name="face_acquired_too_close" msgid="1980310037427755293">"請將感應器移離臉孔。"</string>
+ <string name="face_acquired_too_far" msgid="4494571381828850007">"請將感應器靠近臉孔。"</string>
+ <string name="face_acquired_too_high" msgid="228411096134808372">"請將感應器向上移。"</string>
+ <string name="face_acquired_too_low" msgid="4539774649296349109">"請將感應器向下移。"</string>
+ <string name="face_acquired_too_right" msgid="1650292067226118760">"請將感應器向右移。"</string>
+ <string name="face_acquired_too_left" msgid="2712489669456176505">"請將感應器向左移。"</string>
+ <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"請看著感應器。"</string>
+ <string name="face_acquired_not_detected" msgid="5707782294589511391">"未偵測到任何臉孔。"</string>
+ <string name="face_acquired_not_steady" msgid="3722829465011040042">"請在裝置前保持臉孔不動。"</string>
+ <string-array name="face_acquired_vendor">
+ </string-array>
+ <string name="face_error_hw_not_available" msgid="6255891785768984615">"無法使用臉孔識別硬件。"</string>
+ <string name="face_error_timeout" msgid="4014326147867150054">"臉孔操作已逾時,請再試一次。"</string>
+ <string name="face_error_no_space" msgid="8224993703466381314">"無法儲存臉孔。"</string>
+ <string name="face_error_canceled" msgid="283945501061931023">"臉孔操作已取消。"</string>
+ <string name="face_error_lockout" msgid="3407426963155388504">"嘗試次數過多,請稍後再試。"</string>
+ <string name="face_error_lockout_permanent" msgid="8198354656746088890">"嘗試次數過多,臉孔驗證已停用。"</string>
+ <string name="face_error_unable_to_process" msgid="238761109287767270">"請再試一次。"</string>
+ <string name="face_error_not_enrolled" msgid="9166792142679691323">"未註冊任何臉孔。"</string>
+ <string name="face_error_hw_not_present" msgid="4737289254517095671">"此裝置沒有臉孔驗證感應器"</string>
+ <string name="face_name_template" msgid="7004562145809595384">"臉孔 <xliff:g id="FACEID">%d</xliff:g>"</string>
+ <string-array name="face_error_vendor">
+ </string-array>
+ <string name="face_icon_content_description" msgid="4024817159806482191">"臉孔圖示"</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"讀取同步處理設定"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"允許應用程式讀取帳戶的同步設定,例如確定「通訊錄」應用程式是否和某個帳戶保持同步。"</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"開啟和關閉同步功能"</string>
@@ -1178,6 +1208,9 @@
<skip />
<string name="wifi_no_internet" msgid="8938267198124654938">"Wi-Fi 並未連接互聯網"</string>
<string name="wifi_no_internet_detailed" msgid="8083079241212301741">"輕按即可查看選項"</string>
+ <string name="wifi_softap_config_change" msgid="8475911871165857607">"您的熱點設定變更"</string>
+ <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"您的熱點頻段已變更。"</string>
+ <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"此裝置不支援只限 5 GHz 的偏好設定,但會在 5 GHz 頻段可用時採用。"</string>
<string name="network_switch_metered" msgid="4671730921726992671">"已切換至<xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
<string name="network_switch_metered_detail" msgid="775163331794506615">"裝置會在 <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> 無法連線至互聯網時使用<xliff:g id="NEW_NETWORK">%1$s</xliff:g> (可能需要支付相關費用)。"</string>
<string name="network_switch_metered_toast" msgid="5779283181685974304">"已從<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g>切換至<xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 0e04dc7..d130fba 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -236,9 +236,6 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"飛航模式"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"飛航模式為 [開啟]"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"飛航模式為 [關閉]"</string>
- <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"節約耗電量"</string>
- <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"節約耗電量模式已關閉"</string>
- <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"節約耗電量模式已開啟"</string>
<string name="global_action_settings" msgid="1756531602592545966">"設定"</string>
<string name="global_action_assist" msgid="3892832961594295030">"協助"</string>
<string name="global_action_voice_assist" msgid="7751191495200504480">"語音小幫手"</string>
@@ -276,6 +273,9 @@
<string name="permgrouplab_location" msgid="7275582855722310164">"位置"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"存取這台裝置的位置資訊"</string>
<string name="permgrouprequest_location" msgid="3788275734953323491">"要允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」存取這個裝置的位置資訊嗎?"</string>
+ <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"該應用程式只有在你使用時,才能存取位置資訊。"</string>
+ <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"要一律允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」<b></b>存取這個裝置的位置資訊嗎?"</string>
+ <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"該應用程式即使在你未使用時,也隨時可以存取位置資訊。"</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"日曆"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"存取你的日曆"</string>
<string name="permgrouprequest_calendar" msgid="289900767793189421">"要允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」存取你的日曆嗎?"</string>
@@ -402,8 +402,7 @@
<string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"這個應用程式可在手機上新增、移除或變更日曆活動。提醒你,這個應用程式可能會以日曆擁有者的名義傳送訊息,或是在不通知日曆擁有者的情況下變更活動內容。"</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"接收額外的位置提供者指令"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"允許應用程式存取額外位置資訊提供者指令。這項設定可能會造成應用程式干擾 GPS 或其他位置資訊來源的運作。"</string>
- <!-- no translation found for permlab_accessFineLocation (6265109654698562427) -->
- <skip />
+ <string name="permlab_accessFineLocation" msgid="6265109654698562427">"僅可在前景中取得精確位置"</string>
<string name="permdesc_accessFineLocation" msgid="3520508381065331098">"這個應用程式只能在前景中取得你的確切位置。你必須在手機上開啟這些定位服務,才能讓這個應用程式取得確切位置。請注意,這麼做可能會增加耗電量。"</string>
<string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"存取概略位置 (以網路為依據)"</string>
<string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"這個應用程式可根據網路來源 (例如基地台和 Wi-Fi 網路) 取得你的位置資訊。你必須在平板電腦上開啟這類定位服務,才能讓這個應用程式取得位置資訊。"</string>
@@ -518,6 +517,37 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"指紋圖示"</string>
+ <string name="permlab_manageFace" msgid="2137540986007309781">"管理臉孔驗證硬體"</string>
+ <string name="permdesc_manageFace" msgid="8919637120670185330">"允許應用程式呼叫方法來新增及移除可用的臉孔範本。"</string>
+ <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"使用臉孔驗證硬體"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"允許應用程式使用臉孔驗證硬體進行驗證"</string>
+ <string name="face_acquired_insufficient" msgid="5901287247766106330">"無法識別臉孔,請再試一次。"</string>
+ <string name="face_acquired_too_bright" msgid="610606792381297174">"臉孔過亮。請移至光線適中的場所,然後再試一次。"</string>
+ <string name="face_acquired_too_dark" msgid="7229162716976778371">"臉孔過暗。請增添光源,然後再試一次。"</string>
+ <string name="face_acquired_too_close" msgid="1980310037427755293">"請將感應器移到距離臉孔較遠處。"</string>
+ <string name="face_acquired_too_far" msgid="4494571381828850007">"請將感應器靠近臉孔。"</string>
+ <string name="face_acquired_too_high" msgid="228411096134808372">"請將感應器往上移動。"</string>
+ <string name="face_acquired_too_low" msgid="4539774649296349109">"請將感應器往下移動。"</string>
+ <string name="face_acquired_too_right" msgid="1650292067226118760">"請將感應器往右移動。"</string>
+ <string name="face_acquired_too_left" msgid="2712489669456176505">"請將感應器往左移動。"</string>
+ <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"請直視感應器。"</string>
+ <string name="face_acquired_not_detected" msgid="5707782294589511391">"未偵測到任何臉孔。"</string>
+ <string name="face_acquired_not_steady" msgid="3722829465011040042">"請將臉孔保持在裝置前方。"</string>
+ <string-array name="face_acquired_vendor">
+ </string-array>
+ <string name="face_error_hw_not_available" msgid="6255891785768984615">"無法使用臉部辨識硬體。"</string>
+ <string name="face_error_timeout" msgid="4014326147867150054">"臉孔處理作業逾時,請再試一次。"</string>
+ <string name="face_error_no_space" msgid="8224993703466381314">"無法儲存臉孔。"</string>
+ <string name="face_error_canceled" msgid="283945501061931023">"臉孔處理作業已取消。"</string>
+ <string name="face_error_lockout" msgid="3407426963155388504">"嘗試次數過多,請稍後再試。"</string>
+ <string name="face_error_lockout_permanent" msgid="8198354656746088890">"嘗試次數過多,臉孔驗證功能已停用。"</string>
+ <string name="face_error_unable_to_process" msgid="238761109287767270">"請再試一次。"</string>
+ <string name="face_error_not_enrolled" msgid="9166792142679691323">"未登錄任何臉孔。"</string>
+ <string name="face_error_hw_not_present" msgid="4737289254517095671">"這個裝置沒有臉孔驗證感應器"</string>
+ <string name="face_name_template" msgid="7004562145809595384">"臉孔 <xliff:g id="FACEID">%d</xliff:g>"</string>
+ <string-array name="face_error_vendor">
+ </string-array>
+ <string name="face_icon_content_description" msgid="4024817159806482191">"臉孔圖示"</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"讀取同步處理設定"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"允許應用程式讀取帳戶的同步處理設定,例如判斷「使用者」應用程式是否和某個帳戶進行同步處理。"</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"開啟及關閉同步功能"</string>
@@ -1178,6 +1208,9 @@
<skip />
<string name="wifi_no_internet" msgid="8938267198124654938">"Wi-Fi 沒有網際網路連線"</string>
<string name="wifi_no_internet_detailed" msgid="8083079241212301741">"輕觸即可查看選項"</string>
+ <string name="wifi_softap_config_change" msgid="8475911871165857607">"無線基地台設定變更"</string>
+ <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"你的無線基地台頻帶已變更。"</string>
+ <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"這個裝置不支援你的偏好設定 (僅限 5GHz),而是會在 5GHz 可用時使用該頻帶。"</string>
<string name="network_switch_metered" msgid="4671730921726992671">"已切換至<xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
<string name="network_switch_metered_detail" msgid="775163331794506615">"裝置會在無法連上「<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g>」時切換至「<xliff:g id="NEW_NETWORK">%1$s</xliff:g>」(可能需要支付相關費用)。"</string>
<string name="network_switch_metered_toast" msgid="5779283181685974304">"已從 <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> 切換至<xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index d085068..fed4824 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -236,9 +236,6 @@
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"Imodi yendiza"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Imodi yendiza IVULIWE"</string>
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Imodi yendiza IVALIWE"</string>
- <string name="global_action_toggle_battery_saver" msgid="708515500418994208">"Isilondolozi sebhethri"</string>
- <string name="global_action_battery_saver_on_status" msgid="484059130698197787">"Iseva yebhethri SIVALIWE"</string>
- <string name="global_action_battery_saver_off_status" msgid="75550964969478405">"Isilondolozi sebhethri SIVULIWE"</string>
<string name="global_action_settings" msgid="1756531602592545966">"Izilungiselelo"</string>
<string name="global_action_assist" msgid="3892832961594295030">"Siza"</string>
<string name="global_action_voice_assist" msgid="7751191495200504480">"Isisekeli sezwi"</string>
@@ -276,6 +273,9 @@
<string name="permgrouplab_location" msgid="7275582855722310164">"Indawo"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"finyelela kundawo yale divayisi"</string>
<string name="permgrouprequest_location" msgid="3788275734953323491">"Vumela i-<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ukuthi ingene kundawo yale divayisi?"</string>
+ <string name="permgrouprequestdetail_location" msgid="1113400215566814664">"Uhlelo lokusebenza luzoba nokufinyelela kundawo kuphela uma usebenzisa uhlelo lokusebenza."</string>
+ <string name="permgroupbackgroundrequest_location" msgid="8461841153030844390">"Hlala uvumela i-<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ukuthi ifinyelele indawo yale divayisi?"</string>
+ <string name="permgroupbackgroundrequestdetail_location" msgid="1715668276378108654">"Uhlelo lokusebenza luzohlala lunokufinyelela kundawo, nanoma ungasebenzisi uhlelo lokusebenza."</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Ikhalenda"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"finyelela kukhalenda yakho"</string>
<string name="permgrouprequest_calendar" msgid="289900767793189421">"Vumela i-<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ukuthi ifinyelele kukhalenda yakho?"</string>
@@ -402,8 +402,7 @@
<string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Lolu hlelo lokusebenza lungangeza, lisuse, noma lishintshe imicimbi yekhalenda efonini yakho. Lolu hlelo lokusebenza lingathumela imilayezo engabonakala ivela kusuka kubanikazi bekhalenda, noma lishintshe imicimbi ngaphandle kokwazisa abanikazi."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"finyelela kweminye imiyalo yokunikeza indawo"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Ivumela uhlelo lokusebenza ukufinyelela imiyalo eyengeziwe yabahlinzeki bendawo. Lokhu kungase kuvumele uhlelo lokusebenza ukuthi liphazamisane nomsebenzi we-GPS noma eminye imithombo yendawo."</string>
- <!-- no translation found for permlab_accessFineLocation (6265109654698562427) -->
- <skip />
+ <string name="permlab_accessFineLocation" msgid="6265109654698562427">"finyelela indawo eqondile kuphela phambili"</string>
<string name="permdesc_accessFineLocation" msgid="3520508381065331098">"Lolu hlelo lokusebenza lungakutholela indawo eqondile kuphela uma liphambili. Lawa masevisi endawo kufanele avulwe futhi atholakale efonini yakho ukuze uhlelo lokusebenza lukwazi ukuwasebenzisa. Lokhu kungakhulisa ukusebenza kwebhethri."</string>
<string name="permlab_accessCoarseLocation" msgid="7715277613928539434">"finyelela kundawo elinganiselwe (esuselwa kunethiwekhi)"</string>
<string name="permdesc_accessCoarseLocation" product="tablet" msgid="3373266766487862426">"Lolu hlelo lokusebenza lungathola indawo yakho ngokususelwe kumithombo yenethiwekhi njengamathawa eseli namanethiwekhi e-Wi-Fi. Lawa masevisi endawo kufanele avulwe futhi atholakale kuthebhulethi yakho ukuze uhlelo lokusebenza lukwazi ukuwasebenzisa."</string>
@@ -518,6 +517,37 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Isithonjana sezigxivizo zeminwe"</string>
+ <string name="permlab_manageFace" msgid="2137540986007309781">"phatha izingxenyekazi zekhompuyutha zokufakazela ubuqiniso zobuso"</string>
+ <string name="permdesc_manageFace" msgid="8919637120670185330">"Ivumela uhlelo lokusebenza ukuthi luhoxise izindlela zokungeza nokususa amathempulethi obuso azosetshenziswa."</string>
+ <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"sebenzisa izingxenyekazi zekhompuyutha zokufakazela ubuqiniso kobuso"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Ivumela uhlelo lokusebenza ukuthi lusebenzise ukufakazela ubuqiniso bobuso bezingxenyekazi ukuze kufakazelwe ubuqiniso"</string>
+ <string name="face_acquired_insufficient" msgid="5901287247766106330">"Ayikwazanga ukucubungula ubuso. Sicela uzame futhi."</string>
+ <string name="face_acquired_too_bright" msgid="610606792381297174">"Ubuso bukhanya kakhulu. Sicela uzame ekukhanyeni okuncane."</string>
+ <string name="face_acquired_too_dark" msgid="7229162716976778371">"Ubuso bumnyama kakhulu. Sicela uvule umthombo wokukhanya."</string>
+ <string name="face_acquired_too_close" msgid="1980310037427755293">"Sicela uhambise inzwa kude kunobuso."</string>
+ <string name="face_acquired_too_far" msgid="4494571381828850007">"Sicela ulethe inzwa eduze kobuso."</string>
+ <string name="face_acquired_too_high" msgid="228411096134808372">"Sicela uhambise inzwa phezulu."</string>
+ <string name="face_acquired_too_low" msgid="4539774649296349109">"Sicela uhambise inzwa ngezansi."</string>
+ <string name="face_acquired_too_right" msgid="1650292067226118760">"Sicela uhambise inzwa ngakwesokudla."</string>
+ <string name="face_acquired_too_left" msgid="2712489669456176505">"Sicela uhambise inzwa ngakwesokunxele."</string>
+ <string name="face_acquired_poor_gaze" msgid="8344973502980415859">"Sicela ubheke kunzwa."</string>
+ <string name="face_acquired_not_detected" msgid="5707782294589511391">"Abukho ubuso obutholiwe."</string>
+ <string name="face_acquired_not_steady" msgid="3722829465011040042">"Gcina ubuso bumile ngaphambi kwedivayisi."</string>
+ <string-array name="face_acquired_vendor">
+ </string-array>
+ <string name="face_error_hw_not_available" msgid="6255891785768984615">"Izingxenyekazi zekhompuyutha zobuso azitholakali."</string>
+ <string name="face_error_timeout" msgid="4014326147867150054">"Kufinyelelwe kusikhathi sokuvala sobuso. Zama futhi."</string>
+ <string name="face_error_no_space" msgid="8224993703466381314">"Ubuso abukwazi ukugcinwa."</string>
+ <string name="face_error_canceled" msgid="283945501061931023">"Umsebenzi wobuso ukhanselwe."</string>
+ <string name="face_error_lockout" msgid="3407426963155388504">"Imizamo eminingi kakhulu. Zama futhi emuva kwesikhathi."</string>
+ <string name="face_error_lockout_permanent" msgid="8198354656746088890">"Imizamo eminingi kakhulu. Ukufakazela ubuqiniso kobuso kukhutshaziwe."</string>
+ <string name="face_error_unable_to_process" msgid="238761109287767270">"Zama futhi."</string>
+ <string name="face_error_not_enrolled" msgid="9166792142679691323">"Abukho ubuso obubhalisiwe."</string>
+ <string name="face_error_hw_not_present" msgid="4737289254517095671">"Le divayisi ayinayo inzwa yokufakazela ubuqiniso yobuso"</string>
+ <string name="face_name_template" msgid="7004562145809595384">"Ubuso be-<xliff:g id="FACEID">%d</xliff:g>"</string>
+ <string-array name="face_error_vendor">
+ </string-array>
+ <string name="face_icon_content_description" msgid="4024817159806482191">"Isithonjana sobuso"</string>
<string name="permlab_readSyncSettings" msgid="6201810008230503052">"funda izilungiselelo zokuvumelanisa"</string>
<string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Ivumela uhlelo lokusebenza ukufunda izilungiselelo zokuvumelanisa ze-akhawunti. Isibonelo, lokhu kungacacisa ukuthi noma ngabe uhlelo lokusebenza le-People livumelanisiwe ne-akhawunti."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"shintsha phakathi kokuvula kanye nokucisha ukuvumelanisa"</string>
@@ -1178,6 +1208,9 @@
<skip />
<string name="wifi_no_internet" msgid="8938267198124654938">"I-Wi-Fi ayinakho ukufinyelela kwe-inthanethi"</string>
<string name="wifi_no_internet_detailed" msgid="8083079241212301741">"Thepha ukuze uthole izinketho"</string>
+ <string name="wifi_softap_config_change" msgid="8475911871165857607">"Ushintsho kuzilungiselelo zakho ze-hotspot"</string>
+ <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"Ibhendi yakho ye-hotspot ishintshile."</string>
+ <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"Le divayisi ayisekeli okuncamelayo kwe-5GHz kuphela. Kunalokho, le divayisi izosebenzisa ibhendi ye-5GHz uma itholakala."</string>
<string name="network_switch_metered" msgid="4671730921726992671">"Kushintshelwe ku-<xliff:g id="NETWORK_TYPE">%1$s</xliff:g>"</string>
<string name="network_switch_metered_detail" msgid="775163331794506615">"Idivayisi isebenzisa i-<xliff:g id="NEW_NETWORK">%1$s</xliff:g> uma i-<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> inganakho ukufinyelela kwe-inthanethi. Kungasebenza izindleko."</string>
<string name="network_switch_metered_toast" msgid="5779283181685974304">"Kushintshelewe kusuka ku-<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> kuya ku-<xliff:g id="NEW_NETWORK">%2$s</xliff:g>"</string>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index d2684ce..44eea30c 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -1477,7 +1477,7 @@
used for, typically, account ID or password input. It is expected that IMEs
normally are able to input ASCII even without being told so (such IMEs
already respect this flag in a sense), but there could be some cases they
- aren't when, for instance, only non-ASCII input languagaes like Arabic,
+ aren't when, for instance, only non-ASCII input languages like Arabic,
Greek, Hebrew, Russian are enabled in the IME. Applications need to be
aware that the flag is not a guarantee, and not all IMEs will respect it.
However, it is strongly recommended for IME authors to respect this flag
@@ -3161,7 +3161,7 @@
enabled by a ViewGroup for all its children in specific situations (for
instance during a scrolling.) This property lets you persist the cache
in memory after its initial usage. Persisting the cache consumes more
- memory but may prevent frequent garbage collection is the cache is created
+ memory but may prevent frequent garbage collection if the cache is created
over and over again. By default the persistence is set to scrolling.
Deprecated: The view drawing cache was largely made obsolete with the introduction of
hardware-accelerated rendering in API 11. -->
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index c4fa190..4c96c1b 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1580,6 +1580,7 @@
<attr name="banner" />
<attr name="logo" />
<attr name="permissionGroup" />
+ <attr name="backgroundPermission" />
<attr name="description" />
<attr name="request" />
<attr name="protectionLevel" />
@@ -1610,6 +1611,9 @@
<attr name="logo" />
<attr name="description" />
<attr name="request" />
+ <attr name="requestDetail" />
+ <attr name="backgroundRequest" />
+ <attr name="backgroundRequestDetail" />
<attr name="permissionGroupFlags" />
<attr name="priority" />
</declare-styleable>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 7ffe866..14c9215 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -42,13 +42,13 @@
<item><xliff:g id="id">@string/status_bar_phone_evdo_signal</xliff:g></item>
<item><xliff:g id="id">@string/status_bar_phone_signal</xliff:g></item>
<item><xliff:g id="id">@string/status_bar_secure</xliff:g></item>
- <item><xliff:g id="id">@string/status_bar_bluetooth</xliff:g></item>
<item><xliff:g id="id">@string/status_bar_managed_profile</xliff:g></item>
<item><xliff:g id="id">@string/status_bar_cast</xliff:g></item>
<item><xliff:g id="id">@string/status_bar_vpn</xliff:g></item>
+ <item><xliff:g id="id">@string/status_bar_bluetooth</xliff:g></item>
+ <item><xliff:g id="id">@string/status_bar_location</xliff:g></item>
<item><xliff:g id="id">@string/status_bar_mute</xliff:g></item>
<item><xliff:g id="id">@string/status_bar_volume</xliff:g></item>
- <item><xliff:g id="id">@string/status_bar_location</xliff:g></item>
<item><xliff:g id="id">@string/status_bar_zen</xliff:g></item>
<item><xliff:g id="id">@string/status_bar_ethernet</xliff:g></item>
<item><xliff:g id="id">@string/status_bar_wifi</xliff:g></item>
@@ -491,13 +491,21 @@
Note also: the order of this is important. The first upstream type
for which a satisfying network exists is used.
- -->
+ -->
<integer-array translatable="false" name="config_tether_upstream_types">
<item>1</item>
<item>7</item>
<item>0</item>
</integer-array>
+ <!-- When true, the tethering upstream network follows the current default
+ Internet network (except when the current default network is mobile,
+ in which case a DUN network will be used if required).
+
+ When true, overrides the config_tether_upstream_types setting above.
+ -->
+ <bool translatable="false" name="config_tether_upstream_automatic">true</bool>
+
<!-- If the DUN connection for this CDMA device supports more than just DUN -->
<!-- traffic you should list them here. -->
<!-- If this device is not CDMA this is ignored. If this list is empty on -->
@@ -575,12 +583,14 @@
<integer translatable="false" name="config_wifi_framework_min_rx_rate_for_staying_on_network">16</integer>
<!-- Integer parameters of the wifi to cellular handover feature
wifi should not stick to bad networks -->
- <integer translatable="false" name="config_wifi_framework_wifi_score_bad_rssi_threshold_5GHz">-82</integer>
- <integer translatable="false" name="config_wifi_framework_wifi_score_entry_rssi_threshold_5GHz">-82</integer>
+ <!-- Integer threshold for low network score, should be somewhat less than the entry threshhold -->
+ <integer translatable="false" name="config_wifi_framework_wifi_score_bad_rssi_threshold_5GHz">-80</integer>
+ <!-- Integer threshold, do not connect to APs with RSSI lower than the entry threshold -->
+ <integer translatable="false" name="config_wifi_framework_wifi_score_entry_rssi_threshold_5GHz">-77</integer>
<integer translatable="false" name="config_wifi_framework_wifi_score_low_rssi_threshold_5GHz">-70</integer>
<integer translatable="false" name="config_wifi_framework_wifi_score_good_rssi_threshold_5GHz">-57</integer>
- <integer translatable="false" name="config_wifi_framework_wifi_score_bad_rssi_threshold_24GHz">-85</integer>
- <integer translatable="false" name="config_wifi_framework_wifi_score_entry_rssi_threshold_24GHz">-85</integer>
+ <integer translatable="false" name="config_wifi_framework_wifi_score_bad_rssi_threshold_24GHz">-83</integer>
+ <integer translatable="false" name="config_wifi_framework_wifi_score_entry_rssi_threshold_24GHz">-80</integer>
<integer translatable="false" name="config_wifi_framework_wifi_score_low_rssi_threshold_24GHz">-73</integer>
<integer translatable="false" name="config_wifi_framework_wifi_score_good_rssi_threshold_24GHz">-60</integer>
<integer translatable="false" name="config_wifi_framework_wifi_score_bad_link_speed_24">6</integer>
@@ -662,6 +672,10 @@
<!-- Boolean indicating whether framework needs to set the tx power limit for meeting SAR requirements -->
<bool translatable="false" name="config_wifi_framework_enable_sar_tx_power_limit">false</bool>
+ <!-- Boolean indicating whether framework should use detection of softAP mode to set the tx
+ power limit for meeting SAR requirements -->
+ <bool translatable="false" name="config_wifi_framework_enable_soft_ap_sar_tx_power_limit">false</bool>
+
<!-- Boolean indicating whether framework needs to use body proximity to set the tx power limit
for meeting SAR requirements -->
<bool translatable="false" name="config_wifi_framework_enable_body_proximity_sar_tx_power_limit">false</bool>
@@ -2604,8 +2618,8 @@
<item>lockdown</item>
<item>logout</item>
<item>bugreport</item>
- <item>emergency</item>
<item>screenshot</item>
+ <item>emergency</item>
</string-array>
<!-- Number of milliseconds to hold a wake lock to ensure that drawing is fully
@@ -2905,6 +2919,9 @@
<!-- Keyguard component -->
<string name="config_keyguardComponent" translatable="false">com.android.systemui/com.android.systemui.keyguard.KeyguardService</string>
+ <!-- Limit for the number of face templates per user -->
+ <integer name="config_faceMaxTemplatesPerUser">1</integer>
+
<!-- For performance and storage reasons, limit the number of fingerprints per user -->
<integer name="config_fingerprintMaxTemplatesPerUser">5</integer>
@@ -2955,6 +2972,14 @@
-->
<string translatable="false" name="config_mainBuiltInDisplayCutout"></string>
+ <!-- Like config_mainBuiltInDisplayCutout, but this path is used to report the
+ one single bounding rect per device edge to the app via
+ {@link DisplayCutout#getBoundingRect}. Note that this path should try to match the visual
+ appearance of the cutout as much as possible, and may be smaller than
+ config_mainBuiltInDisplayCutout
+ -->
+ <string translatable="false" name="config_mainBuiltInDisplayCutoutRectApproximation">@string/config_mainBuiltInDisplayCutout</string>
+
<!-- Whether the display cutout region of the main built-in display should be forced to
black in software (to avoid aliasing or emulate a cutout that is not physically existent).
-->
@@ -3161,6 +3186,9 @@
<!-- True if camera app should be pinned via Pinner Service -->
<bool name="config_pinnerCameraApp">false</bool>
+ <!-- True if home app should be pinned via Pinner Service -->
+ <bool name="config_pinnerHomeApp">false</bool>
+
<!-- Number of days preloaded file cache should be preserved on a device before it can be
deleted -->
<integer name="config_keepPreloadsMinDays">7</integer>
@@ -3363,6 +3391,9 @@
<!-- URI for in call notification sound -->
<string translatable="false" name="config_inCallNotificationSound">/system/media/audio/ui/InCallNotification.ogg</string>
+ <!-- Default number of notifications from the same app before they are automatically grouped by the OS -->
+ <integer translatable="false" name="config_autoGroupAtCount">4</integer>
+
<!-- The OEM specified sensor type for the lift trigger to launch the camera app. -->
<integer name="config_cameraLiftTriggerSensorType">-1</integer>
<!-- The OEM specified sensor string type for the gesture to launch camera app, this value
@@ -3390,7 +3421,7 @@
<string-array translatable="false" name="config_batteryPackageTypeService"/>
<!-- Flag indicating whether or not to enable night mode detection. -->
- <bool name="config_enableNightMode">false</bool>
+ <bool name="config_enableNightMode">true</bool>
<!-- Flag indicating that the actions buttons for a notification should be tinted with by the
color supplied by the Notification.Builder if present. -->
@@ -3471,4 +3502,13 @@
<!-- Whether or not swipe up gesture's opt-in setting is available on this device -->
<bool name="config_swipe_up_gesture_setting_available">false</bool>
+ <!-- Applications which are disabled unless matching a particular sku -->
+ <string-array name="config_disableApksUnlessMatchedSku_apk_list" translatable="false" />
+ <string-array name="config_disableApkUnlessMatchedSku_skus_list" translatable="false" />
+
+ <!-- Whether or not we should show the option to show battery percentage -->
+ <bool name="config_battery_percentage_setting_available">true</bool>
+
+ <!-- Whether or not battery saver should be "sticky" when manually enabled. -->
+ <bool name="config_batterySaverStickyBehaviourDisabled">false</bool>
</resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 9dccc88..8847ec8 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -539,15 +539,6 @@
<!-- status message in phone options dialog for when airplane mode is off -->
<string name="global_actions_airplane_mode_off_status">Airplane mode is OFF</string>
- <!-- label for item that enables battery saver in phone options dialog -->
- <string name="global_action_toggle_battery_saver">Battery saver</string>
-
- <!-- status message in phone options dialog for when battery saver is enabled -->
- <string name="global_action_battery_saver_on_status">Battery saver is OFF</string>
-
- <!-- status message in phone options dialog for when battery saver is disabled -->
- <string name="global_action_battery_saver_off_status">Battery saver is ON</string>
-
<!-- label for item that launches settings in phone options dialog [CHAR LIMIT=15]-->
<string name="global_action_settings">Settings</string>
@@ -671,7 +662,7 @@
<string name="permgrouplab_contacts">Contacts</string>
<!-- Description of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permgroupdesc_contacts">access your contacts</string>
- <!-- Message shown to the user when the apps requests permission from this group -->
+ <!-- Message shown to the user when the apps requests permission from this group. If ever possible this should stay below 80 characters (assuming the parameters takes 20 characters). Don't abbreviate until the message reaches 120 characters though. [CHAR LIMIT=120] -->
<string name="permgrouprequest_contacts">Allow
<b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g></b> to access your contacts?</string>
@@ -679,15 +670,22 @@
<string name="permgrouplab_location">Location</string>
<!-- Description of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permgroupdesc_location">access this device\'s location</string>
- <!-- Message shown to the user when the apps requests permission from this group -->
+ <!-- Message shown to the user when the apps requests permission from this group. If ever possible this should stay below 80 characters (assuming the parameters takes 20 characters). Don't abbreviate until the message reaches 120 characters though. [CHAR LIMIT=120] -->
<string name="permgrouprequest_location">Allow
<b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g></b> to access this device\'s location?</string>
+ <!-- Subtitle of the message shown to the user when the apps requests permission to use the location only while app is in foreground [CHAR LIMIT=150]-->
+ <string name="permgrouprequestdetail_location">The app will only have access to the location while you\u2019re using the app.</string>
+ <!-- Message shown to the user when the apps requests permission to use the location while app is in foreground and background. If ever possible this should stay below 80 characters (assuming the parameters takes 20 characters). Don't abbreviate until the message reaches 120 characters though. [CHAR LIMIT=120] -->
+ <string name="permgroupbackgroundrequest_location">Always allow
+ <b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g></b> to access this device\u2019s location?</string>
+ <!-- Subtitle of the message shown to the user when the apps requests permission to use the location while app is in foreground and background [CHAR LIMIT=150] -->
+ <string name="permgroupbackgroundrequestdetail_location">The app will always have access to the location, even when you\u2019re not using the app.</string>
<!-- Title of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permgrouplab_calendar">Calendar</string>
<!-- Description of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permgroupdesc_calendar">access your calendar</string>
- <!-- Message shown to the user when the apps requests permission from this group -->
+ <!-- Message shown to the user when the apps requests permission from this group. If ever possible this should stay below 80 characters (assuming the parameters takes 20 characters). Don't abbreviate until the message reaches 120 characters though. [CHAR LIMIT=120] -->
<string name="permgrouprequest_calendar">Allow
<b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g></b> to access your calendar?</string>
@@ -695,7 +693,7 @@
<string name="permgrouplab_sms">SMS</string>
<!-- Description of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permgroupdesc_sms">send and view SMS messages</string>
- <!-- Message shown to the user when the apps requests permission from this group -->
+ <!-- Message shown to the user when the apps requests permission from this group. If ever possible this should stay below 80 characters (assuming the parameters takes 20 characters). Don't abbreviate until the message reaches 120 characters though. [CHAR LIMIT=120] -->
<string name="permgrouprequest_sms">Allow
<b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g></b> to send and view SMS messages?</string>
@@ -703,7 +701,7 @@
<string name="permgrouplab_storage">Storage</string>
<!-- Description of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permgroupdesc_storage">access photos, media, and files on your device</string>
- <!-- Message shown to the user when the apps requests permission from this group -->
+ <!-- Message shown to the user when the apps requests permission from this group. If ever possible this should stay below 80 characters (assuming the parameters takes 20 characters). Don't abbreviate until the message reaches 120 characters though. [CHAR LIMIT=120] -->
<string name="permgrouprequest_storage">Allow
<b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g></b> to access photos, media, and files on your device?</string>
@@ -711,7 +709,7 @@
<string name="permgrouplab_microphone">Microphone</string>
<!-- Description of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permgroupdesc_microphone">record audio</string>
- <!-- Message shown to the user when the apps requests permission from this group -->
+ <!-- Message shown to the user when the apps requests permission from this group. If ever possible this should stay below 80 characters (assuming the parameters takes 20 characters). Don't abbreviate until the message reaches 120 characters though. [CHAR LIMIT=120] -->
<string name="permgrouprequest_microphone">Allow
<b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g></b> to record audio?</string>
@@ -719,7 +717,7 @@
<string name="permgrouplab_camera">Camera</string>
<!-- Description of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permgroupdesc_camera">take pictures and record video</string>
- <!-- Message shown to the user when the apps requests permission from this group -->
+ <!-- Message shown to the user when the apps requests permission from this group. If ever possible this should stay below 80 characters (assuming the parameters takes 20 characters). Don't abbreviate until the message reaches 120 characters though. [CHAR LIMIT=120] -->
<string name="permgrouprequest_camera">Allow
<b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g></b> to take pictures and record video?</string>
@@ -727,7 +725,7 @@
<string name="permgrouplab_calllog">Call logs</string>
<!-- Description of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permgroupdesc_calllog">read and write phone call log</string>
- <!-- Message shown to the user when the apps requests permission from this group -->
+ <!-- Message shown to the user when the apps requests permission from this group. If ever possible this should stay below 80 characters (assuming the parameters takes 20 characters). Don't abbreviate until the message reaches 120 characters though. [CHAR LIMIT=120] -->
<string name="permgrouprequest_calllog">Allow
<b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g></b> to access your phone call logs?</string>
@@ -735,7 +733,7 @@
<string name="permgrouplab_phone">Phone</string>
<!-- Description of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permgroupdesc_phone">make and manage phone calls</string>
- <!-- Message shown to the user when the apps requests permission from this group -->
+ <!-- Message shown to the user when the apps requests permission from this group. If ever possible this should stay below 80 characters (assuming the parameters takes 20 characters). Don't abbreviate until the message reaches 120 characters though. [CHAR LIMIT=120] -->
<string name="permgrouprequest_phone">Allow
<b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g></b> to make and manage phone calls?</string>
@@ -743,7 +741,7 @@
<string name="permgrouplab_sensors">Body Sensors</string>
<!-- Description of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permgroupdesc_sensors">access sensor data about your vital signs</string>
- <!-- Message shown to the user when the apps requests permission from this group -->
+ <!-- Message shown to the user when the apps requests permission from this group. If ever possible this should stay below 80 characters (assuming the parameters takes 20 characters). Don't abbreviate until the message reaches 120 characters though. [CHAR LIMIT=120] -->
<string name="permgrouprequest_sensors">Allow
<b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g></b> to access sensor data about your vital signs?</string>
@@ -1401,6 +1399,72 @@
<!-- Content description which should be used for the fingerprint icon. -->
<string name="fingerprint_icon_content_description">Fingerprint icon</string>
+ <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=50] -->
+ <string name="permlab_manageFace">manage face authentication hardware</string>
+ <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=90] -->
+ <string name="permdesc_manageFace">Allows the app to invoke methods to add and delete facial templates for use.</string>
+ <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=50] -->
+ <string name="permlab_useFaceAuthentication">use face authentication hardware</string>
+ <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=90] -->
+ <string name="permdesc_useFaceAuthentication">Allows the app to use face authentication hardware for authentication</string>
+
+ <!-- Message shown during face acquisition when the face cannot be recognized [CHAR LIMIT=50] -->
+ <string name="face_acquired_insufficient">Couldn\u2019t process face. Please try again.</string>
+ <!-- Message shown during face acquisition when the image is too bright [CHAR LIMIT=50] -->
+ <string name="face_acquired_too_bright">Face is too bright. Please try in lower light.</string>
+ <!-- Message shown during face acquisition when the image is too dark [CHAR LIMIT=50] -->
+ <string name="face_acquired_too_dark">Face is too dark. Please uncover light source.</string>
+ <!-- Message shown during face acquisition when the user is too close to sensor [CHAR LIMIT=50] -->
+ <string name="face_acquired_too_close">Please move sensor farther from face.</string>
+ <!-- Message shown during face acquisition when the user is too far from sensor [CHAR LIMIT=50] -->
+ <string name="face_acquired_too_far">Please bring sensor closer to face.</string>
+ <!-- Message shown during face acquisition when the user is too high relatively to sensor [CHAR LIMIT=50] -->
+ <string name="face_acquired_too_high">Please move sensor higher.</string>
+ <!-- Message shown during face acquisition when the user is too low relatively to sensor [CHAR LIMIT=50] -->
+ <string name="face_acquired_too_low">Please move sensor lower.</string>
+ <!-- Message shown during face acquisition when the user is too right relatively to sensor [CHAR LIMIT=50] -->
+ <string name="face_acquired_too_right">Please move sensor to the right.</string>
+ <!-- Message shown during face acquisition when the user is too left relatively to sensor [CHAR LIMIT=50] -->
+ <string name="face_acquired_too_left">Please move sensor to the left.</string>
+ <!-- Message shown during face acquisition when the user is not front facing the sensor [CHAR LIMIT=50] -->
+ <string name="face_acquired_poor_gaze">Please look at the sensor.</string>
+ <!-- Message shown during face acquisition when the user is not detected [CHAR LIMIT=50] -->
+ <string name="face_acquired_not_detected">No face detected.</string>
+ <!-- Message shown during face acquisition when the face is not kept steady infront of device [CHAR LIMIT=50] -->
+ <string name="face_acquired_not_steady">Keep face steady infront of device.</string>
+ <!-- Array containing custom messages shown during face acquisition from vendor. Vendor is expected to add and translate these strings -->
+ <string-array name="face_acquired_vendor">
+ </string-array>
+
+ <!-- Error message shown when the face hardware can't be accessed. [CHAR LIMIT=50] -->
+ <string name="face_error_hw_not_available">Face hardware not available.</string>
+ <!-- Error message shown when the face hardware timer has expired and the user needs to restart the operation. [CHAR LIMIT=50] -->
+ <string name="face_error_timeout">Face time out reached. Try again.</string>
+ <!-- Error message shown when the face hardware has run out of room for storing faces. [CHAR LIMIT=50] -->
+ <string name="face_error_no_space">Face can\u2019t be stored.</string>
+ <!-- Generic error message shown when the face operation (e.g. enrollment or authentication) is canceled. Generally not shown to the user. [CHAR LIMIT=50] -->
+ <string name="face_error_canceled">Face operation canceled.</string>
+ <!-- Generic error message shown when the face operation fails because too many attempts have been made. [CHAR LIMIT=50] -->
+ <string name="face_error_lockout">Too many attempts. Try again later.</string>
+ <!-- Generic error message shown when the face operation fails because strong authentication is required. [CHAR LIMIT=50] -->
+ <string name="face_error_lockout_permanent">Too many attempts. Facial authentication disabled.</string>
+ <!-- Generic error message shown when the face hardware can't recognize the face. [CHAR LIMIT=50] -->
+ <string name="face_error_unable_to_process">Try again.</string>
+ <!-- Generic error message shown when the user has no enrolled face. [CHAR LIMIT=50] -->
+ <string name="face_error_not_enrolled">No face enrolled.</string>
+ <!-- Generic error message shown when the app requests face authentication on a device without a sensor. [CHAR LIMIT=60] -->
+ <string name="face_error_hw_not_present">This device does not have a face authentication sensor</string>
+
+ <!-- Template to be used to name enrolled faces by default. [CHAR LIMIT=10] -->
+ <string name="face_name_template">Face <xliff:g id="faceId" example="1">%d</xliff:g></string>
+
+ <!-- Array containing custom error messages from vendor. Vendor is expected to add and translate these strings -->
+ <string-array name="face_error_vendor">
+ </string-array>
+
+ <!-- Content description which should be used for the face icon. [CHAR LIMIT=10] -->
+ <string name="face_icon_content_description">Face icon</string>
+
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permlab_readSyncSettings">read sync settings</string>
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
@@ -3173,6 +3237,21 @@
<!-- A notification is shown when the user connects to a Wi-Fi network and the system detects that that network has no Internet access. This is the notification's message. -->
<string name="wifi_no_internet_detailed">Tap for options</string>
+ <!-- A notification is shown when the user's softap config has been changed due to underlying
+ hardware restrictions. This is the notifications's title.
+ [CHAR_LIMIT=NONE] -->
+ <string name="wifi_softap_config_change">Changes to your hotspot settings</string>
+
+ <!-- A notification is shown when the user's softap config has been changed due to underlying
+ hardware restrictions. This is the notification's summary message.
+ [CHAR_LIMIT=NONE] -->
+ <string name="wifi_softap_config_change_summary">Your hotspot band has changed.</string>
+
+ <!-- A notification is shown when the user's softap config has been changed due to underlying
+ hardware restrictions. This is the notification's full message.
+ [CHAR_LIMIT=NONE] -->
+ <string name="wifi_softap_config_change_detailed">This device doesn\u2019t support your preference for 5GHz only. Instead, this device will use the 5GHz band when available.</string>
+
<!-- A notification might be shown if the device switches to another network type (e.g., mobile data) because it detects that the network it was using (e.g., Wi-Fi) has lost Internet connectivity. This is the notification's title. %1$s is the network type that the device switched to, e.g., cellular data. It is one of the strings in the network_switch_type_name array. -->
<string name="network_switch_metered">Switched to <xliff:g id="network_type">%1$s</xliff:g></string>
diff --git a/core/res/res/values/styles_package_installer.xml b/core/res/res/values/styles_package_installer.xml
new file mode 100644
index 0000000..339f9c7
--- /dev/null
+++ b/core/res/res/values/styles_package_installer.xml
@@ -0,0 +1,98 @@
+<?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.
+ -->
+
+<!-- styles for the permission grant dialog. -->
+<resources>
+ <style name="PermissionGrantDialog">
+ <item name="background">?attr/windowBackground</item>
+ <item name="elevation">?attr/windowElevation</item>
+ <item name="layout_weight">@dimen/permissionGrantDialogWeight</item>
+ <item name="layout_width">@dimen/permissionGrantDialogWidth</item>
+ </style>
+
+ <style name="PermissionGrantTitleIcon">
+ <item name="layout_width">36dp</item>
+ <item name="layout_height">36dp</item>
+ <item name="tint">?attr/colorAccent</item>
+ <item name="scaleType">fitCenter</item>
+ </style>
+
+ <style name="PermissionGrantTitleMessage"
+ parent="@style/TextAppearance.DeviceDefault">
+ <item name="paddingStart">22dp</item>
+ <item name="textSize">20sp</item>
+ <item name="textColor">?attr/textColorPrimary</item>
+ </style>
+
+ <style name="PermissionGrantIndex"
+ parent="@style/TextAppearance.DeviceDefault">
+ <item name="paddingEnd">12dp</item>
+ <item name="singleLine">true</item>
+ <item name="textColor">?attr/textColorSecondary</item>
+ </style>
+
+ <style name="PermissionGrantDescription">
+ <item name="layout_marginTop">20dp</item>
+ <item name="layout_marginStart">24dp</item>
+ <item name="layout_marginBottom">16dp</item>
+ <item name="layout_marginEnd">24dp</item>
+ </style>
+
+ <style name="PermissionGrantContent">
+ <item name="layout_marginStart">16dp</item>
+ <item name="layout_marginEnd">24dp</item>
+ </style>
+
+ <style name="PermissionGrantRadioGroup">
+ <item name="layout_marginStart">8dp</item>
+ <item name="layout_marginTop">-4dp</item>
+ </style>
+
+ <style name="PermissionGrantRadioButton"
+ parent="@style/Widget.DeviceDefault.CompoundButton.RadioButton">
+ <item name="paddingStart">16dp</item>
+ <item name="paddingTop">8dp</item>
+ <item name="paddingBottom">8dp</item>
+ <item name="textSize">16sp</item>
+ </style>
+
+ <style name="PermissionGrantDetailMessage"
+ parent="@style/TextAppearance.DeviceDefault">
+ <item name="layout_marginStart">8dp</item>
+ <item name="layout_marginBottom">4dp</item>
+ <item name="textColor">?attr/textColorPrimary</item>
+ <item name="textSize">16sp</item>
+ </style>
+
+ <style name="PermissionGrantDetailMessageSpace">
+ <item name="layout_height">8dp</item>
+ </style>
+
+ <style name="PermissionGrantCheckbox"
+ parent="@style/Widget.DeviceDefault.CompoundButton.CheckBox">
+ <item name="paddingStart">16dp</item>
+ <item name="textColor">?attr/textColorSecondary</item>
+ <item name="buttonTint">?attr/textColorSecondary</item>
+ <item name="textSize">16sp</item>
+ </style>
+
+ <style name="PermissionGrantButtonBar">
+ <item name="layout_marginStart">24dp</item>
+ <item name="layout_marginEnd">16dp</item>
+ <item name="layout_marginBottom">4dp</item>
+ </style>
+</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 17fb575..3ad3af3 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -337,6 +337,7 @@
<java-symbol type="bool" name="config_wifi_framework_use_single_radio_chain_scan_results_network_selection" />
<java-symbol type="bool" name="config_wifi_only_link_same_credential_configurations" />
<java-symbol type="bool" name="config_wifi_framework_enable_sar_tx_power_limit" />
+ <java-symbol type="bool" name="config_wifi_framework_enable_soft_ap_sar_tx_power_limit" />
<java-symbol type="bool" name="config_wifi_framework_enable_body_proximity_sar_tx_power_limit" />
<java-symbol type="string" name="config_wifi_sar_sensor_type" />
<java-symbol type="integer" name="config_wifi_framework_sar_free_space_event_id" />
@@ -1061,6 +1062,9 @@
<java-symbol type="string" name="network_switch_type_name_unknown" />
<java-symbol type="string" name="wifi_no_internet" />
<java-symbol type="string" name="wifi_no_internet_detailed" />
+ <java-symbol type="string" name="wifi_softap_config_change" />
+ <java-symbol type="string" name="wifi_softap_config_change_summary" />
+ <java-symbol type="string" name="wifi_softap_config_change_detailed" />
<java-symbol type="string" name="wifi_connect_alert_title" />
<java-symbol type="string" name="wifi_connect_alert_message" />
<java-symbol type="string" name="wifi_connect_default_application" />
@@ -1336,7 +1340,6 @@
<java-symbol type="drawable" name="ic_text_dot" />
<java-symbol type="drawable" name="ic_print" />
<java-symbol type="drawable" name="ic_print_error" />
- <java-symbol type="drawable" name="ic_grayedout_printer" />
<java-symbol type="drawable" name="jog_dial_arrow_long_left_green" />
<java-symbol type="drawable" name="jog_dial_arrow_long_right_red" />
<java-symbol type="drawable" name="jog_dial_arrow_short_left_and_right" />
@@ -1773,9 +1776,6 @@
<java-symbol type="string" name="global_action_silent_mode_off_status" />
<java-symbol type="string" name="global_action_silent_mode_on_status" />
<java-symbol type="string" name="global_action_toggle_silent_mode" />
- <java-symbol type="string" name="global_action_battery_saver_off_status" />
- <java-symbol type="string" name="global_action_battery_saver_on_status" />
- <java-symbol type="string" name="global_action_toggle_battery_saver" />
<java-symbol type="string" name="global_action_lockdown" />
<java-symbol type="string" name="global_action_voice_assist" />
<java-symbol type="string" name="global_action_assist" />
@@ -1828,6 +1828,7 @@
<java-symbol type="array" name="config_tether_bluetooth_regexs" />
<java-symbol type="array" name="config_tether_dhcp_range" />
<java-symbol type="array" name="config_tether_upstream_types" />
+ <java-symbol type="bool" name="config_tether_upstream_automatic" />
<java-symbol type="array" name="config_tether_apndata" />
<java-symbol type="array" name="config_tether_usb_regexs" />
<java-symbol type="array" name="config_tether_wifi_regexs" />
@@ -2199,6 +2200,7 @@
<java-symbol type="string" name="ext_media_move_failure_message" />
<java-symbol type="style" name="Animation.RecentApplications" />
<java-symbol type="integer" name="dock_enter_exit_duration" />
+ <java-symbol type="bool" name="config_battery_percentage_setting_available" />
<!-- ImfTest -->
<java-symbol type="layout" name="auto_complete_list" />
@@ -2418,6 +2420,34 @@
<java-symbol type="integer" name="config_fingerprintMaxTemplatesPerUser"/>
<java-symbol type="bool" name="config_fingerprintSupportsGestures"/>
+ <!-- Face authentication messages -->
+ <java-symbol type="string" name="face_error_unable_to_process" />
+ <java-symbol type="string" name="face_error_hw_not_available" />
+ <java-symbol type="string" name="face_error_no_space" />
+ <java-symbol type="string" name="face_error_timeout" />
+ <java-symbol type="array" name="face_error_vendor" />
+ <java-symbol type="string" name="face_error_canceled" />
+ <java-symbol type="string" name="face_error_lockout" />
+ <java-symbol type="string" name="face_error_lockout_permanent" />
+ <java-symbol type="string" name="face_error_not_enrolled" />
+ <java-symbol type="string" name="face_error_hw_not_present" />
+ <java-symbol type="string" name="face_acquired_insufficient" />
+ <java-symbol type="string" name="face_acquired_too_bright" />
+ <java-symbol type="string" name="face_acquired_too_dark" />
+ <java-symbol type="string" name="face_acquired_too_close" />
+ <java-symbol type="string" name="face_acquired_too_far" />
+ <java-symbol type="string" name="face_acquired_too_high" />
+ <java-symbol type="string" name="face_acquired_too_low" />
+ <java-symbol type="string" name="face_acquired_too_right" />
+ <java-symbol type="string" name="face_acquired_too_left" />
+ <java-symbol type="string" name="face_acquired_poor_gaze" />
+ <java-symbol type="string" name="face_acquired_not_detected" />
+ <java-symbol type="array" name="face_acquired_vendor" />
+ <java-symbol type="string" name="face_name_template" />
+
+ <!-- Face config -->
+ <java-symbol type="integer" name="config_faceMaxTemplatesPerUser" />
+
<!-- From various Material changes -->
<java-symbol type="attr" name="titleTextAppearance" />
<java-symbol type="attr" name="subtitleTextAppearance" />
@@ -2893,6 +2923,7 @@
<!-- Pinner Service -->
<java-symbol type="array" name="config_defaultPinnerServiceFiles" />
<java-symbol type="bool" name="config_pinnerCameraApp" />
+ <java-symbol type="bool" name="config_pinnerHomeApp" />
<java-symbol type="string" name="config_doubleTouchGestureEnableFile" />
@@ -3236,6 +3267,7 @@
<java-symbol type="bool" name="config_handleVolumeKeysInWindowManager" />
<java-symbol type="dimen" name="config_inCallNotificationVolume" />
<java-symbol type="string" name="config_inCallNotificationSound" />
+ <java-symbol type="integer" name="config_autoGroupAtCount" />
<java-symbol type="bool" name="config_dozeAlwaysOnDisplayAvailable" />
<java-symbol type="bool" name="config_dozeAlwaysOnEnabled" />
<java-symbol type="bool" name="config_displayBlanksAfterDoze" />
@@ -3333,6 +3365,7 @@
<java-symbol type="string" name="global_action_logout" />
<java-symbol type="string" name="config_mainBuiltInDisplayCutout" />
+ <java-symbol type="string" name="config_mainBuiltInDisplayCutoutRectApproximation" />
<java-symbol type="drawable" name="messaging_user" />
<java-symbol type="bool" name="config_fillMainBuiltInDisplayCutout" />
<java-symbol type="drawable" name="ic_logout" />
@@ -3374,6 +3407,7 @@
<java-symbol type="string" name="notification_app_name_settings" />
<java-symbol type="integer" name="config_lowBatteryAutoTriggerDefaultLevel" />
+ <java-symbol type="bool" name="config_batterySaverStickyBehaviourDisabled" />
<!-- For car devices -->
<java-symbol type="string" name="car_loading_profile" />
@@ -3403,4 +3437,7 @@
<java-symbol type="integer" name="config_defaultHapticFeedbackIntensity" />
<java-symbol type="integer" name="config_defaultNotificationVibrationIntensity" />
+
+ <java-symbol type="array" name="config_disableApksUnlessMatchedSku_apk_list" />
+ <java-symbol type="array" name="config_disableApkUnlessMatchedSku_skus_list" />
</resources>
diff --git a/core/res/res/values/themes_package_installer.xml b/core/res/res/values/themes_package_installer.xml
new file mode 100644
index 0000000..a6341dc
--- /dev/null
+++ b/core/res/res/values/themes_package_installer.xml
@@ -0,0 +1,35 @@
+<?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.
+ -->
+
+<!-- themes for the permission grant dialog. -->
+<resources>
+ <style name="Theme.DeviceDefault.Light.Panel.PermissionGrantApp"
+ parent="@style/Theme.DeviceDefault.Light.Panel">
+ <item name="windowIsFloating">false</item>
+ <item name="windowTranslucentStatus">true</item>
+ <item name="backgroundDimEnabled">true</item>
+ <item name="windowAnimationStyle">@style/Animation.Material.Dialog</item>
+ </style>
+
+ <style name="Theme.DeviceDefault.Light.Dialog.PermissionGrant"
+ parent="@style/Theme.DeviceDefault.Light.Dialog">
+ <item name="titleTextStyle">@style/PermissionGrantTitleMessage</item>
+ <item name="radioButtonStyle">@style/PermissionGrantRadioButton</item>
+ <item name="checkboxStyle">@style/PermissionGrantCheckbox</item>
+ <item name="buttonBarStyle">@style/PermissionGrantButtonBar</item>
+ </style>
+</resources>
diff --git a/core/tests/HdmiCec/HelloWorldTests_HalloWelt.config b/core/tests/HdmiCec/HelloWorldTests_HalloWelt.config
deleted file mode 100644
index 902699b..0000000
--- a/core/tests/HdmiCec/HelloWorldTests_HalloWelt.config
+++ /dev/null
@@ -1,26 +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.
--->
-<configuration description="Runs only the HalloWelt test.">
- <target_preparer class="com.android.tradefed.targetprep.TestFilePushSetup" />
- <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
- <option name="test-file-name" value="HelloWorldTests.apk" />
- </target_preparer>
- <option name="test-suite-tag" value="apct" />
- <option name="test-tag" value="SampleInstrumentationTest" />
- <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
- <option name="package" value="android.test.example.helloworld" />
- <option name="runner" value="android.support.test.runner.AndroidJUnitRunner" />
- <option name="class" value="android.test.example.helloworld.HelloWorldTest" />
- <option name="method" value="testHalloWelt" />
- </test>
-</configuration>
diff --git a/core/tests/HdmiCec/OWNERS b/core/tests/HdmiCec/OWNERS
deleted file mode 100644
index cc016f1..0000000
--- a/core/tests/HdmiCec/OWNERS
+++ /dev/null
@@ -1,3 +0,0 @@
-amyjojo@google.com
-nchalko@google.com
-shubang@google.com
\ No newline at end of file
diff --git a/core/tests/HdmiCec/src/android/test/example/helloworld/HelloWorldTest.java b/core/tests/HdmiCec/src/android/test/example/helloworld/HelloWorldTest.java
deleted file mode 100644
index 09321ad..0000000
--- a/core/tests/HdmiCec/src/android/test/example/helloworld/HelloWorldTest.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.test.example.helloworld;
-
-import android.support.test.filters.SmallTest;
-import android.util.Log;
-import org.junit.After;
-import org.junit.AfterClass;
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.BeforeClass;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-@RunWith(JUnit4.class)
-public class HelloWorldTest {
- private static final String TAG = HelloWorldTest.class.getSimpleName();
-
- @BeforeClass
- public static void beforeClass() {
- Log.d(TAG, "beforeClass()");
- }
-
- @AfterClass
- public static void afterClass() {
- Log.d(TAG, "afterClass()");
- }
-
- @Before
- public void before() {
- Log.d(TAG, "before()");
- }
-
- @After
- public void after() {
- Log.d(TAG, "after()");
- }
-
- @Test
- @SmallTest
- public void testHelloWorld() {
- Log.d(TAG, "testHelloWorld()");
- Assert.assertNotEquals("Hello", "world");
- }
-
- @Test
- @SmallTest
- public void testHalloWelt() {
- Log.d(TAG, "testHalloWelt()");
- Assert.assertNotEquals("Hallo", "Welt");
- }
-}
diff --git a/core/tests/benchmarks/src/android/os/FileUtilsBenchmark.java b/core/tests/benchmarks/src/android/os/FileUtilsBenchmark.java
index 5989da7..c70fc3c 100644
--- a/core/tests/benchmarks/src/android/os/FileUtilsBenchmark.java
+++ b/core/tests/benchmarks/src/android/os/FileUtilsBenchmark.java
@@ -54,7 +54,7 @@
for (int i = 0; i < reps; i++) {
try (FileInputStream in = new FileInputStream(mSrc);
FileOutputStream out = new FileOutputStream(mDest)) {
- copyInternalUserspace(in.getFD(), out.getFD(), null, null, Long.MAX_VALUE);
+ copyInternalUserspace(in.getFD(), out.getFD(), Long.MAX_VALUE, null, null, null);
}
}
}
@@ -63,7 +63,7 @@
for (int i = 0; i < reps; i++) {
try (FileInputStream in = new FileInputStream(mSrc);
FileOutputStream out = new FileOutputStream(mDest)) {
- copyInternalSendfile(in.getFD(), out.getFD(), null, null, Long.MAX_VALUE);
+ copyInternalSendfile(in.getFD(), out.getFD(), Long.MAX_VALUE, null, null, null);
}
}
}
@@ -72,7 +72,7 @@
for (int i = 0; i < reps; i++) {
try (MemoryPipe in = MemoryPipe.createSource(mData);
FileOutputStream out = new FileOutputStream(mDest)) {
- copyInternalUserspace(in.getFD(), out.getFD(), null, null, Long.MAX_VALUE);
+ copyInternalUserspace(in.getFD(), out.getFD(), Long.MAX_VALUE, null, null, null);
}
}
}
@@ -81,7 +81,7 @@
for (int i = 0; i < reps; i++) {
try (MemoryPipe in = MemoryPipe.createSource(mData);
FileOutputStream out = new FileOutputStream(mDest)) {
- copyInternalSplice(in.getFD(), out.getFD(), null, null, Long.MAX_VALUE);
+ copyInternalSplice(in.getFD(), out.getFD(), Long.MAX_VALUE, null, null, null);
}
}
}
@@ -90,7 +90,7 @@
for (int i = 0; i < reps; i++) {
try (FileInputStream in = new FileInputStream(mSrc);
MemoryPipe out = MemoryPipe.createSink(mData)) {
- copyInternalUserspace(in.getFD(), out.getFD(), null, null, Long.MAX_VALUE);
+ copyInternalUserspace(in.getFD(), out.getFD(), Long.MAX_VALUE, null, null, null);
}
}
}
@@ -99,7 +99,7 @@
for (int i = 0; i < reps; i++) {
try (FileInputStream in = new FileInputStream(mSrc);
MemoryPipe out = MemoryPipe.createSink(mData)) {
- copyInternalSplice(in.getFD(), out.getFD(), null, null, Long.MAX_VALUE);
+ copyInternalSplice(in.getFD(), out.getFD(), Long.MAX_VALUE, null, null, null);
}
}
}
diff --git a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java
index ada0366..b906d84 100644
--- a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java
+++ b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java
@@ -446,7 +446,10 @@
final IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
mContext.registerReceiver(receiver, filter);
- assertTrue(adapter.enable());
+ // Note: for Wear Local Edition builds, which have Permission Review Mode enabled to
+ // obey China CMIIT, BluetoothAdapter may not startup immediately on methods enable/disable.
+ // So no assertion applied here.
+ adapter.enable();
boolean success = false;
try {
success = completionSemaphore.tryAcquire(ENABLE_DISABLE_TIMEOUT, TimeUnit.MILLISECONDS);
@@ -489,7 +492,10 @@
final IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
mContext.registerReceiver(receiver, filter);
- assertTrue(adapter.disable());
+ // Note: for Wear Local Edition builds, which have Permission Review Mode enabled to
+ // obey China CMIIT, BluetoothAdapter may not startup immediately on methods enable/disable.
+ // So no assertion applied here.
+ adapter.disable();
boolean success = false;
try {
success = completionSemaphore.tryAcquire(ENABLE_DISABLE_TIMEOUT, TimeUnit.MILLISECONDS);
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index 0f019e71..2a906ae 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -1401,6 +1401,14 @@
<service android:name="android.content.CrossUserContentService"
android:exported="true" />
+ <activity android:name="android.app.assist.EmptyLayoutActivity"
+ android:label="My Title">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
+ </intent-filter>
+ </activity>
+
</application>
<instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/ascii.ttc b/core/tests/coretests/assets/fonts_for_family_selection/ascii.ttc
new file mode 100644
index 0000000..e404f2b
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/ascii.ttc
Binary files differ
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/ascii_vf.ttf b/core/tests/coretests/assets/fonts_for_family_selection/ascii_vf.ttf
new file mode 100644
index 0000000..792b7d7
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/ascii_vf.ttf
Binary files differ
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/ascii_vf.ttx b/core/tests/coretests/assets/fonts_for_family_selection/ascii_vf.ttx
new file mode 100644
index 0000000..22b2941
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/ascii_vf.ttx
@@ -0,0 +1,1942 @@
+<?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.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+ <GlyphOrder>
+ <GlyphID id="0" name=".notdef"/>
+ <GlyphID id="1" name="a"/>
+ <GlyphID id="2" name="b"/>
+ <GlyphID id="3" name="c"/>
+ <GlyphID id="4" name="d"/>
+ <GlyphID id="5" name="e"/>
+ <GlyphID id="6" name="f"/>
+ <GlyphID id="7" name="g"/>
+ <GlyphID id="8" name="h"/>
+ <GlyphID id="9" name="i"/>
+ <GlyphID id="10" name="j"/>
+ <GlyphID id="11" name="k"/>
+ <GlyphID id="12" name="l"/>
+ <GlyphID id="13" name="m"/>
+ <GlyphID id="14" name="n"/>
+ <GlyphID id="15" name="o"/>
+ <GlyphID id="16" name="p"/>
+ <GlyphID id="17" name="q"/>
+ <GlyphID id="18" name="r"/>
+ <GlyphID id="19" name="s"/>
+ <GlyphID id="20" name="t"/>
+ <GlyphID id="21" name="u"/>
+ <GlyphID id="22" name="v"/>
+ <GlyphID id="23" name="w"/>
+ <GlyphID id="24" name="x"/>
+ <GlyphID id="25" name="y"/>
+ <GlyphID id="26" name="z"/>
+ </GlyphOrder>
+
+ <head>
+ <tableVersion value="1.0"/>
+ <fontRevision value="1.0"/>
+ <checkSumAdjustment value="0x640cdb2f"/>
+ <magicNumber value="0x5f0f3cf5"/>
+ <flags value="00000000 00000011"/>
+ <unitsPerEm value="50"/>
+ <created value="Thu Feb 22 10:04:28 2018"/>
+ <macStyle value="00000000 00000000"/>
+ <lowestRecPPEM value="7"/>
+ <fontDirectionHint value="2"/>
+ <glyphDataFormat value="0"/>
+ </head>
+
+ <hhea>
+ <tableVersion value="0x00010000"/>
+ <ascent value="100"/>
+ <descent value="0"/>
+ <lineGap value="0"/>
+ <caretSlopeRise value="1"/>
+ <caretSlopeRun value="0"/>
+ <caretOffset value="0"/>
+ <reserved0 value="0"/>
+ <reserved1 value="0"/>
+ <reserved2 value="0"/>
+ <reserved3 value="0"/>
+ <metricDataFormat value="0"/>
+ </hhea>
+
+ <maxp>
+ <tableVersion value="0x10000"/>
+ <maxZones value="0"/>
+ <maxTwilightPoints value="0"/>
+ <maxStorage value="0"/>
+ <maxFunctionDefs value="0"/>
+ <maxInstructionDefs value="0"/>
+ <maxStackElements value="0"/>
+ <maxSizeOfInstructions value="0"/>
+ <maxComponentElements value="0"/>
+ </maxp>
+
+ <OS_2>
+ <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+ will be recalculated by the compiler -->
+ <version value="3"/>
+ <xAvgCharWidth value="594"/>
+ <usWeightClass value="100"/>
+ <usWidthClass value="5"/>
+ <fsType value="00000000 00001000"/>
+ <ySubscriptXSize value="650"/>
+ <ySubscriptYSize value="600"/>
+ <ySubscriptXOffset value="0"/>
+ <ySubscriptYOffset value="75"/>
+ <ySuperscriptXSize value="650"/>
+ <ySuperscriptYSize value="600"/>
+ <ySuperscriptXOffset value="0"/>
+ <ySuperscriptYOffset value="350"/>
+ <yStrikeoutSize value="50"/>
+ <yStrikeoutPosition value="300"/>
+ <sFamilyClass value="0"/>
+ <panose>
+ <bFamilyType value="0"/>
+ <bSerifStyle value="0"/>
+ <bWeight value="5"/>
+ <bProportion value="0"/>
+ <bContrast value="0"/>
+ <bStrokeVariation value="0"/>
+ <bArmStyle value="0"/>
+ <bLetterForm value="0"/>
+ <bMidline value="0"/>
+ <bXHeight value="0"/>
+ </panose>
+ <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+ <achVendID value="UKWN"/>
+ <fsSelection value="00000000 01000000"/>
+ <usFirstCharIndex value="32"/>
+ <usLastCharIndex value="122"/>
+ <sTypoAscender value="800"/>
+ <sTypoDescender value="-200"/>
+ <sTypoLineGap value="200"/>
+ <usWinAscent value="1000"/>
+ <usWinDescent value="200"/>
+ <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+ <sxHeight value="500"/>
+ <sCapHeight value="700"/>
+ <usDefaultChar value="0"/>
+ <usBreakChar value="32"/>
+ <usMaxContext value="0"/>
+ </OS_2>
+
+ <hmtx>
+ <mtx name=".notdef" width="50" lsb="93"/>
+ <mtx name="a" width="50" lsb="0"/>
+ <mtx name="b" width="50" lsb="0"/>
+ <mtx name="c" width="50" lsb="0"/>
+ <mtx name="d" width="50" lsb="0"/>
+ <mtx name="e" width="50" lsb="0"/>
+ <mtx name="f" width="50" lsb="0"/>
+ <mtx name="g" width="50" lsb="0"/>
+ <mtx name="h" width="50" lsb="0"/>
+ <mtx name="i" width="50" lsb="0"/>
+ <mtx name="j" width="50" lsb="0"/>
+ <mtx name="k" width="50" lsb="0"/>
+ <mtx name="l" width="50" lsb="0"/>
+ <mtx name="m" width="50" lsb="0"/>
+ <mtx name="n" width="50" lsb="0"/>
+ <mtx name="o" width="50" lsb="0"/>
+ <mtx name="p" width="50" lsb="0"/>
+ <mtx name="q" width="50" lsb="0"/>
+ <mtx name="r" width="50" lsb="0"/>
+ <mtx name="s" width="50" lsb="0"/>
+ <mtx name="t" width="50" lsb="0"/>
+ <mtx name="u" width="50" lsb="0"/>
+ <mtx name="v" width="50" lsb="0"/>
+ <mtx name="w" width="50" lsb="0"/>
+ <mtx name="x" width="50" lsb="0"/>
+ <mtx name="y" width="50" lsb="0"/>
+ <mtx name="z" width="50" lsb="0"/>
+ </hmtx>
+
+ <cmap>
+ <tableVersion version="0"/>
+ <cmap_format_4 platformID="0" platEncID="3" language="0">
+ <map code="0x0061" name="a" /> <!-- a -->
+ <map code="0x0062" name="b" /> <!-- b -->
+ <map code="0x0063" name="c" /> <!-- c -->
+ <map code="0x0064" name="d" /> <!-- d -->
+ <map code="0x0065" name="e" /> <!-- e -->
+ <map code="0x0066" name="f" /> <!-- f -->
+ <map code="0x0067" name="g" /> <!-- g -->
+ <map code="0x0068" name="h" /> <!-- h -->
+ <map code="0x0069" name="i" /> <!-- i -->
+ <map code="0x006A" name="j" /> <!-- j -->
+ <map code="0x006B" name="k" /> <!-- k -->
+ <map code="0x006C" name="l" /> <!-- l -->
+ <map code="0x006D" name="m" /> <!-- m -->
+ <map code="0x006E" name="n" /> <!-- n -->
+ <map code="0x006F" name="o" /> <!-- o -->
+ <map code="0x0070" name="p" /> <!-- p -->
+ <map code="0x0071" name="q" /> <!-- q -->
+ <map code="0x0072" name="r" /> <!-- r -->
+ <map code="0x0073" name="s" /> <!-- s -->
+ <map code="0x0074" name="t" /> <!-- t -->
+ <map code="0x0075" name="u" /> <!-- u -->
+ <map code="0x0076" name="v" /> <!-- v -->
+ <map code="0x0077" name="w" /> <!-- w -->
+ <map code="0x0078" name="x" /> <!-- x -->
+ <map code="0x0079" name="y" /> <!-- y -->
+ <map code="0x007A" name="z" /> <!-- z -->
+ </cmap_format_4>
+ </cmap>
+
+ <loca>
+ <!-- The 'loca' table will be calculated by the compiler -->
+ </loca>
+
+ <glyf>
+ <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="a" xMin="0" yMin="0" xMax="50" yMax="50">
+ <contour>
+ <pt x="0" y="0" on="1"/>
+ <pt x="50" y="25" on="1"/>
+ <pt x="0" y="50" on="1"/>
+ </contour>
+ <instructions />
+ </TTGlyph>
+ <TTGlyph name="b" xMin="0" yMin="0" xMax="50" yMax="50">
+ <contour>
+ <pt x="0" y="0" on="1"/>
+ <pt x="50" y="25" on="1"/>
+ <pt x="0" y="50" on="1"/>
+ </contour>
+ <instructions />
+ </TTGlyph>
+ <TTGlyph name="c" xMin="0" yMin="0" xMax="50" yMax="50">
+ <contour>
+ <pt x="0" y="0" on="1"/>
+ <pt x="50" y="25" on="1"/>
+ <pt x="0" y="50" on="1"/>
+ </contour>
+ <instructions />
+ </TTGlyph>
+ <TTGlyph name="d" xMin="0" yMin="0" xMax="50" yMax="50">
+ <contour>
+ <pt x="0" y="0" on="1"/>
+ <pt x="50" y="25" on="1"/>
+ <pt x="0" y="50" on="1"/>
+ </contour>
+ <instructions />
+ </TTGlyph>
+ <TTGlyph name="e" xMin="0" yMin="0" xMax="50" yMax="50">
+ <contour>
+ <pt x="0" y="0" on="1"/>
+ <pt x="50" y="25" on="1"/>
+ <pt x="0" y="50" on="1"/>
+ </contour>
+ <instructions />
+ </TTGlyph>
+ <TTGlyph name="f" xMin="0" yMin="0" xMax="50" yMax="50">
+ <contour>
+ <pt x="0" y="0" on="1"/>
+ <pt x="50" y="25" on="1"/>
+ <pt x="0" y="50" on="1"/>
+ </contour>
+ <instructions />
+ </TTGlyph>
+ <TTGlyph name="g" xMin="0" yMin="0" xMax="50" yMax="50">
+ <contour>
+ <pt x="0" y="0" on="1"/>
+ <pt x="50" y="25" on="1"/>
+ <pt x="0" y="50" on="1"/>
+ </contour>
+ <instructions />
+ </TTGlyph>
+ <TTGlyph name="h" xMin="0" yMin="0" xMax="50" yMax="50">
+ <contour>
+ <pt x="0" y="0" on="1"/>
+ <pt x="50" y="25" on="1"/>
+ <pt x="0" y="50" on="1"/>
+ </contour>
+ <instructions />
+ </TTGlyph>
+ <TTGlyph name="i" xMin="0" yMin="0" xMax="50" yMax="50">
+ <contour>
+ <pt x="0" y="0" on="1"/>
+ <pt x="50" y="25" on="1"/>
+ <pt x="0" y="50" on="1"/>
+ </contour>
+ <instructions />
+ </TTGlyph>
+ <TTGlyph name="j" xMin="0" yMin="0" xMax="50" yMax="50">
+ <contour>
+ <pt x="0" y="0" on="1"/>
+ <pt x="50" y="25" on="1"/>
+ <pt x="0" y="50" on="1"/>
+ </contour>
+ <instructions />
+ </TTGlyph>
+ <TTGlyph name="k" xMin="0" yMin="0" xMax="50" yMax="50">
+ <contour>
+ <pt x="0" y="0" on="1"/>
+ <pt x="50" y="25" on="1"/>
+ <pt x="0" y="50" on="1"/>
+ </contour>
+ <instructions />
+ </TTGlyph>
+ <TTGlyph name="l" xMin="0" yMin="0" xMax="50" yMax="50">
+ <contour>
+ <pt x="0" y="0" on="1"/>
+ <pt x="50" y="25" on="1"/>
+ <pt x="0" y="50" on="1"/>
+ </contour>
+ <instructions />
+ </TTGlyph>
+ <TTGlyph name="m" xMin="0" yMin="0" xMax="50" yMax="50">
+ <contour>
+ <pt x="0" y="0" on="1"/>
+ <pt x="50" y="25" on="1"/>
+ <pt x="0" y="50" on="1"/>
+ </contour>
+ <instructions />
+ </TTGlyph>
+ <TTGlyph name="n" xMin="0" yMin="0" xMax="50" yMax="50">
+ <contour>
+ <pt x="0" y="0" on="1"/>
+ <pt x="50" y="25" on="1"/>
+ <pt x="0" y="50" on="1"/>
+ </contour>
+ <instructions />
+ </TTGlyph>
+ <TTGlyph name="o" xMin="0" yMin="0" xMax="50" yMax="50">
+ <contour>
+ <pt x="0" y="0" on="1"/>
+ <pt x="50" y="25" on="1"/>
+ <pt x="0" y="50" on="1"/>
+ </contour>
+ <instructions />
+ </TTGlyph>
+ <TTGlyph name="p" xMin="0" yMin="0" xMax="50" yMax="50">
+ <contour>
+ <pt x="0" y="0" on="1"/>
+ <pt x="50" y="25" on="1"/>
+ <pt x="0" y="50" on="1"/>
+ </contour>
+ <instructions />
+ </TTGlyph>
+ <TTGlyph name="q" xMin="0" yMin="0" xMax="50" yMax="50">
+ <contour>
+ <pt x="0" y="0" on="1"/>
+ <pt x="50" y="25" on="1"/>
+ <pt x="0" y="50" on="1"/>
+ </contour>
+ <instructions />
+ </TTGlyph>
+ <TTGlyph name="r" xMin="0" yMin="0" xMax="50" yMax="50">
+ <contour>
+ <pt x="0" y="0" on="1"/>
+ <pt x="50" y="25" on="1"/>
+ <pt x="0" y="50" on="1"/>
+ </contour>
+ <instructions />
+ </TTGlyph>
+ <TTGlyph name="s" xMin="0" yMin="0" xMax="50" yMax="50">
+ <contour>
+ <pt x="0" y="0" on="1"/>
+ <pt x="50" y="25" on="1"/>
+ <pt x="0" y="50" on="1"/>
+ </contour>
+ <instructions />
+ </TTGlyph>
+ <TTGlyph name="t" xMin="0" yMin="0" xMax="50" yMax="50">
+ <contour>
+ <pt x="0" y="0" on="1"/>
+ <pt x="50" y="25" on="1"/>
+ <pt x="0" y="50" on="1"/>
+ </contour>
+ <instructions />
+ </TTGlyph>
+ <TTGlyph name="u" xMin="0" yMin="0" xMax="50" yMax="50">
+ <contour>
+ <pt x="0" y="0" on="1"/>
+ <pt x="50" y="25" on="1"/>
+ <pt x="0" y="50" on="1"/>
+ </contour>
+ <instructions />
+ </TTGlyph>
+ <TTGlyph name="v" xMin="0" yMin="0" xMax="50" yMax="50">
+ <contour>
+ <pt x="0" y="0" on="1"/>
+ <pt x="50" y="25" on="1"/>
+ <pt x="0" y="50" on="1"/>
+ </contour>
+ <instructions />
+ </TTGlyph>
+ <TTGlyph name="w" xMin="0" yMin="0" xMax="50" yMax="50">
+ <contour>
+ <pt x="0" y="0" on="1"/>
+ <pt x="50" y="25" on="1"/>
+ <pt x="0" y="50" on="1"/>
+ </contour>
+ <instructions />
+ </TTGlyph>
+ <TTGlyph name="x" xMin="0" yMin="0" xMax="50" yMax="50">
+ <contour>
+ <pt x="0" y="0" on="1"/>
+ <pt x="50" y="25" on="1"/>
+ <pt x="0" y="50" on="1"/>
+ </contour>
+ <instructions />
+ </TTGlyph>
+ <TTGlyph name="y" xMin="0" yMin="0" xMax="50" yMax="50">
+ <contour>
+ <pt x="0" y="0" on="1"/>
+ <pt x="50" y="25" on="1"/>
+ <pt x="0" y="50" on="1"/>
+ </contour>
+ <instructions />
+ </TTGlyph>
+ <TTGlyph name="z" xMin="0" yMin="0" xMax="50" yMax="50">
+ <contour>
+ <pt x="0" y="0" on="1"/>
+ <pt x="50" y="25" on="1"/>
+ <pt x="0" y="50" on="1"/>
+ </contour>
+ <instructions />
+ </TTGlyph>
+ </glyf>
+
+ <fvar>
+ <Axis>
+ <!-- Weight axis but no effects to the glyph. -->
+ <AxisTag>wght</AxisTag>
+ <Flags>0x0</Flags>
+ <MinValue>0.0</MinValue>
+ <DefaultValue>400.0</DefaultValue>
+ <MaxValue>1000.0</MaxValue>
+ <AxisNameID>256</AxisNameID>
+ </Axis>
+ <Axis>
+ <!-- Italic axis but no effects to the glyph. -->
+ <AxisTag>ital</AxisTag>
+ <Flags>0x0</Flags>
+ <MinValue>0.0</MinValue>
+ <DefaultValue>0.0</DefaultValue>
+ <MaxValue>1.0</MaxValue>
+ <AxisNameID>256</AxisNameID>
+ </Axis>
+ <Axis>
+ <AxisTag>Asca</AxisTag>
+ <Flags>0x0</Flags>
+ <MinValue>0.0</MinValue>
+ <DefaultValue>0.0</DefaultValue>
+ <MaxValue>1.0</MaxValue>
+ <AxisNameID>256</AxisNameID>
+ </Axis>
+ <Axis>
+ <AxisTag>Ascb</AxisTag>
+ <Flags>0x0</Flags>
+ <MinValue>0.0</MinValue>
+ <DefaultValue>0.0</DefaultValue>
+ <MaxValue>1.0</MaxValue>
+ <AxisNameID>256</AxisNameID>
+ </Axis>
+ <Axis>
+ <AxisTag>Ascc</AxisTag>
+ <Flags>0x0</Flags>
+ <MinValue>0.0</MinValue>
+ <DefaultValue>0.0</DefaultValue>
+ <MaxValue>1.0</MaxValue>
+ <AxisNameID>256</AxisNameID>
+ </Axis>
+ <Axis>
+ <AxisTag>Ascd</AxisTag>
+ <Flags>0x0</Flags>
+ <MinValue>0.0</MinValue>
+ <DefaultValue>0.0</DefaultValue>
+ <MaxValue>1.0</MaxValue>
+ <AxisNameID>256</AxisNameID>
+ </Axis>
+ <Axis>
+ <AxisTag>Asce</AxisTag>
+ <Flags>0x0</Flags>
+ <MinValue>0.0</MinValue>
+ <DefaultValue>0.0</DefaultValue>
+ <MaxValue>1.0</MaxValue>
+ <AxisNameID>256</AxisNameID>
+ </Axis>
+ <Axis>
+ <AxisTag>Ascf</AxisTag>
+ <Flags>0x0</Flags>
+ <MinValue>0.0</MinValue>
+ <DefaultValue>0.0</DefaultValue>
+ <MaxValue>1.0</MaxValue>
+ <AxisNameID>256</AxisNameID>
+ </Axis>
+ <Axis>
+ <AxisTag>Ascg</AxisTag>
+ <Flags>0x0</Flags>
+ <MinValue>0.0</MinValue>
+ <DefaultValue>0.0</DefaultValue>
+ <MaxValue>1.0</MaxValue>
+ <AxisNameID>256</AxisNameID>
+ </Axis>
+ <Axis>
+ <AxisTag>Asch</AxisTag>
+ <Flags>0x0</Flags>
+ <MinValue>0.0</MinValue>
+ <DefaultValue>0.0</DefaultValue>
+ <MaxValue>1.0</MaxValue>
+ <AxisNameID>256</AxisNameID>
+ </Axis>
+ <Axis>
+ <AxisTag>Asci</AxisTag>
+ <Flags>0x0</Flags>
+ <MinValue>0.0</MinValue>
+ <DefaultValue>0.0</DefaultValue>
+ <MaxValue>1.0</MaxValue>
+ <AxisNameID>256</AxisNameID>
+ </Axis>
+ <Axis>
+ <AxisTag>Ascj</AxisTag>
+ <Flags>0x0</Flags>
+ <MinValue>0.0</MinValue>
+ <DefaultValue>0.0</DefaultValue>
+ <MaxValue>1.0</MaxValue>
+ <AxisNameID>256</AxisNameID>
+ </Axis>
+ <Axis>
+ <AxisTag>Asck</AxisTag>
+ <Flags>0x0</Flags>
+ <MinValue>0.0</MinValue>
+ <DefaultValue>0.0</DefaultValue>
+ <MaxValue>1.0</MaxValue>
+ <AxisNameID>256</AxisNameID>
+ </Axis>
+ <Axis>
+ <AxisTag>Ascl</AxisTag>
+ <Flags>0x0</Flags>
+ <MinValue>0.0</MinValue>
+ <DefaultValue>0.0</DefaultValue>
+ <MaxValue>1.0</MaxValue>
+ <AxisNameID>256</AxisNameID>
+ </Axis>
+ <Axis>
+ <AxisTag>Ascm</AxisTag>
+ <Flags>0x0</Flags>
+ <MinValue>0.0</MinValue>
+ <DefaultValue>0.0</DefaultValue>
+ <MaxValue>1.0</MaxValue>
+ <AxisNameID>256</AxisNameID>
+ </Axis>
+ <Axis>
+ <AxisTag>Ascn</AxisTag>
+ <Flags>0x0</Flags>
+ <MinValue>0.0</MinValue>
+ <DefaultValue>0.0</DefaultValue>
+ <MaxValue>1.0</MaxValue>
+ <AxisNameID>256</AxisNameID>
+ </Axis>
+ <Axis>
+ <AxisTag>Asco</AxisTag>
+ <Flags>0x0</Flags>
+ <MinValue>0.0</MinValue>
+ <DefaultValue>0.0</DefaultValue>
+ <MaxValue>1.0</MaxValue>
+ <AxisNameID>256</AxisNameID>
+ </Axis>
+ <Axis>
+ <AxisTag>Ascp</AxisTag>
+ <Flags>0x0</Flags>
+ <MinValue>0.0</MinValue>
+ <DefaultValue>0.0</DefaultValue>
+ <MaxValue>1.0</MaxValue>
+ <AxisNameID>256</AxisNameID>
+ </Axis>
+ <Axis>
+ <AxisTag>Ascq</AxisTag>
+ <Flags>0x0</Flags>
+ <MinValue>0.0</MinValue>
+ <DefaultValue>0.0</DefaultValue>
+ <MaxValue>1.0</MaxValue>
+ <AxisNameID>256</AxisNameID>
+ </Axis>
+ <Axis>
+ <AxisTag>Ascr</AxisTag>
+ <Flags>0x0</Flags>
+ <MinValue>0.0</MinValue>
+ <DefaultValue>0.0</DefaultValue>
+ <MaxValue>1.0</MaxValue>
+ <AxisNameID>256</AxisNameID>
+ </Axis>
+ <Axis>
+ <AxisTag>Ascs</AxisTag>
+ <Flags>0x0</Flags>
+ <MinValue>0.0</MinValue>
+ <DefaultValue>0.0</DefaultValue>
+ <MaxValue>1.0</MaxValue>
+ <AxisNameID>256</AxisNameID>
+ </Axis>
+ <Axis>
+ <AxisTag>Asct</AxisTag>
+ <Flags>0x0</Flags>
+ <MinValue>0.0</MinValue>
+ <DefaultValue>0.0</DefaultValue>
+ <MaxValue>1.0</MaxValue>
+ <AxisNameID>256</AxisNameID>
+ </Axis>
+ <Axis>
+ <AxisTag>Ascu</AxisTag>
+ <Flags>0x0</Flags>
+ <MinValue>0.0</MinValue>
+ <DefaultValue>0.0</DefaultValue>
+ <MaxValue>1.0</MaxValue>
+ <AxisNameID>256</AxisNameID>
+ </Axis>
+ <Axis>
+ <AxisTag>Ascv</AxisTag>
+ <Flags>0x0</Flags>
+ <MinValue>0.0</MinValue>
+ <DefaultValue>0.0</DefaultValue>
+ <MaxValue>1.0</MaxValue>
+ <AxisNameID>256</AxisNameID>
+ </Axis>
+ <Axis>
+ <AxisTag>Ascw</AxisTag>
+ <Flags>0x0</Flags>
+ <MinValue>0.0</MinValue>
+ <DefaultValue>0.0</DefaultValue>
+ <MaxValue>1.0</MaxValue>
+ <AxisNameID>256</AxisNameID>
+ </Axis>
+ <Axis>
+ <AxisTag>Ascx</AxisTag>
+ <Flags>0x0</Flags>
+ <MinValue>0.0</MinValue>
+ <DefaultValue>0.0</DefaultValue>
+ <MaxValue>1.0</MaxValue>
+ <AxisNameID>256</AxisNameID>
+ </Axis>
+ <Axis>
+ <AxisTag>Ascy</AxisTag>
+ <Flags>0x0</Flags>
+ <MinValue>0.0</MinValue>
+ <DefaultValue>0.0</DefaultValue>
+ <MaxValue>1.0</MaxValue>
+ <AxisNameID>256</AxisNameID>
+ </Axis>
+ <Axis>
+ <AxisTag>Ascz</AxisTag>
+ <Flags>0x0</Flags>
+ <MinValue>0.0</MinValue>
+ <DefaultValue>0.0</DefaultValue>
+ <MaxValue>1.0</MaxValue>
+ <AxisNameID>256</AxisNameID>
+ </Axis>
+ </fvar>
+
+ <HVAR>
+ <Version value="0x00010000"/>
+ <VarStore Format="1">
+ <Format value="1" />
+ <VarRegionList>
+ <Region index="0">
+ <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ </Region>
+ <Region index="1">
+ <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="2"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ </Region>
+ <Region index="2">
+ <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="3"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ </Region>
+ <Region index="3">
+ <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="4"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ </Region>
+ <Region index="4">
+ <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="5"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ </Region>
+ <Region index="5">
+ <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="6"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ </Region>
+ <Region index="6">
+ <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="7"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ </Region>
+ <Region index="7">
+ <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="8"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ </Region>
+ <Region index="8">
+ <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="9"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ </Region>
+ <Region index="9">
+ <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="10"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ </Region>
+ <Region index="10">
+ <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="11"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ </Region>
+ <Region index="11">
+ <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="12"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ </Region>
+ <Region index="12">
+ <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="13"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ </Region>
+ <Region index="13">
+ <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="14"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ </Region>
+ <Region index="14">
+ <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="15"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ </Region>
+ <Region index="15">
+ <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="16"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ </Region>
+ <Region index="16">
+ <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="17"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ </Region>
+ <Region index="17">
+ <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="18"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ </Region>
+ <Region index="18">
+ <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="19"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ </Region>
+ <Region index="19">
+ <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="20"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ </Region>
+ <Region index="20">
+ <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="21"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ </Region>
+ <Region index="21">
+ <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="22"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ </Region>
+ <Region index="22">
+ <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="23"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ </Region>
+ <Region index="23">
+ <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="24"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ </Region>
+ <Region index="24">
+ <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="25"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ </Region>
+ <Region index="25">
+ <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="26"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ </Region>
+ <Region index="26">
+ <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="27"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+ </Region>
+ </VarRegionList>
+ <VarData index="0">
+ <NumShorts value="0" />
+ <VarRegionIndex index="0" value="0" />
+ <VarRegionIndex index="1" value="1" />
+ <VarRegionIndex index="2" value="2" />
+ <VarRegionIndex index="3" value="3" />
+ <VarRegionIndex index="4" value="4" />
+ <VarRegionIndex index="5" value="5" />
+ <VarRegionIndex index="6" value="6" />
+ <VarRegionIndex index="7" value="7" />
+ <VarRegionIndex index="8" value="8" />
+ <VarRegionIndex index="9" value="9" />
+ <VarRegionIndex index="10" value="10" />
+ <VarRegionIndex index="11" value="11" />
+ <VarRegionIndex index="12" value="12" />
+ <VarRegionIndex index="13" value="13" />
+ <VarRegionIndex index="14" value="14" />
+ <VarRegionIndex index="15" value="15" />
+ <VarRegionIndex index="16" value="16" />
+ <VarRegionIndex index="17" value="17" />
+ <VarRegionIndex index="18" value="18" />
+ <VarRegionIndex index="19" value="19" />
+ <VarRegionIndex index="20" value="20" />
+ <VarRegionIndex index="21" value="21" />
+ <VarRegionIndex index="22" value="22" />
+ <VarRegionIndex index="23" value="23" />
+ <VarRegionIndex index="24" value="24" />
+ <VarRegionIndex index="25" value="25" />
+ <VarRegionIndex index="26" value="26" />
+ <Item index="0" value="[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]" />
+ <Item index="1" value="[0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]" />
+ <Item index="2" value="[0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]" />
+ <Item index="3" value="[0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]" />
+ <Item index="4" value="[0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]" />
+ <Item index="5" value="[0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]" />
+ <Item index="6" value="[0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]" />
+ <Item index="7" value="[0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]" />
+ <Item index="8" value="[0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]" />
+ <Item index="9" value="[0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]" />
+ <Item index="10" value="[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]" />
+ <Item index="11" value="[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]" />
+ <Item index="12" value="[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]" />
+ <Item index="13" value="[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]" />
+ <Item index="14" value="[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]" />
+ <Item index="15" value="[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]" />
+ <Item index="16" value="[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]" />
+ <Item index="17" value="[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0]" />
+ <Item index="18" value="[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0]" />
+ <Item index="19" value="[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0]" />
+ <Item index="20" value="[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0]" />
+ <Item index="21" value="[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0]" />
+ <Item index="22" value="[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0]" />
+ <Item index="23" value="[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0]" />
+ <Item index="24" value="[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0]" />
+ <Item index="25" value="[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0]" />
+ <Item index="26" value="[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100]" />
+ </VarData>
+ </VarStore>
+ <AdvWidthMap>
+ <Map index="0" outer="0" inner="0" />
+ <Map index="1" outer="0" inner="1" />
+ <Map index="2" outer="0" inner="2" />
+ <Map index="3" outer="0" inner="3" />
+ <Map index="4" outer="0" inner="4" />
+ <Map index="5" outer="0" inner="5" />
+ <Map index="6" outer="0" inner="6" />
+ <Map index="7" outer="0" inner="7" />
+ <Map index="8" outer="0" inner="8" />
+ <Map index="9" outer="0" inner="9" />
+ <Map index="10" outer="0" inner="10" />
+ <Map index="11" outer="0" inner="11" />
+ <Map index="12" outer="0" inner="12" />
+ <Map index="13" outer="0" inner="13" />
+ <Map index="14" outer="0" inner="14" />
+ <Map index="15" outer="0" inner="15" />
+ <Map index="16" outer="0" inner="16" />
+ <Map index="17" outer="0" inner="17" />
+ <Map index="18" outer="0" inner="18" />
+ <Map index="19" outer="0" inner="19" />
+ <Map index="20" outer="0" inner="20" />
+ <Map index="21" outer="0" inner="21" />
+ <Map index="22" outer="0" inner="22" />
+ <Map index="23" outer="0" inner="23" />
+ <Map index="24" outer="0" inner="24" />
+ <Map index="25" outer="0" inner="25" />
+ <Map index="26" outer="0" inner="26" />
+ </AdvWidthMap>
+ </HVAR>
+
+ <gvar>
+ <version value="1" />
+ <reserved value="0" />
+ <glyphVariations glyph="a">
+ <tuple>
+ <coord axis="Asca" value="1.0" />
+ <delta pt="0" x="0" y="0" />
+ <delta pt="1" x="100" y="0" />
+ <delta pt="2" x="0" y="0" />
+ <!-- deltas for phantom points -->
+ <delta pt="3" x="0" y="0" /> <!-- (left, 0) -->
+ <delta pt="4" x="100" y="0" /> <!-- (right, 0) -->
+ <delta pt="5" x="0" y="0" /> <!-- (0, top) -->
+ <delta pt="6" x="0" y="0" /> <!-- (0, bottom) -->
+ </tuple>
+ </glyphVariations>
+ <glyphVariations glyph="b">
+ <tuple>
+ <coord axis="Ascb" value="1.0" />
+ <delta pt="0" x="0" y="0" />
+ <delta pt="1" x="100" y="0" />
+ <delta pt="2" x="0" y="0" />
+ <!-- deltas for phantom points -->
+ <delta pt="3" x="0" y="0" /> <!-- (left, 0) -->
+ <delta pt="4" x="100" y="0" /> <!-- (right, 0) -->
+ <delta pt="5" x="0" y="0" /> <!-- (0, top) -->
+ <delta pt="6" x="0" y="0" /> <!-- (0, bottom) -->
+ </tuple>
+ </glyphVariations>
+ <glyphVariations glyph="c">
+ <tuple>
+ <coord axis="Ascc" value="1.0" />
+ <delta pt="0" x="0" y="0" />
+ <delta pt="1" x="100" y="0" />
+ <delta pt="2" x="0" y="0" />
+ <!-- deltas for phantom points -->
+ <delta pt="3" x="0" y="0" /> <!-- (left, 0) -->
+ <delta pt="4" x="100" y="0" /> <!-- (right, 0) -->
+ <delta pt="5" x="0" y="0" /> <!-- (0, top) -->
+ <delta pt="6" x="0" y="0" /> <!-- (0, bottom) -->
+ </tuple>
+ </glyphVariations>
+ <glyphVariations glyph="d">
+ <tuple>
+ <coord axis="Ascd" value="1.0" />
+ <delta pt="0" x="0" y="0" />
+ <delta pt="1" x="100" y="0" />
+ <delta pt="2" x="0" y="0" />
+ <!-- deltas for phantom points -->
+ <delta pt="3" x="0" y="0" /> <!-- (left, 0) -->
+ <delta pt="4" x="100" y="0" /> <!-- (right, 0) -->
+ <delta pt="5" x="0" y="0" /> <!-- (0, top) -->
+ <delta pt="6" x="0" y="0" /> <!-- (0, bottom) -->
+ </tuple>
+ </glyphVariations>
+ <glyphVariations glyph="e">
+ <tuple>
+ <coord axis="Asce" value="1.0" />
+ <delta pt="0" x="0" y="0" />
+ <delta pt="1" x="100" y="0" />
+ <delta pt="2" x="0" y="0" />
+ <!-- deltas for phantom points -->
+ <delta pt="3" x="0" y="0" /> <!-- (left, 0) -->
+ <delta pt="4" x="100" y="0" /> <!-- (right, 0) -->
+ <delta pt="5" x="0" y="0" /> <!-- (0, top) -->
+ <delta pt="6" x="0" y="0" /> <!-- (0, bottom) -->
+ </tuple>
+ </glyphVariations>
+ <glyphVariations glyph="f">
+ <tuple>
+ <coord axis="Ascf" value="1.0" />
+ <delta pt="0" x="0" y="0" />
+ <delta pt="1" x="100" y="0" />
+ <delta pt="2" x="0" y="0" />
+ <!-- deltas for phantom points -->
+ <delta pt="3" x="0" y="0" /> <!-- (left, 0) -->
+ <delta pt="4" x="100" y="0" /> <!-- (right, 0) -->
+ <delta pt="5" x="0" y="0" /> <!-- (0, top) -->
+ <delta pt="6" x="0" y="0" /> <!-- (0, bottom) -->
+ </tuple>
+ </glyphVariations>
+ <glyphVariations glyph="g">
+ <tuple>
+ <coord axis="Ascg" value="1.0" />
+ <delta pt="0" x="0" y="0" />
+ <delta pt="1" x="100" y="0" />
+ <delta pt="2" x="0" y="0" />
+ <!-- deltas for phantom points -->
+ <delta pt="3" x="0" y="0" /> <!-- (left, 0) -->
+ <delta pt="4" x="100" y="0" /> <!-- (right, 0) -->
+ <delta pt="5" x="0" y="0" /> <!-- (0, top) -->
+ <delta pt="6" x="0" y="0" /> <!-- (0, bottom) -->
+ </tuple>
+ </glyphVariations>
+ <glyphVariations glyph="h">
+ <tuple>
+ <coord axis="Asch" value="1.0" />
+ <delta pt="0" x="0" y="0" />
+ <delta pt="1" x="100" y="0" />
+ <delta pt="2" x="0" y="0" />
+ <!-- deltas for phantom points -->
+ <delta pt="3" x="0" y="0" /> <!-- (left, 0) -->
+ <delta pt="4" x="100" y="0" /> <!-- (right, 0) -->
+ <delta pt="5" x="0" y="0" /> <!-- (0, top) -->
+ <delta pt="6" x="0" y="0" /> <!-- (0, bottom) -->
+ </tuple>
+ </glyphVariations>
+ <glyphVariations glyph="i">
+ <tuple>
+ <coord axis="Asci" value="1.0" />
+ <delta pt="0" x="0" y="0" />
+ <delta pt="1" x="100" y="0" />
+ <delta pt="2" x="0" y="0" />
+ <!-- deltas for phantom points -->
+ <delta pt="3" x="0" y="0" /> <!-- (left, 0) -->
+ <delta pt="4" x="100" y="0" /> <!-- (right, 0) -->
+ <delta pt="5" x="0" y="0" /> <!-- (0, top) -->
+ <delta pt="6" x="0" y="0" /> <!-- (0, bottom) -->
+ </tuple>
+ </glyphVariations>
+ <glyphVariations glyph="j">
+ <tuple>
+ <coord axis="Ascj" value="1.0" />
+ <delta pt="0" x="0" y="0" />
+ <delta pt="1" x="100" y="0" />
+ <delta pt="2" x="0" y="0" />
+ <!-- deltas for phantom points -->
+ <delta pt="3" x="0" y="0" /> <!-- (left, 0) -->
+ <delta pt="4" x="100" y="0" /> <!-- (right, 0) -->
+ <delta pt="5" x="0" y="0" /> <!-- (0, top) -->
+ <delta pt="6" x="0" y="0" /> <!-- (0, bottom) -->
+ </tuple>
+ </glyphVariations>
+ <glyphVariations glyph="k">
+ <tuple>
+ <coord axis="Asck" value="1.0" />
+ <delta pt="0" x="0" y="0" />
+ <delta pt="1" x="100" y="0" />
+ <delta pt="2" x="0" y="0" />
+ <!-- deltas for phantom points -->
+ <delta pt="3" x="0" y="0" /> <!-- (left, 0) -->
+ <delta pt="4" x="100" y="0" /> <!-- (right, 0) -->
+ <delta pt="5" x="0" y="0" /> <!-- (0, top) -->
+ <delta pt="6" x="0" y="0" /> <!-- (0, bottom) -->
+ </tuple>
+ </glyphVariations>
+ <glyphVariations glyph="l">
+ <tuple>
+ <coord axis="Ascl" value="1.0" />
+ <delta pt="0" x="0" y="0" />
+ <delta pt="1" x="100" y="0" />
+ <delta pt="2" x="0" y="0" />
+ <!-- deltas for phantom points -->
+ <delta pt="3" x="0" y="0" /> <!-- (left, 0) -->
+ <delta pt="4" x="100" y="0" /> <!-- (right, 0) -->
+ <delta pt="5" x="0" y="0" /> <!-- (0, top) -->
+ <delta pt="6" x="0" y="0" /> <!-- (0, bottom) -->
+ </tuple>
+ </glyphVariations>
+ <glyphVariations glyph="m">
+ <tuple>
+ <coord axis="Ascm" value="1.0" />
+ <delta pt="0" x="0" y="0" />
+ <delta pt="1" x="100" y="0" />
+ <delta pt="2" x="0" y="0" />
+ <!-- deltas for phantom points -->
+ <delta pt="3" x="0" y="0" /> <!-- (left, 0) -->
+ <delta pt="4" x="100" y="0" /> <!-- (right, 0) -->
+ <delta pt="5" x="0" y="0" /> <!-- (0, top) -->
+ <delta pt="6" x="0" y="0" /> <!-- (0, bottom) -->
+ </tuple>
+ </glyphVariations>
+ <glyphVariations glyph="n">
+ <tuple>
+ <coord axis="Ascn" value="1.0" />
+ <delta pt="0" x="0" y="0" />
+ <delta pt="1" x="100" y="0" />
+ <delta pt="2" x="0" y="0" />
+ <!-- deltas for phantom points -->
+ <delta pt="3" x="0" y="0" /> <!-- (left, 0) -->
+ <delta pt="4" x="100" y="0" /> <!-- (right, 0) -->
+ <delta pt="5" x="0" y="0" /> <!-- (0, top) -->
+ <delta pt="6" x="0" y="0" /> <!-- (0, bottom) -->
+ </tuple>
+ </glyphVariations>
+ <glyphVariations glyph="o">
+ <tuple>
+ <coord axis="Asco" value="1.0" />
+ <delta pt="0" x="0" y="0" />
+ <delta pt="1" x="100" y="0" />
+ <delta pt="2" x="0" y="0" />
+ <!-- deltas for phantom points -->
+ <delta pt="3" x="0" y="0" /> <!-- (left, 0) -->
+ <delta pt="4" x="100" y="0" /> <!-- (right, 0) -->
+ <delta pt="5" x="0" y="0" /> <!-- (0, top) -->
+ <delta pt="6" x="0" y="0" /> <!-- (0, bottom) -->
+ </tuple>
+ </glyphVariations>
+ <glyphVariations glyph="p">
+ <tuple>
+ <coord axis="Ascp" value="1.0" />
+ <delta pt="0" x="0" y="0" />
+ <delta pt="1" x="100" y="0" />
+ <delta pt="2" x="0" y="0" />
+ <!-- deltas for phantom points -->
+ <delta pt="3" x="0" y="0" /> <!-- (left, 0) -->
+ <delta pt="4" x="100" y="0" /> <!-- (right, 0) -->
+ <delta pt="5" x="0" y="0" /> <!-- (0, top) -->
+ <delta pt="6" x="0" y="0" /> <!-- (0, bottom) -->
+ </tuple>
+ </glyphVariations>
+ <glyphVariations glyph="q">
+ <tuple>
+ <coord axis="Ascq" value="1.0" />
+ <delta pt="0" x="0" y="0" />
+ <delta pt="1" x="100" y="0" />
+ <delta pt="2" x="0" y="0" />
+ <!-- deltas for phantom points -->
+ <delta pt="3" x="0" y="0" /> <!-- (left, 0) -->
+ <delta pt="4" x="100" y="0" /> <!-- (right, 0) -->
+ <delta pt="5" x="0" y="0" /> <!-- (0, top) -->
+ <delta pt="6" x="0" y="0" /> <!-- (0, bottom) -->
+ </tuple>
+ </glyphVariations>
+ <glyphVariations glyph="r">
+ <tuple>
+ <coord axis="Ascr" value="1.0" />
+ <delta pt="0" x="0" y="0" />
+ <delta pt="1" x="100" y="0" />
+ <delta pt="2" x="0" y="0" />
+ <!-- deltas for phantom points -->
+ <delta pt="3" x="0" y="0" /> <!-- (left, 0) -->
+ <delta pt="4" x="100" y="0" /> <!-- (right, 0) -->
+ <delta pt="5" x="0" y="0" /> <!-- (0, top) -->
+ <delta pt="6" x="0" y="0" /> <!-- (0, bottom) -->
+ </tuple>
+ </glyphVariations>
+ <glyphVariations glyph="s">
+ <tuple>
+ <coord axis="Ascs" value="1.0" />
+ <delta pt="0" x="0" y="0" />
+ <delta pt="1" x="100" y="0" />
+ <delta pt="2" x="0" y="0" />
+ <!-- deltas for phantom points -->
+ <delta pt="3" x="0" y="0" /> <!-- (left, 0) -->
+ <delta pt="4" x="100" y="0" /> <!-- (right, 0) -->
+ <delta pt="5" x="0" y="0" /> <!-- (0, top) -->
+ <delta pt="6" x="0" y="0" /> <!-- (0, bottom) -->
+ </tuple>
+ </glyphVariations>
+ <glyphVariations glyph="t">
+ <tuple>
+ <coord axis="Asct" value="1.0" />
+ <delta pt="0" x="0" y="0" />
+ <delta pt="1" x="100" y="0" />
+ <delta pt="2" x="0" y="0" />
+ <!-- deltas for phantom points -->
+ <delta pt="3" x="0" y="0" /> <!-- (left, 0) -->
+ <delta pt="4" x="100" y="0" /> <!-- (right, 0) -->
+ <delta pt="5" x="0" y="0" /> <!-- (0, top) -->
+ <delta pt="6" x="0" y="0" /> <!-- (0, bottom) -->
+ </tuple>
+ </glyphVariations>
+ <glyphVariations glyph="u">
+ <tuple>
+ <coord axis="Ascu" value="1.0" />
+ <delta pt="0" x="0" y="0" />
+ <delta pt="1" x="100" y="0" />
+ <delta pt="2" x="0" y="0" />
+ <!-- deltas for phantom points -->
+ <delta pt="3" x="0" y="0" /> <!-- (left, 0) -->
+ <delta pt="4" x="100" y="0" /> <!-- (right, 0) -->
+ <delta pt="5" x="0" y="0" /> <!-- (0, top) -->
+ <delta pt="6" x="0" y="0" /> <!-- (0, bottom) -->
+ </tuple>
+ </glyphVariations>
+ <glyphVariations glyph="v">
+ <tuple>
+ <coord axis="Ascv" value="1.0" />
+ <delta pt="0" x="0" y="0" />
+ <delta pt="1" x="100" y="0" />
+ <delta pt="2" x="0" y="0" />
+ <!-- deltas for phantom points -->
+ <delta pt="3" x="0" y="0" /> <!-- (left, 0) -->
+ <delta pt="4" x="100" y="0" /> <!-- (right, 0) -->
+ <delta pt="5" x="0" y="0" /> <!-- (0, top) -->
+ <delta pt="6" x="0" y="0" /> <!-- (0, bottom) -->
+ </tuple>
+ </glyphVariations>
+ <glyphVariations glyph="w">
+ <tuple>
+ <coord axis="Ascw" value="1.0" />
+ <delta pt="0" x="0" y="0" />
+ <delta pt="1" x="100" y="0" />
+ <delta pt="2" x="0" y="0" />
+ <!-- deltas for phantom points -->
+ <delta pt="3" x="0" y="0" /> <!-- (left, 0) -->
+ <delta pt="4" x="100" y="0" /> <!-- (right, 0) -->
+ <delta pt="5" x="0" y="0" /> <!-- (0, top) -->
+ <delta pt="6" x="0" y="0" /> <!-- (0, bottom) -->
+ </tuple>
+ </glyphVariations>
+ <glyphVariations glyph="x">
+ <tuple>
+ <coord axis="Ascx" value="1.0" />
+ <delta pt="0" x="0" y="0" />
+ <delta pt="1" x="100" y="0" />
+ <delta pt="2" x="0" y="0" />
+ <!-- deltas for phantom points -->
+ <delta pt="3" x="0" y="0" /> <!-- (left, 0) -->
+ <delta pt="4" x="100" y="0" /> <!-- (right, 0) -->
+ <delta pt="5" x="0" y="0" /> <!-- (0, top) -->
+ <delta pt="6" x="0" y="0" /> <!-- (0, bottom) -->
+ </tuple>
+ </glyphVariations>
+ <glyphVariations glyph="y">
+ <tuple>
+ <coord axis="Ascy" value="1.0" />
+ <delta pt="0" x="0" y="0" />
+ <delta pt="1" x="100" y="0" />
+ <delta pt="2" x="0" y="0" />
+ <!-- deltas for phantom points -->
+ <delta pt="3" x="0" y="0" /> <!-- (left, 0) -->
+ <delta pt="4" x="100" y="0" /> <!-- (right, 0) -->
+ <delta pt="5" x="0" y="0" /> <!-- (0, top) -->
+ <delta pt="6" x="0" y="0" /> <!-- (0, bottom) -->
+ </tuple>
+ </glyphVariations>
+ <glyphVariations glyph="z">
+ <tuple>
+ <coord axis="Ascz" value="1.0" />
+ <delta pt="0" x="0" y="0" />
+ <delta pt="1" x="100" y="0" />
+ <delta pt="2" x="0" y="0" />
+ <!-- deltas for phantom points -->
+ <delta pt="3" x="0" y="0" /> <!-- (left, 0) -->
+ <delta pt="4" x="100" y="0" /> <!-- (right, 0) -->
+ <delta pt="5" x="0" y="0" /> <!-- (0, top) -->
+ <delta pt="6" x="0" y="0" /> <!-- (0, bottom) -->
+ </tuple>
+ </glyphVariations>
+ </gvar>
+
+ <name>
+ <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+ Copyright (C) 2018 The Android Open Source Project
+ </namerecord>
+ <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+ Regular
+ </namerecord>
+ <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+ SampleFont-Regular
+ </namerecord>
+ <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ </namerecord>
+ <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+ http://www.apache.org/licenses/LICENSE-2.0
+ </namerecord>
+ <namerecord nameID="256" platformID="3" platEncID="1" langID="0x409">
+ 3 em signal
+ </namerecord>
+ </name>
+
+ <post>
+ <formatType value="3.0"/>
+ <italicAngle value="0.0"/>
+ <underlinePosition value="-75"/>
+ <underlineThickness value="50"/>
+ <isFixedPitch value="0"/>
+ <minMemType42 value="0"/>
+ <maxMemType42 value="0"/>
+ <minMemType1 value="0"/>
+ <maxMemType1 value="0"/>
+ </post>
+
+</ttFont>
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_a3em_weight100_upright.ttf b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_a3em_weight100_upright.ttf
new file mode 100644
index 0000000..f220eb3
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_a3em_weight100_upright.ttf
Binary files differ
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_a3em_weight100_upright.ttx b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_a3em_weight100_upright.ttx
new file mode 100644
index 0000000..ebedcb6
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_a3em_weight100_upright.ttx
@@ -0,0 +1,208 @@
+<?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.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+ <GlyphOrder>
+ <GlyphID id="0" name=".notdef"/>
+ <GlyphID id="1" name="1em"/>
+ <GlyphID id="2" name="3em"/>
+ </GlyphOrder>
+
+ <head>
+ <tableVersion value="1.0"/>
+ <fontRevision value="1.0"/>
+ <checkSumAdjustment value="0x640cdb2f"/>
+ <magicNumber value="0x5f0f3cf5"/>
+ <flags value="00000000 00000011"/>
+ <unitsPerEm value="1000"/>
+ <created value="Thu Feb 15 18:29:10 2018"/>
+ <macStyle value="00000000 00000000"/>
+ <lowestRecPPEM value="7"/>
+ <fontDirectionHint value="2"/>
+ <glyphDataFormat value="0"/>
+ </head>
+
+ <hhea>
+ <tableVersion value="0x00010000"/>
+ <ascent value="1000"/>
+ <descent value="-200"/>
+ <lineGap value="0"/>
+ <caretSlopeRise value="1"/>
+ <caretSlopeRun value="0"/>
+ <caretOffset value="0"/>
+ <reserved0 value="0"/>
+ <reserved1 value="0"/>
+ <reserved2 value="0"/>
+ <reserved3 value="0"/>
+ <metricDataFormat value="0"/>
+ </hhea>
+
+ <maxp>
+ <tableVersion value="0x10000"/>
+ <maxZones value="0"/>
+ <maxTwilightPoints value="0"/>
+ <maxStorage value="0"/>
+ <maxFunctionDefs value="0"/>
+ <maxInstructionDefs value="0"/>
+ <maxStackElements value="0"/>
+ <maxSizeOfInstructions value="0"/>
+ <maxComponentElements value="0"/>
+ </maxp>
+
+ <OS_2>
+ <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+ will be recalculated by the compiler -->
+ <version value="3"/>
+ <xAvgCharWidth value="594"/>
+ <usWeightClass value="100"/>
+ <usWidthClass value="5"/>
+ <fsType value="00000000 00001000"/>
+ <ySubscriptXSize value="650"/>
+ <ySubscriptYSize value="600"/>
+ <ySubscriptXOffset value="0"/>
+ <ySubscriptYOffset value="75"/>
+ <ySuperscriptXSize value="650"/>
+ <ySuperscriptYSize value="600"/>
+ <ySuperscriptXOffset value="0"/>
+ <ySuperscriptYOffset value="350"/>
+ <yStrikeoutSize value="50"/>
+ <yStrikeoutPosition value="300"/>
+ <sFamilyClass value="0"/>
+ <panose>
+ <bFamilyType value="0"/>
+ <bSerifStyle value="0"/>
+ <bWeight value="5"/>
+ <bProportion value="0"/>
+ <bContrast value="0"/>
+ <bStrokeVariation value="0"/>
+ <bArmStyle value="0"/>
+ <bLetterForm value="0"/>
+ <bMidline value="0"/>
+ <bXHeight value="0"/>
+ </panose>
+ <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+ <achVendID value="UKWN"/>
+ <fsSelection value="00000000 01000000"/>
+ <usFirstCharIndex value="32"/>
+ <usLastCharIndex value="122"/>
+ <sTypoAscender value="800"/>
+ <sTypoDescender value="-200"/>
+ <sTypoLineGap value="200"/>
+ <usWinAscent value="1000"/>
+ <usWinDescent value="200"/>
+ <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+ <sxHeight value="500"/>
+ <sCapHeight value="700"/>
+ <usDefaultChar value="0"/>
+ <usBreakChar value="32"/>
+ <usMaxContext value="0"/>
+ </OS_2>
+
+ <hmtx>
+ <mtx name=".notdef" width="500" lsb="93"/>
+ <mtx name="1em" width="1000" lsb="93"/>
+ <mtx name="3em" width="3000" lsb="93"/>
+ </hmtx>
+
+ <cmap>
+ <tableVersion version="0"/>
+ <cmap_format_4 platformID="3" platEncID="10" language="0">
+ <map code="0x0061" name="3em" /> <!-- a -->
+ <map code="0x0062" name="1em" /> <!-- b -->
+ <map code="0x0063" name="1em" /> <!-- c -->
+ <map code="0x0064" name="1em" /> <!-- d -->
+ <map code="0x0065" name="1em" /> <!-- e -->
+ <map code="0x0066" name="1em" /> <!-- f -->
+ <map code="0x0067" name="1em" /> <!-- g -->
+ <map code="0x0068" name="1em" /> <!-- h -->
+ <map code="0x0069" name="1em" /> <!-- i -->
+ <map code="0x006A" name="1em" /> <!-- j -->
+ <map code="0x006B" name="1em" /> <!-- k -->
+ <map code="0x006C" name="1em" /> <!-- l -->
+ <map code="0x006D" name="1em" /> <!-- m -->
+ <map code="0x006E" name="1em" /> <!-- n -->
+ <map code="0x006F" name="1em" /> <!-- o -->
+ <map code="0x0070" name="1em" /> <!-- p -->
+ <map code="0x0071" name="1em" /> <!-- q -->
+ <map code="0x0072" name="1em" /> <!-- r -->
+ <map code="0x0073" name="1em" /> <!-- s -->
+ <map code="0x0074" name="1em" /> <!-- t -->
+ <map code="0x0075" name="1em" /> <!-- u -->
+ <map code="0x0076" name="1em" /> <!-- v -->
+ <map code="0x0077" name="1em" /> <!-- w -->
+ <map code="0x0078" name="1em" /> <!-- x -->
+ <map code="0x0079" name="1em" /> <!-- y -->
+ <map code="0x007A" name="1em" /> <!-- z -->
+ </cmap_format_4>
+ </cmap>
+
+ <loca>
+ <!-- The 'loca' table will be calculated by the compiler -->
+ </loca>
+
+ <glyf>
+ <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ </glyf>
+
+ <name>
+ <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+ Copyright (C) 2018 The Android Open Source Project
+ </namerecord>
+ <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+ Regular
+ </namerecord>
+ <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+ SampleFont-Regular
+ </namerecord>
+ <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ </namerecord>
+ <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+ http://www.apache.org/licenses/LICENSE-2.0
+ </namerecord>
+ </name>
+
+ <post>
+ <formatType value="3.0"/>
+ <italicAngle value="0.0"/>
+ <underlinePosition value="-75"/>
+ <underlineThickness value="50"/>
+ <isFixedPitch value="0"/>
+ <minMemType42 value="0"/>
+ <maxMemType42 value="0"/>
+ <minMemType1 value="0"/>
+ <maxMemType1 value="0"/>
+ </post>
+
+</ttFont>
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_b3em_weight100_italic.ttf b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_b3em_weight100_italic.ttf
new file mode 100644
index 0000000..b9ffb84
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_b3em_weight100_italic.ttf
Binary files differ
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_b3em_weight100_italic.ttx b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_b3em_weight100_italic.ttx
new file mode 100644
index 0000000..def6a29
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_b3em_weight100_italic.ttx
@@ -0,0 +1,208 @@
+<?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.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+ <GlyphOrder>
+ <GlyphID id="0" name=".notdef"/>
+ <GlyphID id="1" name="1em"/>
+ <GlyphID id="2" name="3em"/>
+ </GlyphOrder>
+
+ <head>
+ <tableVersion value="1.0"/>
+ <fontRevision value="1.0"/>
+ <checkSumAdjustment value="0x640cdb2f"/>
+ <magicNumber value="0x5f0f3cf5"/>
+ <flags value="00000000 00000011"/>
+ <unitsPerEm value="1000"/>
+ <created value="Thu Feb 15 18:29:11 2018"/>
+ <macStyle value="00000000 00000010"/>
+ <lowestRecPPEM value="7"/>
+ <fontDirectionHint value="2"/>
+ <glyphDataFormat value="0"/>
+ </head>
+
+ <hhea>
+ <tableVersion value="0x00010000"/>
+ <ascent value="1000"/>
+ <descent value="-200"/>
+ <lineGap value="0"/>
+ <caretSlopeRise value="1"/>
+ <caretSlopeRun value="0"/>
+ <caretOffset value="0"/>
+ <reserved0 value="0"/>
+ <reserved1 value="0"/>
+ <reserved2 value="0"/>
+ <reserved3 value="0"/>
+ <metricDataFormat value="0"/>
+ </hhea>
+
+ <maxp>
+ <tableVersion value="0x10000"/>
+ <maxZones value="0"/>
+ <maxTwilightPoints value="0"/>
+ <maxStorage value="0"/>
+ <maxFunctionDefs value="0"/>
+ <maxInstructionDefs value="0"/>
+ <maxStackElements value="0"/>
+ <maxSizeOfInstructions value="0"/>
+ <maxComponentElements value="0"/>
+ </maxp>
+
+ <OS_2>
+ <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+ will be recalculated by the compiler -->
+ <version value="3"/>
+ <xAvgCharWidth value="594"/>
+ <usWeightClass value="100"/>
+ <usWidthClass value="5"/>
+ <fsType value="00000000 00001000"/>
+ <ySubscriptXSize value="650"/>
+ <ySubscriptYSize value="600"/>
+ <ySubscriptXOffset value="0"/>
+ <ySubscriptYOffset value="75"/>
+ <ySuperscriptXSize value="650"/>
+ <ySuperscriptYSize value="600"/>
+ <ySuperscriptXOffset value="0"/>
+ <ySuperscriptYOffset value="350"/>
+ <yStrikeoutSize value="50"/>
+ <yStrikeoutPosition value="300"/>
+ <sFamilyClass value="0"/>
+ <panose>
+ <bFamilyType value="0"/>
+ <bSerifStyle value="0"/>
+ <bWeight value="5"/>
+ <bProportion value="0"/>
+ <bContrast value="0"/>
+ <bStrokeVariation value="0"/>
+ <bArmStyle value="0"/>
+ <bLetterForm value="0"/>
+ <bMidline value="0"/>
+ <bXHeight value="0"/>
+ </panose>
+ <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+ <achVendID value="UKWN"/>
+ <fsSelection value="00000000 00000001"/>
+ <usFirstCharIndex value="32"/>
+ <usLastCharIndex value="122"/>
+ <sTypoAscender value="800"/>
+ <sTypoDescender value="-200"/>
+ <sTypoLineGap value="200"/>
+ <usWinAscent value="1000"/>
+ <usWinDescent value="200"/>
+ <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+ <sxHeight value="500"/>
+ <sCapHeight value="700"/>
+ <usDefaultChar value="0"/>
+ <usBreakChar value="32"/>
+ <usMaxContext value="0"/>
+ </OS_2>
+
+ <hmtx>
+ <mtx name=".notdef" width="500" lsb="93"/>
+ <mtx name="1em" width="1000" lsb="93"/>
+ <mtx name="3em" width="3000" lsb="93"/>
+ </hmtx>
+
+ <cmap>
+ <tableVersion version="0"/>
+ <cmap_format_4 platformID="3" platEncID="10" language="0">
+ <map code="0x0061" name="1em" /> <!-- a -->
+ <map code="0x0062" name="3em" /> <!-- b -->
+ <map code="0x0063" name="1em" /> <!-- c -->
+ <map code="0x0064" name="1em" /> <!-- d -->
+ <map code="0x0065" name="1em" /> <!-- e -->
+ <map code="0x0066" name="1em" /> <!-- f -->
+ <map code="0x0067" name="1em" /> <!-- g -->
+ <map code="0x0068" name="1em" /> <!-- h -->
+ <map code="0x0069" name="1em" /> <!-- i -->
+ <map code="0x006A" name="1em" /> <!-- j -->
+ <map code="0x006B" name="1em" /> <!-- k -->
+ <map code="0x006C" name="1em" /> <!-- l -->
+ <map code="0x006D" name="1em" /> <!-- m -->
+ <map code="0x006E" name="1em" /> <!-- n -->
+ <map code="0x006F" name="1em" /> <!-- o -->
+ <map code="0x0070" name="1em" /> <!-- p -->
+ <map code="0x0071" name="1em" /> <!-- q -->
+ <map code="0x0072" name="1em" /> <!-- r -->
+ <map code="0x0073" name="1em" /> <!-- s -->
+ <map code="0x0074" name="1em" /> <!-- t -->
+ <map code="0x0075" name="1em" /> <!-- u -->
+ <map code="0x0076" name="1em" /> <!-- v -->
+ <map code="0x0077" name="1em" /> <!-- w -->
+ <map code="0x0078" name="1em" /> <!-- x -->
+ <map code="0x0079" name="1em" /> <!-- y -->
+ <map code="0x007A" name="1em" /> <!-- z -->
+ </cmap_format_4>
+ </cmap>
+
+ <loca>
+ <!-- The 'loca' table will be calculated by the compiler -->
+ </loca>
+
+ <glyf>
+ <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ </glyf>
+
+ <name>
+ <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+ Copyright (C) 2018 The Android Open Source Project
+ </namerecord>
+ <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+ Regular
+ </namerecord>
+ <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+ SampleFont-Regular
+ </namerecord>
+ <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ </namerecord>
+ <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+ http://www.apache.org/licenses/LICENSE-2.0
+ </namerecord>
+ </name>
+
+ <post>
+ <formatType value="3.0"/>
+ <italicAngle value="0.0"/>
+ <underlinePosition value="-75"/>
+ <underlineThickness value="50"/>
+ <isFixedPitch value="0"/>
+ <minMemType42 value="0"/>
+ <maxMemType42 value="0"/>
+ <minMemType1 value="0"/>
+ <maxMemType1 value="0"/>
+ </post>
+
+</ttFont>
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_c3em_weight200_upright.ttf b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_c3em_weight200_upright.ttf
new file mode 100644
index 0000000..075b068
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_c3em_weight200_upright.ttf
Binary files differ
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_c3em_weight200_upright.ttx b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_c3em_weight200_upright.ttx
new file mode 100644
index 0000000..d201183
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_c3em_weight200_upright.ttx
@@ -0,0 +1,208 @@
+<?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.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+ <GlyphOrder>
+ <GlyphID id="0" name=".notdef"/>
+ <GlyphID id="1" name="1em"/>
+ <GlyphID id="2" name="3em"/>
+ </GlyphOrder>
+
+ <head>
+ <tableVersion value="1.0"/>
+ <fontRevision value="1.0"/>
+ <checkSumAdjustment value="0x640cdb2f"/>
+ <magicNumber value="0x5f0f3cf5"/>
+ <flags value="00000000 00000011"/>
+ <unitsPerEm value="1000"/>
+ <created value="Thu Feb 15 18:29:11 2018"/>
+ <macStyle value="00000000 00000000"/>
+ <lowestRecPPEM value="7"/>
+ <fontDirectionHint value="2"/>
+ <glyphDataFormat value="0"/>
+ </head>
+
+ <hhea>
+ <tableVersion value="0x00010000"/>
+ <ascent value="1000"/>
+ <descent value="-200"/>
+ <lineGap value="0"/>
+ <caretSlopeRise value="1"/>
+ <caretSlopeRun value="0"/>
+ <caretOffset value="0"/>
+ <reserved0 value="0"/>
+ <reserved1 value="0"/>
+ <reserved2 value="0"/>
+ <reserved3 value="0"/>
+ <metricDataFormat value="0"/>
+ </hhea>
+
+ <maxp>
+ <tableVersion value="0x10000"/>
+ <maxZones value="0"/>
+ <maxTwilightPoints value="0"/>
+ <maxStorage value="0"/>
+ <maxFunctionDefs value="0"/>
+ <maxInstructionDefs value="0"/>
+ <maxStackElements value="0"/>
+ <maxSizeOfInstructions value="0"/>
+ <maxComponentElements value="0"/>
+ </maxp>
+
+ <OS_2>
+ <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+ will be recalculated by the compiler -->
+ <version value="3"/>
+ <xAvgCharWidth value="594"/>
+ <usWeightClass value="200"/>
+ <usWidthClass value="5"/>
+ <fsType value="00000000 00001000"/>
+ <ySubscriptXSize value="650"/>
+ <ySubscriptYSize value="600"/>
+ <ySubscriptXOffset value="0"/>
+ <ySubscriptYOffset value="75"/>
+ <ySuperscriptXSize value="650"/>
+ <ySuperscriptYSize value="600"/>
+ <ySuperscriptXOffset value="0"/>
+ <ySuperscriptYOffset value="350"/>
+ <yStrikeoutSize value="50"/>
+ <yStrikeoutPosition value="300"/>
+ <sFamilyClass value="0"/>
+ <panose>
+ <bFamilyType value="0"/>
+ <bSerifStyle value="0"/>
+ <bWeight value="5"/>
+ <bProportion value="0"/>
+ <bContrast value="0"/>
+ <bStrokeVariation value="0"/>
+ <bArmStyle value="0"/>
+ <bLetterForm value="0"/>
+ <bMidline value="0"/>
+ <bXHeight value="0"/>
+ </panose>
+ <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+ <achVendID value="UKWN"/>
+ <fsSelection value="00000000 01000000"/>
+ <usFirstCharIndex value="32"/>
+ <usLastCharIndex value="122"/>
+ <sTypoAscender value="800"/>
+ <sTypoDescender value="-200"/>
+ <sTypoLineGap value="200"/>
+ <usWinAscent value="1000"/>
+ <usWinDescent value="200"/>
+ <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+ <sxHeight value="500"/>
+ <sCapHeight value="700"/>
+ <usDefaultChar value="0"/>
+ <usBreakChar value="32"/>
+ <usMaxContext value="0"/>
+ </OS_2>
+
+ <hmtx>
+ <mtx name=".notdef" width="500" lsb="93"/>
+ <mtx name="1em" width="1000" lsb="93"/>
+ <mtx name="3em" width="3000" lsb="93"/>
+ </hmtx>
+
+ <cmap>
+ <tableVersion version="0"/>
+ <cmap_format_4 platformID="3" platEncID="10" language="0">
+ <map code="0x0061" name="1em" /> <!-- a -->
+ <map code="0x0062" name="1em" /> <!-- b -->
+ <map code="0x0063" name="3em" /> <!-- c -->
+ <map code="0x0064" name="1em" /> <!-- d -->
+ <map code="0x0065" name="1em" /> <!-- e -->
+ <map code="0x0066" name="1em" /> <!-- f -->
+ <map code="0x0067" name="1em" /> <!-- g -->
+ <map code="0x0068" name="1em" /> <!-- h -->
+ <map code="0x0069" name="1em" /> <!-- i -->
+ <map code="0x006A" name="1em" /> <!-- j -->
+ <map code="0x006B" name="1em" /> <!-- k -->
+ <map code="0x006C" name="1em" /> <!-- l -->
+ <map code="0x006D" name="1em" /> <!-- m -->
+ <map code="0x006E" name="1em" /> <!-- n -->
+ <map code="0x006F" name="1em" /> <!-- o -->
+ <map code="0x0070" name="1em" /> <!-- p -->
+ <map code="0x0071" name="1em" /> <!-- q -->
+ <map code="0x0072" name="1em" /> <!-- r -->
+ <map code="0x0073" name="1em" /> <!-- s -->
+ <map code="0x0074" name="1em" /> <!-- t -->
+ <map code="0x0075" name="1em" /> <!-- u -->
+ <map code="0x0076" name="1em" /> <!-- v -->
+ <map code="0x0077" name="1em" /> <!-- w -->
+ <map code="0x0078" name="1em" /> <!-- x -->
+ <map code="0x0079" name="1em" /> <!-- y -->
+ <map code="0x007A" name="1em" /> <!-- z -->
+ </cmap_format_4>
+ </cmap>
+
+ <loca>
+ <!-- The 'loca' table will be calculated by the compiler -->
+ </loca>
+
+ <glyf>
+ <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ </glyf>
+
+ <name>
+ <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+ Copyright (C) 2018 The Android Open Source Project
+ </namerecord>
+ <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+ Regular
+ </namerecord>
+ <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+ SampleFont-Regular
+ </namerecord>
+ <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ </namerecord>
+ <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+ http://www.apache.org/licenses/LICENSE-2.0
+ </namerecord>
+ </name>
+
+ <post>
+ <formatType value="3.0"/>
+ <italicAngle value="0.0"/>
+ <underlinePosition value="-75"/>
+ <underlineThickness value="50"/>
+ <isFixedPitch value="0"/>
+ <minMemType42 value="0"/>
+ <maxMemType42 value="0"/>
+ <minMemType1 value="0"/>
+ <maxMemType1 value="0"/>
+ </post>
+
+</ttFont>
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_d3em_weight200_italic.ttf b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_d3em_weight200_italic.ttf
new file mode 100644
index 0000000..5b47f0d
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_d3em_weight200_italic.ttf
Binary files differ
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_d3em_weight200_italic.ttx b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_d3em_weight200_italic.ttx
new file mode 100644
index 0000000..7c801a0
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_d3em_weight200_italic.ttx
@@ -0,0 +1,208 @@
+<?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.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+ <GlyphOrder>
+ <GlyphID id="0" name=".notdef"/>
+ <GlyphID id="1" name="1em"/>
+ <GlyphID id="2" name="3em"/>
+ </GlyphOrder>
+
+ <head>
+ <tableVersion value="1.0"/>
+ <fontRevision value="1.0"/>
+ <checkSumAdjustment value="0x640cdb2f"/>
+ <magicNumber value="0x5f0f3cf5"/>
+ <flags value="00000000 00000011"/>
+ <unitsPerEm value="1000"/>
+ <created value="Thu Feb 15 18:29:11 2018"/>
+ <macStyle value="00000000 00000010"/>
+ <lowestRecPPEM value="7"/>
+ <fontDirectionHint value="2"/>
+ <glyphDataFormat value="0"/>
+ </head>
+
+ <hhea>
+ <tableVersion value="0x00010000"/>
+ <ascent value="1000"/>
+ <descent value="-200"/>
+ <lineGap value="0"/>
+ <caretSlopeRise value="1"/>
+ <caretSlopeRun value="0"/>
+ <caretOffset value="0"/>
+ <reserved0 value="0"/>
+ <reserved1 value="0"/>
+ <reserved2 value="0"/>
+ <reserved3 value="0"/>
+ <metricDataFormat value="0"/>
+ </hhea>
+
+ <maxp>
+ <tableVersion value="0x10000"/>
+ <maxZones value="0"/>
+ <maxTwilightPoints value="0"/>
+ <maxStorage value="0"/>
+ <maxFunctionDefs value="0"/>
+ <maxInstructionDefs value="0"/>
+ <maxStackElements value="0"/>
+ <maxSizeOfInstructions value="0"/>
+ <maxComponentElements value="0"/>
+ </maxp>
+
+ <OS_2>
+ <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+ will be recalculated by the compiler -->
+ <version value="3"/>
+ <xAvgCharWidth value="594"/>
+ <usWeightClass value="200"/>
+ <usWidthClass value="5"/>
+ <fsType value="00000000 00001000"/>
+ <ySubscriptXSize value="650"/>
+ <ySubscriptYSize value="600"/>
+ <ySubscriptXOffset value="0"/>
+ <ySubscriptYOffset value="75"/>
+ <ySuperscriptXSize value="650"/>
+ <ySuperscriptYSize value="600"/>
+ <ySuperscriptXOffset value="0"/>
+ <ySuperscriptYOffset value="350"/>
+ <yStrikeoutSize value="50"/>
+ <yStrikeoutPosition value="300"/>
+ <sFamilyClass value="0"/>
+ <panose>
+ <bFamilyType value="0"/>
+ <bSerifStyle value="0"/>
+ <bWeight value="5"/>
+ <bProportion value="0"/>
+ <bContrast value="0"/>
+ <bStrokeVariation value="0"/>
+ <bArmStyle value="0"/>
+ <bLetterForm value="0"/>
+ <bMidline value="0"/>
+ <bXHeight value="0"/>
+ </panose>
+ <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+ <achVendID value="UKWN"/>
+ <fsSelection value="00000000 00000001"/>
+ <usFirstCharIndex value="32"/>
+ <usLastCharIndex value="122"/>
+ <sTypoAscender value="800"/>
+ <sTypoDescender value="-200"/>
+ <sTypoLineGap value="200"/>
+ <usWinAscent value="1000"/>
+ <usWinDescent value="200"/>
+ <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+ <sxHeight value="500"/>
+ <sCapHeight value="700"/>
+ <usDefaultChar value="0"/>
+ <usBreakChar value="32"/>
+ <usMaxContext value="0"/>
+ </OS_2>
+
+ <hmtx>
+ <mtx name=".notdef" width="500" lsb="93"/>
+ <mtx name="1em" width="1000" lsb="93"/>
+ <mtx name="3em" width="3000" lsb="93"/>
+ </hmtx>
+
+ <cmap>
+ <tableVersion version="0"/>
+ <cmap_format_4 platformID="3" platEncID="10" language="0">
+ <map code="0x0061" name="1em" /> <!-- a -->
+ <map code="0x0062" name="1em" /> <!-- b -->
+ <map code="0x0063" name="1em" /> <!-- c -->
+ <map code="0x0064" name="3em" /> <!-- d -->
+ <map code="0x0065" name="1em" /> <!-- e -->
+ <map code="0x0066" name="1em" /> <!-- f -->
+ <map code="0x0067" name="1em" /> <!-- g -->
+ <map code="0x0068" name="1em" /> <!-- h -->
+ <map code="0x0069" name="1em" /> <!-- i -->
+ <map code="0x006A" name="1em" /> <!-- j -->
+ <map code="0x006B" name="1em" /> <!-- k -->
+ <map code="0x006C" name="1em" /> <!-- l -->
+ <map code="0x006D" name="1em" /> <!-- m -->
+ <map code="0x006E" name="1em" /> <!-- n -->
+ <map code="0x006F" name="1em" /> <!-- o -->
+ <map code="0x0070" name="1em" /> <!-- p -->
+ <map code="0x0071" name="1em" /> <!-- q -->
+ <map code="0x0072" name="1em" /> <!-- r -->
+ <map code="0x0073" name="1em" /> <!-- s -->
+ <map code="0x0074" name="1em" /> <!-- t -->
+ <map code="0x0075" name="1em" /> <!-- u -->
+ <map code="0x0076" name="1em" /> <!-- v -->
+ <map code="0x0077" name="1em" /> <!-- w -->
+ <map code="0x0078" name="1em" /> <!-- x -->
+ <map code="0x0079" name="1em" /> <!-- y -->
+ <map code="0x007A" name="1em" /> <!-- z -->
+ </cmap_format_4>
+ </cmap>
+
+ <loca>
+ <!-- The 'loca' table will be calculated by the compiler -->
+ </loca>
+
+ <glyf>
+ <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ </glyf>
+
+ <name>
+ <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+ Copyright (C) 2018 The Android Open Source Project
+ </namerecord>
+ <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+ Regular
+ </namerecord>
+ <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+ SampleFont-Regular
+ </namerecord>
+ <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ </namerecord>
+ <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+ http://www.apache.org/licenses/LICENSE-2.0
+ </namerecord>
+ </name>
+
+ <post>
+ <formatType value="3.0"/>
+ <italicAngle value="0.0"/>
+ <underlinePosition value="-75"/>
+ <underlineThickness value="50"/>
+ <isFixedPitch value="0"/>
+ <minMemType42 value="0"/>
+ <maxMemType42 value="0"/>
+ <minMemType1 value="0"/>
+ <maxMemType1 value="0"/>
+ </post>
+
+</ttFont>
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_e3em_weight300_upright.ttf b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_e3em_weight300_upright.ttf
new file mode 100644
index 0000000..3e9133b
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_e3em_weight300_upright.ttf
Binary files differ
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_e3em_weight300_upright.ttx b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_e3em_weight300_upright.ttx
new file mode 100644
index 0000000..acb2006
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_e3em_weight300_upright.ttx
@@ -0,0 +1,208 @@
+<?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.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+ <GlyphOrder>
+ <GlyphID id="0" name=".notdef"/>
+ <GlyphID id="1" name="1em"/>
+ <GlyphID id="2" name="3em"/>
+ </GlyphOrder>
+
+ <head>
+ <tableVersion value="1.0"/>
+ <fontRevision value="1.0"/>
+ <checkSumAdjustment value="0x640cdb2f"/>
+ <magicNumber value="0x5f0f3cf5"/>
+ <flags value="00000000 00000011"/>
+ <unitsPerEm value="1000"/>
+ <created value="Thu Feb 15 18:29:11 2018"/>
+ <macStyle value="00000000 00000000"/>
+ <lowestRecPPEM value="7"/>
+ <fontDirectionHint value="2"/>
+ <glyphDataFormat value="0"/>
+ </head>
+
+ <hhea>
+ <tableVersion value="0x00010000"/>
+ <ascent value="1000"/>
+ <descent value="-200"/>
+ <lineGap value="0"/>
+ <caretSlopeRise value="1"/>
+ <caretSlopeRun value="0"/>
+ <caretOffset value="0"/>
+ <reserved0 value="0"/>
+ <reserved1 value="0"/>
+ <reserved2 value="0"/>
+ <reserved3 value="0"/>
+ <metricDataFormat value="0"/>
+ </hhea>
+
+ <maxp>
+ <tableVersion value="0x10000"/>
+ <maxZones value="0"/>
+ <maxTwilightPoints value="0"/>
+ <maxStorage value="0"/>
+ <maxFunctionDefs value="0"/>
+ <maxInstructionDefs value="0"/>
+ <maxStackElements value="0"/>
+ <maxSizeOfInstructions value="0"/>
+ <maxComponentElements value="0"/>
+ </maxp>
+
+ <OS_2>
+ <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+ will be recalculated by the compiler -->
+ <version value="3"/>
+ <xAvgCharWidth value="594"/>
+ <usWeightClass value="300"/>
+ <usWidthClass value="5"/>
+ <fsType value="00000000 00001000"/>
+ <ySubscriptXSize value="650"/>
+ <ySubscriptYSize value="600"/>
+ <ySubscriptXOffset value="0"/>
+ <ySubscriptYOffset value="75"/>
+ <ySuperscriptXSize value="650"/>
+ <ySuperscriptYSize value="600"/>
+ <ySuperscriptXOffset value="0"/>
+ <ySuperscriptYOffset value="350"/>
+ <yStrikeoutSize value="50"/>
+ <yStrikeoutPosition value="300"/>
+ <sFamilyClass value="0"/>
+ <panose>
+ <bFamilyType value="0"/>
+ <bSerifStyle value="0"/>
+ <bWeight value="5"/>
+ <bProportion value="0"/>
+ <bContrast value="0"/>
+ <bStrokeVariation value="0"/>
+ <bArmStyle value="0"/>
+ <bLetterForm value="0"/>
+ <bMidline value="0"/>
+ <bXHeight value="0"/>
+ </panose>
+ <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+ <achVendID value="UKWN"/>
+ <fsSelection value="00000000 01000000"/>
+ <usFirstCharIndex value="32"/>
+ <usLastCharIndex value="122"/>
+ <sTypoAscender value="800"/>
+ <sTypoDescender value="-200"/>
+ <sTypoLineGap value="200"/>
+ <usWinAscent value="1000"/>
+ <usWinDescent value="200"/>
+ <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+ <sxHeight value="500"/>
+ <sCapHeight value="700"/>
+ <usDefaultChar value="0"/>
+ <usBreakChar value="32"/>
+ <usMaxContext value="0"/>
+ </OS_2>
+
+ <hmtx>
+ <mtx name=".notdef" width="500" lsb="93"/>
+ <mtx name="1em" width="1000" lsb="93"/>
+ <mtx name="3em" width="3000" lsb="93"/>
+ </hmtx>
+
+ <cmap>
+ <tableVersion version="0"/>
+ <cmap_format_4 platformID="3" platEncID="10" language="0">
+ <map code="0x0061" name="1em" /> <!-- a -->
+ <map code="0x0062" name="1em" /> <!-- b -->
+ <map code="0x0063" name="1em" /> <!-- c -->
+ <map code="0x0064" name="1em" /> <!-- d -->
+ <map code="0x0065" name="3em" /> <!-- e -->
+ <map code="0x0066" name="1em" /> <!-- f -->
+ <map code="0x0067" name="1em" /> <!-- g -->
+ <map code="0x0068" name="1em" /> <!-- h -->
+ <map code="0x0069" name="1em" /> <!-- i -->
+ <map code="0x006A" name="1em" /> <!-- j -->
+ <map code="0x006B" name="1em" /> <!-- k -->
+ <map code="0x006C" name="1em" /> <!-- l -->
+ <map code="0x006D" name="1em" /> <!-- m -->
+ <map code="0x006E" name="1em" /> <!-- n -->
+ <map code="0x006F" name="1em" /> <!-- o -->
+ <map code="0x0070" name="1em" /> <!-- p -->
+ <map code="0x0071" name="1em" /> <!-- q -->
+ <map code="0x0072" name="1em" /> <!-- r -->
+ <map code="0x0073" name="1em" /> <!-- s -->
+ <map code="0x0074" name="1em" /> <!-- t -->
+ <map code="0x0075" name="1em" /> <!-- u -->
+ <map code="0x0076" name="1em" /> <!-- v -->
+ <map code="0x0077" name="1em" /> <!-- w -->
+ <map code="0x0078" name="1em" /> <!-- x -->
+ <map code="0x0079" name="1em" /> <!-- y -->
+ <map code="0x007A" name="1em" /> <!-- z -->
+ </cmap_format_4>
+ </cmap>
+
+ <loca>
+ <!-- The 'loca' table will be calculated by the compiler -->
+ </loca>
+
+ <glyf>
+ <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ </glyf>
+
+ <name>
+ <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+ Copyright (C) 2018 The Android Open Source Project
+ </namerecord>
+ <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+ Regular
+ </namerecord>
+ <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+ SampleFont-Regular
+ </namerecord>
+ <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ </namerecord>
+ <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+ http://www.apache.org/licenses/LICENSE-2.0
+ </namerecord>
+ </name>
+
+ <post>
+ <formatType value="3.0"/>
+ <italicAngle value="0.0"/>
+ <underlinePosition value="-75"/>
+ <underlineThickness value="50"/>
+ <isFixedPitch value="0"/>
+ <minMemType42 value="0"/>
+ <maxMemType42 value="0"/>
+ <minMemType1 value="0"/>
+ <maxMemType1 value="0"/>
+ </post>
+
+</ttFont>
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_f3em_weight300_italic.ttf b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_f3em_weight300_italic.ttf
new file mode 100644
index 0000000..3ba3feb
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_f3em_weight300_italic.ttf
Binary files differ
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_f3em_weight300_italic.ttx b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_f3em_weight300_italic.ttx
new file mode 100644
index 0000000..452ff66
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_f3em_weight300_italic.ttx
@@ -0,0 +1,208 @@
+<?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.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+ <GlyphOrder>
+ <GlyphID id="0" name=".notdef"/>
+ <GlyphID id="1" name="1em"/>
+ <GlyphID id="2" name="3em"/>
+ </GlyphOrder>
+
+ <head>
+ <tableVersion value="1.0"/>
+ <fontRevision value="1.0"/>
+ <checkSumAdjustment value="0x640cdb2f"/>
+ <magicNumber value="0x5f0f3cf5"/>
+ <flags value="00000000 00000011"/>
+ <unitsPerEm value="1000"/>
+ <created value="Thu Feb 15 18:29:11 2018"/>
+ <macStyle value="00000000 00000010"/>
+ <lowestRecPPEM value="7"/>
+ <fontDirectionHint value="2"/>
+ <glyphDataFormat value="0"/>
+ </head>
+
+ <hhea>
+ <tableVersion value="0x00010000"/>
+ <ascent value="1000"/>
+ <descent value="-200"/>
+ <lineGap value="0"/>
+ <caretSlopeRise value="1"/>
+ <caretSlopeRun value="0"/>
+ <caretOffset value="0"/>
+ <reserved0 value="0"/>
+ <reserved1 value="0"/>
+ <reserved2 value="0"/>
+ <reserved3 value="0"/>
+ <metricDataFormat value="0"/>
+ </hhea>
+
+ <maxp>
+ <tableVersion value="0x10000"/>
+ <maxZones value="0"/>
+ <maxTwilightPoints value="0"/>
+ <maxStorage value="0"/>
+ <maxFunctionDefs value="0"/>
+ <maxInstructionDefs value="0"/>
+ <maxStackElements value="0"/>
+ <maxSizeOfInstructions value="0"/>
+ <maxComponentElements value="0"/>
+ </maxp>
+
+ <OS_2>
+ <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+ will be recalculated by the compiler -->
+ <version value="3"/>
+ <xAvgCharWidth value="594"/>
+ <usWeightClass value="300"/>
+ <usWidthClass value="5"/>
+ <fsType value="00000000 00001000"/>
+ <ySubscriptXSize value="650"/>
+ <ySubscriptYSize value="600"/>
+ <ySubscriptXOffset value="0"/>
+ <ySubscriptYOffset value="75"/>
+ <ySuperscriptXSize value="650"/>
+ <ySuperscriptYSize value="600"/>
+ <ySuperscriptXOffset value="0"/>
+ <ySuperscriptYOffset value="350"/>
+ <yStrikeoutSize value="50"/>
+ <yStrikeoutPosition value="300"/>
+ <sFamilyClass value="0"/>
+ <panose>
+ <bFamilyType value="0"/>
+ <bSerifStyle value="0"/>
+ <bWeight value="5"/>
+ <bProportion value="0"/>
+ <bContrast value="0"/>
+ <bStrokeVariation value="0"/>
+ <bArmStyle value="0"/>
+ <bLetterForm value="0"/>
+ <bMidline value="0"/>
+ <bXHeight value="0"/>
+ </panose>
+ <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+ <achVendID value="UKWN"/>
+ <fsSelection value="00000000 00000001"/>
+ <usFirstCharIndex value="32"/>
+ <usLastCharIndex value="122"/>
+ <sTypoAscender value="800"/>
+ <sTypoDescender value="-200"/>
+ <sTypoLineGap value="200"/>
+ <usWinAscent value="1000"/>
+ <usWinDescent value="200"/>
+ <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+ <sxHeight value="500"/>
+ <sCapHeight value="700"/>
+ <usDefaultChar value="0"/>
+ <usBreakChar value="32"/>
+ <usMaxContext value="0"/>
+ </OS_2>
+
+ <hmtx>
+ <mtx name=".notdef" width="500" lsb="93"/>
+ <mtx name="1em" width="1000" lsb="93"/>
+ <mtx name="3em" width="3000" lsb="93"/>
+ </hmtx>
+
+ <cmap>
+ <tableVersion version="0"/>
+ <cmap_format_4 platformID="3" platEncID="10" language="0">
+ <map code="0x0061" name="1em" /> <!-- a -->
+ <map code="0x0062" name="1em" /> <!-- b -->
+ <map code="0x0063" name="1em" /> <!-- c -->
+ <map code="0x0064" name="1em" /> <!-- d -->
+ <map code="0x0065" name="1em" /> <!-- e -->
+ <map code="0x0066" name="3em" /> <!-- f -->
+ <map code="0x0067" name="1em" /> <!-- g -->
+ <map code="0x0068" name="1em" /> <!-- h -->
+ <map code="0x0069" name="1em" /> <!-- i -->
+ <map code="0x006A" name="1em" /> <!-- j -->
+ <map code="0x006B" name="1em" /> <!-- k -->
+ <map code="0x006C" name="1em" /> <!-- l -->
+ <map code="0x006D" name="1em" /> <!-- m -->
+ <map code="0x006E" name="1em" /> <!-- n -->
+ <map code="0x006F" name="1em" /> <!-- o -->
+ <map code="0x0070" name="1em" /> <!-- p -->
+ <map code="0x0071" name="1em" /> <!-- q -->
+ <map code="0x0072" name="1em" /> <!-- r -->
+ <map code="0x0073" name="1em" /> <!-- s -->
+ <map code="0x0074" name="1em" /> <!-- t -->
+ <map code="0x0075" name="1em" /> <!-- u -->
+ <map code="0x0076" name="1em" /> <!-- v -->
+ <map code="0x0077" name="1em" /> <!-- w -->
+ <map code="0x0078" name="1em" /> <!-- x -->
+ <map code="0x0079" name="1em" /> <!-- y -->
+ <map code="0x007A" name="1em" /> <!-- z -->
+ </cmap_format_4>
+ </cmap>
+
+ <loca>
+ <!-- The 'loca' table will be calculated by the compiler -->
+ </loca>
+
+ <glyf>
+ <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ </glyf>
+
+ <name>
+ <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+ Copyright (C) 2018 The Android Open Source Project
+ </namerecord>
+ <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+ Regular
+ </namerecord>
+ <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+ SampleFont-Regular
+ </namerecord>
+ <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ </namerecord>
+ <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+ http://www.apache.org/licenses/LICENSE-2.0
+ </namerecord>
+ </name>
+
+ <post>
+ <formatType value="3.0"/>
+ <italicAngle value="0.0"/>
+ <underlinePosition value="-75"/>
+ <underlineThickness value="50"/>
+ <isFixedPitch value="0"/>
+ <minMemType42 value="0"/>
+ <maxMemType42 value="0"/>
+ <minMemType1 value="0"/>
+ <maxMemType1 value="0"/>
+ </post>
+
+</ttFont>
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_g3em_weight400_upright.ttf b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_g3em_weight400_upright.ttf
new file mode 100644
index 0000000..b3175a1
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_g3em_weight400_upright.ttf
Binary files differ
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_g3em_weight400_upright.ttx b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_g3em_weight400_upright.ttx
new file mode 100644
index 0000000..34a638f
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_g3em_weight400_upright.ttx
@@ -0,0 +1,208 @@
+<?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.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+ <GlyphOrder>
+ <GlyphID id="0" name=".notdef"/>
+ <GlyphID id="1" name="1em"/>
+ <GlyphID id="2" name="3em"/>
+ </GlyphOrder>
+
+ <head>
+ <tableVersion value="1.0"/>
+ <fontRevision value="1.0"/>
+ <checkSumAdjustment value="0x640cdb2f"/>
+ <magicNumber value="0x5f0f3cf5"/>
+ <flags value="00000000 00000011"/>
+ <unitsPerEm value="1000"/>
+ <created value="Thu Feb 15 18:29:11 2018"/>
+ <macStyle value="00000000 00000000"/>
+ <lowestRecPPEM value="7"/>
+ <fontDirectionHint value="2"/>
+ <glyphDataFormat value="0"/>
+ </head>
+
+ <hhea>
+ <tableVersion value="0x00010000"/>
+ <ascent value="1000"/>
+ <descent value="-200"/>
+ <lineGap value="0"/>
+ <caretSlopeRise value="1"/>
+ <caretSlopeRun value="0"/>
+ <caretOffset value="0"/>
+ <reserved0 value="0"/>
+ <reserved1 value="0"/>
+ <reserved2 value="0"/>
+ <reserved3 value="0"/>
+ <metricDataFormat value="0"/>
+ </hhea>
+
+ <maxp>
+ <tableVersion value="0x10000"/>
+ <maxZones value="0"/>
+ <maxTwilightPoints value="0"/>
+ <maxStorage value="0"/>
+ <maxFunctionDefs value="0"/>
+ <maxInstructionDefs value="0"/>
+ <maxStackElements value="0"/>
+ <maxSizeOfInstructions value="0"/>
+ <maxComponentElements value="0"/>
+ </maxp>
+
+ <OS_2>
+ <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+ will be recalculated by the compiler -->
+ <version value="3"/>
+ <xAvgCharWidth value="594"/>
+ <usWeightClass value="400"/>
+ <usWidthClass value="5"/>
+ <fsType value="00000000 00001000"/>
+ <ySubscriptXSize value="650"/>
+ <ySubscriptYSize value="600"/>
+ <ySubscriptXOffset value="0"/>
+ <ySubscriptYOffset value="75"/>
+ <ySuperscriptXSize value="650"/>
+ <ySuperscriptYSize value="600"/>
+ <ySuperscriptXOffset value="0"/>
+ <ySuperscriptYOffset value="350"/>
+ <yStrikeoutSize value="50"/>
+ <yStrikeoutPosition value="300"/>
+ <sFamilyClass value="0"/>
+ <panose>
+ <bFamilyType value="0"/>
+ <bSerifStyle value="0"/>
+ <bWeight value="5"/>
+ <bProportion value="0"/>
+ <bContrast value="0"/>
+ <bStrokeVariation value="0"/>
+ <bArmStyle value="0"/>
+ <bLetterForm value="0"/>
+ <bMidline value="0"/>
+ <bXHeight value="0"/>
+ </panose>
+ <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+ <achVendID value="UKWN"/>
+ <fsSelection value="00000000 01000000"/>
+ <usFirstCharIndex value="32"/>
+ <usLastCharIndex value="122"/>
+ <sTypoAscender value="800"/>
+ <sTypoDescender value="-200"/>
+ <sTypoLineGap value="200"/>
+ <usWinAscent value="1000"/>
+ <usWinDescent value="200"/>
+ <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+ <sxHeight value="500"/>
+ <sCapHeight value="700"/>
+ <usDefaultChar value="0"/>
+ <usBreakChar value="32"/>
+ <usMaxContext value="0"/>
+ </OS_2>
+
+ <hmtx>
+ <mtx name=".notdef" width="500" lsb="93"/>
+ <mtx name="1em" width="1000" lsb="93"/>
+ <mtx name="3em" width="3000" lsb="93"/>
+ </hmtx>
+
+ <cmap>
+ <tableVersion version="0"/>
+ <cmap_format_4 platformID="3" platEncID="10" language="0">
+ <map code="0x0061" name="1em" /> <!-- a -->
+ <map code="0x0062" name="1em" /> <!-- b -->
+ <map code="0x0063" name="1em" /> <!-- c -->
+ <map code="0x0064" name="1em" /> <!-- d -->
+ <map code="0x0065" name="1em" /> <!-- e -->
+ <map code="0x0066" name="1em" /> <!-- f -->
+ <map code="0x0067" name="3em" /> <!-- g -->
+ <map code="0x0068" name="1em" /> <!-- h -->
+ <map code="0x0069" name="1em" /> <!-- i -->
+ <map code="0x006A" name="1em" /> <!-- j -->
+ <map code="0x006B" name="1em" /> <!-- k -->
+ <map code="0x006C" name="1em" /> <!-- l -->
+ <map code="0x006D" name="1em" /> <!-- m -->
+ <map code="0x006E" name="1em" /> <!-- n -->
+ <map code="0x006F" name="1em" /> <!-- o -->
+ <map code="0x0070" name="1em" /> <!-- p -->
+ <map code="0x0071" name="1em" /> <!-- q -->
+ <map code="0x0072" name="1em" /> <!-- r -->
+ <map code="0x0073" name="1em" /> <!-- s -->
+ <map code="0x0074" name="1em" /> <!-- t -->
+ <map code="0x0075" name="1em" /> <!-- u -->
+ <map code="0x0076" name="1em" /> <!-- v -->
+ <map code="0x0077" name="1em" /> <!-- w -->
+ <map code="0x0078" name="1em" /> <!-- x -->
+ <map code="0x0079" name="1em" /> <!-- y -->
+ <map code="0x007A" name="1em" /> <!-- z -->
+ </cmap_format_4>
+ </cmap>
+
+ <loca>
+ <!-- The 'loca' table will be calculated by the compiler -->
+ </loca>
+
+ <glyf>
+ <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ </glyf>
+
+ <name>
+ <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+ Copyright (C) 2018 The Android Open Source Project
+ </namerecord>
+ <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+ Regular
+ </namerecord>
+ <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+ SampleFont-Regular
+ </namerecord>
+ <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ </namerecord>
+ <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+ http://www.apache.org/licenses/LICENSE-2.0
+ </namerecord>
+ </name>
+
+ <post>
+ <formatType value="3.0"/>
+ <italicAngle value="0.0"/>
+ <underlinePosition value="-75"/>
+ <underlineThickness value="50"/>
+ <isFixedPitch value="0"/>
+ <minMemType42 value="0"/>
+ <maxMemType42 value="0"/>
+ <minMemType1 value="0"/>
+ <maxMemType1 value="0"/>
+ </post>
+
+</ttFont>
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_h3em_weight400_italic.ttf b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_h3em_weight400_italic.ttf
new file mode 100644
index 0000000..099c4f1
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_h3em_weight400_italic.ttf
Binary files differ
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_h3em_weight400_italic.ttx b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_h3em_weight400_italic.ttx
new file mode 100644
index 0000000..b18af66
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_h3em_weight400_italic.ttx
@@ -0,0 +1,208 @@
+<?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.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+ <GlyphOrder>
+ <GlyphID id="0" name=".notdef"/>
+ <GlyphID id="1" name="1em"/>
+ <GlyphID id="2" name="3em"/>
+ </GlyphOrder>
+
+ <head>
+ <tableVersion value="1.0"/>
+ <fontRevision value="1.0"/>
+ <checkSumAdjustment value="0x640cdb2f"/>
+ <magicNumber value="0x5f0f3cf5"/>
+ <flags value="00000000 00000011"/>
+ <unitsPerEm value="1000"/>
+ <created value="Thu Feb 15 18:29:12 2018"/>
+ <macStyle value="00000000 00000010"/>
+ <lowestRecPPEM value="7"/>
+ <fontDirectionHint value="2"/>
+ <glyphDataFormat value="0"/>
+ </head>
+
+ <hhea>
+ <tableVersion value="0x00010000"/>
+ <ascent value="1000"/>
+ <descent value="-200"/>
+ <lineGap value="0"/>
+ <caretSlopeRise value="1"/>
+ <caretSlopeRun value="0"/>
+ <caretOffset value="0"/>
+ <reserved0 value="0"/>
+ <reserved1 value="0"/>
+ <reserved2 value="0"/>
+ <reserved3 value="0"/>
+ <metricDataFormat value="0"/>
+ </hhea>
+
+ <maxp>
+ <tableVersion value="0x10000"/>
+ <maxZones value="0"/>
+ <maxTwilightPoints value="0"/>
+ <maxStorage value="0"/>
+ <maxFunctionDefs value="0"/>
+ <maxInstructionDefs value="0"/>
+ <maxStackElements value="0"/>
+ <maxSizeOfInstructions value="0"/>
+ <maxComponentElements value="0"/>
+ </maxp>
+
+ <OS_2>
+ <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+ will be recalculated by the compiler -->
+ <version value="3"/>
+ <xAvgCharWidth value="594"/>
+ <usWeightClass value="400"/>
+ <usWidthClass value="5"/>
+ <fsType value="00000000 00001000"/>
+ <ySubscriptXSize value="650"/>
+ <ySubscriptYSize value="600"/>
+ <ySubscriptXOffset value="0"/>
+ <ySubscriptYOffset value="75"/>
+ <ySuperscriptXSize value="650"/>
+ <ySuperscriptYSize value="600"/>
+ <ySuperscriptXOffset value="0"/>
+ <ySuperscriptYOffset value="350"/>
+ <yStrikeoutSize value="50"/>
+ <yStrikeoutPosition value="300"/>
+ <sFamilyClass value="0"/>
+ <panose>
+ <bFamilyType value="0"/>
+ <bSerifStyle value="0"/>
+ <bWeight value="5"/>
+ <bProportion value="0"/>
+ <bContrast value="0"/>
+ <bStrokeVariation value="0"/>
+ <bArmStyle value="0"/>
+ <bLetterForm value="0"/>
+ <bMidline value="0"/>
+ <bXHeight value="0"/>
+ </panose>
+ <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+ <achVendID value="UKWN"/>
+ <fsSelection value="00000000 00000001"/>
+ <usFirstCharIndex value="32"/>
+ <usLastCharIndex value="122"/>
+ <sTypoAscender value="800"/>
+ <sTypoDescender value="-200"/>
+ <sTypoLineGap value="200"/>
+ <usWinAscent value="1000"/>
+ <usWinDescent value="200"/>
+ <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+ <sxHeight value="500"/>
+ <sCapHeight value="700"/>
+ <usDefaultChar value="0"/>
+ <usBreakChar value="32"/>
+ <usMaxContext value="0"/>
+ </OS_2>
+
+ <hmtx>
+ <mtx name=".notdef" width="500" lsb="93"/>
+ <mtx name="1em" width="1000" lsb="93"/>
+ <mtx name="3em" width="3000" lsb="93"/>
+ </hmtx>
+
+ <cmap>
+ <tableVersion version="0"/>
+ <cmap_format_4 platformID="3" platEncID="10" language="0">
+ <map code="0x0061" name="1em" /> <!-- a -->
+ <map code="0x0062" name="1em" /> <!-- b -->
+ <map code="0x0063" name="1em" /> <!-- c -->
+ <map code="0x0064" name="1em" /> <!-- d -->
+ <map code="0x0065" name="1em" /> <!-- e -->
+ <map code="0x0066" name="1em" /> <!-- f -->
+ <map code="0x0067" name="1em" /> <!-- g -->
+ <map code="0x0068" name="3em" /> <!-- h -->
+ <map code="0x0069" name="1em" /> <!-- i -->
+ <map code="0x006A" name="1em" /> <!-- j -->
+ <map code="0x006B" name="1em" /> <!-- k -->
+ <map code="0x006C" name="1em" /> <!-- l -->
+ <map code="0x006D" name="1em" /> <!-- m -->
+ <map code="0x006E" name="1em" /> <!-- n -->
+ <map code="0x006F" name="1em" /> <!-- o -->
+ <map code="0x0070" name="1em" /> <!-- p -->
+ <map code="0x0071" name="1em" /> <!-- q -->
+ <map code="0x0072" name="1em" /> <!-- r -->
+ <map code="0x0073" name="1em" /> <!-- s -->
+ <map code="0x0074" name="1em" /> <!-- t -->
+ <map code="0x0075" name="1em" /> <!-- u -->
+ <map code="0x0076" name="1em" /> <!-- v -->
+ <map code="0x0077" name="1em" /> <!-- w -->
+ <map code="0x0078" name="1em" /> <!-- x -->
+ <map code="0x0079" name="1em" /> <!-- y -->
+ <map code="0x007A" name="1em" /> <!-- z -->
+ </cmap_format_4>
+ </cmap>
+
+ <loca>
+ <!-- The 'loca' table will be calculated by the compiler -->
+ </loca>
+
+ <glyf>
+ <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ </glyf>
+
+ <name>
+ <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+ Copyright (C) 2018 The Android Open Source Project
+ </namerecord>
+ <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+ Regular
+ </namerecord>
+ <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+ SampleFont-Regular
+ </namerecord>
+ <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ </namerecord>
+ <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+ http://www.apache.org/licenses/LICENSE-2.0
+ </namerecord>
+ </name>
+
+ <post>
+ <formatType value="3.0"/>
+ <italicAngle value="0.0"/>
+ <underlinePosition value="-75"/>
+ <underlineThickness value="50"/>
+ <isFixedPitch value="0"/>
+ <minMemType42 value="0"/>
+ <maxMemType42 value="0"/>
+ <minMemType1 value="0"/>
+ <maxMemType1 value="0"/>
+ </post>
+
+</ttFont>
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_i3em_weight500_upright.ttf b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_i3em_weight500_upright.ttf
new file mode 100644
index 0000000..b8edcb6
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_i3em_weight500_upright.ttf
Binary files differ
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_i3em_weight500_upright.ttx b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_i3em_weight500_upright.ttx
new file mode 100644
index 0000000..6daf8da
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_i3em_weight500_upright.ttx
@@ -0,0 +1,208 @@
+<?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.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+ <GlyphOrder>
+ <GlyphID id="0" name=".notdef"/>
+ <GlyphID id="1" name="1em"/>
+ <GlyphID id="2" name="3em"/>
+ </GlyphOrder>
+
+ <head>
+ <tableVersion value="1.0"/>
+ <fontRevision value="1.0"/>
+ <checkSumAdjustment value="0x640cdb2f"/>
+ <magicNumber value="0x5f0f3cf5"/>
+ <flags value="00000000 00000011"/>
+ <unitsPerEm value="1000"/>
+ <created value="Thu Feb 15 18:29:12 2018"/>
+ <macStyle value="00000000 00000000"/>
+ <lowestRecPPEM value="7"/>
+ <fontDirectionHint value="2"/>
+ <glyphDataFormat value="0"/>
+ </head>
+
+ <hhea>
+ <tableVersion value="0x00010000"/>
+ <ascent value="1000"/>
+ <descent value="-200"/>
+ <lineGap value="0"/>
+ <caretSlopeRise value="1"/>
+ <caretSlopeRun value="0"/>
+ <caretOffset value="0"/>
+ <reserved0 value="0"/>
+ <reserved1 value="0"/>
+ <reserved2 value="0"/>
+ <reserved3 value="0"/>
+ <metricDataFormat value="0"/>
+ </hhea>
+
+ <maxp>
+ <tableVersion value="0x10000"/>
+ <maxZones value="0"/>
+ <maxTwilightPoints value="0"/>
+ <maxStorage value="0"/>
+ <maxFunctionDefs value="0"/>
+ <maxInstructionDefs value="0"/>
+ <maxStackElements value="0"/>
+ <maxSizeOfInstructions value="0"/>
+ <maxComponentElements value="0"/>
+ </maxp>
+
+ <OS_2>
+ <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+ will be recalculated by the compiler -->
+ <version value="3"/>
+ <xAvgCharWidth value="594"/>
+ <usWeightClass value="500"/>
+ <usWidthClass value="5"/>
+ <fsType value="00000000 00001000"/>
+ <ySubscriptXSize value="650"/>
+ <ySubscriptYSize value="600"/>
+ <ySubscriptXOffset value="0"/>
+ <ySubscriptYOffset value="75"/>
+ <ySuperscriptXSize value="650"/>
+ <ySuperscriptYSize value="600"/>
+ <ySuperscriptXOffset value="0"/>
+ <ySuperscriptYOffset value="350"/>
+ <yStrikeoutSize value="50"/>
+ <yStrikeoutPosition value="300"/>
+ <sFamilyClass value="0"/>
+ <panose>
+ <bFamilyType value="0"/>
+ <bSerifStyle value="0"/>
+ <bWeight value="5"/>
+ <bProportion value="0"/>
+ <bContrast value="0"/>
+ <bStrokeVariation value="0"/>
+ <bArmStyle value="0"/>
+ <bLetterForm value="0"/>
+ <bMidline value="0"/>
+ <bXHeight value="0"/>
+ </panose>
+ <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+ <achVendID value="UKWN"/>
+ <fsSelection value="00000000 01000000"/>
+ <usFirstCharIndex value="32"/>
+ <usLastCharIndex value="122"/>
+ <sTypoAscender value="800"/>
+ <sTypoDescender value="-200"/>
+ <sTypoLineGap value="200"/>
+ <usWinAscent value="1000"/>
+ <usWinDescent value="200"/>
+ <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+ <sxHeight value="500"/>
+ <sCapHeight value="700"/>
+ <usDefaultChar value="0"/>
+ <usBreakChar value="32"/>
+ <usMaxContext value="0"/>
+ </OS_2>
+
+ <hmtx>
+ <mtx name=".notdef" width="500" lsb="93"/>
+ <mtx name="1em" width="1000" lsb="93"/>
+ <mtx name="3em" width="3000" lsb="93"/>
+ </hmtx>
+
+ <cmap>
+ <tableVersion version="0"/>
+ <cmap_format_4 platformID="3" platEncID="10" language="0">
+ <map code="0x0061" name="1em" /> <!-- a -->
+ <map code="0x0062" name="1em" /> <!-- b -->
+ <map code="0x0063" name="1em" /> <!-- c -->
+ <map code="0x0064" name="1em" /> <!-- d -->
+ <map code="0x0065" name="1em" /> <!-- e -->
+ <map code="0x0066" name="1em" /> <!-- f -->
+ <map code="0x0067" name="1em" /> <!-- g -->
+ <map code="0x0068" name="1em" /> <!-- h -->
+ <map code="0x0069" name="3em" /> <!-- i -->
+ <map code="0x006A" name="1em" /> <!-- j -->
+ <map code="0x006B" name="1em" /> <!-- k -->
+ <map code="0x006C" name="1em" /> <!-- l -->
+ <map code="0x006D" name="1em" /> <!-- m -->
+ <map code="0x006E" name="1em" /> <!-- n -->
+ <map code="0x006F" name="1em" /> <!-- o -->
+ <map code="0x0070" name="1em" /> <!-- p -->
+ <map code="0x0071" name="1em" /> <!-- q -->
+ <map code="0x0072" name="1em" /> <!-- r -->
+ <map code="0x0073" name="1em" /> <!-- s -->
+ <map code="0x0074" name="1em" /> <!-- t -->
+ <map code="0x0075" name="1em" /> <!-- u -->
+ <map code="0x0076" name="1em" /> <!-- v -->
+ <map code="0x0077" name="1em" /> <!-- w -->
+ <map code="0x0078" name="1em" /> <!-- x -->
+ <map code="0x0079" name="1em" /> <!-- y -->
+ <map code="0x007A" name="1em" /> <!-- z -->
+ </cmap_format_4>
+ </cmap>
+
+ <loca>
+ <!-- The 'loca' table will be calculated by the compiler -->
+ </loca>
+
+ <glyf>
+ <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ </glyf>
+
+ <name>
+ <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+ Copyright (C) 2018 The Android Open Source Project
+ </namerecord>
+ <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+ Regular
+ </namerecord>
+ <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+ SampleFont-Regular
+ </namerecord>
+ <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ </namerecord>
+ <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+ http://www.apache.org/licenses/LICENSE-2.0
+ </namerecord>
+ </name>
+
+ <post>
+ <formatType value="3.0"/>
+ <italicAngle value="0.0"/>
+ <underlinePosition value="-75"/>
+ <underlineThickness value="50"/>
+ <isFixedPitch value="0"/>
+ <minMemType42 value="0"/>
+ <maxMemType42 value="0"/>
+ <minMemType1 value="0"/>
+ <maxMemType1 value="0"/>
+ </post>
+
+</ttFont>
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_j3em_weight500_italic.ttf b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_j3em_weight500_italic.ttf
new file mode 100644
index 0000000..6d7dde9
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_j3em_weight500_italic.ttf
Binary files differ
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_j3em_weight500_italic.ttx b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_j3em_weight500_italic.ttx
new file mode 100644
index 0000000..c5843f0
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_j3em_weight500_italic.ttx
@@ -0,0 +1,208 @@
+<?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.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+ <GlyphOrder>
+ <GlyphID id="0" name=".notdef"/>
+ <GlyphID id="1" name="1em"/>
+ <GlyphID id="2" name="3em"/>
+ </GlyphOrder>
+
+ <head>
+ <tableVersion value="1.0"/>
+ <fontRevision value="1.0"/>
+ <checkSumAdjustment value="0x640cdb2f"/>
+ <magicNumber value="0x5f0f3cf5"/>
+ <flags value="00000000 00000011"/>
+ <unitsPerEm value="1000"/>
+ <created value="Thu Feb 15 18:29:12 2018"/>
+ <macStyle value="00000000 00000010"/>
+ <lowestRecPPEM value="7"/>
+ <fontDirectionHint value="2"/>
+ <glyphDataFormat value="0"/>
+ </head>
+
+ <hhea>
+ <tableVersion value="0x00010000"/>
+ <ascent value="1000"/>
+ <descent value="-200"/>
+ <lineGap value="0"/>
+ <caretSlopeRise value="1"/>
+ <caretSlopeRun value="0"/>
+ <caretOffset value="0"/>
+ <reserved0 value="0"/>
+ <reserved1 value="0"/>
+ <reserved2 value="0"/>
+ <reserved3 value="0"/>
+ <metricDataFormat value="0"/>
+ </hhea>
+
+ <maxp>
+ <tableVersion value="0x10000"/>
+ <maxZones value="0"/>
+ <maxTwilightPoints value="0"/>
+ <maxStorage value="0"/>
+ <maxFunctionDefs value="0"/>
+ <maxInstructionDefs value="0"/>
+ <maxStackElements value="0"/>
+ <maxSizeOfInstructions value="0"/>
+ <maxComponentElements value="0"/>
+ </maxp>
+
+ <OS_2>
+ <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+ will be recalculated by the compiler -->
+ <version value="3"/>
+ <xAvgCharWidth value="594"/>
+ <usWeightClass value="500"/>
+ <usWidthClass value="5"/>
+ <fsType value="00000000 00001000"/>
+ <ySubscriptXSize value="650"/>
+ <ySubscriptYSize value="600"/>
+ <ySubscriptXOffset value="0"/>
+ <ySubscriptYOffset value="75"/>
+ <ySuperscriptXSize value="650"/>
+ <ySuperscriptYSize value="600"/>
+ <ySuperscriptXOffset value="0"/>
+ <ySuperscriptYOffset value="350"/>
+ <yStrikeoutSize value="50"/>
+ <yStrikeoutPosition value="300"/>
+ <sFamilyClass value="0"/>
+ <panose>
+ <bFamilyType value="0"/>
+ <bSerifStyle value="0"/>
+ <bWeight value="5"/>
+ <bProportion value="0"/>
+ <bContrast value="0"/>
+ <bStrokeVariation value="0"/>
+ <bArmStyle value="0"/>
+ <bLetterForm value="0"/>
+ <bMidline value="0"/>
+ <bXHeight value="0"/>
+ </panose>
+ <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+ <achVendID value="UKWN"/>
+ <fsSelection value="00000000 00000001"/>
+ <usFirstCharIndex value="32"/>
+ <usLastCharIndex value="122"/>
+ <sTypoAscender value="800"/>
+ <sTypoDescender value="-200"/>
+ <sTypoLineGap value="200"/>
+ <usWinAscent value="1000"/>
+ <usWinDescent value="200"/>
+ <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+ <sxHeight value="500"/>
+ <sCapHeight value="700"/>
+ <usDefaultChar value="0"/>
+ <usBreakChar value="32"/>
+ <usMaxContext value="0"/>
+ </OS_2>
+
+ <hmtx>
+ <mtx name=".notdef" width="500" lsb="93"/>
+ <mtx name="1em" width="1000" lsb="93"/>
+ <mtx name="3em" width="3000" lsb="93"/>
+ </hmtx>
+
+ <cmap>
+ <tableVersion version="0"/>
+ <cmap_format_4 platformID="3" platEncID="10" language="0">
+ <map code="0x0061" name="1em" /> <!-- a -->
+ <map code="0x0062" name="1em" /> <!-- b -->
+ <map code="0x0063" name="1em" /> <!-- c -->
+ <map code="0x0064" name="1em" /> <!-- d -->
+ <map code="0x0065" name="1em" /> <!-- e -->
+ <map code="0x0066" name="1em" /> <!-- f -->
+ <map code="0x0067" name="1em" /> <!-- g -->
+ <map code="0x0068" name="1em" /> <!-- h -->
+ <map code="0x0069" name="1em" /> <!-- i -->
+ <map code="0x006A" name="3em" /> <!-- j -->
+ <map code="0x006B" name="1em" /> <!-- k -->
+ <map code="0x006C" name="1em" /> <!-- l -->
+ <map code="0x006D" name="1em" /> <!-- m -->
+ <map code="0x006E" name="1em" /> <!-- n -->
+ <map code="0x006F" name="1em" /> <!-- o -->
+ <map code="0x0070" name="1em" /> <!-- p -->
+ <map code="0x0071" name="1em" /> <!-- q -->
+ <map code="0x0072" name="1em" /> <!-- r -->
+ <map code="0x0073" name="1em" /> <!-- s -->
+ <map code="0x0074" name="1em" /> <!-- t -->
+ <map code="0x0075" name="1em" /> <!-- u -->
+ <map code="0x0076" name="1em" /> <!-- v -->
+ <map code="0x0077" name="1em" /> <!-- w -->
+ <map code="0x0078" name="1em" /> <!-- x -->
+ <map code="0x0079" name="1em" /> <!-- y -->
+ <map code="0x007A" name="1em" /> <!-- z -->
+ </cmap_format_4>
+ </cmap>
+
+ <loca>
+ <!-- The 'loca' table will be calculated by the compiler -->
+ </loca>
+
+ <glyf>
+ <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ </glyf>
+
+ <name>
+ <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+ Copyright (C) 2018 The Android Open Source Project
+ </namerecord>
+ <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+ Regular
+ </namerecord>
+ <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+ SampleFont-Regular
+ </namerecord>
+ <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ </namerecord>
+ <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+ http://www.apache.org/licenses/LICENSE-2.0
+ </namerecord>
+ </name>
+
+ <post>
+ <formatType value="3.0"/>
+ <italicAngle value="0.0"/>
+ <underlinePosition value="-75"/>
+ <underlineThickness value="50"/>
+ <isFixedPitch value="0"/>
+ <minMemType42 value="0"/>
+ <maxMemType42 value="0"/>
+ <minMemType1 value="0"/>
+ <maxMemType1 value="0"/>
+ </post>
+
+</ttFont>
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_k3em_weight600_upright.ttf b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_k3em_weight600_upright.ttf
new file mode 100644
index 0000000..eb6d7d1
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_k3em_weight600_upright.ttf
Binary files differ
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_k3em_weight600_upright.ttx b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_k3em_weight600_upright.ttx
new file mode 100644
index 0000000..d46059f
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_k3em_weight600_upright.ttx
@@ -0,0 +1,208 @@
+<?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.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+ <GlyphOrder>
+ <GlyphID id="0" name=".notdef"/>
+ <GlyphID id="1" name="1em"/>
+ <GlyphID id="2" name="3em"/>
+ </GlyphOrder>
+
+ <head>
+ <tableVersion value="1.0"/>
+ <fontRevision value="1.0"/>
+ <checkSumAdjustment value="0x640cdb2f"/>
+ <magicNumber value="0x5f0f3cf5"/>
+ <flags value="00000000 00000011"/>
+ <unitsPerEm value="1000"/>
+ <created value="Thu Feb 15 18:29:12 2018"/>
+ <macStyle value="00000000 00000000"/>
+ <lowestRecPPEM value="7"/>
+ <fontDirectionHint value="2"/>
+ <glyphDataFormat value="0"/>
+ </head>
+
+ <hhea>
+ <tableVersion value="0x00010000"/>
+ <ascent value="1000"/>
+ <descent value="-200"/>
+ <lineGap value="0"/>
+ <caretSlopeRise value="1"/>
+ <caretSlopeRun value="0"/>
+ <caretOffset value="0"/>
+ <reserved0 value="0"/>
+ <reserved1 value="0"/>
+ <reserved2 value="0"/>
+ <reserved3 value="0"/>
+ <metricDataFormat value="0"/>
+ </hhea>
+
+ <maxp>
+ <tableVersion value="0x10000"/>
+ <maxZones value="0"/>
+ <maxTwilightPoints value="0"/>
+ <maxStorage value="0"/>
+ <maxFunctionDefs value="0"/>
+ <maxInstructionDefs value="0"/>
+ <maxStackElements value="0"/>
+ <maxSizeOfInstructions value="0"/>
+ <maxComponentElements value="0"/>
+ </maxp>
+
+ <OS_2>
+ <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+ will be recalculated by the compiler -->
+ <version value="3"/>
+ <xAvgCharWidth value="594"/>
+ <usWeightClass value="600"/>
+ <usWidthClass value="5"/>
+ <fsType value="00000000 00001000"/>
+ <ySubscriptXSize value="650"/>
+ <ySubscriptYSize value="600"/>
+ <ySubscriptXOffset value="0"/>
+ <ySubscriptYOffset value="75"/>
+ <ySuperscriptXSize value="650"/>
+ <ySuperscriptYSize value="600"/>
+ <ySuperscriptXOffset value="0"/>
+ <ySuperscriptYOffset value="350"/>
+ <yStrikeoutSize value="50"/>
+ <yStrikeoutPosition value="300"/>
+ <sFamilyClass value="0"/>
+ <panose>
+ <bFamilyType value="0"/>
+ <bSerifStyle value="0"/>
+ <bWeight value="5"/>
+ <bProportion value="0"/>
+ <bContrast value="0"/>
+ <bStrokeVariation value="0"/>
+ <bArmStyle value="0"/>
+ <bLetterForm value="0"/>
+ <bMidline value="0"/>
+ <bXHeight value="0"/>
+ </panose>
+ <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+ <achVendID value="UKWN"/>
+ <fsSelection value="00000000 01000000"/>
+ <usFirstCharIndex value="32"/>
+ <usLastCharIndex value="122"/>
+ <sTypoAscender value="800"/>
+ <sTypoDescender value="-200"/>
+ <sTypoLineGap value="200"/>
+ <usWinAscent value="1000"/>
+ <usWinDescent value="200"/>
+ <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+ <sxHeight value="500"/>
+ <sCapHeight value="700"/>
+ <usDefaultChar value="0"/>
+ <usBreakChar value="32"/>
+ <usMaxContext value="0"/>
+ </OS_2>
+
+ <hmtx>
+ <mtx name=".notdef" width="500" lsb="93"/>
+ <mtx name="1em" width="1000" lsb="93"/>
+ <mtx name="3em" width="3000" lsb="93"/>
+ </hmtx>
+
+ <cmap>
+ <tableVersion version="0"/>
+ <cmap_format_4 platformID="3" platEncID="10" language="0">
+ <map code="0x0061" name="1em" /> <!-- a -->
+ <map code="0x0062" name="1em" /> <!-- b -->
+ <map code="0x0063" name="1em" /> <!-- c -->
+ <map code="0x0064" name="1em" /> <!-- d -->
+ <map code="0x0065" name="1em" /> <!-- e -->
+ <map code="0x0066" name="1em" /> <!-- f -->
+ <map code="0x0067" name="1em" /> <!-- g -->
+ <map code="0x0068" name="1em" /> <!-- h -->
+ <map code="0x0069" name="1em" /> <!-- i -->
+ <map code="0x006A" name="1em" /> <!-- j -->
+ <map code="0x006B" name="3em" /> <!-- k -->
+ <map code="0x006C" name="1em" /> <!-- l -->
+ <map code="0x006D" name="1em" /> <!-- m -->
+ <map code="0x006E" name="1em" /> <!-- n -->
+ <map code="0x006F" name="1em" /> <!-- o -->
+ <map code="0x0070" name="1em" /> <!-- p -->
+ <map code="0x0071" name="1em" /> <!-- q -->
+ <map code="0x0072" name="1em" /> <!-- r -->
+ <map code="0x0073" name="1em" /> <!-- s -->
+ <map code="0x0074" name="1em" /> <!-- t -->
+ <map code="0x0075" name="1em" /> <!-- u -->
+ <map code="0x0076" name="1em" /> <!-- v -->
+ <map code="0x0077" name="1em" /> <!-- w -->
+ <map code="0x0078" name="1em" /> <!-- x -->
+ <map code="0x0079" name="1em" /> <!-- y -->
+ <map code="0x007A" name="1em" /> <!-- z -->
+ </cmap_format_4>
+ </cmap>
+
+ <loca>
+ <!-- The 'loca' table will be calculated by the compiler -->
+ </loca>
+
+ <glyf>
+ <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ </glyf>
+
+ <name>
+ <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+ Copyright (C) 2018 The Android Open Source Project
+ </namerecord>
+ <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+ Regular
+ </namerecord>
+ <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+ SampleFont-Regular
+ </namerecord>
+ <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ </namerecord>
+ <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+ http://www.apache.org/licenses/LICENSE-2.0
+ </namerecord>
+ </name>
+
+ <post>
+ <formatType value="3.0"/>
+ <italicAngle value="0.0"/>
+ <underlinePosition value="-75"/>
+ <underlineThickness value="50"/>
+ <isFixedPitch value="0"/>
+ <minMemType42 value="0"/>
+ <maxMemType42 value="0"/>
+ <minMemType1 value="0"/>
+ <maxMemType1 value="0"/>
+ </post>
+
+</ttFont>
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_l3em_weight600_italic.ttf b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_l3em_weight600_italic.ttf
new file mode 100644
index 0000000..f509e94
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_l3em_weight600_italic.ttf
Binary files differ
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_l3em_weight600_italic.ttx b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_l3em_weight600_italic.ttx
new file mode 100644
index 0000000..03073d33d
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_l3em_weight600_italic.ttx
@@ -0,0 +1,208 @@
+<?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.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+ <GlyphOrder>
+ <GlyphID id="0" name=".notdef"/>
+ <GlyphID id="1" name="1em"/>
+ <GlyphID id="2" name="3em"/>
+ </GlyphOrder>
+
+ <head>
+ <tableVersion value="1.0"/>
+ <fontRevision value="1.0"/>
+ <checkSumAdjustment value="0x640cdb2f"/>
+ <magicNumber value="0x5f0f3cf5"/>
+ <flags value="00000000 00000011"/>
+ <unitsPerEm value="1000"/>
+ <created value="Thu Feb 15 18:29:12 2018"/>
+ <macStyle value="00000000 00000010"/>
+ <lowestRecPPEM value="7"/>
+ <fontDirectionHint value="2"/>
+ <glyphDataFormat value="0"/>
+ </head>
+
+ <hhea>
+ <tableVersion value="0x00010000"/>
+ <ascent value="1000"/>
+ <descent value="-200"/>
+ <lineGap value="0"/>
+ <caretSlopeRise value="1"/>
+ <caretSlopeRun value="0"/>
+ <caretOffset value="0"/>
+ <reserved0 value="0"/>
+ <reserved1 value="0"/>
+ <reserved2 value="0"/>
+ <reserved3 value="0"/>
+ <metricDataFormat value="0"/>
+ </hhea>
+
+ <maxp>
+ <tableVersion value="0x10000"/>
+ <maxZones value="0"/>
+ <maxTwilightPoints value="0"/>
+ <maxStorage value="0"/>
+ <maxFunctionDefs value="0"/>
+ <maxInstructionDefs value="0"/>
+ <maxStackElements value="0"/>
+ <maxSizeOfInstructions value="0"/>
+ <maxComponentElements value="0"/>
+ </maxp>
+
+ <OS_2>
+ <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+ will be recalculated by the compiler -->
+ <version value="3"/>
+ <xAvgCharWidth value="594"/>
+ <usWeightClass value="600"/>
+ <usWidthClass value="5"/>
+ <fsType value="00000000 00001000"/>
+ <ySubscriptXSize value="650"/>
+ <ySubscriptYSize value="600"/>
+ <ySubscriptXOffset value="0"/>
+ <ySubscriptYOffset value="75"/>
+ <ySuperscriptXSize value="650"/>
+ <ySuperscriptYSize value="600"/>
+ <ySuperscriptXOffset value="0"/>
+ <ySuperscriptYOffset value="350"/>
+ <yStrikeoutSize value="50"/>
+ <yStrikeoutPosition value="300"/>
+ <sFamilyClass value="0"/>
+ <panose>
+ <bFamilyType value="0"/>
+ <bSerifStyle value="0"/>
+ <bWeight value="5"/>
+ <bProportion value="0"/>
+ <bContrast value="0"/>
+ <bStrokeVariation value="0"/>
+ <bArmStyle value="0"/>
+ <bLetterForm value="0"/>
+ <bMidline value="0"/>
+ <bXHeight value="0"/>
+ </panose>
+ <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+ <achVendID value="UKWN"/>
+ <fsSelection value="00000000 00000001"/>
+ <usFirstCharIndex value="32"/>
+ <usLastCharIndex value="122"/>
+ <sTypoAscender value="800"/>
+ <sTypoDescender value="-200"/>
+ <sTypoLineGap value="200"/>
+ <usWinAscent value="1000"/>
+ <usWinDescent value="200"/>
+ <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+ <sxHeight value="500"/>
+ <sCapHeight value="700"/>
+ <usDefaultChar value="0"/>
+ <usBreakChar value="32"/>
+ <usMaxContext value="0"/>
+ </OS_2>
+
+ <hmtx>
+ <mtx name=".notdef" width="500" lsb="93"/>
+ <mtx name="1em" width="1000" lsb="93"/>
+ <mtx name="3em" width="3000" lsb="93"/>
+ </hmtx>
+
+ <cmap>
+ <tableVersion version="0"/>
+ <cmap_format_4 platformID="3" platEncID="10" language="0">
+ <map code="0x0061" name="1em" /> <!-- a -->
+ <map code="0x0062" name="1em" /> <!-- b -->
+ <map code="0x0063" name="1em" /> <!-- c -->
+ <map code="0x0064" name="1em" /> <!-- d -->
+ <map code="0x0065" name="1em" /> <!-- e -->
+ <map code="0x0066" name="1em" /> <!-- f -->
+ <map code="0x0067" name="1em" /> <!-- g -->
+ <map code="0x0068" name="1em" /> <!-- h -->
+ <map code="0x0069" name="1em" /> <!-- i -->
+ <map code="0x006A" name="1em" /> <!-- j -->
+ <map code="0x006B" name="1em" /> <!-- k -->
+ <map code="0x006C" name="3em" /> <!-- l -->
+ <map code="0x006D" name="1em" /> <!-- m -->
+ <map code="0x006E" name="1em" /> <!-- n -->
+ <map code="0x006F" name="1em" /> <!-- o -->
+ <map code="0x0070" name="1em" /> <!-- p -->
+ <map code="0x0071" name="1em" /> <!-- q -->
+ <map code="0x0072" name="1em" /> <!-- r -->
+ <map code="0x0073" name="1em" /> <!-- s -->
+ <map code="0x0074" name="1em" /> <!-- t -->
+ <map code="0x0075" name="1em" /> <!-- u -->
+ <map code="0x0076" name="1em" /> <!-- v -->
+ <map code="0x0077" name="1em" /> <!-- w -->
+ <map code="0x0078" name="1em" /> <!-- x -->
+ <map code="0x0079" name="1em" /> <!-- y -->
+ <map code="0x007A" name="1em" /> <!-- z -->
+ </cmap_format_4>
+ </cmap>
+
+ <loca>
+ <!-- The 'loca' table will be calculated by the compiler -->
+ </loca>
+
+ <glyf>
+ <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ </glyf>
+
+ <name>
+ <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+ Copyright (C) 2018 The Android Open Source Project
+ </namerecord>
+ <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+ Regular
+ </namerecord>
+ <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+ SampleFont-Regular
+ </namerecord>
+ <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ </namerecord>
+ <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+ http://www.apache.org/licenses/LICENSE-2.0
+ </namerecord>
+ </name>
+
+ <post>
+ <formatType value="3.0"/>
+ <italicAngle value="0.0"/>
+ <underlinePosition value="-75"/>
+ <underlineThickness value="50"/>
+ <isFixedPitch value="0"/>
+ <minMemType42 value="0"/>
+ <maxMemType42 value="0"/>
+ <minMemType1 value="0"/>
+ <maxMemType1 value="0"/>
+ </post>
+
+</ttFont>
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_m3em_weight700_upright.ttf b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_m3em_weight700_upright.ttf
new file mode 100644
index 0000000..062f299
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_m3em_weight700_upright.ttf
Binary files differ
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_m3em_weight700_upright.ttx b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_m3em_weight700_upright.ttx
new file mode 100644
index 0000000..ca24fde
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_m3em_weight700_upright.ttx
@@ -0,0 +1,208 @@
+<?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.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+ <GlyphOrder>
+ <GlyphID id="0" name=".notdef"/>
+ <GlyphID id="1" name="1em"/>
+ <GlyphID id="2" name="3em"/>
+ </GlyphOrder>
+
+ <head>
+ <tableVersion value="1.0"/>
+ <fontRevision value="1.0"/>
+ <checkSumAdjustment value="0x640cdb2f"/>
+ <magicNumber value="0x5f0f3cf5"/>
+ <flags value="00000000 00000011"/>
+ <unitsPerEm value="1000"/>
+ <created value="Thu Feb 15 18:29:12 2018"/>
+ <macStyle value="00000000 00000000"/>
+ <lowestRecPPEM value="7"/>
+ <fontDirectionHint value="2"/>
+ <glyphDataFormat value="0"/>
+ </head>
+
+ <hhea>
+ <tableVersion value="0x00010000"/>
+ <ascent value="1000"/>
+ <descent value="-200"/>
+ <lineGap value="0"/>
+ <caretSlopeRise value="1"/>
+ <caretSlopeRun value="0"/>
+ <caretOffset value="0"/>
+ <reserved0 value="0"/>
+ <reserved1 value="0"/>
+ <reserved2 value="0"/>
+ <reserved3 value="0"/>
+ <metricDataFormat value="0"/>
+ </hhea>
+
+ <maxp>
+ <tableVersion value="0x10000"/>
+ <maxZones value="0"/>
+ <maxTwilightPoints value="0"/>
+ <maxStorage value="0"/>
+ <maxFunctionDefs value="0"/>
+ <maxInstructionDefs value="0"/>
+ <maxStackElements value="0"/>
+ <maxSizeOfInstructions value="0"/>
+ <maxComponentElements value="0"/>
+ </maxp>
+
+ <OS_2>
+ <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+ will be recalculated by the compiler -->
+ <version value="3"/>
+ <xAvgCharWidth value="594"/>
+ <usWeightClass value="700"/>
+ <usWidthClass value="5"/>
+ <fsType value="00000000 00001000"/>
+ <ySubscriptXSize value="650"/>
+ <ySubscriptYSize value="600"/>
+ <ySubscriptXOffset value="0"/>
+ <ySubscriptYOffset value="75"/>
+ <ySuperscriptXSize value="650"/>
+ <ySuperscriptYSize value="600"/>
+ <ySuperscriptXOffset value="0"/>
+ <ySuperscriptYOffset value="350"/>
+ <yStrikeoutSize value="50"/>
+ <yStrikeoutPosition value="300"/>
+ <sFamilyClass value="0"/>
+ <panose>
+ <bFamilyType value="0"/>
+ <bSerifStyle value="0"/>
+ <bWeight value="5"/>
+ <bProportion value="0"/>
+ <bContrast value="0"/>
+ <bStrokeVariation value="0"/>
+ <bArmStyle value="0"/>
+ <bLetterForm value="0"/>
+ <bMidline value="0"/>
+ <bXHeight value="0"/>
+ </panose>
+ <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+ <achVendID value="UKWN"/>
+ <fsSelection value="00000000 01000000"/>
+ <usFirstCharIndex value="32"/>
+ <usLastCharIndex value="122"/>
+ <sTypoAscender value="800"/>
+ <sTypoDescender value="-200"/>
+ <sTypoLineGap value="200"/>
+ <usWinAscent value="1000"/>
+ <usWinDescent value="200"/>
+ <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+ <sxHeight value="500"/>
+ <sCapHeight value="700"/>
+ <usDefaultChar value="0"/>
+ <usBreakChar value="32"/>
+ <usMaxContext value="0"/>
+ </OS_2>
+
+ <hmtx>
+ <mtx name=".notdef" width="500" lsb="93"/>
+ <mtx name="1em" width="1000" lsb="93"/>
+ <mtx name="3em" width="3000" lsb="93"/>
+ </hmtx>
+
+ <cmap>
+ <tableVersion version="0"/>
+ <cmap_format_4 platformID="3" platEncID="10" language="0">
+ <map code="0x0061" name="1em" /> <!-- a -->
+ <map code="0x0062" name="1em" /> <!-- b -->
+ <map code="0x0063" name="1em" /> <!-- c -->
+ <map code="0x0064" name="1em" /> <!-- d -->
+ <map code="0x0065" name="1em" /> <!-- e -->
+ <map code="0x0066" name="1em" /> <!-- f -->
+ <map code="0x0067" name="1em" /> <!-- g -->
+ <map code="0x0068" name="1em" /> <!-- h -->
+ <map code="0x0069" name="1em" /> <!-- i -->
+ <map code="0x006A" name="1em" /> <!-- j -->
+ <map code="0x006B" name="1em" /> <!-- k -->
+ <map code="0x006C" name="1em" /> <!-- l -->
+ <map code="0x006D" name="3em" /> <!-- m -->
+ <map code="0x006E" name="1em" /> <!-- n -->
+ <map code="0x006F" name="1em" /> <!-- o -->
+ <map code="0x0070" name="1em" /> <!-- p -->
+ <map code="0x0071" name="1em" /> <!-- q -->
+ <map code="0x0072" name="1em" /> <!-- r -->
+ <map code="0x0073" name="1em" /> <!-- s -->
+ <map code="0x0074" name="1em" /> <!-- t -->
+ <map code="0x0075" name="1em" /> <!-- u -->
+ <map code="0x0076" name="1em" /> <!-- v -->
+ <map code="0x0077" name="1em" /> <!-- w -->
+ <map code="0x0078" name="1em" /> <!-- x -->
+ <map code="0x0079" name="1em" /> <!-- y -->
+ <map code="0x007A" name="1em" /> <!-- z -->
+ </cmap_format_4>
+ </cmap>
+
+ <loca>
+ <!-- The 'loca' table will be calculated by the compiler -->
+ </loca>
+
+ <glyf>
+ <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ </glyf>
+
+ <name>
+ <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+ Copyright (C) 2018 The Android Open Source Project
+ </namerecord>
+ <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+ Regular
+ </namerecord>
+ <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+ SampleFont-Regular
+ </namerecord>
+ <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ </namerecord>
+ <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+ http://www.apache.org/licenses/LICENSE-2.0
+ </namerecord>
+ </name>
+
+ <post>
+ <formatType value="3.0"/>
+ <italicAngle value="0.0"/>
+ <underlinePosition value="-75"/>
+ <underlineThickness value="50"/>
+ <isFixedPitch value="0"/>
+ <minMemType42 value="0"/>
+ <maxMemType42 value="0"/>
+ <minMemType1 value="0"/>
+ <maxMemType1 value="0"/>
+ </post>
+
+</ttFont>
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_n3em_weight700_italic.ttf b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_n3em_weight700_italic.ttf
new file mode 100644
index 0000000..fdd0239
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_n3em_weight700_italic.ttf
Binary files differ
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_n3em_weight700_italic.ttx b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_n3em_weight700_italic.ttx
new file mode 100644
index 0000000..468d591
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_n3em_weight700_italic.ttx
@@ -0,0 +1,208 @@
+<?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.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+ <GlyphOrder>
+ <GlyphID id="0" name=".notdef"/>
+ <GlyphID id="1" name="1em"/>
+ <GlyphID id="2" name="3em"/>
+ </GlyphOrder>
+
+ <head>
+ <tableVersion value="1.0"/>
+ <fontRevision value="1.0"/>
+ <checkSumAdjustment value="0x640cdb2f"/>
+ <magicNumber value="0x5f0f3cf5"/>
+ <flags value="00000000 00000011"/>
+ <unitsPerEm value="1000"/>
+ <created value="Thu Feb 15 18:29:13 2018"/>
+ <macStyle value="00000000 00000010"/>
+ <lowestRecPPEM value="7"/>
+ <fontDirectionHint value="2"/>
+ <glyphDataFormat value="0"/>
+ </head>
+
+ <hhea>
+ <tableVersion value="0x00010000"/>
+ <ascent value="1000"/>
+ <descent value="-200"/>
+ <lineGap value="0"/>
+ <caretSlopeRise value="1"/>
+ <caretSlopeRun value="0"/>
+ <caretOffset value="0"/>
+ <reserved0 value="0"/>
+ <reserved1 value="0"/>
+ <reserved2 value="0"/>
+ <reserved3 value="0"/>
+ <metricDataFormat value="0"/>
+ </hhea>
+
+ <maxp>
+ <tableVersion value="0x10000"/>
+ <maxZones value="0"/>
+ <maxTwilightPoints value="0"/>
+ <maxStorage value="0"/>
+ <maxFunctionDefs value="0"/>
+ <maxInstructionDefs value="0"/>
+ <maxStackElements value="0"/>
+ <maxSizeOfInstructions value="0"/>
+ <maxComponentElements value="0"/>
+ </maxp>
+
+ <OS_2>
+ <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+ will be recalculated by the compiler -->
+ <version value="3"/>
+ <xAvgCharWidth value="594"/>
+ <usWeightClass value="700"/>
+ <usWidthClass value="5"/>
+ <fsType value="00000000 00001000"/>
+ <ySubscriptXSize value="650"/>
+ <ySubscriptYSize value="600"/>
+ <ySubscriptXOffset value="0"/>
+ <ySubscriptYOffset value="75"/>
+ <ySuperscriptXSize value="650"/>
+ <ySuperscriptYSize value="600"/>
+ <ySuperscriptXOffset value="0"/>
+ <ySuperscriptYOffset value="350"/>
+ <yStrikeoutSize value="50"/>
+ <yStrikeoutPosition value="300"/>
+ <sFamilyClass value="0"/>
+ <panose>
+ <bFamilyType value="0"/>
+ <bSerifStyle value="0"/>
+ <bWeight value="5"/>
+ <bProportion value="0"/>
+ <bContrast value="0"/>
+ <bStrokeVariation value="0"/>
+ <bArmStyle value="0"/>
+ <bLetterForm value="0"/>
+ <bMidline value="0"/>
+ <bXHeight value="0"/>
+ </panose>
+ <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+ <achVendID value="UKWN"/>
+ <fsSelection value="00000000 00000001"/>
+ <usFirstCharIndex value="32"/>
+ <usLastCharIndex value="122"/>
+ <sTypoAscender value="800"/>
+ <sTypoDescender value="-200"/>
+ <sTypoLineGap value="200"/>
+ <usWinAscent value="1000"/>
+ <usWinDescent value="200"/>
+ <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+ <sxHeight value="500"/>
+ <sCapHeight value="700"/>
+ <usDefaultChar value="0"/>
+ <usBreakChar value="32"/>
+ <usMaxContext value="0"/>
+ </OS_2>
+
+ <hmtx>
+ <mtx name=".notdef" width="500" lsb="93"/>
+ <mtx name="1em" width="1000" lsb="93"/>
+ <mtx name="3em" width="3000" lsb="93"/>
+ </hmtx>
+
+ <cmap>
+ <tableVersion version="0"/>
+ <cmap_format_4 platformID="3" platEncID="10" language="0">
+ <map code="0x0061" name="1em" /> <!-- a -->
+ <map code="0x0062" name="1em" /> <!-- b -->
+ <map code="0x0063" name="1em" /> <!-- c -->
+ <map code="0x0064" name="1em" /> <!-- d -->
+ <map code="0x0065" name="1em" /> <!-- e -->
+ <map code="0x0066" name="1em" /> <!-- f -->
+ <map code="0x0067" name="1em" /> <!-- g -->
+ <map code="0x0068" name="1em" /> <!-- h -->
+ <map code="0x0069" name="1em" /> <!-- i -->
+ <map code="0x006A" name="1em" /> <!-- j -->
+ <map code="0x006B" name="1em" /> <!-- k -->
+ <map code="0x006C" name="1em" /> <!-- l -->
+ <map code="0x006D" name="1em" /> <!-- m -->
+ <map code="0x006E" name="3em" /> <!-- n -->
+ <map code="0x006F" name="1em" /> <!-- o -->
+ <map code="0x0070" name="1em" /> <!-- p -->
+ <map code="0x0071" name="1em" /> <!-- q -->
+ <map code="0x0072" name="1em" /> <!-- r -->
+ <map code="0x0073" name="1em" /> <!-- s -->
+ <map code="0x0074" name="1em" /> <!-- t -->
+ <map code="0x0075" name="1em" /> <!-- u -->
+ <map code="0x0076" name="1em" /> <!-- v -->
+ <map code="0x0077" name="1em" /> <!-- w -->
+ <map code="0x0078" name="1em" /> <!-- x -->
+ <map code="0x0079" name="1em" /> <!-- y -->
+ <map code="0x007A" name="1em" /> <!-- z -->
+ </cmap_format_4>
+ </cmap>
+
+ <loca>
+ <!-- The 'loca' table will be calculated by the compiler -->
+ </loca>
+
+ <glyf>
+ <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ </glyf>
+
+ <name>
+ <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+ Copyright (C) 2018 The Android Open Source Project
+ </namerecord>
+ <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+ Regular
+ </namerecord>
+ <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+ SampleFont-Regular
+ </namerecord>
+ <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ </namerecord>
+ <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+ http://www.apache.org/licenses/LICENSE-2.0
+ </namerecord>
+ </name>
+
+ <post>
+ <formatType value="3.0"/>
+ <italicAngle value="0.0"/>
+ <underlinePosition value="-75"/>
+ <underlineThickness value="50"/>
+ <isFixedPitch value="0"/>
+ <minMemType42 value="0"/>
+ <maxMemType42 value="0"/>
+ <minMemType1 value="0"/>
+ <maxMemType1 value="0"/>
+ </post>
+
+</ttFont>
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_names.txt b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_names.txt
new file mode 100644
index 0000000..986d712
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_names.txt
@@ -0,0 +1,18 @@
+ascii_a3em_weight100_upright.ttf
+ascii_b3em_weight100_italic.ttf
+ascii_c3em_weight200_upright.ttf
+ascii_d3em_weight200_italic.ttf
+ascii_e3em_weight300_upright.ttf
+ascii_f3em_weight300_italic.ttf
+ascii_g3em_weight400_upright.ttf
+ascii_h3em_weight400_italic.ttf
+ascii_i3em_weight500_upright.ttf
+ascii_j3em_weight500_italic.ttf
+ascii_k3em_weight600_upright.ttf
+ascii_l3em_weight600_italic.ttf
+ascii_m3em_weight700_upright.ttf
+ascii_n3em_weight700_italic.ttf
+ascii_o3em_weight800_upright.ttf
+ascii_p3em_weight800_italic.ttf
+ascii_q3em_weight900_upright.ttf
+ascii_r3em_weight900_italic.ttf
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_o3em_weight800_upright.ttf b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_o3em_weight800_upright.ttf
new file mode 100644
index 0000000..993e7fa
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_o3em_weight800_upright.ttf
Binary files differ
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_o3em_weight800_upright.ttx b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_o3em_weight800_upright.ttx
new file mode 100644
index 0000000..b8c712f
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_o3em_weight800_upright.ttx
@@ -0,0 +1,208 @@
+<?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.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+ <GlyphOrder>
+ <GlyphID id="0" name=".notdef"/>
+ <GlyphID id="1" name="1em"/>
+ <GlyphID id="2" name="3em"/>
+ </GlyphOrder>
+
+ <head>
+ <tableVersion value="1.0"/>
+ <fontRevision value="1.0"/>
+ <checkSumAdjustment value="0x640cdb2f"/>
+ <magicNumber value="0x5f0f3cf5"/>
+ <flags value="00000000 00000011"/>
+ <unitsPerEm value="1000"/>
+ <created value="Thu Feb 15 18:29:13 2018"/>
+ <macStyle value="00000000 00000000"/>
+ <lowestRecPPEM value="7"/>
+ <fontDirectionHint value="2"/>
+ <glyphDataFormat value="0"/>
+ </head>
+
+ <hhea>
+ <tableVersion value="0x00010000"/>
+ <ascent value="1000"/>
+ <descent value="-200"/>
+ <lineGap value="0"/>
+ <caretSlopeRise value="1"/>
+ <caretSlopeRun value="0"/>
+ <caretOffset value="0"/>
+ <reserved0 value="0"/>
+ <reserved1 value="0"/>
+ <reserved2 value="0"/>
+ <reserved3 value="0"/>
+ <metricDataFormat value="0"/>
+ </hhea>
+
+ <maxp>
+ <tableVersion value="0x10000"/>
+ <maxZones value="0"/>
+ <maxTwilightPoints value="0"/>
+ <maxStorage value="0"/>
+ <maxFunctionDefs value="0"/>
+ <maxInstructionDefs value="0"/>
+ <maxStackElements value="0"/>
+ <maxSizeOfInstructions value="0"/>
+ <maxComponentElements value="0"/>
+ </maxp>
+
+ <OS_2>
+ <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+ will be recalculated by the compiler -->
+ <version value="3"/>
+ <xAvgCharWidth value="594"/>
+ <usWeightClass value="800"/>
+ <usWidthClass value="5"/>
+ <fsType value="00000000 00001000"/>
+ <ySubscriptXSize value="650"/>
+ <ySubscriptYSize value="600"/>
+ <ySubscriptXOffset value="0"/>
+ <ySubscriptYOffset value="75"/>
+ <ySuperscriptXSize value="650"/>
+ <ySuperscriptYSize value="600"/>
+ <ySuperscriptXOffset value="0"/>
+ <ySuperscriptYOffset value="350"/>
+ <yStrikeoutSize value="50"/>
+ <yStrikeoutPosition value="300"/>
+ <sFamilyClass value="0"/>
+ <panose>
+ <bFamilyType value="0"/>
+ <bSerifStyle value="0"/>
+ <bWeight value="5"/>
+ <bProportion value="0"/>
+ <bContrast value="0"/>
+ <bStrokeVariation value="0"/>
+ <bArmStyle value="0"/>
+ <bLetterForm value="0"/>
+ <bMidline value="0"/>
+ <bXHeight value="0"/>
+ </panose>
+ <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+ <achVendID value="UKWN"/>
+ <fsSelection value="00000000 01000000"/>
+ <usFirstCharIndex value="32"/>
+ <usLastCharIndex value="122"/>
+ <sTypoAscender value="800"/>
+ <sTypoDescender value="-200"/>
+ <sTypoLineGap value="200"/>
+ <usWinAscent value="1000"/>
+ <usWinDescent value="200"/>
+ <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+ <sxHeight value="500"/>
+ <sCapHeight value="700"/>
+ <usDefaultChar value="0"/>
+ <usBreakChar value="32"/>
+ <usMaxContext value="0"/>
+ </OS_2>
+
+ <hmtx>
+ <mtx name=".notdef" width="500" lsb="93"/>
+ <mtx name="1em" width="1000" lsb="93"/>
+ <mtx name="3em" width="3000" lsb="93"/>
+ </hmtx>
+
+ <cmap>
+ <tableVersion version="0"/>
+ <cmap_format_4 platformID="3" platEncID="10" language="0">
+ <map code="0x0061" name="1em" /> <!-- a -->
+ <map code="0x0062" name="1em" /> <!-- b -->
+ <map code="0x0063" name="1em" /> <!-- c -->
+ <map code="0x0064" name="1em" /> <!-- d -->
+ <map code="0x0065" name="1em" /> <!-- e -->
+ <map code="0x0066" name="1em" /> <!-- f -->
+ <map code="0x0067" name="1em" /> <!-- g -->
+ <map code="0x0068" name="1em" /> <!-- h -->
+ <map code="0x0069" name="1em" /> <!-- i -->
+ <map code="0x006A" name="1em" /> <!-- j -->
+ <map code="0x006B" name="1em" /> <!-- k -->
+ <map code="0x006C" name="1em" /> <!-- l -->
+ <map code="0x006D" name="1em" /> <!-- m -->
+ <map code="0x006E" name="1em" /> <!-- n -->
+ <map code="0x006F" name="3em" /> <!-- o -->
+ <map code="0x0070" name="1em" /> <!-- p -->
+ <map code="0x0071" name="1em" /> <!-- q -->
+ <map code="0x0072" name="1em" /> <!-- r -->
+ <map code="0x0073" name="1em" /> <!-- s -->
+ <map code="0x0074" name="1em" /> <!-- t -->
+ <map code="0x0075" name="1em" /> <!-- u -->
+ <map code="0x0076" name="1em" /> <!-- v -->
+ <map code="0x0077" name="1em" /> <!-- w -->
+ <map code="0x0078" name="1em" /> <!-- x -->
+ <map code="0x0079" name="1em" /> <!-- y -->
+ <map code="0x007A" name="1em" /> <!-- z -->
+ </cmap_format_4>
+ </cmap>
+
+ <loca>
+ <!-- The 'loca' table will be calculated by the compiler -->
+ </loca>
+
+ <glyf>
+ <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ </glyf>
+
+ <name>
+ <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+ Copyright (C) 2018 The Android Open Source Project
+ </namerecord>
+ <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+ Regular
+ </namerecord>
+ <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+ SampleFont-Regular
+ </namerecord>
+ <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ </namerecord>
+ <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+ http://www.apache.org/licenses/LICENSE-2.0
+ </namerecord>
+ </name>
+
+ <post>
+ <formatType value="3.0"/>
+ <italicAngle value="0.0"/>
+ <underlinePosition value="-75"/>
+ <underlineThickness value="50"/>
+ <isFixedPitch value="0"/>
+ <minMemType42 value="0"/>
+ <maxMemType42 value="0"/>
+ <minMemType1 value="0"/>
+ <maxMemType1 value="0"/>
+ </post>
+
+</ttFont>
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_p3em_weight800_italic.ttf b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_p3em_weight800_italic.ttf
new file mode 100644
index 0000000..f0d54f0
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_p3em_weight800_italic.ttf
Binary files differ
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_p3em_weight800_italic.ttx b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_p3em_weight800_italic.ttx
new file mode 100644
index 0000000..5d8ee18
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_p3em_weight800_italic.ttx
@@ -0,0 +1,208 @@
+<?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.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+ <GlyphOrder>
+ <GlyphID id="0" name=".notdef"/>
+ <GlyphID id="1" name="1em"/>
+ <GlyphID id="2" name="3em"/>
+ </GlyphOrder>
+
+ <head>
+ <tableVersion value="1.0"/>
+ <fontRevision value="1.0"/>
+ <checkSumAdjustment value="0x640cdb2f"/>
+ <magicNumber value="0x5f0f3cf5"/>
+ <flags value="00000000 00000011"/>
+ <unitsPerEm value="1000"/>
+ <created value="Thu Feb 15 18:29:13 2018"/>
+ <macStyle value="00000000 00000010"/>
+ <lowestRecPPEM value="7"/>
+ <fontDirectionHint value="2"/>
+ <glyphDataFormat value="0"/>
+ </head>
+
+ <hhea>
+ <tableVersion value="0x00010000"/>
+ <ascent value="1000"/>
+ <descent value="-200"/>
+ <lineGap value="0"/>
+ <caretSlopeRise value="1"/>
+ <caretSlopeRun value="0"/>
+ <caretOffset value="0"/>
+ <reserved0 value="0"/>
+ <reserved1 value="0"/>
+ <reserved2 value="0"/>
+ <reserved3 value="0"/>
+ <metricDataFormat value="0"/>
+ </hhea>
+
+ <maxp>
+ <tableVersion value="0x10000"/>
+ <maxZones value="0"/>
+ <maxTwilightPoints value="0"/>
+ <maxStorage value="0"/>
+ <maxFunctionDefs value="0"/>
+ <maxInstructionDefs value="0"/>
+ <maxStackElements value="0"/>
+ <maxSizeOfInstructions value="0"/>
+ <maxComponentElements value="0"/>
+ </maxp>
+
+ <OS_2>
+ <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+ will be recalculated by the compiler -->
+ <version value="3"/>
+ <xAvgCharWidth value="594"/>
+ <usWeightClass value="800"/>
+ <usWidthClass value="5"/>
+ <fsType value="00000000 00001000"/>
+ <ySubscriptXSize value="650"/>
+ <ySubscriptYSize value="600"/>
+ <ySubscriptXOffset value="0"/>
+ <ySubscriptYOffset value="75"/>
+ <ySuperscriptXSize value="650"/>
+ <ySuperscriptYSize value="600"/>
+ <ySuperscriptXOffset value="0"/>
+ <ySuperscriptYOffset value="350"/>
+ <yStrikeoutSize value="50"/>
+ <yStrikeoutPosition value="300"/>
+ <sFamilyClass value="0"/>
+ <panose>
+ <bFamilyType value="0"/>
+ <bSerifStyle value="0"/>
+ <bWeight value="5"/>
+ <bProportion value="0"/>
+ <bContrast value="0"/>
+ <bStrokeVariation value="0"/>
+ <bArmStyle value="0"/>
+ <bLetterForm value="0"/>
+ <bMidline value="0"/>
+ <bXHeight value="0"/>
+ </panose>
+ <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+ <achVendID value="UKWN"/>
+ <fsSelection value="00000000 00000001"/>
+ <usFirstCharIndex value="32"/>
+ <usLastCharIndex value="122"/>
+ <sTypoAscender value="800"/>
+ <sTypoDescender value="-200"/>
+ <sTypoLineGap value="200"/>
+ <usWinAscent value="1000"/>
+ <usWinDescent value="200"/>
+ <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+ <sxHeight value="500"/>
+ <sCapHeight value="700"/>
+ <usDefaultChar value="0"/>
+ <usBreakChar value="32"/>
+ <usMaxContext value="0"/>
+ </OS_2>
+
+ <hmtx>
+ <mtx name=".notdef" width="500" lsb="93"/>
+ <mtx name="1em" width="1000" lsb="93"/>
+ <mtx name="3em" width="3000" lsb="93"/>
+ </hmtx>
+
+ <cmap>
+ <tableVersion version="0"/>
+ <cmap_format_4 platformID="3" platEncID="10" language="0">
+ <map code="0x0061" name="1em" /> <!-- a -->
+ <map code="0x0062" name="1em" /> <!-- b -->
+ <map code="0x0063" name="1em" /> <!-- c -->
+ <map code="0x0064" name="1em" /> <!-- d -->
+ <map code="0x0065" name="1em" /> <!-- e -->
+ <map code="0x0066" name="1em" /> <!-- f -->
+ <map code="0x0067" name="1em" /> <!-- g -->
+ <map code="0x0068" name="1em" /> <!-- h -->
+ <map code="0x0069" name="1em" /> <!-- i -->
+ <map code="0x006A" name="1em" /> <!-- j -->
+ <map code="0x006B" name="1em" /> <!-- k -->
+ <map code="0x006C" name="1em" /> <!-- l -->
+ <map code="0x006D" name="1em" /> <!-- m -->
+ <map code="0x006E" name="1em" /> <!-- n -->
+ <map code="0x006F" name="1em" /> <!-- o -->
+ <map code="0x0070" name="3em" /> <!-- p -->
+ <map code="0x0071" name="1em" /> <!-- q -->
+ <map code="0x0072" name="1em" /> <!-- r -->
+ <map code="0x0073" name="1em" /> <!-- s -->
+ <map code="0x0074" name="1em" /> <!-- t -->
+ <map code="0x0075" name="1em" /> <!-- u -->
+ <map code="0x0076" name="1em" /> <!-- v -->
+ <map code="0x0077" name="1em" /> <!-- w -->
+ <map code="0x0078" name="1em" /> <!-- x -->
+ <map code="0x0079" name="1em" /> <!-- y -->
+ <map code="0x007A" name="1em" /> <!-- z -->
+ </cmap_format_4>
+ </cmap>
+
+ <loca>
+ <!-- The 'loca' table will be calculated by the compiler -->
+ </loca>
+
+ <glyf>
+ <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ </glyf>
+
+ <name>
+ <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+ Copyright (C) 2018 The Android Open Source Project
+ </namerecord>
+ <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+ Regular
+ </namerecord>
+ <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+ SampleFont-Regular
+ </namerecord>
+ <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ </namerecord>
+ <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+ http://www.apache.org/licenses/LICENSE-2.0
+ </namerecord>
+ </name>
+
+ <post>
+ <formatType value="3.0"/>
+ <italicAngle value="0.0"/>
+ <underlinePosition value="-75"/>
+ <underlineThickness value="50"/>
+ <isFixedPitch value="0"/>
+ <minMemType42 value="0"/>
+ <maxMemType42 value="0"/>
+ <minMemType1 value="0"/>
+ <maxMemType1 value="0"/>
+ </post>
+
+</ttFont>
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_q3em_weight900_upright.ttf b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_q3em_weight900_upright.ttf
new file mode 100644
index 0000000..c776c783
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_q3em_weight900_upright.ttf
Binary files differ
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_q3em_weight900_upright.ttx b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_q3em_weight900_upright.ttx
new file mode 100644
index 0000000..169fd73
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_q3em_weight900_upright.ttx
@@ -0,0 +1,208 @@
+<?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.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+ <GlyphOrder>
+ <GlyphID id="0" name=".notdef"/>
+ <GlyphID id="1" name="1em"/>
+ <GlyphID id="2" name="3em"/>
+ </GlyphOrder>
+
+ <head>
+ <tableVersion value="1.0"/>
+ <fontRevision value="1.0"/>
+ <checkSumAdjustment value="0x640cdb2f"/>
+ <magicNumber value="0x5f0f3cf5"/>
+ <flags value="00000000 00000011"/>
+ <unitsPerEm value="1000"/>
+ <created value="Thu Feb 15 18:29:13 2018"/>
+ <macStyle value="00000000 00000000"/>
+ <lowestRecPPEM value="7"/>
+ <fontDirectionHint value="2"/>
+ <glyphDataFormat value="0"/>
+ </head>
+
+ <hhea>
+ <tableVersion value="0x00010000"/>
+ <ascent value="1000"/>
+ <descent value="-200"/>
+ <lineGap value="0"/>
+ <caretSlopeRise value="1"/>
+ <caretSlopeRun value="0"/>
+ <caretOffset value="0"/>
+ <reserved0 value="0"/>
+ <reserved1 value="0"/>
+ <reserved2 value="0"/>
+ <reserved3 value="0"/>
+ <metricDataFormat value="0"/>
+ </hhea>
+
+ <maxp>
+ <tableVersion value="0x10000"/>
+ <maxZones value="0"/>
+ <maxTwilightPoints value="0"/>
+ <maxStorage value="0"/>
+ <maxFunctionDefs value="0"/>
+ <maxInstructionDefs value="0"/>
+ <maxStackElements value="0"/>
+ <maxSizeOfInstructions value="0"/>
+ <maxComponentElements value="0"/>
+ </maxp>
+
+ <OS_2>
+ <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+ will be recalculated by the compiler -->
+ <version value="3"/>
+ <xAvgCharWidth value="594"/>
+ <usWeightClass value="900"/>
+ <usWidthClass value="5"/>
+ <fsType value="00000000 00001000"/>
+ <ySubscriptXSize value="650"/>
+ <ySubscriptYSize value="600"/>
+ <ySubscriptXOffset value="0"/>
+ <ySubscriptYOffset value="75"/>
+ <ySuperscriptXSize value="650"/>
+ <ySuperscriptYSize value="600"/>
+ <ySuperscriptXOffset value="0"/>
+ <ySuperscriptYOffset value="350"/>
+ <yStrikeoutSize value="50"/>
+ <yStrikeoutPosition value="300"/>
+ <sFamilyClass value="0"/>
+ <panose>
+ <bFamilyType value="0"/>
+ <bSerifStyle value="0"/>
+ <bWeight value="5"/>
+ <bProportion value="0"/>
+ <bContrast value="0"/>
+ <bStrokeVariation value="0"/>
+ <bArmStyle value="0"/>
+ <bLetterForm value="0"/>
+ <bMidline value="0"/>
+ <bXHeight value="0"/>
+ </panose>
+ <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+ <achVendID value="UKWN"/>
+ <fsSelection value="00000000 01000000"/>
+ <usFirstCharIndex value="32"/>
+ <usLastCharIndex value="122"/>
+ <sTypoAscender value="800"/>
+ <sTypoDescender value="-200"/>
+ <sTypoLineGap value="200"/>
+ <usWinAscent value="1000"/>
+ <usWinDescent value="200"/>
+ <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+ <sxHeight value="500"/>
+ <sCapHeight value="700"/>
+ <usDefaultChar value="0"/>
+ <usBreakChar value="32"/>
+ <usMaxContext value="0"/>
+ </OS_2>
+
+ <hmtx>
+ <mtx name=".notdef" width="500" lsb="93"/>
+ <mtx name="1em" width="1000" lsb="93"/>
+ <mtx name="3em" width="3000" lsb="93"/>
+ </hmtx>
+
+ <cmap>
+ <tableVersion version="0"/>
+ <cmap_format_4 platformID="3" platEncID="10" language="0">
+ <map code="0x0061" name="1em" /> <!-- a -->
+ <map code="0x0062" name="1em" /> <!-- b -->
+ <map code="0x0063" name="1em" /> <!-- c -->
+ <map code="0x0064" name="1em" /> <!-- d -->
+ <map code="0x0065" name="1em" /> <!-- e -->
+ <map code="0x0066" name="1em" /> <!-- f -->
+ <map code="0x0067" name="1em" /> <!-- g -->
+ <map code="0x0068" name="1em" /> <!-- h -->
+ <map code="0x0069" name="1em" /> <!-- i -->
+ <map code="0x006A" name="1em" /> <!-- j -->
+ <map code="0x006B" name="1em" /> <!-- k -->
+ <map code="0x006C" name="1em" /> <!-- l -->
+ <map code="0x006D" name="1em" /> <!-- m -->
+ <map code="0x006E" name="1em" /> <!-- n -->
+ <map code="0x006F" name="1em" /> <!-- o -->
+ <map code="0x0070" name="1em" /> <!-- p -->
+ <map code="0x0071" name="3em" /> <!-- q -->
+ <map code="0x0072" name="1em" /> <!-- r -->
+ <map code="0x0073" name="1em" /> <!-- s -->
+ <map code="0x0074" name="1em" /> <!-- t -->
+ <map code="0x0075" name="1em" /> <!-- u -->
+ <map code="0x0076" name="1em" /> <!-- v -->
+ <map code="0x0077" name="1em" /> <!-- w -->
+ <map code="0x0078" name="1em" /> <!-- x -->
+ <map code="0x0079" name="1em" /> <!-- y -->
+ <map code="0x007A" name="1em" /> <!-- z -->
+ </cmap_format_4>
+ </cmap>
+
+ <loca>
+ <!-- The 'loca' table will be calculated by the compiler -->
+ </loca>
+
+ <glyf>
+ <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ </glyf>
+
+ <name>
+ <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+ Copyright (C) 2018 The Android Open Source Project
+ </namerecord>
+ <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+ Regular
+ </namerecord>
+ <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+ SampleFont-Regular
+ </namerecord>
+ <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ </namerecord>
+ <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+ http://www.apache.org/licenses/LICENSE-2.0
+ </namerecord>
+ </name>
+
+ <post>
+ <formatType value="3.0"/>
+ <italicAngle value="0.0"/>
+ <underlinePosition value="-75"/>
+ <underlineThickness value="50"/>
+ <isFixedPitch value="0"/>
+ <minMemType42 value="0"/>
+ <maxMemType42 value="0"/>
+ <minMemType1 value="0"/>
+ <maxMemType1 value="0"/>
+ </post>
+
+</ttFont>
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_r3em_weight900_italic.ttf b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_r3em_weight900_italic.ttf
new file mode 100644
index 0000000..02f6246
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_r3em_weight900_italic.ttf
Binary files differ
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_r3em_weight900_italic.ttx b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_r3em_weight900_italic.ttx
new file mode 100644
index 0000000..131d7b1
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_r3em_weight900_italic.ttx
@@ -0,0 +1,208 @@
+<?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.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+ <GlyphOrder>
+ <GlyphID id="0" name=".notdef"/>
+ <GlyphID id="1" name="1em"/>
+ <GlyphID id="2" name="3em"/>
+ </GlyphOrder>
+
+ <head>
+ <tableVersion value="1.0"/>
+ <fontRevision value="1.0"/>
+ <checkSumAdjustment value="0x640cdb2f"/>
+ <magicNumber value="0x5f0f3cf5"/>
+ <flags value="00000000 00000011"/>
+ <unitsPerEm value="1000"/>
+ <created value="Thu Feb 15 18:29:13 2018"/>
+ <macStyle value="00000000 00000010"/>
+ <lowestRecPPEM value="7"/>
+ <fontDirectionHint value="2"/>
+ <glyphDataFormat value="0"/>
+ </head>
+
+ <hhea>
+ <tableVersion value="0x00010000"/>
+ <ascent value="1000"/>
+ <descent value="-200"/>
+ <lineGap value="0"/>
+ <caretSlopeRise value="1"/>
+ <caretSlopeRun value="0"/>
+ <caretOffset value="0"/>
+ <reserved0 value="0"/>
+ <reserved1 value="0"/>
+ <reserved2 value="0"/>
+ <reserved3 value="0"/>
+ <metricDataFormat value="0"/>
+ </hhea>
+
+ <maxp>
+ <tableVersion value="0x10000"/>
+ <maxZones value="0"/>
+ <maxTwilightPoints value="0"/>
+ <maxStorage value="0"/>
+ <maxFunctionDefs value="0"/>
+ <maxInstructionDefs value="0"/>
+ <maxStackElements value="0"/>
+ <maxSizeOfInstructions value="0"/>
+ <maxComponentElements value="0"/>
+ </maxp>
+
+ <OS_2>
+ <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+ will be recalculated by the compiler -->
+ <version value="3"/>
+ <xAvgCharWidth value="594"/>
+ <usWeightClass value="900"/>
+ <usWidthClass value="5"/>
+ <fsType value="00000000 00001000"/>
+ <ySubscriptXSize value="650"/>
+ <ySubscriptYSize value="600"/>
+ <ySubscriptXOffset value="0"/>
+ <ySubscriptYOffset value="75"/>
+ <ySuperscriptXSize value="650"/>
+ <ySuperscriptYSize value="600"/>
+ <ySuperscriptXOffset value="0"/>
+ <ySuperscriptYOffset value="350"/>
+ <yStrikeoutSize value="50"/>
+ <yStrikeoutPosition value="300"/>
+ <sFamilyClass value="0"/>
+ <panose>
+ <bFamilyType value="0"/>
+ <bSerifStyle value="0"/>
+ <bWeight value="5"/>
+ <bProportion value="0"/>
+ <bContrast value="0"/>
+ <bStrokeVariation value="0"/>
+ <bArmStyle value="0"/>
+ <bLetterForm value="0"/>
+ <bMidline value="0"/>
+ <bXHeight value="0"/>
+ </panose>
+ <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+ <achVendID value="UKWN"/>
+ <fsSelection value="00000000 00000001"/>
+ <usFirstCharIndex value="32"/>
+ <usLastCharIndex value="122"/>
+ <sTypoAscender value="800"/>
+ <sTypoDescender value="-200"/>
+ <sTypoLineGap value="200"/>
+ <usWinAscent value="1000"/>
+ <usWinDescent value="200"/>
+ <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+ <sxHeight value="500"/>
+ <sCapHeight value="700"/>
+ <usDefaultChar value="0"/>
+ <usBreakChar value="32"/>
+ <usMaxContext value="0"/>
+ </OS_2>
+
+ <hmtx>
+ <mtx name=".notdef" width="500" lsb="93"/>
+ <mtx name="1em" width="1000" lsb="93"/>
+ <mtx name="3em" width="3000" lsb="93"/>
+ </hmtx>
+
+ <cmap>
+ <tableVersion version="0"/>
+ <cmap_format_4 platformID="3" platEncID="10" language="0">
+ <map code="0x0061" name="1em" /> <!-- a -->
+ <map code="0x0062" name="1em" /> <!-- b -->
+ <map code="0x0063" name="1em" /> <!-- c -->
+ <map code="0x0064" name="1em" /> <!-- d -->
+ <map code="0x0065" name="1em" /> <!-- e -->
+ <map code="0x0066" name="1em" /> <!-- f -->
+ <map code="0x0067" name="1em" /> <!-- g -->
+ <map code="0x0068" name="1em" /> <!-- h -->
+ <map code="0x0069" name="1em" /> <!-- i -->
+ <map code="0x006A" name="1em" /> <!-- j -->
+ <map code="0x006B" name="1em" /> <!-- k -->
+ <map code="0x006C" name="1em" /> <!-- l -->
+ <map code="0x006D" name="1em" /> <!-- m -->
+ <map code="0x006E" name="1em" /> <!-- n -->
+ <map code="0x006F" name="1em" /> <!-- o -->
+ <map code="0x0070" name="1em" /> <!-- p -->
+ <map code="0x0071" name="1em" /> <!-- q -->
+ <map code="0x0072" name="3em" /> <!-- r -->
+ <map code="0x0073" name="1em" /> <!-- s -->
+ <map code="0x0074" name="1em" /> <!-- t -->
+ <map code="0x0075" name="1em" /> <!-- u -->
+ <map code="0x0076" name="1em" /> <!-- v -->
+ <map code="0x0077" name="1em" /> <!-- w -->
+ <map code="0x0078" name="1em" /> <!-- x -->
+ <map code="0x0079" name="1em" /> <!-- y -->
+ <map code="0x007A" name="1em" /> <!-- z -->
+ </cmap_format_4>
+ </cmap>
+
+ <loca>
+ <!-- The 'loca' table will be calculated by the compiler -->
+ </loca>
+
+ <glyf>
+ <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ </glyf>
+
+ <name>
+ <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+ Copyright (C) 2018 The Android Open Source Project
+ </namerecord>
+ <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+ Regular
+ </namerecord>
+ <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+ SampleFont-Regular
+ </namerecord>
+ <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ </namerecord>
+ <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+ http://www.apache.org/licenses/LICENSE-2.0
+ </namerecord>
+ </name>
+
+ <post>
+ <formatType value="3.0"/>
+ <italicAngle value="0.0"/>
+ <underlinePosition value="-75"/>
+ <underlineThickness value="50"/>
+ <isFixedPitch value="0"/>
+ <minMemType42 value="0"/>
+ <maxMemType42 value="0"/>
+ <minMemType1 value="0"/>
+ <maxMemType1 value="0"/>
+ </post>
+
+</ttFont>
diff --git a/core/tests/coretests/res/layout/empty_layout.xml b/core/tests/coretests/res/layout/empty_layout.xml
new file mode 100644
index 0000000..c208b95
--- /dev/null
+++ b/core/tests/coretests/res/layout/empty_layout.xml
@@ -0,0 +1,26 @@
+<?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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/parent"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:focusable="true"
+ android:focusableInTouchMode="true"
+ android:orientation="vertical">
+</LinearLayout>
diff --git a/core/tests/coretests/src/android/app/assist/AssistStructureTest.java b/core/tests/coretests/src/android/app/assist/AssistStructureTest.java
new file mode 100644
index 0000000..b6a37bc
--- /dev/null
+++ b/core/tests/coretests/src/android/app/assist/AssistStructureTest.java
@@ -0,0 +1,269 @@
+/*
+ * 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.app.assist;
+
+import static android.view.View.IMPORTANT_FOR_AUTOFILL_AUTO;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.assist.AssistStructure.ViewNode;
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.Parcel;
+import android.os.SystemClock;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.Log;
+import android.widget.EditText;
+import android.widget.FrameLayout;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Unit test for {@link AssistStructure}.
+ *
+ * <p>To run it: {@code atest app.assist.AssistStructureTest}
+ *
+ * <p>TODO: right now this test is focused in the parcelization of a big object, due to an
+ * upcoming refactoring on the Autofill parcelization that does not use
+ * {@link AssistStructure#ensureData()} to load the structure (which in turn requires calls from
+ * system server to the app). Ideally it should be emprove to:
+ *
+ * <ol>
+ * <li>Add tests for Assist (to make sure autofill properties are not parcelized).
+ * <li>Assert all properties and make sure just the relevant properties (for Autofill or Assist)
+ * are parcelized.
+ * <li>Add tests for Autofill requests with the {@code FLAG_MANUAL_REQUEST} flag.
+ * </ol>
+ */
+@RunWith(AndroidJUnit4.class)
+public class AssistStructureTest {
+
+ private static final String TAG = "AssistStructureTest";
+
+ private static final boolean FOR_AUTOFILL = true;
+ private static final int NO_FLAGS = 0;
+
+ private static final int BIG_VIEW_SIZE = 10_000_000;
+ private static final char BIG_VIEW_CHAR = '6';
+ private static final String BIG_STRING = repeat(BIG_VIEW_CHAR, BIG_VIEW_SIZE);
+ // Cannot be much big because it could hang test due to blocking GC
+ private static final int NUMBER_SMALL_VIEWS = 10_000;
+
+ private EmptyLayoutActivity mActivity;
+
+ private final ActivityTestRule<EmptyLayoutActivity> mActivityTestRule =
+ new ActivityTestRule<>(EmptyLayoutActivity.class);
+
+ private final Context mContext = InstrumentationRegistry.getTargetContext();
+
+ @Before
+ public void setup() {
+ mActivity = mActivityTestRule.launchActivity(null);
+ }
+
+ @Test
+ public void testParcelizationForAutofill_manySmallViews() {
+ Log.d(TAG, "Adding " + NUMBER_SMALL_VIEWS + " small views");
+
+ for (int i = 1; i <= NUMBER_SMALL_VIEWS; i++) {
+ mActivity.addView(newSmallView());
+ }
+
+ waitUntilViewsAreLaidOff();
+
+ AssistStructure structure = new AssistStructure(mActivity, FOR_AUTOFILL, NO_FLAGS);
+
+ // Check properties on "original" structure
+ assertStructureWithManySmallViews(structure);
+
+ // Check properties on "cloned" structure
+ AssistStructure clone = cloneThroughParcel(structure);
+ assertStructureWithManySmallViews(clone);
+ }
+
+ private void assertStructureWithManySmallViews(AssistStructure structure) {
+ int i = 0;
+ try {
+ assertPackageName(structure);
+
+ assertThat(structure.getWindowNodeCount()).isEqualTo(1);
+
+ ViewNode rootView = structure.getWindowNodeAt(0).getRootViewNode();
+ assertThat(rootView.getClassName()).isEqualTo(FrameLayout.class.getName());
+ assertThat(rootView.getChildCount()).isEqualTo(2); // title and parent
+ assertThat(rootView.getAutofillId()).isNotNull();
+ assertThat(rootView.getImportantForAutofill()).isEqualTo(IMPORTANT_FOR_AUTOFILL_AUTO);
+
+ // Title
+ ViewNode title = rootView.getChildAt(0);
+ assertThat(title.getClassName()).isEqualTo(TextView.class.getName());
+ assertThat(title.getChildCount()).isEqualTo(0);
+ assertThat(title.getText().toString()).isEqualTo("My Title");
+ assertThat(title.getAutofillId()).isNotNull();
+
+ // Parent
+ ViewNode parent = rootView.getChildAt(1);
+ assertThat(parent.getClassName()).isEqualTo(LinearLayout.class.getName());
+ assertThat(parent.getChildCount()).isEqualTo(NUMBER_SMALL_VIEWS);
+ assertThat(parent.getIdEntry()).isEqualTo("parent");
+ assertThat(parent.getAutofillId()).isNotNull();
+
+ // Children
+ for (i = 0; i < NUMBER_SMALL_VIEWS; i++) {
+ ViewNode smallView = parent.getChildAt(i);
+ assertSmallView(smallView);
+ }
+ } catch (RuntimeException | Error e) {
+ Log.e(TAG, "dumping structure because of error at index #" + i + ": " + e);
+ structure.dump(true);
+ throw e;
+ }
+ }
+
+ @Test
+ public void testParcelizationForAutofill_oneBigView() {
+ Log.d(TAG, "Adding view with " + BIG_VIEW_SIZE + " chars");
+
+ mActivity.addView(newBigView());
+ waitUntilViewsAreLaidOff();
+
+ AssistStructure structure = new AssistStructure(mActivity, FOR_AUTOFILL, NO_FLAGS);
+
+ // Check properties on "original" structure
+ assertStructureWithOneBigView(structure);
+
+ // Check properties on "cloned" structure
+ AssistStructure clone = cloneThroughParcel(structure);
+ assertStructureWithOneBigView(clone);
+ }
+
+ private void assertStructureWithOneBigView(AssistStructure structure) {
+ try {
+ assertPackageName(structure);
+
+ assertThat(structure.getWindowNodeCount()).isEqualTo(1);
+
+ ViewNode rootView = structure.getWindowNodeAt(0).getRootViewNode();
+ assertThat(rootView.getClassName()).isEqualTo(FrameLayout.class.getName());
+ assertThat(rootView.getChildCount()).isEqualTo(2); // title and parent
+ assertThat(rootView.getAutofillId()).isNotNull();
+ assertThat(rootView.getImportantForAutofill()).isEqualTo(IMPORTANT_FOR_AUTOFILL_AUTO);
+
+ // Title
+ ViewNode title = rootView.getChildAt(0);
+ assertThat(title.getClassName()).isEqualTo(TextView.class.getName());
+ assertThat(title.getChildCount()).isEqualTo(0);
+ assertThat(title.getText().toString()).isEqualTo("My Title");
+ assertThat(title.getAutofillId()).isNotNull();
+
+ // Parent
+ ViewNode parent = rootView.getChildAt(1);
+ assertThat(parent.getClassName()).isEqualTo(LinearLayout.class.getName());
+ assertThat(parent.getChildCount()).isEqualTo(1);
+ assertThat(parent.getIdEntry()).isEqualTo("parent");
+ assertThat(parent.getAutofillId()).isNotNull();
+
+ // Children
+ ViewNode bigView = parent.getChildAt(0);
+ assertBigView(bigView);
+ } catch (RuntimeException | Error e) {
+ Log.e(TAG, "dumping structure because of error: " + e);
+ structure.dump(true);
+ throw e;
+ }
+ }
+
+ private EditText newSmallView() {
+ EditText view = new EditText(mContext);
+ view.setText("I AM GROOT");
+ return view;
+ }
+
+ private void assertSmallView(ViewNode view) {
+ assertThat(view.getClassName()).isEqualTo(EditText.class.getName());
+ assertThat(view.getChildCount()).isEqualTo(0);
+ assertThat(view.getIdEntry()).isNull();
+ assertThat(view.getAutofillId()).isNotNull();
+ assertThat(view.getText().toString()).isEqualTo("I AM GROOT");
+ }
+
+ private EditText newBigView() {
+ EditText view = new EditText(mContext);
+ view.setText("Big Hint in Little View");
+ view.setAutofillHints(BIG_STRING);
+ return view;
+ }
+
+ private void assertBigView(ViewNode view) {
+ assertThat(view.getClassName()).isEqualTo(EditText.class.getName());
+ assertThat(view.getChildCount()).isEqualTo(0);
+ assertThat(view.getIdEntry()).isNull();
+ assertThat(view.getAutofillId()).isNotNull();
+ assertThat(view.getText().toString()).isEqualTo("Big Hint in Little View");
+
+ String[] hints = view.getAutofillHints();
+ assertThat(hints.length).isEqualTo(1);
+ String hint = hints[0];
+ // Cannot assert the whole string because it takes too long and crashes the test by ANR
+ assertThat(hint.length()).isEqualTo(BIG_VIEW_SIZE);
+ assertThat(hint.charAt(0)).isEqualTo(BIG_VIEW_CHAR);
+ assertThat(hint.charAt(BIG_VIEW_SIZE - 1)).isEqualTo(BIG_VIEW_CHAR);
+ }
+
+ private void assertPackageName(AssistStructure structure) {
+ assertThat(structure.getActivityComponent()).isEqualTo(
+ new ComponentName("com.android.frameworks.coretests",
+ "android.app.assist.EmptyLayoutActivity"));
+ }
+
+ private AssistStructure cloneThroughParcel(AssistStructure structure) {
+ Parcel parcel = Parcel.obtain();
+
+ try {
+ // Write to parcel
+ parcel.setDataPosition(0); // Sanity / paranoid check
+ structure.writeToParcel(parcel, NO_FLAGS);
+
+ // Read from parcel
+ parcel.setDataPosition(0);
+ AssistStructure clone = AssistStructure.CREATOR.createFromParcel(parcel);
+ assertThat(clone).isNotNull();
+ return clone;
+ } finally {
+ parcel.recycle();
+ }
+ }
+
+ private void waitUntilViewsAreLaidOff() {
+ // TODO: use a more robust mechanism than just sleeping
+ SystemClock.sleep(3000);
+ }
+
+ // TODO: use some common helper
+ private static String repeat(char c, int size) {
+ StringBuilder builder = new StringBuilder(size);
+ for (int i = 1; i <= size; i++) {
+ builder.append(c);
+ }
+ return builder.toString();
+ }
+}
diff --git a/core/tests/coretests/src/android/app/assist/EmptyLayoutActivity.java b/core/tests/coretests/src/android/app/assist/EmptyLayoutActivity.java
new file mode 100644
index 0000000..f4b6bed
--- /dev/null
+++ b/core/tests/coretests/src/android/app/assist/EmptyLayoutActivity.java
@@ -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.app.assist;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.View;
+import android.view.WindowManager.LayoutParams;
+import android.widget.LinearLayout;
+
+import com.android.frameworks.coretests.R;
+
+public class EmptyLayoutActivity extends Activity {
+
+ private LinearLayout mParent;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.empty_layout);
+
+ mParent = findViewById(R.id.parent);
+ }
+
+ public void addView(View view) {
+ view.setLayoutParams(new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT,
+ LayoutParams.WRAP_CONTENT));
+ runOnUiThread(() -> mParent.addView(view));
+ }
+}
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java
index fe58116..3d114f4 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java
@@ -47,6 +47,7 @@
import android.platform.test.annotations.Presubmit;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
+import android.util.ArrayMap;
import org.junit.Before;
import org.junit.Test;
@@ -56,6 +57,7 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import java.util.Map;
import java.util.stream.Collectors;
/** Test {@link TransactionExecutor} logic. */
@@ -232,6 +234,44 @@
}
@Test
+ public void testDoNotLaunchDestroyedActivity() {
+ final Map<IBinder, ClientTransactionItem> activitiesToBeDestroyed = new ArrayMap<>();
+ when(mTransactionHandler.getActivitiesToBeDestroyed()).thenReturn(activitiesToBeDestroyed);
+ // Assume launch transaction is still in queue, so there is no client record.
+ when(mTransactionHandler.getActivityClient(any())).thenReturn(null);
+
+ // An incoming destroy transaction enters binder thread (preExecute).
+ final IBinder token = mock(IBinder.class);
+ final ClientTransaction destroyTransaction = ClientTransaction.obtain(null /* client */,
+ token /* activityToken */);
+ destroyTransaction.setLifecycleStateRequest(
+ DestroyActivityItem.obtain(false /* finished */, 0 /* configChanges */));
+ destroyTransaction.preExecute(mTransactionHandler);
+ // The activity should be added to to-be-destroyed container.
+ assertEquals(1, mTransactionHandler.getActivitiesToBeDestroyed().size());
+
+ // A previous queued launch transaction runs on main thread (execute).
+ final ClientTransaction launchTransaction = ClientTransaction.obtain(null /* client */,
+ token /* activityToken */);
+ final LaunchActivityItem launchItem = spy(LaunchActivityItem.obtain(
+ null /* intent */, 0 /* ident */, null /* info */, null /* curConfig */,
+ null, /* overrideConfig */ null /* compatInfo */, null /* referrer */ ,
+ null /* voiceInteractor */, 0 /* procState */, null /* state */,
+ null /* persistentState */, null /* pendingResults */,
+ null /* pendingNewIntents */, false /* isForward */, null /* profilerInfo */));
+ launchTransaction.addCallback(launchItem);
+ mExecutor.execute(launchTransaction);
+
+ // The launch transaction should not be executed because its token is in the
+ // to-be-destroyed container.
+ verify(launchItem, times(0)).execute(any(), any(), any());
+
+ // After the destroy transaction has been executed, the token should be removed.
+ mExecutor.execute(destroyTransaction);
+ assertEquals(0, mTransactionHandler.getActivitiesToBeDestroyed().size());
+ }
+
+ @Test
public void testActivityResultRequiredStateResolution() {
PostExecItem postExecItem = new PostExecItem(ON_RESUME);
diff --git a/core/tests/coretests/src/android/graphics/FontFileUtilTest.java b/core/tests/coretests/src/android/graphics/FontFileUtilTest.java
new file mode 100644
index 0000000..76267b2
--- /dev/null
+++ b/core/tests/coretests/src/android/graphics/FontFileUtilTest.java
@@ -0,0 +1,146 @@
+/*
+ * 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.graphics;
+
+import static org.junit.Assert.assertEquals;
+
+import android.content.Context;
+import android.content.res.AssetManager;
+import android.graphics.fonts.FontFileUtil;
+import android.graphics.fonts.FontVariationAxis;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.util.Log;
+import android.util.Pair;
+
+import org.junit.Test;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.nio.channels.FileChannel;
+
+@SmallTest
+public class FontFileUtilTest {
+ private static final String TAG = "FontFileUtilTest";
+ private static final String CACHE_FILE_PREFIX = ".font";
+
+ private static File getTempFile() {
+ Context ctx = InstrumentationRegistry.getTargetContext();
+ final String prefix = CACHE_FILE_PREFIX;
+ for (int i = 0; i < 100; ++i) {
+ final File file = new File(ctx.getCacheDir(), prefix + i);
+ try {
+ if (file.createNewFile()) {
+ return file;
+ }
+ } catch (IOException e) {
+ // ignore. Try next file.
+ }
+ }
+ return null;
+ }
+
+ private static ByteBuffer mmap(AssetManager am, String path) {
+ File file = getTempFile();
+ try (InputStream is = am.open(path)) {
+ if (!copyToFile(file, is)) {
+ return null;
+ }
+ return mmap(file);
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to open assets");
+ return null;
+ } finally {
+ file.delete();
+ }
+ }
+
+ private static ByteBuffer mmap(File file) {
+ try (FileInputStream fis = new FileInputStream(file)) {
+ FileChannel channel = fis.getChannel();
+ return channel.map(FileChannel.MapMode.READ_ONLY, 0, channel.size());
+ } catch (IOException e) {
+ return null;
+ }
+ }
+
+ private static boolean copyToFile(File file, InputStream is) {
+ try (FileOutputStream os = new FileOutputStream(file, false)) {
+ byte[] buffer = new byte[1024];
+ int readLen;
+ while ((readLen = is.read(buffer)) != -1) {
+ os.write(buffer, 0, readLen);
+ }
+ return true;
+ } catch (IOException e) {
+ Log.e(TAG, "Error copying resource contents to temp file: " + e.getMessage());
+ return false;
+ }
+ }
+
+ @Test
+ public void testRegularFonts() throws IOException {
+ AssetManager am = InstrumentationRegistry.getTargetContext().getAssets();
+ for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
+ int weight = style.first.intValue();
+ boolean italic = style.second.booleanValue();
+ String path = FontTestUtil.getFontPathFromStyle(weight, italic);
+
+ ByteBuffer buffer = mmap(am, path);
+ int packed = FontFileUtil.analyzeStyle(buffer, 0, null);
+ assertEquals(path, weight, FontFileUtil.unpackWeight(packed));
+ assertEquals(path, italic, FontFileUtil.unpackItalic(packed));
+ }
+ }
+
+ @Test
+ public void testTtcFont() throws IOException {
+ AssetManager am = InstrumentationRegistry.getTargetContext().getAssets();
+ String path = FontTestUtil.getTtcFontFileInAsset();
+ ByteBuffer buffer = mmap(am, path);
+ for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
+ int weight = style.first.intValue();
+ boolean italic = style.second.booleanValue();
+ int ttcIndex = FontTestUtil.getTtcIndexFromStyle(weight, italic);
+
+ int packed = FontFileUtil.analyzeStyle(buffer, ttcIndex, null);
+ assertEquals(path + "#" + ttcIndex, weight, FontFileUtil.unpackWeight(packed));
+ assertEquals(path + "#" + ttcIndex, italic, FontFileUtil.unpackItalic(packed));
+ }
+ }
+
+ @Test
+ public void testVariationFont() throws IOException {
+ AssetManager am = InstrumentationRegistry.getTargetContext().getAssets();
+ String path = FontTestUtil.getVFFontInAsset();
+ ByteBuffer buffer = mmap(am, path);
+ for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
+ int weight = style.first.intValue();
+ boolean italic = style.second.booleanValue();
+ String axes = FontTestUtil.getVarSettingsFromStyle(weight, italic);
+
+ int packed = FontFileUtil.analyzeStyle(buffer, 0,
+ FontVariationAxis.fromFontVariationSettings(axes));
+ assertEquals(path + "#" + axes, weight, FontFileUtil.unpackWeight(packed));
+ assertEquals(path + "#" + axes, italic, FontFileUtil.unpackItalic(packed));
+ }
+ }
+}
diff --git a/core/tests/coretests/src/android/graphics/FontTestUtil.java b/core/tests/coretests/src/android/graphics/FontTestUtil.java
new file mode 100644
index 0000000..2becb4b
--- /dev/null
+++ b/core/tests/coretests/src/android/graphics/FontTestUtil.java
@@ -0,0 +1,208 @@
+/*
+ * 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.graphics;
+
+import android.util.Pair;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Provides a utility for testing fonts
+ *
+ * For the purpose of testing font selection of families or fallbacks, this class provies following
+ * regular font files.
+ *
+ * - ascii_a3em_weight100_upright.ttf
+ * 'a' has 3em width and others have 1em width. The metadata has weight=100, non-italic value.
+ * - ascii_b3em_weight100_italic.ttf
+ * 'b' has 3em width and others have 1em width. The metadata has weight=100, italic value.
+ * - ascii_c3em_weight200_upright.ttf
+ * 'c' has 3em width and others have 1em width. The metadata has weight=200, non-italic value.
+ * - ascii_d3em_weight200_italic.ttf
+ * 'd' has 3em width and others have 1em width. The metadata has weight=200, italic value.
+ * - ascii_e3em_weight300_upright.ttf
+ * 'e' has 3em width and others have 1em width. The metadata has weight=300, non-italic value.
+ * - ascii_f3em_weight300_italic.ttf
+ * 'f' has 3em width and others have 1em width. The metadata has weight=300, italic value.
+ * - ascii_g3em_weight400_upright.ttf
+ * 'g' has 3em width and others have 1em width. The metadata has weight=400, non-italic value.
+ * - ascii_h3em_weight400_italic.ttf
+ * 'h' has 3em width and others have 1em width. The metadata has weight=400, italic value.
+ * - ascii_i3em_weight500_upright.ttf
+ * 'i' has 3em width and others have 1em width. The metadata has weight=500, non-italic value.
+ * - ascii_j3em_weight500_italic.ttf
+ * 'j' has 3em width and others have 1em width. The metadata has weight=500, italic value.
+ * - ascii_k3em_weight600_upright.ttf
+ * 'k' has 3em width and others have 1em width. The metadata has weight=600, non-italic value.
+ * - ascii_l3em_weight600_italic.ttf
+ * 'l' has 3em width and others have 1em width. The metadata has weight=600, italic value.
+ * - ascii_m3em_weight700_upright.ttf
+ * 'm' has 3em width and others have 1em width. The metadata has weight=700, non-italic value.
+ * - ascii_n3em_weight700_italic.ttf
+ * 'n' has 3em width and others have 1em width. The metadata has weight=700, italic value.
+ * - ascii_o3em_weight800_upright.ttf
+ * 'o' has 3em width and others have 1em width. The metadata has weight=800, non-italic value.
+ * - ascii_p3em_weight800_italic.ttf
+ * 'p' has 3em width and others have 1em width. The metadata has weight=800, italic value.
+ * - ascii_q3em_weight900_upright.ttf
+ * 'q' has 3em width and others have 1em width. The metadata has weight=900, non-italic value.
+ * - ascii_r3em_weight900_italic.ttf
+ * 'r' has 3em width and others have 1em width. The metadata has weight=900, italic value.
+ *
+ * In addition to above font files, this class provides a font collection file and a variable font
+ * file.
+ * - ascii.ttc
+ * The collection of above 18 fonts with above order.
+ * - ascii_vf.ttf
+ * This font supports a-z characters and all characters has 1em width. This font supports 'wght',
+ * 'ital' axes but no effect for the glyph width. This font also supports 'Asc[a-z]' 26 axes which
+ * makes glyph width 3em. For example, 'Asca 1.0' makes a glyph width of 'a' 3em, 'Ascb 1.0' makes
+ * a glyph width of 'b' 3em. With these axes, above font can be replicated like
+ * - 'Asca' 1.0, 'wght' 100.0' is equivalent with ascii_a3em_width100_upright.ttf
+ * - 'Ascb' 1.0, 'wght' 100.0, 'ital' 1.0' is equivalent with ascii_b3em_width100_italic.ttf
+ */
+public class FontTestUtil {
+ private static final String FAMILY_SELECTION_FONT_PATH_IN_ASSET = "fonts_for_family_selection";
+ private static final List<Pair<Integer, Boolean>> sStyleList;
+ private static final Map<Pair<Integer, Boolean>, String> sFontMap;
+ private static final Map<Pair<Integer, Boolean>, Integer> sTtcMap;
+ private static final Map<Pair<Integer, Boolean>, String> sVariationSettingsMap;
+ private static final String[] sFontList = { // Same order of ascii.ttc
+ FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/fonts/ascii_a3em_weight100_upright.ttf",
+ FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/fonts/ascii_b3em_weight100_italic.ttf",
+ FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/fonts/ascii_c3em_weight200_upright.ttf",
+ FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/fonts/ascii_d3em_weight200_italic.ttf",
+ FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/fonts/ascii_e3em_weight300_upright.ttf",
+ FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/fonts/ascii_f3em_weight300_italic.ttf",
+ FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/fonts/ascii_g3em_weight400_upright.ttf",
+ FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/fonts/ascii_h3em_weight400_italic.ttf",
+ FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/fonts/ascii_i3em_weight500_upright.ttf",
+ FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/fonts/ascii_j3em_weight500_italic.ttf",
+ FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/fonts/ascii_k3em_weight600_upright.ttf",
+ FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/fonts/ascii_l3em_weight600_italic.ttf",
+ FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/fonts/ascii_m3em_weight700_upright.ttf",
+ FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/fonts/ascii_n3em_weight700_italic.ttf",
+ FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/fonts/ascii_o3em_weight800_upright.ttf",
+ FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/fonts/ascii_p3em_weight800_italic.ttf",
+ FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/fonts/ascii_q3em_weight900_upright.ttf",
+ FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/fonts/ascii_r3em_weight900_italic.ttf",
+ };
+
+ private static final String[] FONT_VARIATION_SETTING_LIST = {
+ "'Asca' 1.0, 'wght' 100.0",
+ "'Ascb' 1.0, 'wght' 100.0, 'ital' 1.0",
+ "'Ascc' 1.0, 'wght' 200.0",
+ "'Ascd' 1.0, 'wght' 200.0, 'ital' 1.0",
+ "'Asce' 1.0, 'wght' 300.0",
+ "'Ascf' 1.0, 'wght' 300.0, 'ital' 1.0",
+ "'Ascg' 1.0, 'wght' 400.0",
+ "'Asch' 1.0, 'wght' 400.0, 'ital' 1.0",
+ "'Asci' 1.0, 'wght' 500.0",
+ "'Ascj' 1.0, 'wght' 500.0, 'ital' 1.0",
+ "'Asck' 1.0, 'wght' 600.0",
+ "'Ascl' 1.0, 'wght' 600.0, 'ital' 1.0",
+ "'Ascm' 1.0, 'wght' 700.0",
+ "'Ascn' 1.0, 'wght' 700.0, 'ital' 1.0",
+ "'Asco' 1.0, 'wght' 800.0",
+ "'Ascp' 1.0, 'wght' 800.0, 'ital' 1.0",
+ "'Ascq' 1.0, 'wght' 900.0",
+ "'Ascr' 1.0, 'wght' 900.0, 'ital' 1.0",
+ };
+
+ static {
+ // Style list with the same order of sFontList.
+ ArrayList<Pair<Integer, Boolean>> styles = new ArrayList<>();
+ styles.add(new Pair<>(100, false));
+ styles.add(new Pair<>(100, true));
+ styles.add(new Pair<>(200, false));
+ styles.add(new Pair<>(200, true));
+ styles.add(new Pair<>(300, false));
+ styles.add(new Pair<>(300, true));
+ styles.add(new Pair<>(400, false));
+ styles.add(new Pair<>(400, true));
+ styles.add(new Pair<>(500, false));
+ styles.add(new Pair<>(500, true));
+ styles.add(new Pair<>(600, false));
+ styles.add(new Pair<>(600, true));
+ styles.add(new Pair<>(700, false));
+ styles.add(new Pair<>(700, true));
+ styles.add(new Pair<>(800, false));
+ styles.add(new Pair<>(800, true));
+ styles.add(new Pair<>(900, false));
+ styles.add(new Pair<>(900, true));
+ sStyleList = Collections.unmodifiableList(styles);
+
+ HashMap<Pair<Integer, Boolean>, String> map = new HashMap<>();
+ HashMap<Pair<Integer, Boolean>, Integer> ttcMap = new HashMap<>();
+ HashMap<Pair<Integer, Boolean>, String> variationMap = new HashMap<>();
+ HashMap<Character, Pair<Integer, Boolean>> reverseMap = new HashMap<>();
+ for (int i = 0; i < sFontList.length; ++i) {
+ map.put(sStyleList.get(i), sFontList[i]);
+ ttcMap.put(sStyleList.get(i), i);
+ variationMap.put(sStyleList.get(i), FONT_VARIATION_SETTING_LIST[i]);
+ }
+ sFontMap = Collections.unmodifiableMap(map);
+ sTtcMap = Collections.unmodifiableMap(ttcMap);
+ sVariationSettingsMap = Collections.unmodifiableMap(variationMap);
+ }
+
+ /**
+ * Returns a path to the font collection file in asset directory.
+ */
+ public static String getTtcFontFileInAsset() {
+ return FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/ascii.ttc";
+ }
+
+ /**
+ * Returns a path to the variable font file in asset directory.
+ */
+ public static String getVFFontInAsset() {
+ return FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/ascii_vf.ttf";
+ }
+
+ /**
+ * Returns a ttc index of the specified style.
+ */
+ public static int getTtcIndexFromStyle(int weight, boolean italic) {
+ return sTtcMap.get(new Pair<>(weight, italic)).intValue();
+ }
+
+ /**
+ * Returns a variation settings string of the specified style.
+ */
+ public static String getVarSettingsFromStyle(int weight, boolean italic) {
+ return sVariationSettingsMap.get(new Pair<>(weight, italic));
+ }
+
+ /**
+ * Returns a font path from the specified style.
+ */
+ public static String getFontPathFromStyle(int weight, boolean italic) {
+ return sFontMap.get(new Pair<>(weight, italic));
+ }
+
+ /**
+ * Returns all supported styles.
+ */
+ public static List<Pair<Integer, Boolean>> getAllStyles() {
+ return sStyleList;
+ }
+}
diff --git a/core/tests/coretests/src/android/os/FileUtilsTest.java b/core/tests/coretests/src/android/os/FileUtilsTest.java
index 0bc3a2d..9c9f11b 100644
--- a/core/tests/coretests/src/android/os/FileUtilsTest.java
+++ b/core/tests/coretests/src/android/os/FileUtilsTest.java
@@ -193,7 +193,7 @@
try (MemoryPipe in = MemoryPipe.createSource(source);
FileOutputStream out = new FileOutputStream(dest)) {
- FileUtils.copy(in.getFD(), out.getFD(), null, null, size);
+ FileUtils.copy(in.getFD(), out.getFD(), size, null, null, null);
}
actual = readFile(dest);
diff --git a/core/tests/coretests/src/android/os/RedactingFileDescriptorTest.java b/core/tests/coretests/src/android/os/RedactingFileDescriptorTest.java
new file mode 100644
index 0000000..c8bc35c
--- /dev/null
+++ b/core/tests/coretests/src/android/os/RedactingFileDescriptorTest.java
@@ -0,0 +1,109 @@
+/*
+ * 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 static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+import android.system.Os;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.util.Arrays;
+
+@RunWith(AndroidJUnit4.class)
+public class RedactingFileDescriptorTest {
+ private Context mContext;
+ private File mFile;
+
+ @Before
+ public void setUp() throws Exception {
+ mContext = InstrumentationRegistry.getContext();
+ mFile = File.createTempFile("redacting", "dat");
+ try (FileOutputStream out = new FileOutputStream(mFile)) {
+ final byte[] buf = new byte[1_000_000];
+ Arrays.fill(buf, (byte) 64);
+ out.write(buf);
+ }
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mFile.delete();
+ }
+
+ @Test
+ public void testSingleByte() throws Exception {
+ final FileDescriptor fd = RedactingFileDescriptor
+ .open(mContext, mFile, new long[] { 10, 11 }).getFileDescriptor();
+
+ final byte[] buf = new byte[1_000];
+ assertEquals(buf.length, Os.read(fd, buf, 0, buf.length));
+ for (int i = 0; i < buf.length; i++) {
+ if (i == 10) {
+ assertEquals(0, buf[i]);
+ } else {
+ assertEquals(64, buf[i]);
+ }
+ }
+ }
+
+ @Test
+ public void testRanges() throws Exception {
+ final FileDescriptor fd = RedactingFileDescriptor
+ .open(mContext, mFile, new long[] { 100, 200, 300, 400 }).getFileDescriptor();
+
+ final byte[] buf = new byte[10];
+ assertEquals(buf.length, Os.pread(fd, buf, 0, 10, 90));
+ assertArrayEquals(new byte[] { 64, 64, 64, 64, 64, 64, 64, 64, 64, 64 }, buf);
+
+ assertEquals(buf.length, Os.pread(fd, buf, 0, 10, 95));
+ assertArrayEquals(new byte[] { 64, 64, 64, 64, 64, 0, 0, 0, 0, 0 }, buf);
+
+ assertEquals(buf.length, Os.pread(fd, buf, 0, 10, 100));
+ assertArrayEquals(new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, buf);
+
+ assertEquals(buf.length, Os.pread(fd, buf, 0, 10, 195));
+ assertArrayEquals(new byte[] { 0, 0, 0, 0, 0, 64, 64, 64, 64, 64 }, buf);
+
+ assertEquals(buf.length, Os.pread(fd, buf, 0, 10, 395));
+ assertArrayEquals(new byte[] { 0, 0, 0, 0, 0, 64, 64, 64, 64, 64 }, buf);
+ }
+
+ @Test
+ public void testEntireFile() throws Exception {
+ final FileDescriptor fd = RedactingFileDescriptor
+ .open(mContext, mFile, new long[] { 0, 5_000_000 }).getFileDescriptor();
+
+ try (FileInputStream in = new FileInputStream(fd)) {
+ int val;
+ while ((val = in.read()) != -1) {
+ assertEquals(0, val);
+ }
+ }
+ }
+}
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 14c641a..60e512c 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -66,7 +66,6 @@
Settings.System.LOCKSCREEN_DISABLED, // ?
Settings.System.MEDIA_BUTTON_RECEIVER, // candidate for backup?
Settings.System.MUTE_STREAMS_AFFECTED, // candidate for backup?
- Settings.System.NOTIFICATION_LIGHT_PULSE, // candidate for backup?
Settings.System.NOTIFICATION_SOUND_CACHE, // internal cache
Settings.System.POINTER_LOCATION, // backup candidate?
Settings.System.DEBUG_ENABLE_ENHANCED_CALL_BLOCKING, // used for testing only
@@ -124,6 +123,7 @@
Settings.Global.BATTERY_DISCHARGE_THRESHOLD,
Settings.Global.BATTERY_SAVER_DEVICE_SPECIFIC_CONSTANTS,
Settings.Global.BATTERY_STATS_CONSTANTS,
+ Settings.Global.BINDER_CALLS_STATS,
Settings.Global.BLE_SCAN_ALWAYS_AVAILABLE,
Settings.Global.BLE_SCAN_LOW_POWER_WINDOW_MS,
Settings.Global.BLE_SCAN_LOW_POWER_INTERVAL_MS,
@@ -272,6 +272,7 @@
Settings.Global.LOCATION_BACKGROUND_THROTTLE_INTERVAL_MS,
Settings.Global.LOCATION_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS,
Settings.Global.LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST,
+ Settings.Global.LOCATION_LAST_LOCATION_MAX_AGE_MILLIS,
Settings.Global.LOCATION_GLOBAL_KILL_SWITCH,
Settings.Global.LOCATION_SETTINGS_LINK_TO_PERMISSIONS_ENABLED,
Settings.Global.LOCK_SOUND,
@@ -459,6 +460,8 @@
Settings.Global.WIFI_BOUNCE_DELAY_OVERRIDE_MS,
Settings.Global.WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED,
Settings.Global.WIFI_COUNTRY_CODE,
+ Settings.Global.WIFI_DATA_STALL_MIN_TX_BAD,
+ Settings.Global.WIFI_DATA_STALL_MIN_TX_SUCCESS_WITHOUT_RX,
Settings.Global.WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN,
Settings.Global.WIFI_DISPLAY_CERTIFICATION_ON,
Settings.Global.WIFI_DISPLAY_ON,
@@ -468,6 +471,8 @@
Settings.Global.WIFI_FRAMEWORK_SCAN_INTERVAL_MS,
Settings.Global.WIFI_FREQUENCY_BAND,
Settings.Global.WIFI_IDLE_MS,
+ Settings.Global.WIFI_IS_UNUSABLE_EVENT_METRICS_ENABLED,
+ Settings.Global.WIFI_LINK_SPEED_METRICS_ENABLED,
Settings.Global.WIFI_MAX_DHCP_RETRY_COUNT,
Settings.Global.WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS,
Settings.Global.WIFI_NETWORK_SHOW_RSSI,
@@ -554,10 +559,8 @@
Settings.Secure.LAST_SETUP_SHOWN,
Settings.Secure.LOCATION_CHANGER,
Settings.Secure.LOCATION_MODE,
- Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, // Candidate?
Settings.Secure.LOCK_SCREEN_ALLOW_REMOTE_INPUT, // Candidate?
Settings.Secure.LOCK_SCREEN_LOCK_AFTER_TIMEOUT,
- Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, // Candidate?
Settings.Secure.LOCK_TO_APP_EXIT_LOCKED,
Settings.Secure.MANAGED_PROFILE_CONTACT_REMOTE_SEARCH,
Settings.Secure.MULTI_PRESS_TIMEOUT,
@@ -591,12 +594,15 @@
Settings.Secure.SEARCH_THREAD_KEEPALIVE_SECONDS,
Settings.Secure.SEARCH_WEB_RESULTS_OVERRIDE_LIMIT,
Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE,
+ Settings.Secure.SELECTED_SPELL_CHECKER, // Intentionally removed in Q
+ Settings.Secure.SELECTED_SPELL_CHECKER_SUBTYPE, // Intentionally removed in Q
Settings.Secure.SETTINGS_CLASSNAME,
Settings.Secure.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING, // candidate?
Settings.Secure.SHOW_ROTATION_SUGGESTIONS,
Settings.Secure.SKIP_FIRST_USE_HINTS, // candidate?
+ Settings.Secure.SLEEP_TIMEOUT,
Settings.Secure.SMS_DEFAULT_APPLICATION,
- Settings.Secure.THEME_MODE,
+ Settings.Secure.SPELL_CHECKER_ENABLED, // Intentionally removed in Q
Settings.Secure.TRUST_AGENTS_INITIALIZED,
Settings.Secure.TV_INPUT_CUSTOM_LABELS,
Settings.Secure.TV_INPUT_HIDDEN_INPUTS,
diff --git a/core/tests/coretests/src/android/text/TextUtilsTest.java b/core/tests/coretests/src/android/text/TextUtilsTest.java
index 870d6b2..72290bf 100644
--- a/core/tests/coretests/src/android/text/TextUtilsTest.java
+++ b/core/tests/coretests/src/android/text/TextUtilsTest.java
@@ -785,4 +785,11 @@
assertEquals(2, TextUtils.length(" "));
assertEquals(6, TextUtils.length("Hello!"));
}
+
+ @Test
+ public void testTrimToLengthWithEllipsis() {
+ assertEquals("ABC...", TextUtils.trimToLengthWithEllipsis("ABCDEF", 3));
+ assertEquals("ABC", TextUtils.trimToLengthWithEllipsis("ABC", 3));
+ assertEquals("", TextUtils.trimToLengthWithEllipsis("", 3));
+ }
}
diff --git a/core/tests/coretests/src/android/text/method/ForwardDeleteTest.java b/core/tests/coretests/src/android/text/method/ForwardDeleteTest.java
index 15dbbc8..c98e646 100644
--- a/core/tests/coretests/src/android/text/method/ForwardDeleteTest.java
+++ b/core/tests/coretests/src/android/text/method/ForwardDeleteTest.java
@@ -20,6 +20,7 @@
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.Suppress;
import android.text.InputType;
import android.util.KeyUtils;
import android.view.KeyEvent;
@@ -238,6 +239,7 @@
}
@Test
+ @Suppress
public void testEmojiModifier() {
EditorState state = new EditorState();
@@ -267,6 +269,7 @@
}
@Test
+ @Suppress
public void testMixedEdgeCases() {
EditorState state = new EditorState();
diff --git a/core/tests/coretests/src/android/util/TimestampedValueTest.java b/core/tests/coretests/src/android/util/TimestampedValueTest.java
index 7117c1b..03b4abd 100644
--- a/core/tests/coretests/src/android/util/TimestampedValueTest.java
+++ b/core/tests/coretests/src/android/util/TimestampedValueTest.java
@@ -116,4 +116,14 @@
parcel.recycle();
}
}
+
+ @Test
+ public void testReferenceTimeDifference() {
+ TimestampedValue<Long> value1 = new TimestampedValue<>(1000, 123L);
+ assertEquals(0, TimestampedValue.referenceTimeDifference(value1, value1));
+
+ TimestampedValue<Long> value2 = new TimestampedValue<>(1, 321L);
+ assertEquals(999, TimestampedValue.referenceTimeDifference(value1, value2));
+ assertEquals(-999, TimestampedValue.referenceTimeDifference(value2, value1));
+ }
}
diff --git a/core/tests/coretests/src/android/view/KeyEventTest.java b/core/tests/coretests/src/android/view/KeyEventTest.java
index aabf816..b9d95e5 100644
--- a/core/tests/coretests/src/android/view/KeyEventTest.java
+++ b/core/tests/coretests/src/android/view/KeyEventTest.java
@@ -16,6 +16,8 @@
package android.view;
+import static android.view.Display.INVALID_DISPLAY;
+
import static org.junit.Assert.assertEquals;
import android.support.test.filters.SmallTest;
@@ -28,12 +30,71 @@
@RunWith(AndroidJUnit4.class)
public class KeyEventTest {
+ private static final int DOWN_TIME = 50;
+ private static final long EVENT_TIME = 100;
+ private static final int ACTION = KeyEvent.ACTION_DOWN;
+ private static final int KEYCODE = KeyEvent.KEYCODE_0;
+ private static final int REPEAT = 0;
+ private static final int METASTATE = 0;
+ private static final int DEVICE_ID = 0;
+ private static final int SCAN_CODE = 0;
+ private static final int FLAGS = 0;
+ private static final int SOURCE = InputDevice.SOURCE_KEYBOARD;
+ private static final String CHARACTERS = null;
+
@Test
public void testObtain() {
- KeyEvent keyEvent = KeyEvent.obtain(0, 0, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_0,
- 0, 0, 0, 0, 0, InputDevice.SOURCE_KEYBOARD, null);
- assertEquals(KeyEvent.ACTION_DOWN, keyEvent.getAction());
- assertEquals(KeyEvent.KEYCODE_0, keyEvent.getKeyCode());
- assertEquals(InputDevice.SOURCE_KEYBOARD, keyEvent.getSource());
+ KeyEvent keyEvent = KeyEvent.obtain(DOWN_TIME, EVENT_TIME, ACTION, KEYCODE, REPEAT,
+ METASTATE, DEVICE_ID, SCAN_CODE, FLAGS, SOURCE, CHARACTERS);
+ assertEquals(DOWN_TIME, keyEvent.getDownTime());
+ assertEquals(EVENT_TIME, keyEvent.getEventTime());
+ assertEquals(ACTION, keyEvent.getAction());
+ assertEquals(KEYCODE, keyEvent.getKeyCode());
+ assertEquals(REPEAT, keyEvent.getRepeatCount());
+ assertEquals(METASTATE, keyEvent.getMetaState());
+ assertEquals(DEVICE_ID, keyEvent.getDeviceId());
+ assertEquals(SCAN_CODE, keyEvent.getScanCode());
+ assertEquals(FLAGS, keyEvent.getFlags());
+ assertEquals(SOURCE, keyEvent.getSource());
+ assertEquals(INVALID_DISPLAY, keyEvent.getDisplayId());
+ assertEquals(CHARACTERS, keyEvent.getCharacters());
+ }
+
+ @Test
+ public void testObtainFromKeyEvent() {
+ KeyEvent keyEvent = KeyEvent.obtain(DOWN_TIME, EVENT_TIME, ACTION, KEYCODE, REPEAT,
+ METASTATE, DEVICE_ID, SCAN_CODE, FLAGS, SOURCE, CHARACTERS);
+ KeyEvent keyEvent2 = KeyEvent.obtain(keyEvent);
+ assertEquals(keyEvent.getDownTime(), keyEvent2.getDownTime());
+ assertEquals(keyEvent.getEventTime(), keyEvent2.getEventTime());
+ assertEquals(keyEvent.getAction(), keyEvent2.getAction());
+ assertEquals(keyEvent.getKeyCode(), keyEvent2.getKeyCode());
+ assertEquals(keyEvent.getRepeatCount(), keyEvent2.getRepeatCount());
+ assertEquals(keyEvent.getMetaState(), keyEvent2.getMetaState());
+ assertEquals(keyEvent.getDeviceId(), keyEvent2.getDeviceId());
+ assertEquals(keyEvent.getScanCode(), keyEvent2.getScanCode());
+ assertEquals(keyEvent.getFlags(), keyEvent2.getFlags());
+ assertEquals(keyEvent.getSource(), keyEvent2.getSource());
+ assertEquals(keyEvent.getDisplayId(), keyEvent2.getDisplayId());
+ assertEquals(keyEvent.getCharacters(), keyEvent2.getCharacters());
+ }
+
+ @Test
+ public void testObtainWithDisplayId() {
+ final int displayId = 5;
+ KeyEvent keyEvent = KeyEvent.obtain(DOWN_TIME, EVENT_TIME, ACTION, KEYCODE, REPEAT,
+ METASTATE, DEVICE_ID, SCAN_CODE, FLAGS, SOURCE, displayId, CHARACTERS);
+ assertEquals(DOWN_TIME, keyEvent.getDownTime());
+ assertEquals(EVENT_TIME, keyEvent.getEventTime());
+ assertEquals(ACTION, keyEvent.getAction());
+ assertEquals(KEYCODE, keyEvent.getKeyCode());
+ assertEquals(REPEAT, keyEvent.getRepeatCount());
+ assertEquals(METASTATE, keyEvent.getMetaState());
+ assertEquals(DEVICE_ID, keyEvent.getDeviceId());
+ assertEquals(SCAN_CODE, keyEvent.getScanCode());
+ assertEquals(FLAGS, keyEvent.getFlags());
+ assertEquals(SOURCE, keyEvent.getSource());
+ assertEquals(displayId, keyEvent.getDisplayId());
+ assertEquals(CHARACTERS, keyEvent.getCharacters());
}
}
diff --git a/core/tests/coretests/src/android/view/ViewGroupTransientViewTest.java b/core/tests/coretests/src/android/view/ViewGroupTransientViewTest.java
new file mode 100644
index 0000000..93ad41f0
--- /dev/null
+++ b/core/tests/coretests/src/android/view/ViewGroupTransientViewTest.java
@@ -0,0 +1,163 @@
+/*
+ * 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 static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.fail;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.support.test.annotation.UiThreadTest;
+import android.support.test.filters.MediumTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.widget.FrameLayout;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class ViewGroupTransientViewTest {
+
+ @Rule
+ public ActivityTestRule<Activity> mActivityRule = new ActivityTestRule<>(Activity.class);
+
+ private FrameLayout mBasePanel;
+ private ViewGroup mTestViewGroup;
+ private TestView mTestView;
+
+ @Before
+ public void setUp() {
+ final Activity activity = mActivityRule.getActivity();
+ mBasePanel = new FrameLayout(activity);
+ mTestViewGroup = new FrameLayout(activity);
+ mTestView = new TestView(activity);
+ activity.runOnUiThread(() -> activity.setContentView(mBasePanel));
+ }
+
+ @UiThreadTest
+ @Test
+ public void addAndRemove_inNonAttachedViewGroup_shouldNotAttachAndDetach() {
+ mTestViewGroup.addTransientView(mTestView, 0);
+ assertEquals(0, mTestView.mAttachedCount);
+
+ mTestViewGroup.removeTransientView(mTestView);
+ assertEquals(0, mTestView.mDetachedCount);
+ }
+
+ @UiThreadTest
+ @Test
+ public void addAndRemove_inAttachedViewGroup_shouldAttachAndDetachOnce() {
+ mBasePanel.addView(mTestViewGroup);
+ mTestViewGroup.addTransientView(mTestView, 0);
+ assertEquals(mTestView, mTestViewGroup.getTransientView(0));
+ assertEquals(1, mTestViewGroup.getTransientViewCount());
+ assertEquals(1, mTestView.mAttachedCount);
+
+ mBasePanel.removeView(mTestViewGroup);
+ mTestViewGroup.removeTransientView(mTestView);
+ assertEquals(null, mTestViewGroup.getTransientView(0));
+ assertEquals(0, mTestViewGroup.getTransientViewCount());
+ assertEquals(1, mTestView.mDetachedCount);
+ }
+
+ @UiThreadTest
+ @Test
+ public void addRemoveAdd_noException() {
+ mBasePanel.addView(mTestViewGroup);
+ mTestViewGroup.addTransientView(mTestView, 1);
+ mTestViewGroup.removeTransientView(mTestView);
+ mTestViewGroup.addTransientView(mTestView, 2);
+ }
+
+ @UiThreadTest
+ @Test
+ public void reAddBeforeRemove_shouldThrowException() {
+ mTestViewGroup.addView(mTestView);
+
+ try {
+ mTestViewGroup.addTransientView(mTestView, 0);
+ fail("Not allow to add as transient view before removing it");
+ } catch (IllegalStateException e) {
+ // Expected
+ }
+
+ mTestViewGroup.removeView(mTestView);
+ mTestViewGroup.addTransientView(mTestView, 0);
+ try {
+ mTestViewGroup.addTransientView(mTestView, 1);
+ fail("Not allow to add the same transient view again");
+ } catch (IllegalStateException e) {
+ // Expected
+ }
+ }
+
+ @Test
+ public void drawTransientView() throws Exception {
+ // For view can be drawn if keyguard is active.
+ mActivityRule.getActivity().setShowWhenLocked(true);
+
+ final CountDownLatch latch = new CountDownLatch(1);
+ mTestView.mOnDraw = () -> latch.countDown();
+
+ mActivityRule.getActivity().runOnUiThread(() -> {
+ mBasePanel.addView(mTestViewGroup);
+ mTestViewGroup.addTransientView(mTestView, 0);
+ });
+
+ if (!latch.await(3, TimeUnit.SECONDS)) {
+ fail("Transient view does not draw");
+ }
+ }
+
+ private static class TestView extends View {
+ int mAttachedCount;
+ int mDetachedCount;
+ Runnable mOnDraw;
+
+ TestView(Context context) {
+ super(context);
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ mAttachedCount++;
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ mDetachedCount++;
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ if (mOnDraw != null) {
+ mOnDraw.run();
+ mOnDraw = null;
+ }
+ }
+ }
+}
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java
index 4de8155..993378d 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java
@@ -300,6 +300,26 @@
}
@Test
+ public void subTreeChangeEventFromUncachedNode_clearsNodeInCache() {
+ AccessibilityNodeInfo nodeInfo = getNodeWithA11yAndWindowId(CHILD_VIEW_ID, WINDOW_ID_1);
+ long id = nodeInfo.getSourceNodeId();
+ mAccessibilityCache.add(nodeInfo);
+ nodeInfo.recycle();
+
+ AccessibilityEvent event = AccessibilityEvent
+ .obtain(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
+ event.setContentChangeTypes(AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE);
+ event.setSource(getMockViewWithA11yAndWindowIds(PARENT_VIEW_ID, WINDOW_ID_1));
+
+ mAccessibilityCache.onAccessibilityEvent(event);
+ AccessibilityNodeInfo shouldBeNull = mAccessibilityCache.getNode(WINDOW_ID_1, id);
+ if (shouldBeNull != null) {
+ shouldBeNull.recycle();
+ }
+ assertNull(shouldBeNull);
+ }
+
+ @Test
public void scrollEvent_clearsNodeAndChild() {
AccessibilityEvent event = AccessibilityEvent
.obtain(AccessibilityEvent.TYPE_VIEW_SCROLLED);
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsHistoryTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsHistoryTest.java
new file mode 100644
index 0000000..10a3189
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsHistoryTest.java
@@ -0,0 +1,145 @@
+/*
+ * 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.os;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import static org.junit.Assert.assertEquals;
+
+import android.content.Context;
+import android.os.Parcel;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoAnnotations;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Test BatteryStatsHistory.
+ */
+@RunWith(AndroidJUnit4.class)
+public class BatteryStatsHistoryTest {
+ private static final int MAX_HISTORY_FILES = 32;
+ private final BatteryStatsImpl mBatteryStatsImpl = new MockBatteryStatsImpl();
+ private final Parcel mHistoryBuffer = Parcel.obtain();
+ private File mSystemDir;
+ private File mHistoryDir;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ Context context = InstrumentationRegistry.getContext();
+ mSystemDir = context.getDataDir();
+ mHistoryDir = new File(mSystemDir, BatteryStatsHistory.HISTORY_DIR);
+ mHistoryDir.delete();
+ }
+
+ @Test
+ public void testConstruct() {
+ BatteryStatsHistory history =
+ new BatteryStatsHistory(mBatteryStatsImpl, mSystemDir, mHistoryBuffer);
+ verifyFileNumbers(history, Arrays.asList(0));
+ verifyActiveFile(history, "0.bin");
+ }
+
+ @Test
+ public void testCreateNextFile() {
+ BatteryStatsHistory history =
+ new BatteryStatsHistory(mBatteryStatsImpl, mSystemDir, mHistoryBuffer);
+
+ List<Integer> fileList = new ArrayList<>();
+ fileList.add(0);
+
+ // create file 1 to 31.
+ for (int i = 1; i < MAX_HISTORY_FILES; i++) {
+ fileList.add(i);
+ history.createNextFile();
+ verifyFileNumbers(history, fileList);
+ verifyActiveFile(history, i + ".bin");
+ }
+
+ // create file 32
+ history.createNextFile();
+ fileList.add(32);
+ fileList.remove(0);
+ // verify file 0 is deleted.
+ verifyFileDeleted("0.bin");
+ verifyFileNumbers(history, fileList);
+ verifyActiveFile(history, "32.bin");
+
+ // create file 33
+ history.createNextFile();
+ // verify file 1 is deleted
+ fileList.add(33);
+ fileList.remove(0);
+ verifyFileDeleted("1.bin");
+ verifyFileNumbers(history, fileList);
+ verifyActiveFile(history, "33.bin");
+
+ assertEquals(0, history.getHistoryUsedSize());
+
+ // create a new BatteryStatsHistory object, it will pick up existing history files.
+ BatteryStatsHistory history2 =
+ new BatteryStatsHistory(mBatteryStatsImpl, mSystemDir, mHistoryBuffer);
+ // verify construct can pick up all files from file system.
+ verifyFileNumbers(history2, fileList);
+ verifyActiveFile(history2, "33.bin");
+
+ history2.resetAllFiles();
+ // verify all existing files are deleted.
+ for (int i = 2; i < 33; ++i) {
+ verifyFileDeleted(i + ".bin");
+ }
+
+ // verify file 0 is created
+ verifyFileNumbers(history2, Arrays.asList(0));
+ verifyActiveFile(history2, "0.bin");
+
+ // create file 1.
+ history2.createNextFile();
+ verifyFileNumbers(history2, Arrays.asList(0, 1));
+ verifyActiveFile(history2, "1.bin");
+ }
+
+ private void verifyActiveFile(BatteryStatsHistory history, String file) {
+ final File expectedFile = new File(mHistoryDir, file);
+ assertEquals(expectedFile.getPath(), history.getActiveFile().getBaseFile().getPath());
+ assertTrue(expectedFile.exists());
+ }
+
+ private void verifyFileNumbers(BatteryStatsHistory history, List<Integer> fileList) {
+ assertEquals(fileList.size(), history.getFilesNumbers().size());
+ for (int i = 0; i < fileList.size(); i++) {
+ assertEquals(fileList.get(i), history.getFilesNumbers().get(i));
+ final File expectedFile =
+ new File(mHistoryDir, fileList.get(i) + ".bin");
+ assertTrue(expectedFile.exists());
+ }
+ }
+
+ private void verifyFileDeleted(String file) {
+ assertFalse(new File(mHistoryDir, file).exists());
+ }
+}
\ No newline at end of file
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 94492ba..b798042 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
@@ -26,6 +26,7 @@
BatteryStatsDualTimerTest.class,
BatteryStatsDurationTimerTest.class,
BatteryStatsHelperTest.class,
+ BatteryStatsHistoryTest.class,
BatteryStatsImplTest.class,
BatteryStatsNoteTest.class,
BatteryStatsSamplingTimerTest.class,
diff --git a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
index b7fef13..d46c154 100644
--- a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
@@ -20,14 +20,19 @@
import android.platform.test.annotations.Presubmit;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
+import android.util.ArrayMap;
import android.util.SparseArray;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.io.PrintWriter;
+import java.io.StringWriter;
import java.util.Arrays;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import static org.junit.Assert.assertEquals;
@@ -41,7 +46,9 @@
@Test
public void testDetailedOff() {
- TestBinderCallsStats bcs = new TestBinderCallsStats(false);
+ TestBinderCallsStats bcs = new TestBinderCallsStats();
+ bcs.setDetailedTracking(false);
+
Binder binder = new Binder();
BinderCallsStats.CallSession callSession = bcs.callStarted(binder, 1);
bcs.time += 10;
@@ -93,7 +100,9 @@
@Test
public void testDetailedOn() {
- TestBinderCallsStats bcs = new TestBinderCallsStats(true);
+ TestBinderCallsStats bcs = new TestBinderCallsStats();
+ bcs.setDetailedTracking(true);
+
Binder binder = new Binder();
BinderCallsStats.CallSession callSession = bcs.callStarted(binder, 1);
bcs.time += 10;
@@ -140,8 +149,106 @@
}
@Test
+ public void testDisabled() {
+ TestBinderCallsStats bcs = new TestBinderCallsStats();
+ bcs.setEnabled(false);
+
+ Binder binder = new Binder();
+ BinderCallsStats.CallSession callSession = bcs.callStarted(binder, 1);
+ bcs.time += 10;
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+
+ SparseArray<BinderCallsStats.UidEntry> uidEntries = bcs.getUidEntries();
+ assertEquals(0, uidEntries.size());
+ }
+
+ @Test
+ public void testDisableInBetweenCall() {
+ TestBinderCallsStats bcs = new TestBinderCallsStats();
+ bcs.setEnabled(true);
+
+ Binder binder = new Binder();
+ BinderCallsStats.CallSession callSession = bcs.callStarted(binder, 1);
+ bcs.time += 10;
+ bcs.setEnabled(false);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+
+ SparseArray<BinderCallsStats.UidEntry> uidEntries = bcs.getUidEntries();
+ assertEquals(0, uidEntries.size());
+ }
+
+ @Test
+ public void testEnableInBetweenCall() {
+ TestBinderCallsStats bcs = new TestBinderCallsStats();
+ bcs.setEnabled(false);
+
+ Binder binder = new Binder();
+ BinderCallsStats.CallSession callSession = bcs.callStarted(binder, 1);
+ bcs.time += 10;
+ bcs.setEnabled(true);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+
+ SparseArray<BinderCallsStats.UidEntry> uidEntries = bcs.getUidEntries();
+ assertEquals(0, uidEntries.size());
+ }
+
+ @Test
+ public void testSampling() {
+ TestBinderCallsStats bcs = new TestBinderCallsStats();
+ bcs.setDetailedTracking(false);
+ bcs.setSamplingInterval(2);
+
+ Binder binder = new Binder();
+ BinderCallsStats.CallSession callSession = bcs.callStarted(binder, 1);
+ bcs.time += 10;
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+
+ callSession = bcs.callStarted(binder, 1);
+ bcs.time += 1000; // shoud be ignored.
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+
+ callSession = bcs.callStarted(binder, 1);
+ bcs.time += 50;
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+
+ SparseArray<BinderCallsStats.UidEntry> uidEntries = bcs.getUidEntries();
+ assertEquals(1, uidEntries.size());
+ BinderCallsStats.UidEntry uidEntry = uidEntries.get(TEST_UID);
+ Assert.assertNotNull(uidEntry);
+ assertEquals(3, uidEntry.callCount);
+ assertEquals(70, uidEntry.cpuTimeMicros);
+ assertEquals("Detailed tracking off - no entries should be returned",
+ 0, uidEntry.getCallStatsList().size());
+
+ BinderCallsStats.UidEntry sampledEntries = bcs.getSampledEntries();
+ List<BinderCallsStats.CallStat> sampledCallStatsList = sampledEntries.getCallStatsList();
+ assertEquals(1, sampledCallStatsList.size());
+ }
+
+ @Test
+ public void testTransactionCodeResolved() {
+ TestBinderCallsStats bcs = new TestBinderCallsStats();
+ bcs.setDetailedTracking(true);
+ Binder binder = new Binder() {
+ @Override
+ public String getTransactionName(int code) {
+ return "resolved";
+ }
+ };
+ BinderCallsStats.CallSession callSession = bcs.callStarted(binder, 1);
+ bcs.time += 10;
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+
+ List<BinderCallsStats.CallStat> callStatsList =
+ bcs.getUidEntries().get(TEST_UID).getCallStatsList();
+ assertEquals(1, callStatsList.get(0).msg);
+ assertEquals("resolved", callStatsList.get(0).methodName);
+ }
+
+ @Test
public void testParcelSize() {
- TestBinderCallsStats bcs = new TestBinderCallsStats(true);
+ TestBinderCallsStats bcs = new TestBinderCallsStats();
+ bcs.setDetailedTracking(true);
Binder binder = new Binder();
BinderCallsStats.CallSession callSession = bcs.callStarted(binder, 1);
bcs.time += 10;
@@ -155,6 +262,44 @@
}
@Test
+ public void testMaxCpu() {
+ TestBinderCallsStats bcs = new TestBinderCallsStats();
+ bcs.setDetailedTracking(true);
+ Binder binder = new Binder();
+ BinderCallsStats.CallSession callSession = bcs.callStarted(binder, 1);
+ bcs.time += 50;
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+
+ callSession = bcs.callStarted(binder, 1);
+ bcs.time += 10;
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+
+ List<BinderCallsStats.CallStat> callStatsList =
+ bcs.getUidEntries().get(TEST_UID).getCallStatsList();
+
+ assertEquals(50, callStatsList.get(0).maxCpuTimeMicros);
+ }
+
+ @Test
+ public void testMaxLatency() {
+ TestBinderCallsStats bcs = new TestBinderCallsStats();
+ bcs.setDetailedTracking(true);
+ Binder binder = new Binder();
+ BinderCallsStats.CallSession callSession = bcs.callStarted(binder, 1);
+ bcs.elapsedTime += 5;
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+
+ callSession = bcs.callStarted(binder, 1);
+ bcs.elapsedTime += 1;
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+
+ List<BinderCallsStats.CallStat> callStatsList =
+ bcs.getUidEntries().get(TEST_UID).getCallStatsList();
+
+ assertEquals(5, callStatsList.get(0).maxLatencyMicros);
+ }
+
+ @Test
public void testGetHighestValues() {
List<Integer> list = Arrays.asList(1, 2, 3, 4);
List<Integer> highestValues = BinderCallsStats
@@ -162,12 +307,84 @@
assertEquals(Arrays.asList(4, 3, 2), highestValues);
}
+ @Test
+ public void testExceptionCount() {
+ TestBinderCallsStats bcs = new TestBinderCallsStats();
+ bcs.setDetailedTracking(true);
+ Binder binder = new Binder();
+ BinderCallsStats.CallSession callSession = bcs.callStarted(binder, 1);
+ bcs.callThrewException(callSession, new IllegalStateException());
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+
+ callSession = bcs.callStarted(binder, 1);
+ bcs.callThrewException(callSession, new IllegalStateException());
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+
+ callSession = bcs.callStarted(binder, 1);
+ bcs.callThrewException(callSession, new RuntimeException());
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+
+ ArrayMap<String, Integer> expected = new ArrayMap<>();
+ expected.put("java.lang.IllegalStateException", 2);
+ expected.put("java.lang.RuntimeException", 1);
+ assertEquals(expected, bcs.getExceptionCounts());
+ }
+
+ @Test
+ public void testDumpDoesNotThrowException() {
+ TestBinderCallsStats bcs = new TestBinderCallsStats();
+ bcs.setDetailedTracking(true);
+ Binder binder = new Binder();
+ BinderCallsStats.CallSession callSession = bcs.callStarted(binder, 1);
+ bcs.callThrewException(callSession, new IllegalStateException());
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+
+ PrintWriter pw = new PrintWriter(new StringWriter());
+ bcs.dump(pw, new HashMap<>(), true);
+ }
+
+ @Test
+ public void testGetExportedStatsWhenDetailedTrackingDisabled() {
+ TestBinderCallsStats bcs = new TestBinderCallsStats();
+ bcs.setDetailedTracking(false);
+ Binder binder = new Binder();
+ BinderCallsStats.CallSession callSession = bcs.callStarted(binder, 1);
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+
+ assertEquals(0, bcs.getExportedCallStats().size());
+ }
+
+ @Test
+ public void testGetExportedStatsWhenDetailedTrackingEnabled() {
+ TestBinderCallsStats bcs = new TestBinderCallsStats();
+ bcs.setDetailedTracking(true);
+ Binder binder = new Binder();
+ BinderCallsStats.CallSession callSession = bcs.callStarted(binder, 1);
+ bcs.time += 10;
+ bcs.elapsedTime += 20;
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+
+ assertEquals(1, bcs.getExportedCallStats().size());
+ BinderCallsStats.ExportedCallStat stat = bcs.getExportedCallStats().get(0);
+ assertEquals(TEST_UID, stat.uid);
+ assertEquals("android.os.Binder", stat.className);
+ assertEquals("1", stat.methodName);
+ assertEquals(10, stat.cpuTimeMicros);
+ assertEquals(10, stat.maxCpuTimeMicros);
+ assertEquals(20, stat.latencyMicros);
+ assertEquals(20, stat.maxLatencyMicros);
+ assertEquals(1, stat.callCount);
+ assertEquals(REQUEST_SIZE, stat.maxRequestSizeBytes);
+ assertEquals(REPLY_SIZE, stat.maxReplySizeBytes);
+ assertEquals(0, stat.exceptionCount);
+ }
+
static class TestBinderCallsStats extends BinderCallsStats {
int callingUid = TEST_UID;
long time = 1234;
+ long elapsedTime = 0;
- TestBinderCallsStats(boolean detailedTracking) {
- super(detailedTracking);
+ TestBinderCallsStats() {
}
@Override
@@ -176,6 +393,11 @@
}
@Override
+ protected long getElapsedRealtimeMicro() {
+ return elapsedTime;
+ }
+
+ @Override
protected int getCallingUid() {
return callingUid;
}
diff --git a/core/tests/coretests/src/com/android/internal/util/DumpUtilsTest.java b/core/tests/coretests/src/com/android/internal/util/DumpUtilsTest.java
index 45b19bc..a44b860 100644
--- a/core/tests/coretests/src/com/android/internal/util/DumpUtilsTest.java
+++ b/core/tests/coretests/src/com/android/internal/util/DumpUtilsTest.java
@@ -15,8 +15,11 @@
*/
package com.android.internal.util;
+import static com.android.internal.util.DumpUtils.CRITICAL_SECTION_COMPONENTS;
import static com.android.internal.util.DumpUtils.filterRecord;
import static com.android.internal.util.DumpUtils.isNonPlatformPackage;
+import static com.android.internal.util.DumpUtils.isPlatformCriticalPackage;
+import static com.android.internal.util.DumpUtils.isPlatformNonCriticalPackage;
import static com.android.internal.util.DumpUtils.isPlatformPackage;
import android.content.ComponentName;
@@ -25,7 +28,7 @@
/**
* Run with:
- atest /android/pi-dev/frameworks/base/core/tests/coretests/src/com/android/internal/util/DumpTest.java
+ atest FrameworksCoreTests:DumpUtilsTest
*/
public class DumpUtilsTest extends TestCase {
@@ -89,6 +92,32 @@
assertTrue(isNonPlatformPackage(wcn("com.google.def/abc")));
}
+ public void testIsPlatformCriticalPackage() {
+ for (final ComponentName componentName : CRITICAL_SECTION_COMPONENTS) {
+ assertTrue(isPlatformCriticalPackage(() -> componentName));
+ assertTrue(isPlatformPackage(componentName));
+ }
+ assertFalse(isPlatformCriticalPackage(wcn("com.google.p/abc")));
+ assertFalse(isPlatformCriticalPackage(wcn("com.android.def/abc")));
+ assertFalse(isPlatformCriticalPackage(wcn("com.android.abc")));
+ assertFalse(isPlatformCriticalPackage(wcn("com.android")));
+ assertFalse(isPlatformCriticalPackage(wcn(null)));
+ assertFalse(isPlatformCriticalPackage(null));
+ }
+
+ public void testIsPlatformNonCriticalPackage() {
+ for (final ComponentName componentName : CRITICAL_SECTION_COMPONENTS) {
+ assertFalse(isPlatformNonCriticalPackage(() -> componentName));
+ }
+ assertTrue(isPlatformNonCriticalPackage(wcn("android/abc")));
+ assertTrue(isPlatformNonCriticalPackage(wcn("android.abc/abc")));
+ assertTrue(isPlatformNonCriticalPackage(wcn("com.android.def/abc")));
+
+ assertFalse(isPlatformNonCriticalPackage(wcn("com.google.def/abc")));
+ assertFalse(isPlatformNonCriticalPackage(wcn(null)));
+ assertFalse(isPlatformNonCriticalPackage(null));
+ }
+
public void testFilterRecord() {
assertFalse(filterRecord(null).test(wcn("com.google.p/abc")));
assertFalse(filterRecord(null).test(wcn("com.android.p/abc")));
@@ -105,6 +134,19 @@
assertFalse(filterRecord("all-non-platform").test(wcn("com.android.p/abc")));
assertFalse(filterRecord("all-non-platform").test(wcn(null)));
+ for (final ComponentName componentName : CRITICAL_SECTION_COMPONENTS) {
+ assertTrue(filterRecord("all-platform-critical").test((() -> componentName)));
+ assertFalse(filterRecord("all-platform-non-critical").test((() -> componentName)));
+ assertTrue(filterRecord("all-platform").test((() -> componentName)));
+ }
+ assertFalse(filterRecord("all-platform-critical").test(wcn("com.google.p/abc")));
+ assertFalse(filterRecord("all-platform-critical").test(wcn("com.android.p/abc")));
+ assertFalse(filterRecord("all-platform-critical").test(wcn(null)));
+
+ assertTrue(filterRecord("all-platform-non-critical").test(wcn("com.android.p/abc")));
+ assertFalse(filterRecord("all-platform-non-critical").test(wcn("com.google.p/abc")));
+ assertFalse(filterRecord("all-platform-non-critical").test(wcn(null)));
+
// Partial string match.
assertTrue(filterRecord("abc").test(wcn("com.google.p/.abc")));
assertFalse(filterRecord("abc").test(wcn("com.google.p/.def")));
diff --git a/core/tests/HdmiCec/Android.mk b/core/tests/hdmitests/Android.mk
similarity index 84%
rename from core/tests/HdmiCec/Android.mk
rename to core/tests/hdmitests/Android.mk
index 450068b..e0d2c09 100644
--- a/core/tests/HdmiCec/Android.mk
+++ b/core/tests/hdmitests/Android.mk
@@ -13,18 +13,19 @@
# limitations under the License.
LOCAL_PATH := $(call my-dir)
-
include $(CLEAR_VARS)
+LOCAL_MODULE_TAGS := tests
+
# Include all test java files
LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_MODULE_TAGS := tests
-LOCAL_PACKAGE_NAME := HdmiCecTests
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test frameworks-base-testutils
-LOCAL_SDK_VERSION := current
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_PACKAGE_NAME := HdmiCecTests
+LOCAL_PRIVATE_PLATFORM_APIS := true
+
LOCAL_CERTIFICATE := platform
-LOCAL_COMPATIBILITY_SUITE := device-tests
include $(BUILD_PACKAGE)
diff --git a/core/tests/HdmiCec/AndroidManifest.xml b/core/tests/hdmitests/AndroidManifest.xml
similarity index 80%
rename from core/tests/HdmiCec/AndroidManifest.xml
rename to core/tests/hdmitests/AndroidManifest.xml
index 80129d0d..1460b41 100644
--- a/core/tests/HdmiCec/AndroidManifest.xml
+++ b/core/tests/hdmitests/AndroidManifest.xml
@@ -1,23 +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="android.test.example.helloworld"
+ package="android.hardware.hdmi"
android:sharedUserId="android.uid.system" >
- <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="21" />
+
<application>
<uses-library android:name="android.test.runner" />
</application>
+
<instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
- android:targetPackage="android.test.example.helloworld"
- android:label="Hello World Test"/>
-</manifest>
+ android:targetPackage="android.hardware.hdmi"
+ android:label="HDMI CEC Tests"/>
+
+</manifest>
\ No newline at end of file
diff --git a/core/tests/HdmiCec/AndroidTest.xml b/core/tests/hdmitests/AndroidTest.xml
similarity index 63%
rename from core/tests/HdmiCec/AndroidTest.xml
rename to core/tests/hdmitests/AndroidTest.xml
index 5af30fb..7ef672d 100644
--- a/core/tests/HdmiCec/AndroidTest.xml
+++ b/core/tests/hdmitests/AndroidTest.xml
@@ -1,28 +1,34 @@
<?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.
-->
-<configuration description="Runs sample instrumentation test.">
+
+<configuration description="Runs HDMI CEC Tests.">
<option name="test-suite-tag" value="apct"/>
<option name="test-suite-tag" value="apct-instrumentation"/>
- <target_preparer class="com.android.tradefed.targetprep.TestFilePushSetup"/>
- <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
- <option name="test-file-name" value="HelloWorldTests.apk"/>
+
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="HdmiCecTests.apk" />
</target_preparer>
- <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"/>
- <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"/>
+
<option name="test-suite-tag" value="apct"/>
- <option name="test-tag" value="SampleInstrumentationTest"/>
- <test class="com.android.tradefed.testtype.AndroidJUnitTest">
- <option name="package" value="android.test.example.helloworld"/>
+ <option name="test-tag" value="HdmiTests"/>
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="android.hardware.hdmi" />
+ <option name="hidden-api-checks" value="false"/>
<option name="runner" value="android.support.test.runner.AndroidJUnitRunner"/>
</test>
-</configuration>
+</configuration>
\ No newline at end of file
diff --git a/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java b/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java
new file mode 100644
index 0000000..7b76a08
--- /dev/null
+++ b/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java
@@ -0,0 +1,320 @@
+/*
+ * 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.hardware.hdmi;
+
+import android.os.Handler;
+import android.os.test.TestLooper;
+import android.support.test.filters.SmallTest;
+import android.util.Log;
+import java.util.List;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * Tests for {@link HdmiAudioSystemClient}
+ */
+@RunWith(JUnit4.class)
+@SmallTest
+public class HdmiAudioSystemClientTest {
+ private static final String TAG = "HdmiAudioSystemClientTe";
+
+ private HdmiAudioSystemClient mHdmiAudioSystemClient;
+ private TestLooper mTestLooper;
+ private int mVolume;
+ private int mMaxVolume;
+ private boolean mIsMute;
+
+ @Before
+ public void before() {
+ Log.d(TAG, "before()");
+ IHdmiControlService mService = new TestHdmiControlService();
+ mTestLooper = new TestLooper();
+ mHdmiAudioSystemClient =
+ new HdmiAudioSystemClient(mService, new Handler(mTestLooper.getLooper()));
+ resetVariables();
+ }
+
+ @Test
+ public void testSingleCommand() {
+ mHdmiAudioSystemClient.sendReportAudioStatusCecCommand(false, 50, 100, false);
+ assertAudioStatus(50, 100, false);
+ }
+
+ @Test
+ public void testMultipleCommands_longTimeBetweenCalls() {
+ mHdmiAudioSystemClient.sendReportAudioStatusCecCommand(false, 50, 100, false);
+ assertAudioStatus(50, 100, false);
+ mTestLooper.moveTimeForward(500);
+ mTestLooper.dispatchAll();
+ mHdmiAudioSystemClient.sendReportAudioStatusCecCommand(false, 60, 100, false);
+ assertAudioStatus(60, 100, false);
+ }
+
+ @Test
+ public void testMultipleCommands_shortTimeBetweenCalls() {
+ mHdmiAudioSystemClient.sendReportAudioStatusCecCommand(false, 1, 100, false);
+ assertAudioStatus(1, 100, false);
+
+ mTestLooper.moveTimeForward(100); // current time: 100ms
+ mTestLooper.dispatchAll();
+ mHdmiAudioSystemClient.sendReportAudioStatusCecCommand(false, 10, 100, false);
+ assertAudioStatus(1, 100, false); // command not sent, no change
+
+ mTestLooper.moveTimeForward(100); // current time: 200ms
+ mTestLooper.dispatchAll();
+ mHdmiAudioSystemClient.sendReportAudioStatusCecCommand(false, 20, 100, false);
+ assertAudioStatus(1, 100, false); // command not sent, no change
+
+ mTestLooper.moveTimeForward(300); // current time: 500ms
+ mTestLooper.dispatchAll();
+ assertAudioStatus(20, 100, false); // pending command sent, changed to 20
+
+ mTestLooper.moveTimeForward(100); // current time: 600ms
+ mTestLooper.dispatchAll();
+ mHdmiAudioSystemClient.sendReportAudioStatusCecCommand(false, 60, 100, false);
+ assertAudioStatus(20, 100, false); // command not sent, no change
+
+ mTestLooper.moveTimeForward(200); // current time: 800ms
+ mTestLooper.dispatchAll();
+ mHdmiAudioSystemClient.sendReportAudioStatusCecCommand(false, 80, 100, false);
+ assertAudioStatus(20, 100, false); // command not sent, no change
+
+ mTestLooper.moveTimeForward(200); // current time: 1000ms
+ mTestLooper.dispatchAll();
+ assertAudioStatus(80, 100, false); // command sent, changed to 80
+ }
+
+ @Test
+ public void testMultipleCommands_shortTimeAndReturn() {
+ mHdmiAudioSystemClient.sendReportAudioStatusCecCommand(false, 1, 100, false);
+ assertAudioStatus(1, 100, false);
+
+ mTestLooper.moveTimeForward(100); // current time: 100ms
+ mTestLooper.dispatchAll();
+ mHdmiAudioSystemClient.sendReportAudioStatusCecCommand(false, 20, 100, false);
+ assertAudioStatus(1, 100, false); // command not sent, no change
+
+ mTestLooper.moveTimeForward(100); // current time: 200ms
+ mTestLooper.dispatchAll();
+ mHdmiAudioSystemClient.sendReportAudioStatusCecCommand(false, 1, 100, false);
+ assertAudioStatus(1, 100, false); // command not sent, no change
+
+ mTestLooper.moveTimeForward(300); // current time: 500ms
+ mTestLooper.dispatchAll();
+ assertAudioStatus(1, 100, false); // pending command sent
+ }
+
+ @Test
+ public void testMultipleCommands_muteAdjust() {
+ mHdmiAudioSystemClient.sendReportAudioStatusCecCommand(false, 1, 100, false);
+ assertAudioStatus(1, 100, false);
+
+ mTestLooper.moveTimeForward(100); // current time: 100ms
+ mTestLooper.dispatchAll();
+ mHdmiAudioSystemClient.sendReportAudioStatusCecCommand(true, 10, 100, true);
+ assertAudioStatus(10, 100, true); // mute adjust, command sent, changed to 10
+
+ mTestLooper.moveTimeForward(100); // current time: 200ms
+ mTestLooper.dispatchAll();
+ mHdmiAudioSystemClient.sendReportAudioStatusCecCommand(false, 20, 100, false);
+ assertAudioStatus(10, 100, true); // command not sent, no change
+
+ mTestLooper.moveTimeForward(300); // current time: 500ms
+ mTestLooper.dispatchAll();
+ assertAudioStatus(20, 100, false); // pending command sent, changed to 20, unmuted
+ }
+
+ private void assertAudioStatus(int volume, int maxVolume, boolean isMute) {
+ Assert.assertEquals(volume, mVolume);
+ Assert.assertEquals(maxVolume, mMaxVolume);
+ Assert.assertEquals(isMute, mIsMute);
+ }
+
+ private void resetVariables() {
+ mVolume = -1;
+ mMaxVolume = -1;
+ mIsMute = true;
+ }
+
+ private final class TestHdmiControlService extends IHdmiControlService.Stub {
+
+ @Override
+ public int[] getSupportedTypes() {
+ return null;
+ }
+
+ @Override
+ public HdmiDeviceInfo getActiveSource() {
+ return null;
+ }
+
+ @Override
+ public void deviceSelect(final int deviceId, final IHdmiControlCallback callback) {
+ }
+
+ @Override
+ public void portSelect(final int portId, final IHdmiControlCallback callback) {
+ }
+
+ @Override
+ public void sendKeyEvent(final int deviceType, final int keyCode, final boolean isPressed) {
+ }
+
+ @Override
+ public void oneTouchPlay(final IHdmiControlCallback callback) {
+ }
+
+ @Override
+ public void queryDisplayStatus(final IHdmiControlCallback callback) {
+ }
+
+ @Override
+ public void addHotplugEventListener(final IHdmiHotplugEventListener listener) {
+ }
+
+ @Override
+ public void removeHotplugEventListener(final IHdmiHotplugEventListener listener) {
+ }
+
+ @Override
+ public void addDeviceEventListener(final IHdmiDeviceEventListener listener) {
+ }
+
+ @Override
+ public List<HdmiPortInfo> getPortInfo() {
+ return null;
+ }
+
+ @Override
+ public boolean canChangeSystemAudioMode() {
+ return false;
+ }
+
+ @Override
+ public boolean getSystemAudioMode() {
+ return false;
+ }
+
+ @Override
+ public void setSystemAudioMode(final boolean enabled, final IHdmiControlCallback callback) {
+ }
+
+ @Override
+ public void addSystemAudioModeChangeListener(
+ final IHdmiSystemAudioModeChangeListener listener) {
+ }
+
+ @Override
+ public void removeSystemAudioModeChangeListener(
+ final IHdmiSystemAudioModeChangeListener listener) {
+ }
+
+ @Override
+ public void setInputChangeListener(final IHdmiInputChangeListener listener) {
+ }
+
+ @Override
+ public List<HdmiDeviceInfo> getInputDevices() {
+ return null;
+ }
+
+ // Returns all the CEC devices on the bus including system audio, switch,
+ // even those of reserved type.
+ @Override
+ public List<HdmiDeviceInfo> getDeviceList() {
+ return null;
+ }
+
+ @Override
+ public void setSystemAudioVolume(final int oldIndex, final int newIndex,
+ final int maxIndex) {
+ }
+
+ @Override
+ public void setSystemAudioMute(final boolean mute) {
+ }
+
+ @Override
+ public void setArcMode(final boolean enabled) {
+ }
+
+ @Override
+ public void setProhibitMode(final boolean enabled) {
+ }
+
+ @Override
+ public void addVendorCommandListener(final IHdmiVendorCommandListener listener,
+ final int deviceType) {
+ }
+
+ @Override
+ public void sendVendorCommand(final int deviceType, final int targetAddress,
+ final byte[] params, final boolean hasVendorId) {
+ }
+
+ @Override
+ public void sendStandby(final int deviceType, final int deviceId) {
+ }
+
+ @Override
+ public void setHdmiRecordListener(IHdmiRecordListener listener) {
+ }
+
+ @Override
+ public void startOneTouchRecord(final int recorderAddress, final byte[] recordSource) {
+ }
+
+ @Override
+ public void stopOneTouchRecord(final int recorderAddress) {
+ }
+
+ @Override
+ public void startTimerRecording(final int recorderAddress, final int sourceType,
+ final byte[] recordSource) {
+ }
+
+ @Override
+ public void clearTimerRecording(final int recorderAddress, final int sourceType,
+ final byte[] recordSource) {
+ }
+
+ @Override
+ public void sendMhlVendorCommand(final int portId, final int offset, final int length,
+ final byte[] data) {
+ }
+
+ @Override
+ public void addHdmiMhlVendorCommandListener(IHdmiMhlVendorCommandListener listener) {
+ }
+
+ @Override
+ public void setStandbyMode(final boolean isStandbyModeOn) {
+ }
+
+ @Override
+ public void reportAudioStatus(final int deviceType, final int volume, final int maxVolume,
+ final boolean isMute) {
+ mVolume = volume;
+ mMaxVolume = maxVolume;
+ mIsMute = isMute;
+ }
+ }
+
+}
diff --git a/core/tests/utiltests/src/com/android/internal/util/ArrayUtilsTest.java b/core/tests/utiltests/src/com/android/internal/util/ArrayUtilsTest.java
index 433d4d2..6464ad3 100644
--- a/core/tests/utiltests/src/com/android/internal/util/ArrayUtilsTest.java
+++ b/core/tests/utiltests/src/com/android/internal/util/ArrayUtilsTest.java
@@ -16,9 +16,8 @@
package com.android.internal.util;
-import android.test.MoreAsserts;
+import static org.junit.Assert.assertArrayEquals;
-import java.util.Arrays;
import junit.framework.TestCase;
/**
@@ -92,29 +91,29 @@
}
public void testAppendInt() throws Exception {
- MoreAsserts.assertEquals(new int[] { 1 },
+ assertArrayEquals(new int[] { 1 },
ArrayUtils.appendInt(null, 1));
- MoreAsserts.assertEquals(new int[] { 1 },
+ assertArrayEquals(new int[] { 1 },
ArrayUtils.appendInt(new int[] { }, 1));
- MoreAsserts.assertEquals(new int[] { 1, 2 },
+ assertArrayEquals(new int[] { 1, 2 },
ArrayUtils.appendInt(new int[] { 1 }, 2));
- MoreAsserts.assertEquals(new int[] { 1, 2 },
+ assertArrayEquals(new int[] { 1, 2 },
ArrayUtils.appendInt(new int[] { 1, 2 }, 1));
}
public void testRemoveInt() throws Exception {
assertNull(ArrayUtils.removeInt(null, 1));
- MoreAsserts.assertEquals(new int[] { },
+ assertArrayEquals(new int[] { },
ArrayUtils.removeInt(new int[] { }, 1));
- MoreAsserts.assertEquals(new int[] { 1, 2, 3, },
+ assertArrayEquals(new int[] { 1, 2, 3, },
ArrayUtils.removeInt(new int[] { 1, 2, 3}, 4));
- MoreAsserts.assertEquals(new int[] { 2, 3, },
+ assertArrayEquals(new int[] { 2, 3, },
ArrayUtils.removeInt(new int[] { 1, 2, 3}, 1));
- MoreAsserts.assertEquals(new int[] { 1, 3, },
+ assertArrayEquals(new int[] { 1, 3, },
ArrayUtils.removeInt(new int[] { 1, 2, 3}, 2));
- MoreAsserts.assertEquals(new int[] { 1, 2, },
+ assertArrayEquals(new int[] { 1, 2, },
ArrayUtils.removeInt(new int[] { 1, 2, 3}, 3));
- MoreAsserts.assertEquals(new int[] { 2, 3, 1 },
+ assertArrayEquals(new int[] { 2, 3, 1 },
ArrayUtils.removeInt(new int[] { 1, 2, 3, 1 }, 1));
}
@@ -129,30 +128,51 @@
}
public void testAppendLong() throws Exception {
- MoreAsserts.assertEquals(new long[] { 1 },
+ assertArrayEquals(new long[] { 1 },
ArrayUtils.appendLong(null, 1));
- MoreAsserts.assertEquals(new long[] { 1 },
+ assertArrayEquals(new long[] { 1 },
ArrayUtils.appendLong(new long[] { }, 1));
- MoreAsserts.assertEquals(new long[] { 1, 2 },
+ assertArrayEquals(new long[] { 1, 2 },
ArrayUtils.appendLong(new long[] { 1 }, 2));
- MoreAsserts.assertEquals(new long[] { 1, 2 },
+ assertArrayEquals(new long[] { 1, 2 },
ArrayUtils.appendLong(new long[] { 1, 2 }, 1));
}
public void testRemoveLong() throws Exception {
assertNull(ArrayUtils.removeLong(null, 1));
- MoreAsserts.assertEquals(new long[] { },
+ assertArrayEquals(new long[] { },
ArrayUtils.removeLong(new long[] { }, 1));
- MoreAsserts.assertEquals(new long[] { 1, 2, 3, },
+ assertArrayEquals(new long[] { 1, 2, 3, },
ArrayUtils.removeLong(new long[] { 1, 2, 3}, 4));
- MoreAsserts.assertEquals(new long[] { 2, 3, },
+ assertArrayEquals(new long[] { 2, 3, },
ArrayUtils.removeLong(new long[] { 1, 2, 3}, 1));
- MoreAsserts.assertEquals(new long[] { 1, 3, },
+ assertArrayEquals(new long[] { 1, 3, },
ArrayUtils.removeLong(new long[] { 1, 2, 3}, 2));
- MoreAsserts.assertEquals(new long[] { 1, 2, },
+ assertArrayEquals(new long[] { 1, 2, },
ArrayUtils.removeLong(new long[] { 1, 2, 3}, 3));
- MoreAsserts.assertEquals(new long[] { 2, 3, 1 },
+ assertArrayEquals(new long[] { 2, 3, 1 },
ArrayUtils.removeLong(new long[] { 1, 2, 3, 1 }, 1));
}
+ public void testConcatEmpty() throws Exception {
+ assertArrayEquals(new Long[] {},
+ ArrayUtils.concat(Long.class, null, null));
+ assertArrayEquals(new Long[] {},
+ ArrayUtils.concat(Long.class, new Long[] {}, null));
+ assertArrayEquals(new Long[] {},
+ ArrayUtils.concat(Long.class, null, new Long[] {}));
+ assertArrayEquals(new Long[] {},
+ ArrayUtils.concat(Long.class, new Long[] {}, new Long[] {}));
+ }
+
+ public void testConcat() throws Exception {
+ assertArrayEquals(new Long[] { 1L },
+ ArrayUtils.concat(Long.class, new Long[] { 1L }, new Long[] {}));
+ assertArrayEquals(new Long[] { 1L },
+ ArrayUtils.concat(Long.class, new Long[] {}, new Long[] { 1L }));
+ assertArrayEquals(new Long[] { 1L, 2L },
+ ArrayUtils.concat(Long.class, new Long[] { 1L }, new Long[] { 2L }));
+ assertArrayEquals(new Long[] { 1L, 2L, 3L, 4L },
+ ArrayUtils.concat(Long.class, new Long[] { 1L, 2L }, new Long[] { 3L, 4L }));
+ }
}
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 82b6a22..434af14 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -330,6 +330,7 @@
<permission name="android.permission.START_TASKS_FROM_RECENTS" />
<permission name="android.permission.STOP_APP_SWITCHES"/>
<permission name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME"/>
+ <permission name="android.permission.SUSPEND_APPS" />
<permission name="android.permission.UPDATE_APP_OPS_STATS"/>
<permission name="android.permission.USE_RESERVED_DISK"/>
<permission name="android.permission.WRITE_MEDIA_STORAGE"/>
diff --git a/data/sounds/AllAudio.mk b/data/sounds/AllAudio.mk
index bf8067c..bb8add1 100644
--- a/data/sounds/AllAudio.mk
+++ b/data/sounds/AllAudio.mk
@@ -234,3 +234,8 @@
$(LOCAL_PATH)/effects/ogg/camera_focus.ogg:system/media/audio/ui/camera_focus.ogg \
$(LOCAL_PATH)/effects/ogg/ChargingStarted.ogg:system/media/audio/ui/ChargingStarted.ogg \
$(LOCAL_PATH)/effects/ogg/InCallNotification.ogg:system/media/audio/ui/InCallNotification.ogg \
+ $(LOCAL_PATH)/effects/ogg/NFCFailure.ogg:system/media/audio/ui/NFCFailure.ogg \
+ $(LOCAL_PATH)/effects/ogg/NFCInitiated.ogg:system/media/audio/ui/NFCInitiated.ogg \
+ $(LOCAL_PATH)/effects/ogg/NFCSuccess.ogg:system/media/audio/ui/NFCSuccess.ogg \
+ $(LOCAL_PATH)/effects/ogg/NFCTransferComplete.ogg:system/media/audio/ui/NFCTransferComplete.ogg \
+ $(LOCAL_PATH)/effects/ogg/NFCTransferInitiated.ogg:system/media/audio/ui/NFCTransferInitiated.ogg \
diff --git a/data/sounds/AudioPackage14.mk b/data/sounds/AudioPackage14.mk
new file mode 100644
index 0000000..c903a2b
--- /dev/null
+++ b/data/sounds/AudioPackage14.mk
@@ -0,0 +1,32 @@
+#
+# Audio Package 14 - P
+#
+# Include this file in a product makefile to include these audio files
+#
+#
+
+LOCAL_PATH := frameworks/base/data/sounds
+
+# Simple files that do not require renaming
+ALARM_FILES := Argon Carbon Helium Krypton Neon Oxygen Osmium Platinum Timer
+NOTIFICATION_FILES := Ariel Ceres Carme Elara Europa Iapetus Io Rhea Salacia Titan Tethys
+RINGTONE_FILES := Atria Callisto Dione Ganymede Luna Oberon Phobos Pyxis Sedna Titania Triton \
+ Umbriel
+EFFECT_FILES := Effect_Tick KeypressReturn KeypressInvalid KeypressDelete KeypressSpacebar KeypressStandard \
+ camera_focus Dock Undock Lock Unlock Trusted ChargingStarted InCallNotification \
+ NFCFailure NFCInitiated NFCSuccess NFCTransferComplete NFCTransferInitiated
+MATERIAL_EFFECT_FILES := camera_click VideoRecord WirelessChargingStarted LowBattery VideoStop
+
+PRODUCT_COPY_FILES += $(foreach fn,$(ALARM_FILES),\
+ $(LOCAL_PATH)/alarms/material/ogg/$(fn).ogg:system/media/audio/alarms/$(fn).ogg)
+
+PRODUCT_COPY_FILES += $(foreach fn,$(NOTIFICATION_FILES),\
+ $(LOCAL_PATH)/notifications/material/ogg/$(fn).ogg:system/media/audio/notifications/$(fn).ogg)
+
+PRODUCT_COPY_FILES += $(foreach fn,$(RINGTONE_FILES),\
+ $(LOCAL_PATH)/ringtones/material/ogg/$(fn).ogg:system/media/audio/ringtones/$(fn).ogg)
+
+PRODUCT_COPY_FILES += $(foreach fn,$(EFFECT_FILES),\
+ $(LOCAL_PATH)/effects/ogg/$(fn).ogg:system/media/audio/ui/$(fn).ogg)
+PRODUCT_COPY_FILES += $(foreach fn,$(MATERIAL_EFFECT_FILES),\
+ $(LOCAL_PATH)/effects/material/ogg/$(fn).ogg:system/media/audio/ui/$(fn).ogg)
diff --git a/data/sounds/README.txt b/data/sounds/README.txt
index 193fd71..db20319 100644
--- a/data/sounds/README.txt
+++ b/data/sounds/README.txt
@@ -31,3 +31,13 @@
./effects/ogg/VideoStop_48k.ogg
unused
+NFC
+---
+
+./effects/ogg/NFCFailure.ogg
+./effects/ogg/NFCInitiated.ogg
+./effects/ogg/NFCSuccess.ogg
+./effects/ogg/NFCTransferComplete.ogg
+./effects/ogg/NFCTransferInitiated.ogg
+
+referenced in AudioPackage14.mk (= AudioPackage13.mk + NFC sounds).
diff --git a/data/sounds/effects/ogg/NFCFailure.ogg b/data/sounds/effects/ogg/NFCFailure.ogg
new file mode 100644
index 0000000..e9ee662
--- /dev/null
+++ b/data/sounds/effects/ogg/NFCFailure.ogg
Binary files differ
diff --git a/data/sounds/effects/ogg/NFCInitiated.ogg b/data/sounds/effects/ogg/NFCInitiated.ogg
new file mode 100644
index 0000000..a86319f
--- /dev/null
+++ b/data/sounds/effects/ogg/NFCInitiated.ogg
Binary files differ
diff --git a/data/sounds/effects/ogg/NFCSuccess.ogg b/data/sounds/effects/ogg/NFCSuccess.ogg
new file mode 100644
index 0000000..39dfd1f
--- /dev/null
+++ b/data/sounds/effects/ogg/NFCSuccess.ogg
Binary files differ
diff --git a/data/sounds/effects/ogg/NFCTransferComplete.ogg b/data/sounds/effects/ogg/NFCTransferComplete.ogg
new file mode 100644
index 0000000..f00cd98
--- /dev/null
+++ b/data/sounds/effects/ogg/NFCTransferComplete.ogg
Binary files differ
diff --git a/data/sounds/effects/ogg/NFCTransferInitiated.ogg b/data/sounds/effects/ogg/NFCTransferInitiated.ogg
new file mode 100644
index 0000000..7be1bcb
--- /dev/null
+++ b/data/sounds/effects/ogg/NFCTransferInitiated.ogg
Binary files differ
diff --git a/graphics/java/android/graphics/Point.java b/graphics/java/android/graphics/Point.java
index c6b6c66..f291e27 100644
--- a/graphics/java/android/graphics/Point.java
+++ b/graphics/java/android/graphics/Point.java
@@ -16,6 +16,7 @@
package android.graphics;
+import android.annotation.NonNull;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.proto.ProtoOutputStream;
@@ -37,7 +38,7 @@
this.y = y;
}
- public Point(Point src) {
+ public Point(@NonNull Point src) {
this.x = src.x;
this.y = src.y;
}
@@ -99,7 +100,7 @@
}
/** @hide */
- public void printShortString(PrintWriter pw) {
+ public void printShortString(@NonNull PrintWriter pw) {
pw.print("["); pw.print(x); pw.print(","); pw.print(y); pw.print("]");
}
@@ -130,7 +131,7 @@
* @param fieldId Field Id of the Rect as defined in the parent message
* @hide
*/
- public void writeToProto(ProtoOutputStream protoOutputStream, long fieldId) {
+ public void writeToProto(@NonNull ProtoOutputStream protoOutputStream, long fieldId) {
final long token = protoOutputStream.start(fieldId);
protoOutputStream.write(PointProto.X, x);
protoOutputStream.write(PointProto.Y, y);
@@ -141,6 +142,7 @@
/**
* Return a new point from the data in the specified parcel.
*/
+ @Override
public Point createFromParcel(Parcel in) {
Point r = new Point();
r.readFromParcel(in);
@@ -150,6 +152,7 @@
/**
* Return an array of rectangles of the specified size.
*/
+ @Override
public Point[] newArray(int size) {
return new Point[size];
}
@@ -161,7 +164,7 @@
*
* @param in The parcel to read the point's coordinates from
*/
- public void readFromParcel(Parcel in) {
+ public void readFromParcel(@NonNull Parcel in) {
x = in.readInt();
y = in.readInt();
}
diff --git a/graphics/java/android/graphics/PointF.java b/graphics/java/android/graphics/PointF.java
index 8e4288e..a3b4194 100644
--- a/graphics/java/android/graphics/PointF.java
+++ b/graphics/java/android/graphics/PointF.java
@@ -16,10 +16,10 @@
package android.graphics;
+import android.annotation.NonNull;
import android.os.Parcel;
import android.os.Parcelable;
-
/**
* PointF holds two float coordinates
*/
@@ -34,7 +34,7 @@
this.y = y;
}
- public PointF(Point p) {
+ public PointF(@NonNull Point p) {
this.x = p.x;
this.y = p.y;
}
@@ -50,7 +50,7 @@
/**
* Set the point's x and y coordinates to the coordinates of p
*/
- public final void set(PointF p) {
+ public final void set(@NonNull PointF p) {
this.x = p.x;
this.y = p.y;
}
@@ -134,6 +134,7 @@
/**
* Return a new point from the data in the specified parcel.
*/
+ @Override
public PointF createFromParcel(Parcel in) {
PointF r = new PointF();
r.readFromParcel(in);
@@ -143,6 +144,7 @@
/**
* Return an array of rectangles of the specified size.
*/
+ @Override
public PointF[] newArray(int size) {
return new PointF[size];
}
@@ -154,7 +156,7 @@
*
* @param in The parcel to read the point's coordinates from
*/
- public void readFromParcel(Parcel in) {
+ public void readFromParcel(@NonNull Parcel in) {
x = in.readFloat();
y = in.readFloat();
}
diff --git a/graphics/java/android/graphics/Region.java b/graphics/java/android/graphics/Region.java
index dca6d9e..b27fadd 100644
--- a/graphics/java/android/graphics/Region.java
+++ b/graphics/java/android/graphics/Region.java
@@ -16,6 +16,7 @@
package android.graphics;
+import android.annotation.NonNull;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Pools.SynchronizedPool;
@@ -59,14 +60,14 @@
/** Return a copy of the specified region
*/
- public Region(Region region) {
+ public Region(@NonNull Region region) {
this(nativeConstructor());
nativeSetRegion(mNativeRegion, region.mNativeRegion);
}
/** Return a region set to the specified rectangle
*/
- public Region(Rect r) {
+ public Region(@NonNull Rect r) {
mNativeRegion = nativeConstructor();
nativeSetRect(mNativeRegion, r.left, r.top, r.right, r.bottom);
}
@@ -86,14 +87,14 @@
/** Set the region to the specified region.
*/
- public boolean set(Region region) {
+ public boolean set(@NonNull Region region) {
nativeSetRegion(mNativeRegion, region.mNativeRegion);
return true;
}
/** Set the region to the specified rectangle
*/
- public boolean set(Rect r) {
+ public boolean set(@NonNull Rect r) {
return nativeSetRect(mNativeRegion, r.left, r.top, r.right, r.bottom);
}
@@ -109,7 +110,7 @@
* that is identical to the pixels that would be drawn by the path
* (with no antialiasing).
*/
- public boolean setPath(Path path, Region clip) {
+ public boolean setPath(@NonNull Path path, @NonNull Region clip) {
return nativeSetPath(mNativeRegion, path.readOnlyNI(), clip.mNativeRegion);
}
@@ -132,6 +133,7 @@
* Return a new Rect set to the bounds of the region. If the region is
* empty, the Rect will be set to [0, 0, 0, 0]
*/
+ @NonNull
public Rect getBounds() {
Rect r = new Rect();
nativeGetBounds(mNativeRegion, r);
@@ -142,7 +144,7 @@
* Set the Rect to the bounds of the region. If the region is empty, the
* Rect will be set to [0, 0, 0, 0]
*/
- public boolean getBounds(Rect r) {
+ public boolean getBounds(@NonNull Rect r) {
if (r == null) {
throw new NullPointerException();
}
@@ -153,6 +155,7 @@
* Return the boundary of the region as a new Path. If the region is empty,
* the path will also be empty.
*/
+ @NonNull
public Path getBoundaryPath() {
Path path = new Path();
nativeGetBoundaryPath(mNativeRegion, path.mutateNI());
@@ -163,7 +166,7 @@
* Set the path to the boundary of the region. If the region is empty, the
* path will also be empty.
*/
- public boolean getBoundaryPath(Path path) {
+ public boolean getBoundaryPath(@NonNull Path path) {
return nativeGetBoundaryPath(mNativeRegion, path.mutateNI());
}
@@ -178,7 +181,7 @@
* that the rectangle is not contained by this region, but return true is a
* guarantee that the rectangle is contained by this region.
*/
- public boolean quickContains(Rect r) {
+ public boolean quickContains(@NonNull Rect r) {
return quickContains(r.left, r.top, r.right, r.bottom);
}
@@ -196,7 +199,7 @@
* not intersect the region. Returning false is not a guarantee that they
* intersect, but returning true is a guarantee that they do not.
*/
- public boolean quickReject(Rect r) {
+ public boolean quickReject(@NonNull Rect r) {
return quickReject(r.left, r.top, r.right, r.bottom);
}
@@ -247,7 +250,7 @@
*/
public native void scale(float scale, Region dst);
- public final boolean union(Rect r) {
+ public final boolean union(@NonNull Rect r) {
return op(r, Op.UNION);
}
@@ -255,7 +258,7 @@
* Perform the specified Op on this region and the specified rect. Return
* true if the result of the op is not empty.
*/
- public boolean op(Rect r, Op op) {
+ public boolean op(@NonNull Rect r, @NonNull Op op) {
return nativeOp(mNativeRegion, r.left, r.top, r.right, r.bottom,
op.nativeInt);
}
@@ -264,7 +267,7 @@
* Perform the specified Op on this region and the specified rect. Return
* true if the result of the op is not empty.
*/
- public boolean op(int left, int top, int right, int bottom, Op op) {
+ public boolean op(int left, int top, int right, int bottom, @NonNull Op op) {
return nativeOp(mNativeRegion, left, top, right, bottom,
op.nativeInt);
}
@@ -273,7 +276,7 @@
* Perform the specified Op on this region and the specified region. Return
* true if the result of the op is not empty.
*/
- public boolean op(Region region, Op op) {
+ public boolean op(@NonNull Region region, @NonNull Op op) {
return op(this, region, op);
}
@@ -281,7 +284,7 @@
* Set this region to the result of performing the Op on the specified rect
* and region. Return true if the result is not empty.
*/
- public boolean op(Rect rect, Region region, Op op) {
+ public boolean op(@NonNull Rect rect, @NonNull Region region, @NonNull Op op) {
return nativeOp(mNativeRegion, rect, region.mNativeRegion,
op.nativeInt);
}
@@ -290,11 +293,12 @@
* Set this region to the result of performing the Op on the specified
* regions. Return true if the result is not empty.
*/
- public boolean op(Region region1, Region region2, Op op) {
+ public boolean op(@NonNull Region region1, @NonNull Region region2, @NonNull Op op) {
return nativeOp(mNativeRegion, region1.mNativeRegion,
region2.mNativeRegion, op.nativeInt);
}
+ @Override
public String toString() {
return nativeToString(mNativeRegion);
}
@@ -304,6 +308,7 @@
*
* @hide
*/
+ @NonNull
public static Region obtain() {
Region region = sPool.acquire();
return (region != null) ? region : new Region();
@@ -316,7 +321,8 @@
*
* @hide
*/
- public static Region obtain(Region other) {
+ @NonNull
+ public static Region obtain(@NonNull Region other) {
Region region = obtain();
region.set(other);
return region;
@@ -341,6 +347,7 @@
* @param p Parcel object to read the region from
* @return a new region created from the data in the parcel
*/
+ @Override
public Region createFromParcel(Parcel p) {
long ni = nativeCreateFromParcel(p);
if (ni == 0) {
@@ -348,11 +355,13 @@
}
return new Region(ni);
}
+ @Override
public Region[] newArray(int size) {
return new Region[size];
}
};
+ @Override
public int describeContents() {
return 0;
}
@@ -362,6 +371,7 @@
* rebuilt from the parcel by calling CREATOR.createFromParcel().
* @param p Parcel object to write the region data into
*/
+ @Override
public void writeToParcel(Parcel p, int flags) {
if (!nativeWriteToParcel(mNativeRegion, p)) {
throw new RuntimeException();
@@ -377,6 +387,7 @@
return nativeEquals(mNativeRegion, peer.mNativeRegion);
}
+ @Override
protected void finalize() throws Throwable {
try {
nativeDestructor(mNativeRegion);
diff --git a/graphics/java/android/graphics/fonts/FontFileUtil.java b/graphics/java/android/graphics/fonts/FontFileUtil.java
new file mode 100644
index 0000000..d15f581
--- /dev/null
+++ b/graphics/java/android/graphics/fonts/FontFileUtil.java
@@ -0,0 +1,127 @@
+/*
+ * 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.graphics.fonts;
+
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+/**
+ * Provides a utility for font file operations.
+ * @hide
+ */
+public class FontFileUtil {
+
+ private FontFileUtil() {} // Do not instanciate
+
+ /**
+ * Unpack the weight value from packed integer.
+ */
+ public static int unpackWeight(int packed) {
+ return packed & 0xFFFF;
+ }
+
+ /**
+ * Unpack the italic value from packed integer.
+ */
+ public static boolean unpackItalic(int packed) {
+ return (packed & 0x10000) != 0;
+ }
+
+ private static int pack(@IntRange(from = 0, to = 1000) int weight, boolean italic) {
+ return weight | (italic ? 0x10000 : 0);
+ }
+
+ private static final int SFNT_VERSION_1 = 0x00010000;
+ private static final int SFNT_VERSION_OTTO = 0x4F54544F;
+ private static final int TTC_TAG = 0x74746366;
+ private static final int OS2_TABLE_TAG = 0x4F532F32;
+
+ /**
+ * Analyze the font file returns packed style info
+ */
+ public static final int analyzeStyle(@NonNull ByteBuffer buffer,
+ @IntRange(from = 0) int ttcIndex, @Nullable FontVariationAxis[] varSettings)
+ throws IOException {
+ int weight = -1;
+ int italic = -1;
+ if (varSettings != null) {
+ for (FontVariationAxis axis :varSettings) {
+ if ("wght".equals(axis.getTag())) {
+ weight = (int) axis.getStyleValue();
+ } else if ("ital".equals(axis.getTag())) {
+ italic = (axis.getStyleValue() == 1.0f) ? 1 : 0;
+ }
+ }
+ }
+
+ if (weight != -1 && italic != -1) {
+ // Both weight/italic style are specifeid by variation settings.
+ // No need to look into OS/2 table.
+ // TODO: Good to look HVAR table to check if this font supports wght/ital axes.
+ return pack(weight, italic == 1);
+ }
+
+ ByteOrder originalOrder = buffer.order();
+ buffer.order(ByteOrder.BIG_ENDIAN);
+ try {
+ int fontFileOffset = 0;
+ int magicNumber = buffer.getInt(0);
+ if (magicNumber == TTC_TAG) {
+ // TTC file.
+ if (ttcIndex >= buffer.getInt(8 /* offset to number of fonts in TTC */)) {
+ throw new IOException("Font index out of bounds");
+ }
+ fontFileOffset = buffer.getInt(
+ 12 /* offset to array of offsets of font files */ + 4 * ttcIndex);
+ }
+ int sfntVersion = buffer.getInt(fontFileOffset);
+
+ if (sfntVersion != SFNT_VERSION_1 && sfntVersion != SFNT_VERSION_OTTO) {
+ throw new IOException("Unknown font file format");
+ }
+
+ int numTables = buffer.getShort(fontFileOffset + 4 /* offset to number of tables */);
+ int os2TableOffset = -1;
+ for (int i = 0; i < numTables; ++i) {
+ int tableOffset = fontFileOffset + 12 /* size of offset table */
+ + i * 16 /* size of table record */;
+ if (buffer.getInt(tableOffset) == OS2_TABLE_TAG) {
+ os2TableOffset = buffer.getInt(tableOffset + 8 /* offset to the table */);
+ break;
+ }
+ }
+
+ if (os2TableOffset == -1) {
+ // Couldn't find OS/2 table. use regular style
+ return pack(400, false);
+ }
+
+ int weightFromOS2 = buffer.getShort(os2TableOffset + 4 /* offset to weight class */);
+ boolean italicFromOS2 =
+ (buffer.getShort(os2TableOffset + 62 /* offset to fsSelection */) & 1) != 0;
+ return pack(weight == -1 ? weightFromOS2 : weight,
+ italic == -1 ? italicFromOS2 : italic == 1);
+ } finally {
+ buffer.order(originalOrder);
+ }
+ }
+}
diff --git a/keystore/java/android/security/KeyChain.java b/keystore/java/android/security/KeyChain.java
index 030fa60..78dbb6a 100644
--- a/keystore/java/android/security/KeyChain.java
+++ b/keystore/java/android/security/KeyChain.java
@@ -283,10 +283,16 @@
public static final int KEY_GEN_NO_KEYSTORE_PROVIDER = 5;
/**
+ * StrongBox unavailable when calling {@link #generateKeyPair}
+ * @hide
+ */
+ public static final int KEY_GEN_STRONGBOX_UNAVAILABLE = 6;
+
+ /**
* General failure while calling {@link #generateKeyPair}
* @hide
*/
- public static final int KEY_GEN_FAILURE = 6;
+ public static final int KEY_GEN_FAILURE = 7;
/**
* Successful call to {@link #attestKey}
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java
index 2b5a37b..4c007cb 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java
@@ -811,7 +811,7 @@
}
int errorCode = mKeyStore.importWrappedKey(
- Credentials.USER_SECRET_KEY + alias,
+ Credentials.USER_PRIVATE_KEY + alias,
entry.getWrappedKeyBytes(),
Credentials.USER_PRIVATE_KEY + entry.getWrappingKeyAlias(),
maskingKey,
diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
index 452024c..89d370f 100644
--- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
@@ -268,6 +268,11 @@
private final boolean mIsStrongBoxBacked;
private final boolean mUserConfirmationRequired;
private final boolean mUnlockedDeviceRequired;
+ /*
+ * ***NOTE***: All new fields MUST also be added to the following:
+ * ParcelableKeyGenParameterSpec class.
+ * The KeyGenParameterSpec.Builder constructor that takes a KeyGenParameterSpec
+ */
/**
* @hide should be built with Builder
@@ -791,6 +796,9 @@
mUniqueIdIncluded = sourceSpec.isUniqueIdIncluded();
mUserAuthenticationValidWhileOnBody = sourceSpec.isUserAuthenticationValidWhileOnBody();
mInvalidatedByBiometricEnrollment = sourceSpec.isInvalidatedByBiometricEnrollment();
+ mIsStrongBoxBacked = sourceSpec.isStrongBoxBacked();
+ mUserConfirmationRequired = sourceSpec.isUserConfirmationRequired();
+ mUnlockedDeviceRequired = sourceSpec.isUnlockedDeviceRequired();
}
/**
diff --git a/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java b/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java
index 911bbf8..8231dc9 100644
--- a/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java
@@ -97,11 +97,14 @@
out.writeBoolean(mSpec.isRandomizedEncryptionRequired());
out.writeBoolean(mSpec.isUserAuthenticationRequired());
out.writeInt(mSpec.getUserAuthenticationValidityDurationSeconds());
+ out.writeBoolean(mSpec.isUserPresenceRequired());
out.writeByteArray(mSpec.getAttestationChallenge());
out.writeBoolean(mSpec.isUniqueIdIncluded());
out.writeBoolean(mSpec.isUserAuthenticationValidWhileOnBody());
out.writeBoolean(mSpec.isInvalidatedByBiometricEnrollment());
- out.writeBoolean(mSpec.isUserPresenceRequired());
+ out.writeBoolean(mSpec.isStrongBoxBacked());
+ out.writeBoolean(mSpec.isUserConfirmationRequired());
+ out.writeBoolean(mSpec.isUnlockedDeviceRequired());
}
private static Date readDateOrNull(Parcel in) {
@@ -114,19 +117,12 @@
}
private ParcelableKeyGenParameterSpec(Parcel in) {
- String keystoreAlias = in.readString();
- int purposes = in.readInt();
- KeyGenParameterSpec.Builder builder = new KeyGenParameterSpec.Builder(
- keystoreAlias, purposes);
- builder.setUid(in.readInt());
- // KeySize is -1 by default, if the KeyGenParameterSpec previously parcelled had the default
- // value, do not set it as this will cause setKeySize to throw.
- int keySize = in.readInt();
- if (keySize >= 0) {
- builder.setKeySize(keySize);
- }
+ final String keystoreAlias = in.readString();
+ final int purposes = in.readInt();
+ final int uid = in.readInt();
+ final int keySize = in.readInt();
- int keySpecType = in.readInt();
+ final int keySpecType = in.readInt();
AlgorithmParameterSpec algorithmSpec = null;
if (keySpecType == ALGORITHM_PARAMETER_SPEC_NONE) {
algorithmSpec = null;
@@ -141,32 +137,60 @@
throw new IllegalArgumentException(
String.format("Unknown algorithm parameter spec: %d", keySpecType));
}
- if (algorithmSpec != null) {
- builder.setAlgorithmParameterSpec(algorithmSpec);
- }
- builder.setCertificateSubject(new X500Principal(in.createByteArray()));
- builder.setCertificateSerialNumber(new BigInteger(in.createByteArray()));
- builder.setCertificateNotBefore(new Date(in.readLong()));
- builder.setCertificateNotAfter(new Date(in.readLong()));
- builder.setKeyValidityStart(readDateOrNull(in));
- builder.setKeyValidityForOriginationEnd(readDateOrNull(in));
- builder.setKeyValidityForConsumptionEnd(readDateOrNull(in));
- String[] digests = in.createStringArray();
- if (digests != null) {
- builder.setDigests(digests);
- }
- builder.setEncryptionPaddings(in.createStringArray());
- builder.setSignaturePaddings(in.createStringArray());
- builder.setBlockModes(in.createStringArray());
- builder.setRandomizedEncryptionRequired(in.readBoolean());
- builder.setUserAuthenticationRequired(in.readBoolean());
- builder.setUserAuthenticationValidityDurationSeconds(in.readInt());
- builder.setAttestationChallenge(in.createByteArray());
- builder.setUniqueIdIncluded(in.readBoolean());
- builder.setUserAuthenticationValidWhileOnBody(in.readBoolean());
- builder.setInvalidatedByBiometricEnrollment(in.readBoolean());
- builder.setUserPresenceRequired(in.readBoolean());
- mSpec = builder.build();
+
+ final X500Principal certificateSubject = new X500Principal(in.createByteArray());
+ final BigInteger certificateSerialNumber = new BigInteger(in.createByteArray());
+ final Date certificateNotBefore = new Date(in.readLong());
+ final Date certificateNotAfter = new Date(in.readLong());
+ final Date keyValidityStartDate = readDateOrNull(in);
+ final Date keyValidityForOriginationEnd = readDateOrNull(in);
+ final Date keyValidityForConsumptionEnd = readDateOrNull(in);
+ final String[] digests = in.createStringArray();
+ final String[] encryptionPaddings = in.createStringArray();
+ final String[] signaturePaddings = in.createStringArray();
+ final String[] blockModes = in.createStringArray();
+ final boolean randomizedEncryptionRequired = in.readBoolean();
+ final boolean userAuthenticationRequired = in.readBoolean();
+ final int userAuthenticationValidityDurationSeconds = in.readInt();
+ final boolean userPresenceRequired = in.readBoolean();
+ final byte[] attestationChallenge = in.createByteArray();
+ final boolean uniqueIdIncluded = in.readBoolean();
+ final boolean userAuthenticationValidWhileOnBody = in.readBoolean();
+ final boolean invalidatedByBiometricEnrollment = in.readBoolean();
+ final boolean isStrongBoxBacked = in.readBoolean();
+ final boolean userConfirmationRequired = in.readBoolean();
+ final boolean unlockedDeviceRequired = in.readBoolean();
+ // The KeyGenParameterSpec is intentionally not constructed using a Builder here:
+ // The intention is for this class to break if new parameters are added to the
+ // KeyGenParameterSpec constructor (whereas using a builder would silently drop them).
+ mSpec = new KeyGenParameterSpec(
+ keystoreAlias,
+ uid,
+ keySize,
+ algorithmSpec,
+ certificateSubject,
+ certificateSerialNumber,
+ certificateNotBefore,
+ certificateNotAfter,
+ keyValidityStartDate,
+ keyValidityForOriginationEnd,
+ keyValidityForConsumptionEnd,
+ purposes,
+ digests,
+ encryptionPaddings,
+ signaturePaddings,
+ blockModes,
+ randomizedEncryptionRequired,
+ userAuthenticationRequired,
+ userAuthenticationValidityDurationSeconds,
+ userPresenceRequired,
+ attestationChallenge,
+ uniqueIdIncluded,
+ userAuthenticationValidWhileOnBody,
+ invalidatedByBiometricEnrollment,
+ isStrongBoxBacked,
+ userConfirmationRequired,
+ unlockedDeviceRequired);
}
public static final Creator<ParcelableKeyGenParameterSpec> CREATOR = new Creator<ParcelableKeyGenParameterSpec>() {
diff --git a/keystore/tests/src/android/security/ParcelableKeyGenParameterSpecTest.java b/keystore/tests/src/android/security/ParcelableKeyGenParameterSpecTest.java
index 254b6be..32f8ec4 100644
--- a/keystore/tests/src/android/security/ParcelableKeyGenParameterSpecTest.java
+++ b/keystore/tests/src/android/security/ParcelableKeyGenParameterSpecTest.java
@@ -77,6 +77,9 @@
.setUniqueIdIncluded(true)
.setUserAuthenticationValidWhileOnBody(true)
.setInvalidatedByBiometricEnrollment(true)
+ .setIsStrongBoxBacked(true)
+ .setUserConfirmationRequired(true)
+ .setUnlockedDeviceRequired(true)
.build();
}
@@ -105,6 +108,9 @@
assertThat(spec.isUniqueIdIncluded(), is(true));
assertThat(spec.isUserAuthenticationValidWhileOnBody(), is(true));
assertThat(spec.isInvalidatedByBiometricEnrollment(), is(true));
+ assertThat(spec.isStrongBoxBacked(), is(true));
+ assertThat(spec.isUserConfirmationRequired(), is(true));
+ assertThat(spec.isUnlockedDeviceRequired(), is(true));
}
private Parcel parcelForReading(ParcelableKeyGenParameterSpec spec) {
diff --git a/libs/androidfw/ChunkIterator.cpp b/libs/androidfw/ChunkIterator.cpp
index d8adbe5..8fc3219 100644
--- a/libs/androidfw/ChunkIterator.cpp
+++ b/libs/androidfw/ChunkIterator.cpp
@@ -32,11 +32,30 @@
if (len_ != 0) {
// Prepare the next chunk.
- VerifyNextChunk();
+ if (VerifyNextChunkNonFatal()) {
+ VerifyNextChunk();
+ }
}
return Chunk(this_chunk);
}
+// TODO(b/111401637) remove this and have full resource file verification
+// Returns false if there was an error.
+bool ChunkIterator::VerifyNextChunkNonFatal() {
+ if (len_ < sizeof(ResChunk_header)) {
+ last_error_ = "not enough space for header";
+ last_error_was_fatal_ = false;
+ return false;
+ }
+ const size_t size = dtohl(next_chunk_->size);
+ if (size > len_) {
+ last_error_ = "chunk size is bigger than given data";
+ last_error_was_fatal_ = false;
+ return false;
+ }
+ return true;
+}
+
// Returns false if there was an error.
bool ChunkIterator::VerifyNextChunk() {
const uintptr_t header_start = reinterpret_cast<uintptr_t>(next_chunk_);
diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp
index 04d506a..c2740c9 100644
--- a/libs/androidfw/LoadedArsc.cpp
+++ b/libs/androidfw/LoadedArsc.cpp
@@ -560,7 +560,9 @@
if (iter.HadError()) {
LOG(ERROR) << iter.GetLastError();
- return {};
+ if (iter.HadFatalError()) {
+ return {};
+ }
}
// Flatten and construct the TypeSpecs.
@@ -641,7 +643,9 @@
if (iter.HadError()) {
LOG(ERROR) << iter.GetLastError();
- return false;
+ if (iter.HadFatalError()) {
+ return false;
+ }
}
return true;
}
@@ -673,7 +677,9 @@
if (iter.HadError()) {
LOG(ERROR) << iter.GetLastError();
- return {};
+ if (iter.HadFatalError()) {
+ return {};
+ }
}
// Need to force a move for mingw32.
diff --git a/libs/androidfw/include/androidfw/Chunk.h b/libs/androidfw/include/androidfw/Chunk.h
index 89b588e..99a52dc 100644
--- a/libs/androidfw/include/androidfw/Chunk.h
+++ b/libs/androidfw/include/androidfw/Chunk.h
@@ -94,18 +94,27 @@
Chunk Next();
inline bool HasNext() const { return !HadError() && len_ != 0; };
+ // Returns whether there was an error and processing should stop
inline bool HadError() const { return last_error_ != nullptr; }
inline std::string GetLastError() const { return last_error_; }
+ // Returns whether there was an error and processing should stop. For legacy purposes,
+ // some errors are considered "non fatal". Fatal errors stop processing new chunks and
+ // throw away any chunks already processed. Non fatal errors also stop processing new
+ // chunks, but, will retain and use any valid chunks already processed.
+ inline bool HadFatalError() const { return HadError() && last_error_was_fatal_; }
private:
DISALLOW_COPY_AND_ASSIGN(ChunkIterator);
// Returns false if there was an error.
bool VerifyNextChunk();
+ // Returns false if there was an error. For legacy purposes.
+ bool VerifyNextChunkNonFatal();
const ResChunk_header* next_chunk_;
size_t len_;
const char* last_error_;
+ bool last_error_was_fatal_ = true;
};
} // namespace android
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 0db7799..59760ab 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -48,6 +48,11 @@
device_uses_hwc2: {
cflags: ["-DUSE_HWC2"],
},
+ eng: {
+ lto: {
+ never: true,
+ },
+ },
},
}
@@ -198,6 +203,7 @@
"AnimatorManager.cpp",
"Caches.cpp",
"CanvasState.cpp",
+ "CanvasTransform.cpp",
"ClipArea.cpp",
"DamageAccumulator.cpp",
"DeferredLayerUpdater.cpp",
diff --git a/libs/hwui/CanvasTransform.cpp b/libs/hwui/CanvasTransform.cpp
new file mode 100644
index 0000000..bac7a4d
--- /dev/null
+++ b/libs/hwui/CanvasTransform.cpp
@@ -0,0 +1,113 @@
+/*
+ * 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 "CanvasTransform.h"
+#include "Properties.h"
+
+#include <SkColorFilter.h>
+#include <SkPaint.h>
+#include <log/log.h>
+
+namespace android::uirenderer {
+
+static SkColor makeLight(SkColor color) {
+ SkScalar hsv[3];
+ SkColorToHSV(color, hsv);
+ if (hsv[1] > .2f) return color;
+ // hsv[1] *= .85f;
+ // hsv[2] = std::min(1.0f, std::max(hsv[2], 1 - hsv[2]) * 1.3f);
+ hsv[2] = std::max(hsv[2], 1.1f - hsv[2]);
+ return SkHSVToColor(SkColorGetA(color), hsv);
+}
+
+static SkColor makeDark(SkColor color) {
+ SkScalar hsv[3];
+ SkColorToHSV(color, hsv);
+ if (hsv[1] > .2f) return color;
+ // hsv[1] *= .85f;
+ // hsv[2] = std::max(0.0f, std::min(hsv[2], 1 - hsv[2]) * .7f);
+ hsv[2] = std::min(hsv[2], 1.1f - hsv[2]);
+ return SkHSVToColor(SkColorGetA(color), hsv);
+}
+
+static SkColor transformColor(ColorTransform transform, SkColor color) {
+ switch (transform) {
+ case ColorTransform::Light:
+ return makeLight(color);
+ case ColorTransform::Dark:
+ return makeDark(color);
+ default:
+ return color;
+ }
+}
+
+static void applyColorTransform(ColorTransform transform, SkPaint& paint) {
+ if (transform == ColorTransform::None) return;
+
+ SkColor newColor = transformColor(transform, paint.getColor());
+ paint.setColor(newColor);
+
+ if (paint.getColorFilter()) {
+ SkBlendMode mode;
+ SkColor color;
+ // TODO: LRU this or something to avoid spamming new color mode filters
+ if (paint.getColorFilter()->asColorMode(&color, &mode)) {
+ color = transformColor(transform, color);
+ paint.setColorFilter(SkColorFilter::MakeModeFilter(color, mode));
+ }
+ }
+}
+
+class ColorFilterCanvas : public SkPaintFilterCanvas {
+public:
+ ColorFilterCanvas(ColorTransform transform, SkCanvas* canvas)
+ : SkPaintFilterCanvas(canvas), mTransform(transform) {}
+
+ bool onFilter(SkTCopyOnFirstWrite<SkPaint>* paint, Type type) const override {
+ if (*paint) {
+ applyColorTransform(mTransform, *(paint->writable()));
+ }
+ return true;
+ }
+
+private:
+ ColorTransform mTransform;
+};
+
+std::unique_ptr<SkCanvas> makeTransformCanvas(SkCanvas* inCanvas, ColorTransform transform) {
+ switch (transform) {
+ case ColorTransform::Light:
+ return std::make_unique<ColorFilterCanvas>(ColorTransform::Light, inCanvas);
+ case ColorTransform::Dark:
+ return std::make_unique<ColorFilterCanvas>(ColorTransform::Dark, inCanvas);
+ default:
+ return nullptr;
+ }
+}
+
+std::unique_ptr<SkCanvas> makeTransformCanvas(SkCanvas* inCanvas, UsageHint usageHint) {
+ if (Properties::forceDarkMode) {
+ switch (usageHint) {
+ case UsageHint::Unknown:
+ return makeTransformCanvas(inCanvas, ColorTransform::Light);
+ case UsageHint::Background:
+ return makeTransformCanvas(inCanvas, ColorTransform::Dark);
+ }
+ }
+ return nullptr;
+}
+
+}; // namespace android::uirenderer
\ No newline at end of file
diff --git a/libs/hwui/CanvasTransform.h b/libs/hwui/CanvasTransform.h
new file mode 100644
index 0000000..f71fdfa
--- /dev/null
+++ b/libs/hwui/CanvasTransform.h
@@ -0,0 +1,40 @@
+/*
+ * 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 <SkCanvas.h>
+#include <SkPaintFilterCanvas.h>
+
+#include <memory>
+
+namespace android::uirenderer {
+
+enum class UsageHint {
+ Unknown = 0,
+ Background = 1,
+};
+
+enum class ColorTransform {
+ None,
+ Light,
+ Dark,
+};
+
+std::unique_ptr<SkCanvas> makeTransformCanvas(SkCanvas* inCanvas, ColorTransform transform);
+std::unique_ptr<SkCanvas> makeTransformCanvas(SkCanvas* inCanvas, UsageHint usageHint);
+
+} // namespace android::uirenderer;
\ No newline at end of file
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp
index d284269..17bec19 100644
--- a/libs/hwui/Properties.cpp
+++ b/libs/hwui/Properties.cpp
@@ -17,6 +17,7 @@
#include "Properties.h"
#include "Debug.h"
#include "DeviceInfo.h"
+#include "SkTraceEventCommon.h"
#include <algorithm>
#include <cstdlib>
@@ -59,6 +60,7 @@
bool Properties::filterOutTestOverhead = false;
bool Properties::disableVsync = false;
bool Properties::skpCaptureEnabled = false;
+bool Properties::forceDarkMode = false;
bool Properties::enableRTAnimations = true;
bool Properties::runningInEmulator = false;
@@ -140,8 +142,13 @@
skpCaptureEnabled = debuggingEnabled && property_get_bool(PROPERTY_CAPTURE_SKP_ENABLED, false);
+ SkAndroidFrameworkTraceUtil::setEnableTracing(
+ property_get_bool(PROPERTY_SKIA_ATRACE_ENABLED, false));
+
runningInEmulator = property_get_bool(PROPERTY_QEMU_KERNEL, false);
+ forceDarkMode = property_get_bool(PROPERTY_FORCE_DARK, false);
+
return (prevDebugLayersUpdates != debugLayersUpdates) || (prevDebugOverdraw != debugOverdraw) ||
(prevDebugStencilClip != debugStencilClip);
}
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index 657f9aa..ea017a7 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -171,6 +171,11 @@
#define PROPERTY_CAPTURE_SKP_ENABLED "debug.hwui.capture_skp_enabled"
/**
+ * Allows to record Skia drawing commands with systrace.
+ */
+#define PROPERTY_SKIA_ATRACE_ENABLED "debug.hwui.skia_atrace_enabled"
+
+/**
* Defines how many frames in a sequence to capture.
*/
#define PROPERTY_CAPTURE_SKP_FRAMES "debug.hwui.capture_skp_frames"
@@ -185,6 +190,8 @@
*/
#define PROPERTY_QEMU_KERNEL "ro.kernel.qemu"
+#define PROPERTY_FORCE_DARK "debug.hwui.force_dark"
+
///////////////////////////////////////////////////////////////////////////////
// Misc
///////////////////////////////////////////////////////////////////////////////
@@ -258,6 +265,7 @@
static bool disableVsync;
static bool skpCaptureEnabled;
+ static bool forceDarkMode;
// For experimentation b/68769804
ANDROID_API static bool enableRTAnimations;
diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h
index dc962f3..8393288 100644
--- a/libs/hwui/RenderNode.h
+++ b/libs/hwui/RenderNode.h
@@ -28,6 +28,7 @@
#include <androidfw/ResourceTypes.h>
#include "AnimatorManager.h"
+#include "CanvasTransform.h"
#include "Debug.h"
#include "DisplayList.h"
#include "Matrix.h"
@@ -208,6 +209,14 @@
void output(std::ostream& output, uint32_t level);
+ void setUsageHint(UsageHint usageHint) {
+ mUsageHint = usageHint;
+ }
+
+ UsageHint usageHint() const {
+ return mUsageHint;
+ }
+
private:
void computeOrderingImpl(RenderNodeOp* opState,
std::vector<RenderNodeOp*>* compositedChildrenOfProjectionSurface,
@@ -263,6 +272,8 @@
sp<PositionListener> mPositionListener;
+ UsageHint mUsageHint = UsageHint::Unknown;
+
// METHODS & FIELDS ONLY USED BY THE SKIA RENDERER
public:
/**
diff --git a/libs/hwui/RenderProperties.cpp b/libs/hwui/RenderProperties.cpp
index e495744..ff9cf45 100644
--- a/libs/hwui/RenderProperties.cpp
+++ b/libs/hwui/RenderProperties.cpp
@@ -44,8 +44,8 @@
}
bool LayerProperties::setColorFilter(SkColorFilter* filter) {
- if (mColorFilter == filter) return false;
- SkRefCnt_SafeAssign(mColorFilter, filter);
+ if (mColorFilter.get() == filter) return false;
+ mColorFilter = sk_ref_sp(filter);
return true;
}
@@ -62,7 +62,7 @@
setOpaque(other.opaque());
setAlpha(other.alpha());
setXferMode(other.xferMode());
- setColorFilter(other.colorFilter());
+ setColorFilter(other.getColorFilter());
return *this;
}
diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h
index c024373..0766e3b 100644
--- a/libs/hwui/RenderProperties.h
+++ b/libs/hwui/RenderProperties.h
@@ -89,9 +89,7 @@
SkBlendMode xferMode() const { return mMode; }
- bool setColorFilter(SkColorFilter* filter);
-
- SkColorFilter* colorFilter() const { return mColorFilter; }
+ SkColorFilter* getColorFilter() const { return mColorFilter.get(); }
// Sets alpha, xfermode, and colorfilter from an SkPaint
// paint may be NULL, in which case defaults will be set
@@ -105,6 +103,7 @@
LayerProperties();
~LayerProperties();
void reset();
+ bool setColorFilter(SkColorFilter* filter);
// Private since external users should go through properties().effectiveLayerType()
LayerType type() const { return mType; }
@@ -116,7 +115,7 @@
bool mOpaque;
uint8_t mAlpha;
SkBlendMode mMode;
- SkColorFilter* mColorFilter = nullptr;
+ sk_sp<SkColorFilter> mColorFilter;
};
/*
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index 7b41f89..17f1a3b 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -21,6 +21,7 @@
#include "VectorDrawable.h"
#include "hwui/Bitmap.h"
#include "hwui/MinikinUtils.h"
+#include "hwui/PaintFilter.h"
#include "pipeline/skia/AnimatedDrawables.h"
#include <SkAnimatedImage.h>
@@ -28,7 +29,6 @@
#include <SkColorFilter.h>
#include <SkColorSpaceXformCanvas.h>
#include <SkDeque.h>
-#include <SkDrawFilter.h>
#include <SkDrawable.h>
#include <SkGraphics.h>
#include <SkImage.h>
@@ -40,6 +40,8 @@
#include <SkTextBlob.h>
#include <memory>
+#include <optional>
+#include <utility>
namespace android {
@@ -211,7 +213,7 @@
Clip(const SkRRect& rrect, SkClipOp op, const SkMatrix& m)
: mType(Type::RRect), mOp(op), mMatrix(m), mRRect(rrect) {}
Clip(const SkPath& path, SkClipOp op, const SkMatrix& m)
- : mType(Type::Path), mOp(op), mMatrix(m), mPath(&path) {}
+ : mType(Type::Path), mOp(op), mMatrix(m), mPath(std::in_place, path) {}
void apply(SkCanvas* canvas) const {
canvas->setMatrix(mMatrix);
@@ -223,7 +225,7 @@
canvas->clipRRect(mRRect, mOp);
break;
case Type::Path:
- canvas->clipPath(*mPath.get(), mOp);
+ canvas->clipPath(mPath.value(), mOp);
break;
}
}
@@ -240,7 +242,7 @@
SkMatrix mMatrix;
// These are logically a union (tracked separately due to non-POD path).
- SkTLazy<SkPath> mPath;
+ std::optional<SkPath> mPath;
SkRRect mRRect;
};
@@ -400,12 +402,12 @@
// Canvas state operations: Filters
// ----------------------------------------------------------------------------
-SkDrawFilter* SkiaCanvas::getDrawFilter() {
- return mCanvas->getDrawFilter();
+PaintFilter* SkiaCanvas::getPaintFilter() {
+ return mPaintFilter.get();
}
-void SkiaCanvas::setDrawFilter(SkDrawFilter* drawFilter) {
- mCanvas->setDrawFilter(drawFilter);
+void SkiaCanvas::setPaintFilter(sk_sp<PaintFilter> paintFilter) {
+ mPaintFilter = std::move(paintFilter);
}
// ----------------------------------------------------------------------------
@@ -439,8 +441,15 @@
mCanvas->drawColor(color, mode);
}
+SkiaCanvas::PaintCoW&& SkiaCanvas::filterPaint(PaintCoW&& paint) const {
+ if (mPaintFilter) {
+ mPaintFilter->filter(&paint.writeable());
+ }
+ return std::move(paint);
+}
+
void SkiaCanvas::drawPaint(const SkPaint& paint) {
- mCanvas->drawPaint(paint);
+ mCanvas->drawPaint(*filterPaint(paint));
}
// ----------------------------------------------------------------------------
@@ -457,53 +466,53 @@
pts[i].set(points[0], points[1]);
points += 2;
}
- mCanvas->drawPoints(mode, count, pts.get(), paint);
+ mCanvas->drawPoints(mode, count, pts.get(), *filterPaint(paint));
}
void SkiaCanvas::drawPoint(float x, float y, const SkPaint& paint) {
- mCanvas->drawPoint(x, y, paint);
+ mCanvas->drawPoint(x, y, *filterPaint(paint));
}
void SkiaCanvas::drawPoints(const float* points, int count, const SkPaint& paint) {
- this->drawPoints(points, count, paint, SkCanvas::kPoints_PointMode);
+ this->drawPoints(points, count, *filterPaint(paint), SkCanvas::kPoints_PointMode);
}
void SkiaCanvas::drawLine(float startX, float startY, float stopX, float stopY,
const SkPaint& paint) {
- mCanvas->drawLine(startX, startY, stopX, stopY, paint);
+ mCanvas->drawLine(startX, startY, stopX, stopY, *filterPaint(paint));
}
void SkiaCanvas::drawLines(const float* points, int count, const SkPaint& paint) {
if (CC_UNLIKELY(count < 4 || paint.nothingToDraw())) return;
- this->drawPoints(points, count, paint, SkCanvas::kLines_PointMode);
+ this->drawPoints(points, count, *filterPaint(paint), SkCanvas::kLines_PointMode);
}
void SkiaCanvas::drawRect(float left, float top, float right, float bottom, const SkPaint& paint) {
if (CC_UNLIKELY(paint.nothingToDraw())) return;
- mCanvas->drawRect({left, top, right, bottom}, paint);
+ mCanvas->drawRect({left, top, right, bottom}, *filterPaint(paint));
}
void SkiaCanvas::drawRegion(const SkRegion& region, const SkPaint& paint) {
if (CC_UNLIKELY(paint.nothingToDraw())) return;
- mCanvas->drawRegion(region, paint);
+ mCanvas->drawRegion(region, *filterPaint(paint));
}
void SkiaCanvas::drawRoundRect(float left, float top, float right, float bottom, float rx, float ry,
const SkPaint& paint) {
if (CC_UNLIKELY(paint.nothingToDraw())) return;
SkRect rect = SkRect::MakeLTRB(left, top, right, bottom);
- mCanvas->drawRoundRect(rect, rx, ry, paint);
+ mCanvas->drawRoundRect(rect, rx, ry, *filterPaint(paint));
}
void SkiaCanvas::drawCircle(float x, float y, float radius, const SkPaint& paint) {
if (CC_UNLIKELY(radius <= 0 || paint.nothingToDraw())) return;
- mCanvas->drawCircle(x, y, radius, paint);
+ mCanvas->drawCircle(x, y, radius, *filterPaint(paint));
}
void SkiaCanvas::drawOval(float left, float top, float right, float bottom, const SkPaint& paint) {
if (CC_UNLIKELY(paint.nothingToDraw())) return;
SkRect oval = SkRect::MakeLTRB(left, top, right, bottom);
- mCanvas->drawOval(oval, paint);
+ mCanvas->drawOval(oval, *filterPaint(paint));
}
void SkiaCanvas::drawArc(float left, float top, float right, float bottom, float startAngle,
@@ -511,9 +520,9 @@
if (CC_UNLIKELY(paint.nothingToDraw())) return;
SkRect arc = SkRect::MakeLTRB(left, top, right, bottom);
if (fabs(sweepAngle) >= 360.0f) {
- mCanvas->drawOval(arc, paint);
+ mCanvas->drawOval(arc, *filterPaint(paint));
} else {
- mCanvas->drawArc(arc, startAngle, sweepAngle, useCenter, paint);
+ mCanvas->drawArc(arc, startAngle, sweepAngle, useCenter, *filterPaint(paint));
}
}
@@ -522,19 +531,19 @@
if (CC_UNLIKELY(path.isEmpty() && (!path.isInverseFillType()))) {
return;
}
- mCanvas->drawPath(path, paint);
+ mCanvas->drawPath(path, *filterPaint(paint));
}
void SkiaCanvas::drawVertices(const SkVertices* vertices, SkBlendMode mode, const SkPaint& paint) {
- mCanvas->drawVertices(vertices, mode, paint);
+ mCanvas->drawVertices(vertices, mode, *filterPaint(paint));
}
// ----------------------------------------------------------------------------
// Canvas draw operations: Bitmaps
// ----------------------------------------------------------------------------
-const SkPaint* SkiaCanvas::addFilter(const SkPaint* origPaint, SkPaint* tmpPaint,
- sk_sp<SkColorFilter> colorSpaceFilter) {
+SkiaCanvas::PaintCoW&& SkiaCanvas::filterBitmap(PaintCoW&& paint,
+ sk_sp<SkColorFilter> colorSpaceFilter) const {
/* We don't apply the colorSpace filter if this canvas is already wrapped with
* a SkColorSpaceXformCanvas since it already takes care of converting the
* contents of the bitmap into the appropriate colorspace. The mCanvasWrapper
@@ -542,39 +551,31 @@
* to have a non-sRGB colorspace.
*/
if (!mCanvasWrapper && colorSpaceFilter) {
- if (origPaint) {
- *tmpPaint = *origPaint;
- }
-
- if (tmpPaint->getColorFilter()) {
- tmpPaint->setColorFilter(
- SkColorFilter::MakeComposeFilter(tmpPaint->refColorFilter(), colorSpaceFilter));
- LOG_ALWAYS_FATAL_IF(!tmpPaint->getColorFilter());
+ SkPaint& tmpPaint = paint.writeable();
+ if (tmpPaint.getColorFilter()) {
+ tmpPaint.setColorFilter(SkColorFilter::MakeComposeFilter(tmpPaint.refColorFilter(),
+ std::move(colorSpaceFilter)));
+ LOG_ALWAYS_FATAL_IF(!tmpPaint.getColorFilter());
} else {
- tmpPaint->setColorFilter(colorSpaceFilter);
+ tmpPaint.setColorFilter(std::move(colorSpaceFilter));
}
-
- return tmpPaint;
- } else {
- return origPaint;
}
+ return filterPaint(std::move(paint));
}
void SkiaCanvas::drawBitmap(Bitmap& bitmap, float left, float top, const SkPaint* paint) {
- SkPaint tmpPaint;
sk_sp<SkColorFilter> colorFilter;
sk_sp<SkImage> image = bitmap.makeImage(&colorFilter);
- mCanvas->drawImage(image, left, top, addFilter(paint, &tmpPaint, colorFilter));
+ mCanvas->drawImage(image, left, top, filterBitmap(paint, std::move(colorFilter)));
}
void SkiaCanvas::drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, const SkPaint* paint) {
SkAutoCanvasRestore acr(mCanvas, true);
mCanvas->concat(matrix);
- SkPaint tmpPaint;
sk_sp<SkColorFilter> colorFilter;
sk_sp<SkImage> image = bitmap.makeImage(&colorFilter);
- mCanvas->drawImage(image, 0, 0, addFilter(paint, &tmpPaint, colorFilter));
+ mCanvas->drawImage(image, 0, 0, filterBitmap(paint, std::move(colorFilter)));
}
void SkiaCanvas::drawBitmap(Bitmap& bitmap, float srcLeft, float srcTop, float srcRight,
@@ -583,10 +584,9 @@
SkRect srcRect = SkRect::MakeLTRB(srcLeft, srcTop, srcRight, srcBottom);
SkRect dstRect = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom);
- SkPaint tmpPaint;
sk_sp<SkColorFilter> colorFilter;
sk_sp<SkImage> image = bitmap.makeImage(&colorFilter);
- mCanvas->drawImageRect(image, srcRect, dstRect, addFilter(paint, &tmpPaint, colorFilter),
+ mCanvas->drawImageRect(image, srcRect, dstRect, filterBitmap(paint, std::move(colorFilter)),
SkCanvas::kFast_SrcRectConstraint);
}
@@ -665,21 +665,20 @@
#endif
// cons-up a shader for the bitmap
- SkPaint tmpPaint;
- if (paint) {
- tmpPaint = *paint;
- }
+ PaintCoW paintCoW(paint);
+ SkPaint& tmpPaint = paintCoW.writeable();
sk_sp<SkColorFilter> colorFilter;
sk_sp<SkImage> image = bitmap.makeImage(&colorFilter);
sk_sp<SkShader> shader =
image->makeShader(SkShader::kClamp_TileMode, SkShader::kClamp_TileMode);
if (colorFilter) {
- shader = shader->makeWithColorFilter(colorFilter);
+ shader = shader->makeWithColorFilter(std::move(colorFilter));
}
- tmpPaint.setShader(shader);
+ tmpPaint.setShader(std::move(shader));
- mCanvas->drawVertices(builder.detach(), SkBlendMode::kModulate, tmpPaint);
+ mCanvas->drawVertices(builder.detach(), SkBlendMode::kModulate,
+ *filterPaint(std::move(paintCoW)));
}
void SkiaCanvas::drawNinePatch(Bitmap& bitmap, const Res_png_9patch& chunk, float dstLeft,
@@ -706,10 +705,10 @@
lattice.fBounds = nullptr;
SkRect dst = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom);
- SkPaint tmpPaint;
sk_sp<SkColorFilter> colorFilter;
sk_sp<SkImage> image = bitmap.makeImage(&colorFilter);
- mCanvas->drawImageLattice(image.get(), lattice, dst, addFilter(paint, &tmpPaint, colorFilter));
+ mCanvas->drawImageLattice(image.get(), lattice, dst,
+ filterBitmap(paint, std::move(colorFilter)));
}
double SkiaCanvas::drawAnimatedImage(AnimatedImageDrawable* imgDrawable) {
@@ -732,6 +731,9 @@
// glyphs centered or right-aligned; the offset above takes
// care of all alignment.
SkPaint paintCopy(paint);
+ if (mPaintFilter) {
+ mPaintFilter->filter(&paintCopy);
+ }
paintCopy.setTextAlign(SkPaint::kLeft_Align);
SkASSERT(paintCopy.getTextEncoding() == SkPaint::kGlyphID_TextEncoding);
// Stroke with a hairline is drawn on HW with a fill style for compatibility with Android O and
@@ -760,6 +762,9 @@
// glyphs centered or right-aligned; the offsets take care of
// that portion of the alignment.
SkPaint paintCopy(paint);
+ if (mPaintFilter) {
+ mPaintFilter->filter(&paintCopy);
+ }
paintCopy.setTextAlign(SkPaint::kLeft_Align);
SkASSERT(paintCopy.getTextEncoding() == SkPaint::kGlyphID_TextEncoding);
diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h
index 3efc22a..24b7ec6 100644
--- a/libs/hwui/SkiaCanvas.h
+++ b/libs/hwui/SkiaCanvas.h
@@ -22,7 +22,9 @@
#include "hwui/Canvas.h"
#include <SkCanvas.h>
-#include <SkTLazy.h>
+
+#include <cassert>
+#include <optional>
namespace android {
@@ -87,8 +89,8 @@
virtual bool clipRect(float left, float top, float right, float bottom, SkClipOp op) override;
virtual bool clipPath(const SkPath* path, SkClipOp op) override;
- virtual SkDrawFilter* getDrawFilter() override;
- virtual void setDrawFilter(SkDrawFilter* drawFilter) override;
+ virtual PaintFilter* getPaintFilter() override;
+ virtual void setPaintFilter(sk_sp<PaintFilter> paintFilter) override;
virtual SkCanvasState* captureCanvasState() const override;
@@ -158,6 +160,46 @@
const SkPaint& paint, const SkPath& path, size_t start,
size_t end) override;
+ /** This class acts as a copy on write SkPaint.
+ *
+ * Initially this will be the SkPaint passed to the contructor.
+ * The first time writable() is called this will become a copy of the
+ * initial SkPaint (or a default SkPaint if nullptr).
+ */
+ struct PaintCoW {
+ PaintCoW(const SkPaint& that) : mPtr(&that) {}
+ PaintCoW(const SkPaint* ptr) : mPtr(ptr) {}
+ PaintCoW(const PaintCoW&) = delete;
+ PaintCoW(PaintCoW&&) = delete;
+ PaintCoW& operator=(const PaintCoW&) = delete;
+ PaintCoW& operator=(PaintCoW&&) = delete;
+ SkPaint& writeable() {
+ if (!mStorage) {
+ if (!mPtr) {
+ mStorage.emplace();
+ } else {
+ mStorage.emplace(*mPtr);
+ }
+ mPtr = &*mStorage;
+ }
+ return *mStorage;
+ }
+ operator const SkPaint*() const { return mPtr; }
+ const SkPaint* operator->() const { assert(mPtr); return mPtr; }
+ const SkPaint& operator*() const { assert(mPtr); return *mPtr; }
+ explicit operator bool() { return mPtr != nullptr; }
+ private:
+ const SkPaint* mPtr;
+ std::optional<SkPaint> mStorage;
+ };
+
+ /** Filters the paint using the current paint filter.
+ *
+ * @param paint the paint to filter. Will be initialized with the default
+ * SkPaint before filtering if filtering is required.
+ */
+ PaintCoW&& filterPaint(PaintCoW&& paint) const;
+
private:
struct SaveRec {
int saveCount;
@@ -174,8 +216,15 @@
void drawPoints(const float* points, int count, const SkPaint& paint, SkCanvas::PointMode mode);
- const SkPaint* addFilter(const SkPaint* origPaint, SkPaint* tmpPaint,
- sk_sp<SkColorFilter> colorSpaceFilter);
+ /** Filters the paint for bitmap drawing.
+ *
+ * After filtering the paint for bitmap drawing,
+ * also calls filterPaint on the paint.
+ *
+ * @param paint the paint to filter. Will be initialized with the default
+ * SkPaint before filtering if filtering is required.
+ */
+ PaintCoW&& filterBitmap(PaintCoW&& paint, sk_sp<SkColorFilter> colorSpaceFilter) const;
class Clip;
@@ -185,6 +234,7 @@
// unless it is the same as mCanvasOwned.get()
std::unique_ptr<SkDeque> mSaveStack; // lazily allocated, tracks partial saves.
std::vector<Clip> mClipStack; // tracks persistent clips.
+ sk_sp<PaintFilter> mPaintFilter;
};
} // namespace android
diff --git a/libs/hwui/VectorDrawable.h b/libs/hwui/VectorDrawable.h
index e84b9ac..af0ae05 100644
--- a/libs/hwui/VectorDrawable.h
+++ b/libs/hwui/VectorDrawable.h
@@ -59,12 +59,6 @@
onPropertyChanged(); \
retVal; \
})
-#define UPDATE_SKPROP(field, value) \
- ({ \
- bool retVal = ((field) != (value)); \
- if ((field) != (value)) SkRefCnt_SafeAssign((field), (value)); \
- retVal; \
- })
/* A VectorDrawable is composed of a tree of nodes.
* Each node can be a group node, or a path.
@@ -223,29 +217,28 @@
int fillType = 0; /* non-zero or kWinding_FillType in Skia */
};
explicit FullPathProperties(Node* mNode) : Properties(mNode), mTrimDirty(false) {}
- ~FullPathProperties() {
- SkSafeUnref(fillGradient);
- SkSafeUnref(strokeGradient);
- }
+ ~FullPathProperties() {}
void syncProperties(const FullPathProperties& prop) {
mPrimitiveFields = prop.mPrimitiveFields;
mTrimDirty = true;
- UPDATE_SKPROP(fillGradient, prop.fillGradient);
- UPDATE_SKPROP(strokeGradient, prop.strokeGradient);
+ fillGradient = prop.fillGradient;
+ strokeGradient = prop.strokeGradient;
onPropertyChanged();
}
void setFillGradient(SkShader* gradient) {
- if (UPDATE_SKPROP(fillGradient, gradient)) {
+ if (fillGradient.get() != gradient) {
+ fillGradient = sk_ref_sp(gradient);
onPropertyChanged();
}
}
void setStrokeGradient(SkShader* gradient) {
- if (UPDATE_SKPROP(strokeGradient, gradient)) {
+ if (strokeGradient.get() != gradient) {
+ strokeGradient = sk_ref_sp(gradient);
onPropertyChanged();
}
}
- SkShader* getFillGradient() const { return fillGradient; }
- SkShader* getStrokeGradient() const { return strokeGradient; }
+ SkShader* getFillGradient() const { return fillGradient.get(); }
+ SkShader* getStrokeGradient() const { return strokeGradient.get(); }
float getStrokeWidth() const { return mPrimitiveFields.strokeWidth; }
void setStrokeWidth(float strokeWidth) {
VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(strokeWidth, strokeWidth);
@@ -325,8 +318,8 @@
count,
};
PrimitiveFields mPrimitiveFields;
- SkShader* fillGradient = nullptr;
- SkShader* strokeGradient = nullptr;
+ sk_sp<SkShader> fillGradient;
+ sk_sp<SkShader> strokeGradient;
};
// Called from UI thread
@@ -550,8 +543,7 @@
SkRect bounds;
int scaledWidth = 0;
int scaledHeight = 0;
- SkColorFilter* colorFilter = nullptr;
- ~NonAnimatableProperties() { SkSafeUnref(colorFilter); }
+ sk_sp<SkColorFilter> colorFilter;
} mNonAnimatableProperties;
bool mNonAnimatablePropertiesDirty = true;
@@ -561,8 +553,7 @@
void syncNonAnimatableProperties(const TreeProperties& prop) {
// Copy over the data that can only be changed in UI thread
if (mNonAnimatableProperties.colorFilter != prop.mNonAnimatableProperties.colorFilter) {
- SkRefCnt_SafeAssign(mNonAnimatableProperties.colorFilter,
- prop.mNonAnimatableProperties.colorFilter);
+ mNonAnimatableProperties.colorFilter = prop.mNonAnimatableProperties.colorFilter;
}
mNonAnimatableProperties = prop.mNonAnimatableProperties;
}
@@ -599,12 +590,13 @@
}
}
void setColorFilter(SkColorFilter* filter) {
- if (UPDATE_SKPROP(mNonAnimatableProperties.colorFilter, filter)) {
+ if (mNonAnimatableProperties.colorFilter.get() != filter) {
+ mNonAnimatableProperties.colorFilter = sk_ref_sp(filter);
mNonAnimatablePropertiesDirty = true;
mTree->onPropertyChanged(this);
}
}
- SkColorFilter* getColorFilter() const { return mNonAnimatableProperties.colorFilter; }
+ SkColorFilter* getColorFilter() const { return mNonAnimatableProperties.colorFilter.get(); }
float getViewportWidth() const { return mNonAnimatableProperties.viewportWidth; }
float getViewportHeight() const { return mNonAnimatableProperties.viewportHeight; }
diff --git a/libs/hwui/hwui/Canvas.cpp b/libs/hwui/hwui/Canvas.cpp
index fb6bd6f..af7f013 100644
--- a/libs/hwui/hwui/Canvas.cpp
+++ b/libs/hwui/hwui/Canvas.cpp
@@ -23,7 +23,7 @@
#include "Typeface.h"
#include "pipeline/skia/SkiaRecordingCanvas.h"
-#include <SkDrawFilter.h>
+#include "hwui/PaintFilter.h"
namespace android {
@@ -40,10 +40,10 @@
void Canvas::drawTextDecorations(float x, float y, float length, const SkPaint& paint) {
uint32_t flags;
- SkDrawFilter* drawFilter = getDrawFilter();
- if (drawFilter) {
+ PaintFilter* paintFilter = getPaintFilter();
+ if (paintFilter) {
SkPaint paintCopy(paint);
- drawFilter->filter(&paintCopy, SkDrawFilter::kText_Type);
+ paintFilter->filter(&paintCopy);
flags = paintCopy.getFlags();
} else {
flags = paint.getFlags();
diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h
index 88b12b2..5d380a6 100644
--- a/libs/hwui/hwui/Canvas.h
+++ b/libs/hwui/hwui/Canvas.h
@@ -39,6 +39,7 @@
}
namespace android {
+class PaintFilter;
namespace uirenderer {
class CanvasPropertyPaint;
@@ -213,8 +214,8 @@
virtual bool clipPath(const SkPath* path, SkClipOp op) = 0;
// filters
- virtual SkDrawFilter* getDrawFilter() = 0;
- virtual void setDrawFilter(SkDrawFilter* drawFilter) = 0;
+ virtual PaintFilter* getPaintFilter() = 0;
+ virtual void setPaintFilter(sk_sp<PaintFilter> paintFilter) = 0;
// WebView only
virtual SkCanvasState* captureCanvasState() const { return nullptr; }
diff --git a/libs/hwui/hwui/PaintFilter.h b/libs/hwui/hwui/PaintFilter.h
new file mode 100644
index 0000000..bf5627e
--- /dev/null
+++ b/libs/hwui/hwui/PaintFilter.h
@@ -0,0 +1,19 @@
+#ifndef ANDROID_GRAPHICS_PAINT_FILTER_H_
+#define ANDROID_GRAPHICS_PAINT_FILTER_H_
+
+class SkPaint;
+
+namespace android {
+
+class PaintFilter : public SkRefCnt {
+public:
+ /**
+ * Called with the paint that will be used to draw.
+ * The implementation may modify the paint as they wish.
+ */
+ virtual void filter(SkPaint*) = 0;
+};
+
+} // namespace android
+
+#endif
diff --git a/libs/hwui/pipeline/skia/LayerDrawable.cpp b/libs/hwui/pipeline/skia/LayerDrawable.cpp
index 14d31b2..c41f6a6 100644
--- a/libs/hwui/pipeline/skia/LayerDrawable.cpp
+++ b/libs/hwui/pipeline/skia/LayerDrawable.cpp
@@ -82,7 +82,14 @@
textureMatrix = textureMatrixInv;
}
- SkMatrix matrix = SkMatrix::Concat(layerTransform, textureMatrix);
+ SkMatrix matrix;
+ if (dstRect) {
+ // Destination rectangle is set only when we are trying to read back the content
+ // of the layer. In this case we don't want to apply layer transform.
+ matrix = textureMatrix;
+ } else {
+ matrix = SkMatrix::Concat(layerTransform, textureMatrix);
+ }
SkPaint paint;
paint.setAlpha(layer->getAlpha());
diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
index 1b816fe..85fdc103 100644
--- a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
+++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
@@ -142,7 +142,7 @@
LOG_ALWAYS_FATAL_IF(!mProjectedDisplayList->mProjectedOutline);
const bool shouldClip = mProjectedDisplayList->mProjectedOutline->getPath();
SkAutoCanvasRestore acr2(canvas, shouldClip);
- canvas->setMatrix(mProjectedDisplayList->mProjectedReceiverParentMatrix);
+ canvas->setMatrix(mProjectedDisplayList->mParentMatrix);
if (shouldClip) {
clipOutline(*mProjectedDisplayList->mProjectedOutline, canvas, nullptr);
}
@@ -156,10 +156,10 @@
SkPaint* paint) {
paint->setFilterQuality(kLow_SkFilterQuality);
if (alphaMultiplier < 1.0f || properties.alpha() < 255 ||
- properties.xferMode() != SkBlendMode::kSrcOver || properties.colorFilter() != nullptr) {
+ properties.xferMode() != SkBlendMode::kSrcOver || properties.getColorFilter() != nullptr) {
paint->setAlpha(properties.alpha() * alphaMultiplier);
paint->setBlendMode(properties.xferMode());
- paint->setColorFilter(sk_ref_sp(properties.colorFilter()));
+ paint->setColorFilter(sk_ref_sp(properties.getColorFilter()));
return true;
}
return false;
@@ -200,9 +200,7 @@
setViewProperties(properties, canvas, &alphaMultiplier);
}
SkiaDisplayList* displayList = (SkiaDisplayList*)mRenderNode->getDisplayList();
- if (displayList->containsProjectionReceiver()) {
- displayList->mProjectedReceiverParentMatrix = canvas->getTotalMatrix();
- }
+ displayList->mParentMatrix = canvas->getTotalMatrix();
// TODO should we let the bound of the drawable do this for us?
const SkRect bounds = SkRect::MakeWH(properties.getWidth(), properties.getHeight());
diff --git a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp
index 6292a6c..dba97fe 100644
--- a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp
+++ b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp
@@ -55,6 +55,11 @@
if (casterZ >= -NON_ZERO_EPSILON) { // draw only children with negative Z
return;
}
+ SkAutoCanvasRestore acr(canvas, true);
+ // Since we're drawing out of recording order, the child's matrix needs to be applied to the
+ // canvas. In in-order drawing, the canvas already has the child's matrix applied.
+ canvas->setMatrix(mDisplayList->mParentMatrix);
+ canvas->concat(childNode->getRecordedMatrix());
childNode->forceDraw(canvas);
drawIndex++;
}
@@ -102,6 +107,11 @@
RenderNodeDrawable* childNode = zChildren[drawIndex];
SkASSERT(childNode);
+ SkAutoCanvasRestore acr(canvas, true);
+ // Since we're drawing out of recording order, the child's matrix needs to be applied to the
+ // canvas. In in-order drawing, the canvas already has the child's matrix applied.
+ canvas->setMatrix(mStartBarrier->mDisplayList->mParentMatrix);
+ canvas->concat(childNode->getRecordedMatrix());
childNode->forceDraw(canvas);
drawIndex++;
@@ -153,10 +163,15 @@
}
SkAutoCanvasRestore acr(canvas, true);
+ // Since we're drawing out of recording order, the child's matrix needs to be applied to the
+ // canvas. In in-order drawing, the canvas already has the child's matrix applied.
+ canvas->setMatrix(mStartBarrier->mDisplayList->mParentMatrix);
SkMatrix shadowMatrix;
- mat4 hwuiMatrix;
+ mat4 hwuiMatrix(caster->getRecordedMatrix());
// TODO we don't pass the optional boolean to treat it as a 4x4 matrix
+ // applyViewPropertyTransforms gets the same matrix, which render nodes apply with
+ // RenderNodeDrawable::setViewProperties as a part if their draw.
caster->getRenderNode()->applyViewPropertyTransforms(hwuiMatrix);
hwuiMatrix.copyTo(shadowMatrix);
canvas->concat(shadowMatrix);
diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.h b/libs/hwui/pipeline/skia/SkiaDisplayList.h
index 58b9242..6eff589 100644
--- a/libs/hwui/pipeline/skia/SkiaDisplayList.h
+++ b/libs/hwui/pipeline/skia/SkiaDisplayList.h
@@ -173,12 +173,12 @@
// node is drawn.
const Outline* mProjectedOutline = nullptr;
- // mProjectedReceiverParentMatrix is valid when render node tree is traversed during the draw
- // pass. Render nodes that have a child receiver node, will store their matrix in
- // mProjectedReceiverParentMatrix. Child receiver node will set the matrix and then clip with
- // the
- // outline of their parent.
- SkMatrix mProjectedReceiverParentMatrix;
+ // mParentMatrix is set and valid when render node tree is traversed during the draw
+ // pass. Render nodes, which draw in a order different than recording order (e.g. nodes with a
+ // child receiver node or Z elevation), can use mParentMatrix to calculate the final transform
+ // without replaying the matrix transform OPs from the display list.
+ // Child receiver node will set the matrix and then clip with the outline of their parent.
+ SkMatrix mParentMatrix;
};
}; // namespace skiapipeline
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp
index 208910a..f2f5056 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp
@@ -26,6 +26,7 @@
#include "DeviceInfo.h"
#include "Matrix.h"
#include "Properties.h"
+#include "utils/MathUtils.h"
using namespace android::uirenderer::renderthread;
@@ -115,9 +116,9 @@
paint.setBlendMode(SkBlendMode::kSrc);
// Apply a filter, which is matching OpenGL pipeline readback behaviour. Filter usage
// is codified by tests using golden images like DecodeAccuracyTest.
- if (skiaSrcRect.width() != bitmap->width() ||
- skiaSrcRect.height() != bitmap->height()) {
- // TODO: apply filter always, but check if tests will be fine
+ bool disableFilter = MathUtils::areEqual(skiaSrcRect.width(), skiaDestRect.width())
+ && MathUtils::areEqual(skiaSrcRect.height(), skiaDestRect.height());
+ if (!disableFilter) {
paint.setFilterQuality(kLow_SkFilterQuality);
}
scaledSurface->getCanvas()->concat(textureMatrix);
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
index f0da660..46e39aa 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
@@ -17,6 +17,7 @@
#include "SkiaRecordingCanvas.h"
#include <SkImagePriv.h>
+#include "CanvasTransform.h"
#include "Layer.h"
#include "LayerDrawable.h"
#include "NinePatchUtils.h"
@@ -44,13 +45,21 @@
}
mDisplayList->attachRecorder(&mRecorder, SkIRect::MakeWH(width, height));
- SkiaCanvas::reset(&mRecorder);
+ SkCanvas* canvas = &mRecorder;
+ if (renderNode) {
+ mWrappedCanvas = makeTransformCanvas(&mRecorder, renderNode->usageHint());
+ if (mWrappedCanvas) {
+ canvas = mWrappedCanvas.get();
+ }
+ }
+ SkiaCanvas::reset(canvas);
}
uirenderer::DisplayList* SkiaRecordingCanvas::finishRecording() {
// close any existing chunks if necessary
insertReorderBarrier(false);
mRecorder.restoreToCount(1);
+ mWrappedCanvas = nullptr;
return mDisplayList.release();
}
@@ -148,12 +157,45 @@
// Recording Canvas draw operations: Bitmaps
// ----------------------------------------------------------------------------
+SkiaCanvas::PaintCoW&& SkiaRecordingCanvas::filterBitmap(PaintCoW&& paint,
+ sk_sp<SkColorFilter> colorSpaceFilter) {
+ bool fixBlending = false;
+ bool fixAA = false;
+ if (paint) {
+ // kClear blend mode is drawn as kDstOut on HW for compatibility with Android O and
+ // older.
+ fixBlending = sApiLevel <= 27 && paint->getBlendMode() == SkBlendMode::kClear;
+ fixAA = paint->isAntiAlias();
+ }
+
+ if (fixBlending || fixAA || colorSpaceFilter) {
+ SkPaint& tmpPaint = paint.writeable();
+
+ if (fixBlending) {
+ tmpPaint.setBlendMode(SkBlendMode::kDstOut);
+ }
+
+ if (colorSpaceFilter) {
+ if (tmpPaint.getColorFilter()) {
+ tmpPaint.setColorFilter(SkColorFilter::MakeComposeFilter(
+ tmpPaint.refColorFilter(), std::move(colorSpaceFilter)));
+ } else {
+ tmpPaint.setColorFilter(std::move(colorSpaceFilter));
+ }
+ LOG_ALWAYS_FATAL_IF(!tmpPaint.getColorFilter());
+ }
+
+ // disabling AA on bitmap draws matches legacy HWUI behavior
+ tmpPaint.setAntiAlias(false);
+ }
+
+ return filterPaint(std::move(paint));
+}
void SkiaRecordingCanvas::drawBitmap(Bitmap& bitmap, float left, float top, const SkPaint* paint) {
- SkPaint tmpPaint;
sk_sp<SkColorFilter> colorFilter;
sk_sp<SkImage> image = bitmap.makeImage(&colorFilter);
- mRecorder.drawImage(image, left, top, bitmapPaint(paint, &tmpPaint, colorFilter));
+ mRecorder.drawImage(image, left, top, filterBitmap(paint, std::move(colorFilter)));
// if image->unique() is true, then mRecorder.drawImage failed for some reason. It also means
// it is not safe to store a raw SkImage pointer, because the image object will be destroyed
// when this function ends.
@@ -166,10 +208,9 @@
SkAutoCanvasRestore acr(&mRecorder, true);
concat(matrix);
- SkPaint tmpPaint;
sk_sp<SkColorFilter> colorFilter;
sk_sp<SkImage> image = bitmap.makeImage(&colorFilter);
- mRecorder.drawImage(image, 0, 0, bitmapPaint(paint, &tmpPaint, colorFilter));
+ mRecorder.drawImage(image, 0, 0, filterBitmap(paint, std::move(colorFilter)));
if (!bitmap.isImmutable() && image.get() && !image->unique()) {
mDisplayList->mMutableImages.push_back(image.get());
}
@@ -181,10 +222,9 @@
SkRect srcRect = SkRect::MakeLTRB(srcLeft, srcTop, srcRight, srcBottom);
SkRect dstRect = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom);
- SkPaint tmpPaint;
sk_sp<SkColorFilter> colorFilter;
sk_sp<SkImage> image = bitmap.makeImage(&colorFilter);
- mRecorder.drawImageRect(image, srcRect, dstRect, bitmapPaint(paint, &tmpPaint, colorFilter),
+ mRecorder.drawImageRect(image, srcRect, dstRect, filterBitmap(paint, std::move(colorFilter)),
SkCanvas::kFast_SrcRectConstraint);
if (!bitmap.isImmutable() && image.get() && !image->unique() && !srcRect.isEmpty() &&
!dstRect.isEmpty()) {
@@ -216,23 +256,16 @@
lattice.fBounds = nullptr;
SkRect dst = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom);
- SkPaint tmpPaint;
- sk_sp<SkColorFilter> colorFilter;
- sk_sp<SkImage> image = bitmap.makeImage(&colorFilter);
- const SkPaint* filteredPaint = bitmapPaint(paint, &tmpPaint, colorFilter);
+ PaintCoW filteredPaint(paint);
// Besides kNone, the other three SkFilterQualities are treated the same. And Android's
// Java API only supports kLow and kNone anyway.
if (!filteredPaint || filteredPaint->getFilterQuality() == kNone_SkFilterQuality) {
- if (filteredPaint != &tmpPaint) {
- if (paint) {
- tmpPaint = *paint;
- }
- filteredPaint = &tmpPaint;
- }
- tmpPaint.setFilterQuality(kLow_SkFilterQuality);
+ filteredPaint.writeable().setFilterQuality(kLow_SkFilterQuality);
}
-
- mRecorder.drawImageLattice(image.get(), lattice, dst, filteredPaint);
+ sk_sp<SkColorFilter> colorFilter;
+ sk_sp<SkImage> image = bitmap.makeImage(&colorFilter);
+ mRecorder.drawImageLattice(image.get(), lattice, dst,
+ filterBitmap(std::move(filteredPaint), std::move(colorFilter)));
if (!bitmap.isImmutable() && image.get() && !image->unique() && !dst.isEmpty()) {
mDisplayList->mMutableImages.push_back(image.get());
}
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
index 93807a5..50d84d6 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
@@ -78,6 +78,7 @@
private:
SkLiteRecorder mRecorder;
std::unique_ptr<SkiaDisplayList> mDisplayList;
+ std::unique_ptr<SkCanvas> mWrappedCanvas;
StartReorderBarrierDrawable* mCurrentBarrier;
/**
@@ -89,44 +90,7 @@
*/
void initDisplayList(uirenderer::RenderNode* renderNode, int width, int height);
- inline static const SkPaint* bitmapPaint(const SkPaint* origPaint, SkPaint* tmpPaint,
- sk_sp<SkColorFilter> colorSpaceFilter) {
- bool fixBlending = false;
- bool fixAA = false;
- if (origPaint) {
- // kClear blend mode is drawn as kDstOut on HW for compatibility with Android O and
- // older.
- fixBlending = sApiLevel <= 27 && origPaint->getBlendMode() == SkBlendMode::kClear;
- fixAA = origPaint->isAntiAlias();
- }
-
- if (fixBlending || fixAA || colorSpaceFilter) {
- if (origPaint) {
- *tmpPaint = *origPaint;
- }
-
- if (fixBlending) {
- tmpPaint->setBlendMode(SkBlendMode::kDstOut);
- }
-
- if (colorSpaceFilter) {
- if (tmpPaint->getColorFilter()) {
- tmpPaint->setColorFilter(SkColorFilter::MakeComposeFilter(
- tmpPaint->refColorFilter(), colorSpaceFilter));
- } else {
- tmpPaint->setColorFilter(colorSpaceFilter);
- }
- LOG_ALWAYS_FATAL_IF(!tmpPaint->getColorFilter());
- }
-
- // disabling AA on bitmap draws matches legacy HWUI behavior
- tmpPaint->setAntiAlias(false);
- return tmpPaint;
- } else {
- return origPaint;
- }
- }
-
+ PaintCoW&& filterBitmap(PaintCoW&& paint, sk_sp<SkColorFilter> colorSpaceFilter);
};
}; // namespace skiapipeline
diff --git a/libs/hwui/renderthread/CacheManager.cpp b/libs/hwui/renderthread/CacheManager.cpp
index b07fb2d..bec80b1e 100644
--- a/libs/hwui/renderthread/CacheManager.cpp
+++ b/libs/hwui/renderthread/CacheManager.cpp
@@ -134,6 +134,7 @@
}
contextOptions->fPersistentCache = &skiapipeline::ShaderCache::get();
+ contextOptions->fGpuPathRenderers &= ~GpuPathRenderers::kCoverageCounting;
}
void CacheManager::trimMemory(TrimMemoryMode mode) {
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index 6eca8d2..e3807e6 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -85,6 +85,11 @@
[ this, surf = surface ]() mutable { mContext->setSurface(std::move(surf)); });
}
+void RenderProxy::allocateBuffers(const sp<Surface>& surface) {
+ mRenderThread.queue().post(
+ [ surf = surface ]() mutable { surf->allocateBuffers(); });
+}
+
void RenderProxy::updateSurface(const sp<Surface>& surface) {
mRenderThread.queue().post(
[ this, surf = surface ]() mutable { mContext->setSurface(std::move(surf)); });
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index 5668484..c2964a4 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -70,6 +70,7 @@
ANDROID_API void setName(const char* name);
ANDROID_API void initialize(const sp<Surface>& surface);
+ ANDROID_API void allocateBuffers(const sp<Surface>& surface);
ANDROID_API void updateSurface(const sp<Surface>& surface);
ANDROID_API bool pauseSurface(const sp<Surface>& surface);
ANDROID_API void setStopped(bool stopped);
diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp
index 62f820a..a19edae 100644
--- a/libs/hwui/renderthread/VulkanManager.cpp
+++ b/libs/hwui/renderthread/VulkanManager.cpp
@@ -31,44 +31,242 @@
namespace uirenderer {
namespace renderthread {
-#define GET_PROC(F) m##F = (PFN_vk##F)vkGetInstanceProcAddr(instance, "vk" #F)
-#define GET_DEV_PROC(F) m##F = (PFN_vk##F)vkGetDeviceProcAddr(device, "vk" #F)
+#define GET_PROC(F) m##F = (PFN_vk##F)vkGetInstanceProcAddr(VK_NULL_HANDLE, "vk" #F)
+#define GET_INST_PROC(F) m##F = (PFN_vk##F)vkGetInstanceProcAddr(mInstance, "vk" #F)
+#define GET_DEV_PROC(F) m##F = (PFN_vk##F)vkGetDeviceProcAddr(mDevice, "vk" #F)
VulkanManager::VulkanManager(RenderThread& thread) : mRenderThread(thread) {}
void VulkanManager::destroy() {
- if (!hasVkContext()) return;
-
mRenderThread.renderState().onVkContextDestroyed();
mRenderThread.setGrContext(nullptr);
if (VK_NULL_HANDLE != mCommandPool) {
- mDestroyCommandPool(mBackendContext->fDevice, mCommandPool, nullptr);
+ mDestroyCommandPool(mDevice, mCommandPool, nullptr);
mCommandPool = VK_NULL_HANDLE;
}
- mBackendContext.reset();
-}
-void VulkanManager::initialize() {
- if (hasVkContext()) {
- return;
+ if (mDevice != VK_NULL_HANDLE) {
+ mDeviceWaitIdle(mDevice);
+ mDestroyDevice(mDevice, nullptr);
}
- auto canPresent = [](VkInstance, VkPhysicalDevice, uint32_t) { return true; };
+ if (mInstance != VK_NULL_HANDLE) {
+ mDestroyInstance(mInstance, nullptr);
+ }
- mBackendContext.reset(GrVkBackendContext::Create(vkGetInstanceProcAddr, vkGetDeviceProcAddr,
- &mPresentQueueIndex, canPresent));
- LOG_ALWAYS_FATAL_IF(!mBackendContext.get());
+ mGraphicsQueue = VK_NULL_HANDLE;
+ mPresentQueue = VK_NULL_HANDLE;
+ mDevice = VK_NULL_HANDLE;
+ mPhysicalDevice = VK_NULL_HANDLE;
+ mInstance = VK_NULL_HANDLE;
+}
- // Get all the addresses of needed vulkan functions
- VkInstance instance = mBackendContext->fInstance;
- VkDevice device = mBackendContext->fDevice;
- GET_PROC(CreateAndroidSurfaceKHR);
- GET_PROC(DestroySurfaceKHR);
- GET_PROC(GetPhysicalDeviceSurfaceSupportKHR);
- GET_PROC(GetPhysicalDeviceSurfaceCapabilitiesKHR);
- GET_PROC(GetPhysicalDeviceSurfaceFormatsKHR);
- GET_PROC(GetPhysicalDeviceSurfacePresentModesKHR);
+bool VulkanManager::setupDevice(VkPhysicalDeviceFeatures& deviceFeatures) {
+ VkResult err;
+
+ constexpr VkApplicationInfo app_info = {
+ VK_STRUCTURE_TYPE_APPLICATION_INFO, // sType
+ nullptr, // pNext
+ "android framework", // pApplicationName
+ 0, // applicationVersion
+ "android framework", // pEngineName
+ 0, // engineVerison
+ VK_MAKE_VERSION(1, 0, 0), // apiVersion
+ };
+
+ std::vector<const char*> instanceExtensions;
+ {
+ GET_PROC(EnumerateInstanceExtensionProperties);
+
+ uint32_t extensionCount = 0;
+ err = mEnumerateInstanceExtensionProperties(nullptr, &extensionCount, nullptr);
+ if (VK_SUCCESS != err) {
+ return false;
+ }
+ std::unique_ptr<VkExtensionProperties[]> extensions(
+ new VkExtensionProperties[extensionCount]);
+ err = mEnumerateInstanceExtensionProperties(nullptr, &extensionCount, extensions.get());
+ if (VK_SUCCESS != err) {
+ return false;
+ }
+ bool hasKHRSurfaceExtension = false;
+ bool hasKHRAndroidSurfaceExtension = false;
+ for (uint32_t i = 0; i < extensionCount; ++i) {
+ instanceExtensions.push_back(extensions[i].extensionName);
+ if (!strcmp(extensions[i].extensionName, VK_KHR_SURFACE_EXTENSION_NAME)) {
+ hasKHRSurfaceExtension = true;
+ }
+ if (!strcmp(extensions[i].extensionName,VK_KHR_ANDROID_SURFACE_EXTENSION_NAME)) {
+ hasKHRAndroidSurfaceExtension = true;
+ }
+ }
+ if (!hasKHRSurfaceExtension || !hasKHRAndroidSurfaceExtension) {
+ this->destroy();
+ return false;
+ }
+ }
+
+ const VkInstanceCreateInfo instance_create = {
+ VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, // sType
+ nullptr, // pNext
+ 0, // flags
+ &app_info, // pApplicationInfo
+ 0, // enabledLayerNameCount
+ nullptr, // ppEnabledLayerNames
+ (uint32_t) instanceExtensions.size(), // enabledExtensionNameCount
+ instanceExtensions.data(), // ppEnabledExtensionNames
+ };
+
+ GET_PROC(CreateInstance);
+ err = mCreateInstance(&instance_create, nullptr, &mInstance);
+ if (err < 0) {
+ this->destroy();
+ return false;
+ }
+
+ GET_INST_PROC(DestroyInstance);
+ GET_INST_PROC(EnumeratePhysicalDevices);
+ GET_INST_PROC(GetPhysicalDeviceQueueFamilyProperties);
+ GET_INST_PROC(GetPhysicalDeviceFeatures);
+ GET_INST_PROC(CreateDevice);
+ GET_INST_PROC(EnumerateDeviceExtensionProperties);
+ GET_INST_PROC(CreateAndroidSurfaceKHR);
+ GET_INST_PROC(DestroySurfaceKHR);
+ GET_INST_PROC(GetPhysicalDeviceSurfaceSupportKHR);
+ GET_INST_PROC(GetPhysicalDeviceSurfaceCapabilitiesKHR);
+ GET_INST_PROC(GetPhysicalDeviceSurfaceFormatsKHR);
+ GET_INST_PROC(GetPhysicalDeviceSurfacePresentModesKHR);
+
+ uint32_t gpuCount;
+ err = mEnumeratePhysicalDevices(mInstance, &gpuCount, nullptr);
+ if (err) {
+ this->destroy();
+ return false;
+ }
+ if (!gpuCount) {
+ this->destroy();
+ return false;
+ }
+ // Just returning the first physical device instead of getting the whole array. Since there
+ // should only be one device on android.
+ gpuCount = 1;
+ err = mEnumeratePhysicalDevices(mInstance, &gpuCount, &mPhysicalDevice);
+ // VK_INCOMPLETE is returned when the count we provide is less than the total device count.
+ if (err && VK_INCOMPLETE != err) {
+ this->destroy();
+ return false;
+ }
+
+ // query to get the initial queue props size
+ uint32_t queueCount;
+ mGetPhysicalDeviceQueueFamilyProperties(mPhysicalDevice, &queueCount, nullptr);
+ if (!queueCount) {
+ this->destroy();
+ return false;
+ }
+
+ // now get the actual queue props
+ std::unique_ptr<VkQueueFamilyProperties[]> queueProps(new VkQueueFamilyProperties[queueCount]);
+ mGetPhysicalDeviceQueueFamilyProperties(mPhysicalDevice, &queueCount, queueProps.get());
+
+ // iterate to find the graphics queue
+ mGraphicsQueueIndex = queueCount;
+ for (uint32_t i = 0; i < queueCount; i++) {
+ if (queueProps[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) {
+ mGraphicsQueueIndex = i;
+ break;
+ }
+ }
+ if (mGraphicsQueueIndex == queueCount) {
+ this->destroy();
+ return false;
+ }
+
+ // All physical devices and queue families on Android must be capable of
+ // presentation with any native window. So just use the first one.
+ mPresentQueueIndex = 0;
+
+ std::vector<const char*> deviceExtensions;
+ {
+ uint32_t extensionCount = 0;
+ err = mEnumerateDeviceExtensionProperties(mPhysicalDevice, nullptr, &extensionCount,
+ nullptr);
+ if (VK_SUCCESS != err) {
+ this->destroy();
+ return false;
+ }
+ std::unique_ptr<VkExtensionProperties[]> extensions(
+ new VkExtensionProperties[extensionCount]);
+ err = mEnumerateDeviceExtensionProperties(mPhysicalDevice, nullptr, &extensionCount,
+ extensions.get());
+ if (VK_SUCCESS != err) {
+ this->destroy();
+ return false;
+ }
+ bool hasKHRSwapchainExtension = false;
+ for (uint32_t i = 0; i < extensionCount; ++i) {
+ deviceExtensions.push_back(extensions[i].extensionName);
+ if (!strcmp(extensions[i].extensionName, VK_KHR_SWAPCHAIN_EXTENSION_NAME)) {
+ hasKHRSwapchainExtension = true;
+ }
+ }
+ if (!hasKHRSwapchainExtension) {
+ this->destroy();
+ return false;
+ }
+ }
+
+ // query to get the physical device properties
+ mGetPhysicalDeviceFeatures(mPhysicalDevice, &deviceFeatures);
+ // this looks like it would slow things down,
+ // and we can't depend on it on all platforms
+ deviceFeatures.robustBufferAccess = VK_FALSE;
+
+ float queuePriorities[1] = { 0.0 };
+
+ const VkDeviceQueueCreateInfo queueInfo[2] = {
+ {
+ VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, // sType
+ nullptr, // pNext
+ 0, // VkDeviceQueueCreateFlags
+ mGraphicsQueueIndex, // queueFamilyIndex
+ 1, // queueCount
+ queuePriorities, // pQueuePriorities
+ },
+ {
+ VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, // sType
+ nullptr, // pNext
+ 0, // VkDeviceQueueCreateFlags
+ mPresentQueueIndex, // queueFamilyIndex
+ 1, // queueCount
+ queuePriorities, // pQueuePriorities
+ }
+ };
+ uint32_t queueInfoCount = (mPresentQueueIndex != mGraphicsQueueIndex) ? 2 : 1;
+
+ const VkDeviceCreateInfo deviceInfo = {
+ VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, // sType
+ nullptr, // pNext
+ 0, // VkDeviceCreateFlags
+ queueInfoCount, // queueCreateInfoCount
+ queueInfo, // pQueueCreateInfos
+ 0, // layerCount
+ nullptr, // ppEnabledLayerNames
+ (uint32_t) deviceExtensions.size(), // extensionCount
+ deviceExtensions.data(), // ppEnabledExtensionNames
+ &deviceFeatures // ppEnabledFeatures
+ };
+
+ err = mCreateDevice(mPhysicalDevice, &deviceInfo, nullptr, &mDevice);
+ if (err) {
+ this->destroy();
+ return false;
+ }
+
+ GET_DEV_PROC(GetDeviceQueue);
+ GET_DEV_PROC(DeviceWaitIdle);
+ GET_DEV_PROC(DestroyDevice);
GET_DEV_PROC(CreateSwapchainKHR);
GET_DEV_PROC(DestroySwapchainKHR);
GET_DEV_PROC(GetSwapchainImagesKHR);
@@ -93,25 +291,74 @@
GET_DEV_PROC(WaitForFences);
GET_DEV_PROC(ResetFences);
+ return true;
+}
+
+void VulkanManager::initialize() {
+ if (mDevice != VK_NULL_HANDLE) {
+ return;
+ }
+
+ std::vector<const char*> instanceExtensions;
+ std::vector<const char*> deviceExtensions;
+ VkPhysicalDeviceFeatures deviceFeatures;
+ LOG_ALWAYS_FATAL_IF(!this->setupDevice(deviceFeatures));
+
+ mGetDeviceQueue(mDevice, mGraphicsQueueIndex, 0, &mGraphicsQueue);
+
+ uint32_t extensionFlags = kKHR_surface_GrVkExtensionFlag |
+ kKHR_android_surface_GrVkExtensionFlag |
+ kKHR_swapchain_GrVkExtensionFlag;
+
+ uint32_t featureFlags = 0;
+ if (deviceFeatures.geometryShader) {
+ featureFlags |= kGeometryShader_GrVkFeatureFlag;
+ }
+ if (deviceFeatures.dualSrcBlend) {
+ featureFlags |= kDualSrcBlend_GrVkFeatureFlag;
+ }
+ if (deviceFeatures.sampleRateShading) {
+ featureFlags |= kSampleRateShading_GrVkFeatureFlag;
+ }
+
+ auto getProc = [] (const char* proc_name, VkInstance instance, VkDevice device) {
+ if (device != VK_NULL_HANDLE) {
+ return vkGetDeviceProcAddr(device, proc_name);
+ }
+ return vkGetInstanceProcAddr(instance, proc_name);
+ };
+
+ GrVkBackendContext backendContext;
+ backendContext.fInstance = mInstance;
+ backendContext.fPhysicalDevice = mPhysicalDevice;
+ backendContext.fDevice = mDevice;
+ backendContext.fQueue = mGraphicsQueue;
+ backendContext.fGraphicsQueueIndex = mGraphicsQueueIndex;
+ backendContext.fMinAPIVersion = VK_MAKE_VERSION(1, 0, 0);
+ backendContext.fExtensions = extensionFlags;
+ backendContext.fFeatures = featureFlags;
+ backendContext.fGetProc = std::move(getProc);
+ backendContext.fOwnsInstanceAndDevice = false;
+
// create the command pool for the command buffers
if (VK_NULL_HANDLE == mCommandPool) {
VkCommandPoolCreateInfo commandPoolInfo;
memset(&commandPoolInfo, 0, sizeof(VkCommandPoolCreateInfo));
commandPoolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
// this needs to be on the render queue
- commandPoolInfo.queueFamilyIndex = mBackendContext->fGraphicsQueueIndex;
+ commandPoolInfo.queueFamilyIndex = mGraphicsQueueIndex;
commandPoolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
- SkDEBUGCODE(VkResult res =) mCreateCommandPool(mBackendContext->fDevice, &commandPoolInfo,
- nullptr, &mCommandPool);
+ SkDEBUGCODE(VkResult res =) mCreateCommandPool(mDevice, &commandPoolInfo, nullptr,
+ &mCommandPool);
SkASSERT(VK_SUCCESS == res);
}
- mGetDeviceQueue(mBackendContext->fDevice, mPresentQueueIndex, 0, &mPresentQueue);
+ mGetDeviceQueue(mDevice, mPresentQueueIndex, 0, &mPresentQueue);
GrContextOptions options;
options.fDisableDistanceFieldPaths = true;
mRenderThread.cacheManager().configureContext(&options);
- sk_sp<GrContext> grContext(GrContext::MakeVulkan(mBackendContext, options));
+ sk_sp<GrContext> grContext(GrContext::MakeVulkan(backendContext, options));
LOG_ALWAYS_FATAL_IF(!grContext.get());
mRenderThread.setGrContext(grContext);
DeviceInfo::initialize(mRenderThread.getGrContext()->maxRenderTargetSize());
@@ -138,8 +385,7 @@
// Before we reuse a backbuffer, make sure its fences have all signaled so that we can safely
// reuse its commands buffers.
- VkResult res =
- mWaitForFences(mBackendContext->fDevice, 2, backbuffer->mUsageFences, true, UINT64_MAX);
+ VkResult res = mWaitForFences(mDevice, 2, backbuffer->mUsageFences, true, UINT64_MAX);
if (res != VK_SUCCESS) {
return nullptr;
}
@@ -153,12 +399,12 @@
VkResult res;
- res = mResetFences(mBackendContext->fDevice, 2, backbuffer->mUsageFences);
+ res = mResetFences(mDevice, 2, backbuffer->mUsageFences);
SkASSERT(VK_SUCCESS == res);
// The acquire will signal the attached mAcquireSemaphore. We use this to know the image has
// finished presenting and that it is safe to begin sending new commands to the returned image.
- res = mAcquireNextImageKHR(mBackendContext->fDevice, surface->mSwapchain, UINT64_MAX,
+ res = mAcquireNextImageKHR(mDevice, surface->mSwapchain, UINT64_MAX,
backbuffer->mAcquireSemaphore, VK_NULL_HANDLE,
&backbuffer->mImageIndex);
@@ -173,11 +419,11 @@
return nullptr;
}
backbuffer = getAvailableBackbuffer(surface);
- res = mResetFences(mBackendContext->fDevice, 2, backbuffer->mUsageFences);
+ res = mResetFences(mDevice, 2, backbuffer->mUsageFences);
SkASSERT(VK_SUCCESS == res);
// acquire the image
- res = mAcquireNextImageKHR(mBackendContext->fDevice, surface->mSwapchain, UINT64_MAX,
+ res = mAcquireNextImageKHR(mDevice, surface->mSwapchain, UINT64_MAX,
backbuffer->mAcquireSemaphore, VK_NULL_HANDLE,
&backbuffer->mImageIndex);
@@ -205,7 +451,7 @@
layout, // oldLayout
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, // newLayout
mPresentQueueIndex, // srcQueueFamilyIndex
- mBackendContext->fGraphicsQueueIndex, // dstQueueFamilyIndex
+ mGraphicsQueueIndex, // dstQueueFamilyIndex
surface->mImages[backbuffer->mImageIndex], // image
{VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1} // subresourceRange
};
@@ -236,7 +482,7 @@
submitInfo.signalSemaphoreCount = 0;
// Attach first fence to submission here so we can track when the command buffer finishes.
- mQueueSubmit(mBackendContext->fQueue, 1, &submitInfo, backbuffer->mUsageFences[0]);
+ mQueueSubmit(mGraphicsQueue, 1, &submitInfo, backbuffer->mUsageFences[0]);
// We need to notify Skia that we changed the layout of the wrapped VkImage
sk_sp<SkSurface> skSurface = surface->mImageInfos[backbuffer->mImageIndex].mSurface;
@@ -255,17 +501,14 @@
void VulkanManager::destroyBuffers(VulkanSurface* surface) {
if (surface->mBackbuffers) {
for (uint32_t i = 0; i < surface->mImageCount + 1; ++i) {
- mWaitForFences(mBackendContext->fDevice, 2, surface->mBackbuffers[i].mUsageFences, true,
- UINT64_MAX);
+ mWaitForFences(mDevice, 2, surface->mBackbuffers[i].mUsageFences, true, UINT64_MAX);
surface->mBackbuffers[i].mImageIndex = -1;
- mDestroySemaphore(mBackendContext->fDevice, surface->mBackbuffers[i].mAcquireSemaphore,
- nullptr);
- mDestroySemaphore(mBackendContext->fDevice, surface->mBackbuffers[i].mRenderSemaphore,
- nullptr);
- mFreeCommandBuffers(mBackendContext->fDevice, mCommandPool, 2,
- surface->mBackbuffers[i].mTransitionCmdBuffers);
- mDestroyFence(mBackendContext->fDevice, surface->mBackbuffers[i].mUsageFences[0], 0);
- mDestroyFence(mBackendContext->fDevice, surface->mBackbuffers[i].mUsageFences[1], 0);
+ mDestroySemaphore(mDevice, surface->mBackbuffers[i].mAcquireSemaphore, nullptr);
+ mDestroySemaphore(mDevice, surface->mBackbuffers[i].mRenderSemaphore, nullptr);
+ mFreeCommandBuffers(mDevice, mCommandPool, 2,
+ surface->mBackbuffers[i].mTransitionCmdBuffers);
+ mDestroyFence(mDevice, surface->mBackbuffers[i].mUsageFences[0], 0);
+ mDestroyFence(mDevice, surface->mBackbuffers[i].mUsageFences[1], 0);
}
}
@@ -282,29 +525,27 @@
if (VK_NULL_HANDLE != mPresentQueue) {
mQueueWaitIdle(mPresentQueue);
}
- mDeviceWaitIdle(mBackendContext->fDevice);
+ mDeviceWaitIdle(mDevice);
destroyBuffers(surface);
if (VK_NULL_HANDLE != surface->mSwapchain) {
- mDestroySwapchainKHR(mBackendContext->fDevice, surface->mSwapchain, nullptr);
+ mDestroySwapchainKHR(mDevice, surface->mSwapchain, nullptr);
surface->mSwapchain = VK_NULL_HANDLE;
}
if (VK_NULL_HANDLE != surface->mVkSurface) {
- mDestroySurfaceKHR(mBackendContext->fInstance, surface->mVkSurface, nullptr);
+ mDestroySurfaceKHR(mInstance, surface->mVkSurface, nullptr);
surface->mVkSurface = VK_NULL_HANDLE;
}
delete surface;
}
void VulkanManager::createBuffers(VulkanSurface* surface, VkFormat format, VkExtent2D extent) {
- mGetSwapchainImagesKHR(mBackendContext->fDevice, surface->mSwapchain, &surface->mImageCount,
- nullptr);
+ mGetSwapchainImagesKHR(mDevice, surface->mSwapchain, &surface->mImageCount, nullptr);
SkASSERT(surface->mImageCount);
surface->mImages = new VkImage[surface->mImageCount];
- mGetSwapchainImagesKHR(mBackendContext->fDevice, surface->mSwapchain, &surface->mImageCount,
- surface->mImages);
+ mGetSwapchainImagesKHR(mDevice, surface->mSwapchain, &surface->mImageCount, surface->mImages);
SkSurfaceProps props(0, kUnknown_SkPixelGeometry);
@@ -354,15 +595,15 @@
for (uint32_t i = 0; i < surface->mImageCount + 1; ++i) {
SkDEBUGCODE(VkResult res);
surface->mBackbuffers[i].mImageIndex = -1;
- SkDEBUGCODE(res =) mCreateSemaphore(mBackendContext->fDevice, &semaphoreInfo, nullptr,
+ SkDEBUGCODE(res =) mCreateSemaphore(mDevice, &semaphoreInfo, nullptr,
&surface->mBackbuffers[i].mAcquireSemaphore);
- SkDEBUGCODE(res =) mCreateSemaphore(mBackendContext->fDevice, &semaphoreInfo, nullptr,
+ SkDEBUGCODE(res =) mCreateSemaphore(mDevice, &semaphoreInfo, nullptr,
&surface->mBackbuffers[i].mRenderSemaphore);
- SkDEBUGCODE(res =) mAllocateCommandBuffers(mBackendContext->fDevice, &commandBuffersInfo,
+ SkDEBUGCODE(res =) mAllocateCommandBuffers(mDevice, &commandBuffersInfo,
surface->mBackbuffers[i].mTransitionCmdBuffers);
- SkDEBUGCODE(res =) mCreateFence(mBackendContext->fDevice, &fenceInfo, nullptr,
+ SkDEBUGCODE(res =) mCreateFence(mDevice, &fenceInfo, nullptr,
&surface->mBackbuffers[i].mUsageFences[0]);
- SkDEBUGCODE(res =) mCreateFence(mBackendContext->fDevice, &fenceInfo, nullptr,
+ SkDEBUGCODE(res =) mCreateFence(mDevice, &fenceInfo, nullptr,
&surface->mBackbuffers[i].mUsageFences[1]);
SkASSERT(VK_SUCCESS == res);
}
@@ -372,35 +613,35 @@
bool VulkanManager::createSwapchain(VulkanSurface* surface) {
// check for capabilities
VkSurfaceCapabilitiesKHR caps;
- VkResult res = mGetPhysicalDeviceSurfaceCapabilitiesKHR(mBackendContext->fPhysicalDevice,
+ VkResult res = mGetPhysicalDeviceSurfaceCapabilitiesKHR(mPhysicalDevice,
surface->mVkSurface, &caps);
if (VK_SUCCESS != res) {
return false;
}
uint32_t surfaceFormatCount;
- res = mGetPhysicalDeviceSurfaceFormatsKHR(mBackendContext->fPhysicalDevice, surface->mVkSurface,
+ res = mGetPhysicalDeviceSurfaceFormatsKHR(mPhysicalDevice, surface->mVkSurface,
&surfaceFormatCount, nullptr);
if (VK_SUCCESS != res) {
return false;
}
FatVector<VkSurfaceFormatKHR, 4> surfaceFormats(surfaceFormatCount);
- res = mGetPhysicalDeviceSurfaceFormatsKHR(mBackendContext->fPhysicalDevice, surface->mVkSurface,
+ res = mGetPhysicalDeviceSurfaceFormatsKHR(mPhysicalDevice, surface->mVkSurface,
&surfaceFormatCount, surfaceFormats.data());
if (VK_SUCCESS != res) {
return false;
}
uint32_t presentModeCount;
- res = mGetPhysicalDeviceSurfacePresentModesKHR(mBackendContext->fPhysicalDevice,
+ res = mGetPhysicalDeviceSurfacePresentModesKHR(mPhysicalDevice,
surface->mVkSurface, &presentModeCount, nullptr);
if (VK_SUCCESS != res) {
return false;
}
FatVector<VkPresentModeKHR, VK_PRESENT_MODE_RANGE_SIZE_KHR> presentModes(presentModeCount);
- res = mGetPhysicalDeviceSurfacePresentModesKHR(mBackendContext->fPhysicalDevice,
+ res = mGetPhysicalDeviceSurfacePresentModesKHR(mPhysicalDevice,
surface->mVkSurface, &presentModeCount,
presentModes.data());
if (VK_SUCCESS != res) {
@@ -482,8 +723,8 @@
swapchainCreateInfo.imageArrayLayers = 1;
swapchainCreateInfo.imageUsage = usageFlags;
- uint32_t queueFamilies[] = {mBackendContext->fGraphicsQueueIndex, mPresentQueueIndex};
- if (mBackendContext->fGraphicsQueueIndex != mPresentQueueIndex) {
+ uint32_t queueFamilies[] = {mGraphicsQueueIndex, mPresentQueueIndex};
+ if (mGraphicsQueueIndex != mPresentQueueIndex) {
swapchainCreateInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
swapchainCreateInfo.queueFamilyIndexCount = 2;
swapchainCreateInfo.pQueueFamilyIndices = queueFamilies;
@@ -499,19 +740,18 @@
swapchainCreateInfo.clipped = true;
swapchainCreateInfo.oldSwapchain = surface->mSwapchain;
- res = mCreateSwapchainKHR(mBackendContext->fDevice, &swapchainCreateInfo, nullptr,
- &surface->mSwapchain);
+ res = mCreateSwapchainKHR(mDevice, &swapchainCreateInfo, nullptr, &surface->mSwapchain);
if (VK_SUCCESS != res) {
return false;
}
// destroy the old swapchain
if (swapchainCreateInfo.oldSwapchain != VK_NULL_HANDLE) {
- mDeviceWaitIdle(mBackendContext->fDevice);
+ mDeviceWaitIdle(mDevice);
destroyBuffers(surface);
- mDestroySwapchainKHR(mBackendContext->fDevice, swapchainCreateInfo.oldSwapchain, nullptr);
+ mDestroySwapchainKHR(mDevice, swapchainCreateInfo.oldSwapchain, nullptr);
}
createBuffers(surface, surfaceFormat, extent);
@@ -535,20 +775,18 @@
surfaceCreateInfo.flags = 0;
surfaceCreateInfo.window = window;
- VkResult res = mCreateAndroidSurfaceKHR(mBackendContext->fInstance, &surfaceCreateInfo, nullptr,
- &surface->mVkSurface);
+ VkResult res = mCreateAndroidSurfaceKHR(mInstance, &surfaceCreateInfo, nullptr,
+ &surface->mVkSurface);
if (VK_SUCCESS != res) {
delete surface;
return nullptr;
}
SkDEBUGCODE(VkBool32 supported; res = mGetPhysicalDeviceSurfaceSupportKHR(
- mBackendContext->fPhysicalDevice, mPresentQueueIndex,
- surface->mVkSurface, &supported);
- // All physical devices and queue families on Android must be capable of
- // presentation with any
- // native window.
- SkASSERT(VK_SUCCESS == res && supported););
+ mPhysicalDevice, mPresentQueueIndex, surface->mVkSurface, &supported);
+ // All physical devices and queue families on Android must be capable of
+ // presentation with any native window.
+ SkASSERT(VK_SUCCESS == res && supported););
if (!createSwapchain(surface)) {
destroySurface(surface);
@@ -605,7 +843,7 @@
void VulkanManager::swapBuffers(VulkanSurface* surface) {
if (CC_UNLIKELY(Properties::waitForGpuCompletion)) {
ATRACE_NAME("Finishing GPU work");
- mDeviceWaitIdle(mBackendContext->fDevice);
+ mDeviceWaitIdle(mDevice);
}
SkASSERT(surface->mBackbuffers);
@@ -638,7 +876,7 @@
dstAccessMask, // inputMask
layout, // oldLayout
VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, // newLayout
- mBackendContext->fGraphicsQueueIndex, // srcQueueFamilyIndex
+ mGraphicsQueueIndex, // srcQueueFamilyIndex
mPresentQueueIndex, // dstQueueFamilyIndex
surface->mImages[backbuffer->mImageIndex], // image
{VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1} // subresourceRange
@@ -670,7 +908,7 @@
submitInfo.pSignalSemaphores = &backbuffer->mRenderSemaphore;
// Attach second fence to submission here so we can track when the command buffer finishes.
- mQueueSubmit(mBackendContext->fQueue, 1, &submitInfo, backbuffer->mUsageFences[1]);
+ mQueueSubmit(mGraphicsQueue, 1, &submitInfo, backbuffer->mUsageFences[1]);
// Submit present operation to present queue. We use a semaphore here to make sure all rendering
// to the image is complete and that the layout has been change to present on the graphics
diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h
index c319c9e..9a72706 100644
--- a/libs/hwui/renderthread/VulkanManager.h
+++ b/libs/hwui/renderthread/VulkanManager.h
@@ -79,7 +79,7 @@
void initialize();
// Quick check to see if the VulkanManager has been initialized.
- bool hasVkContext() { return mBackendContext.get() != nullptr; }
+ bool hasVkContext() { return mDevice != VK_NULL_HANDLE; }
// Given a window this creates a new VkSurfaceKHR and VkSwapchain and stores them inside a new
// VulkanSurface object which is returned.
@@ -111,6 +111,10 @@
explicit VulkanManager(RenderThread& thread);
~VulkanManager() { destroy(); }
+ // Sets up the VkInstance and VkDevice objects. Also fills out the passed in
+ // VkPhysicalDeviceFeatures struct.
+ bool setupDevice(VkPhysicalDeviceFeatures& deviceFeatures);
+
void destroyBuffers(VulkanSurface* surface);
bool createSwapchain(VulkanSurface* surface);
@@ -148,7 +152,21 @@
VkPtr<PFN_vkQueuePresentKHR> mQueuePresentKHR;
VkPtr<PFN_vkCreateSharedSwapchainsKHR> mCreateSharedSwapchainsKHR;
- // Additional vulkan functions
+ // Instance Functions
+ VkPtr<PFN_vkEnumerateInstanceExtensionProperties> mEnumerateInstanceExtensionProperties;
+ VkPtr<PFN_vkCreateInstance> mCreateInstance;
+
+ VkPtr<PFN_vkDestroyInstance> mDestroyInstance;
+ VkPtr<PFN_vkEnumeratePhysicalDevices> mEnumeratePhysicalDevices;
+ VkPtr<PFN_vkGetPhysicalDeviceQueueFamilyProperties> mGetPhysicalDeviceQueueFamilyProperties;
+ VkPtr<PFN_vkGetPhysicalDeviceFeatures> mGetPhysicalDeviceFeatures;
+ VkPtr<PFN_vkCreateDevice> mCreateDevice;
+ VkPtr<PFN_vkEnumerateDeviceExtensionProperties> mEnumerateDeviceExtensionProperties;
+
+ // Device Functions
+ VkPtr<PFN_vkGetDeviceQueue> mGetDeviceQueue;
+ VkPtr<PFN_vkDeviceWaitIdle> mDeviceWaitIdle;
+ VkPtr<PFN_vkDestroyDevice> mDestroyDevice;
VkPtr<PFN_vkCreateCommandPool> mCreateCommandPool;
VkPtr<PFN_vkDestroyCommandPool> mDestroyCommandPool;
VkPtr<PFN_vkAllocateCommandBuffers> mAllocateCommandBuffers;
@@ -158,10 +176,8 @@
VkPtr<PFN_vkEndCommandBuffer> mEndCommandBuffer;
VkPtr<PFN_vkCmdPipelineBarrier> mCmdPipelineBarrier;
- VkPtr<PFN_vkGetDeviceQueue> mGetDeviceQueue;
VkPtr<PFN_vkQueueSubmit> mQueueSubmit;
VkPtr<PFN_vkQueueWaitIdle> mQueueWaitIdle;
- VkPtr<PFN_vkDeviceWaitIdle> mDeviceWaitIdle;
VkPtr<PFN_vkCreateSemaphore> mCreateSemaphore;
VkPtr<PFN_vkDestroySemaphore> mDestroySemaphore;
@@ -172,7 +188,12 @@
RenderThread& mRenderThread;
- sk_sp<const GrVkBackendContext> mBackendContext;
+ VkInstance mInstance = VK_NULL_HANDLE;
+ VkPhysicalDevice mPhysicalDevice = VK_NULL_HANDLE;
+ VkDevice mDevice = VK_NULL_HANDLE;
+
+ uint32_t mGraphicsQueueIndex;
+ VkQueue mGraphicsQueue = VK_NULL_HANDLE;
uint32_t mPresentQueueIndex;
VkQueue mPresentQueue = VK_NULL_HANDLE;
VkCommandPool mCommandPool = VK_NULL_HANDLE;
diff --git a/libs/hwui/service/GraphicsStatsService.cpp b/libs/hwui/service/GraphicsStatsService.cpp
index 7f8cb2d..3d50d2d 100644
--- a/libs/hwui/service/GraphicsStatsService.cpp
+++ b/libs/hwui/service/GraphicsStatsService.cpp
@@ -127,7 +127,7 @@
return false;
}
void* addr = mmap(nullptr, sb.st_size, PROT_READ, MAP_SHARED, fd, 0);
- if (!addr) {
+ if (addr == MAP_FAILED) {
int err = errno;
// The file not existing is normal for addToDump(), so only log if
// we get an unexpected error
diff --git a/libs/hwui/tests/macrobench/main.cpp b/libs/hwui/tests/macrobench/main.cpp
index b74d359..e3c97ce 100644
--- a/libs/hwui/tests/macrobench/main.cpp
+++ b/libs/hwui/tests/macrobench/main.cpp
@@ -136,8 +136,6 @@
gBenchmarkReporter.reset(new benchmark::ConsoleReporter());
} else if (!strcmp(format, "json")) {
gBenchmarkReporter.reset(new benchmark::JSONReporter());
- } else if (!strcmp(format, "csv")) {
- gBenchmarkReporter.reset(new benchmark::CSVReporter());
} else {
fprintf(stderr, "Unknown format '%s'", format);
return false;
diff --git a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
index 15c0ab1..eb67e6c 100644
--- a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
+++ b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
@@ -1094,7 +1094,7 @@
class ShadowTestCanvas : public SkCanvas {
public:
ShadowTestCanvas(int width, int height) : SkCanvas(width, height) {}
- int getIndex() { return mDrawCounter; }
+ int getDrawCounter() { return mDrawCounter; }
virtual void onDrawDrawable(SkDrawable* drawable, const SkMatrix* matrix) override {
// expect to draw 2 RenderNodeDrawable, 1 StartReorderBarrierDrawable,
@@ -1109,17 +1109,36 @@
EXPECT_EQ(dy, TRANSLATE_Y);
}
- virtual void didConcat(const SkMatrix& matrix) override {
- // This function is invoked by EndReorderBarrierDrawable::drawShadow to apply shadow
- // matrix.
+ virtual void didSetMatrix(const SkMatrix& matrix) override {
mDrawCounter++;
- EXPECT_EQ(SkMatrix::MakeTrans(CASTER_X, CASTER_Y), matrix);
- EXPECT_EQ(SkMatrix::MakeTrans(CASTER_X + TRANSLATE_X, CASTER_Y + TRANSLATE_Y),
- getTotalMatrix());
+ // First invocation is EndReorderBarrierDrawable::drawShadow to apply shadow matrix.
+ // Second invocation is preparing the matrix for an elevated RenderNodeDrawable.
+ EXPECT_TRUE(matrix.isIdentity());
+ EXPECT_TRUE(getTotalMatrix().isIdentity());
+ }
+
+ virtual void didConcat(const SkMatrix& matrix) override {
+ mDrawCounter++;
+ if (mFirstDidConcat) {
+ // First invocation is EndReorderBarrierDrawable::drawShadow to apply shadow matrix.
+ mFirstDidConcat = false;
+ EXPECT_EQ(SkMatrix::MakeTrans(CASTER_X + TRANSLATE_X, CASTER_Y + TRANSLATE_Y),
+ matrix);
+ EXPECT_EQ(SkMatrix::MakeTrans(CASTER_X + TRANSLATE_X, CASTER_Y + TRANSLATE_Y),
+ getTotalMatrix());
+ } else {
+ // Second invocation is preparing the matrix for an elevated RenderNodeDrawable.
+ EXPECT_EQ(SkMatrix::MakeTrans(TRANSLATE_X, TRANSLATE_Y),
+ matrix);
+ EXPECT_EQ(SkMatrix::MakeTrans(TRANSLATE_X, TRANSLATE_Y),
+ getTotalMatrix());
+ }
}
protected:
int mDrawCounter = 0;
+ private:
+ bool mFirstDidConcat = true;
};
auto parent = TestUtils::createSkiaNode(
@@ -1143,7 +1162,7 @@
ShadowTestCanvas canvas(CANVAS_WIDTH, CANVAS_HEIGHT);
RenderNodeDrawable drawable(parent.get(), &canvas, false);
canvas.drawDrawable(&drawable);
- EXPECT_EQ(6, canvas.getIndex());
+ EXPECT_EQ(9, canvas.getDrawCounter());
}
// Draw a vector drawable twice but with different bounds and verify correct bounds are used.
diff --git a/libs/usb/Android.bp b/libs/usb/Android.bp
index b8f2904..027a748 100644
--- a/libs/usb/Android.bp
+++ b/libs/usb/Android.bp
@@ -1 +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.
+//
+
+java_sdk_library {
+ name: "com.android.future.usb.accessory",
+ srcs: ["src/**/*.java"],
+ api_packages: ["com.android.future.usb"],
+}
+
subdirs = ["tests/*"]
diff --git a/libs/usb/Android.mk b/libs/usb/Android.mk
deleted file mode 100644
index 129828f..0000000
--- a/libs/usb/Android.mk
+++ /dev/null
@@ -1,27 +0,0 @@
-#
-# Copyright (C) 2011 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := $(call all-java-files-under,src)
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_MODULE:= com.android.future.usb.accessory
-
-include $(BUILD_JAVA_LIBRARY)
diff --git a/libs/usb/api/current.txt b/libs/usb/api/current.txt
new file mode 100644
index 0000000..8488db5
--- /dev/null
+++ b/libs/usb/api/current.txt
@@ -0,0 +1,25 @@
+package com.android.future.usb {
+
+ public class UsbAccessory {
+ method public java.lang.String getDescription();
+ method public java.lang.String getManufacturer();
+ method public java.lang.String getModel();
+ method public java.lang.String getSerial();
+ method public java.lang.String getUri();
+ method public java.lang.String getVersion();
+ }
+
+ public class UsbManager {
+ method public static com.android.future.usb.UsbAccessory getAccessory(android.content.Intent);
+ method public com.android.future.usb.UsbAccessory[] getAccessoryList();
+ method public static com.android.future.usb.UsbManager getInstance(android.content.Context);
+ method public boolean hasPermission(com.android.future.usb.UsbAccessory);
+ method public android.os.ParcelFileDescriptor openAccessory(com.android.future.usb.UsbAccessory);
+ method public void requestPermission(com.android.future.usb.UsbAccessory, android.app.PendingIntent);
+ field public static final java.lang.String ACTION_USB_ACCESSORY_ATTACHED = "android.hardware.usb.action.USB_ACCESSORY_ATTACHED";
+ field public static final java.lang.String ACTION_USB_ACCESSORY_DETACHED = "android.hardware.usb.action.USB_ACCESSORY_DETACHED";
+ field public static final java.lang.String EXTRA_PERMISSION_GRANTED = "permission";
+ }
+
+}
+
diff --git a/libs/usb/api/removed.txt b/libs/usb/api/removed.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/libs/usb/api/removed.txt
diff --git a/libs/usb/api/system-current.txt b/libs/usb/api/system-current.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/libs/usb/api/system-current.txt
diff --git a/libs/usb/api/system-removed.txt b/libs/usb/api/system-removed.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/libs/usb/api/system-removed.txt
diff --git a/libs/usb/api/test-current.txt b/libs/usb/api/test-current.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/libs/usb/api/test-current.txt
diff --git a/libs/usb/api/test-removed.txt b/libs/usb/api/test-removed.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/libs/usb/api/test-removed.txt
diff --git a/libs/usb/tests/AccessoryChat/Android.mk b/libs/usb/tests/AccessoryChat/Android.mk
index ecb455a..cfe2da1 100644
--- a/libs/usb/tests/AccessoryChat/Android.mk
+++ b/libs/usb/tests/AccessoryChat/Android.mk
@@ -23,4 +23,6 @@
LOCAL_PACKAGE_NAME := AccessoryChat
+LOCAL_PRIVATE_PLATFORM_APIS := true
+
include $(BUILD_PACKAGE)
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 6eb3d8d..ee9e732 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -22,7 +22,6 @@
import static android.Manifest.permission.WRITE_SECURE_SETTINGS;
import android.Manifest;
-import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresFeature;
import android.annotation.RequiresPermission;
@@ -42,16 +41,11 @@
import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
-import android.provider.Settings;
-import android.text.TextUtils;
-import android.util.ArraySet;
import android.util.Log;
import com.android.internal.location.ProviderProperties;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
-import java.util.Set;
/**
* This class provides access to the system location services. These
@@ -331,7 +325,7 @@
Message msg = Message.obtain();
msg.what = TYPE_LOCATION_CHANGED;
msg.obj = location;
- mListenerHandler.sendMessage(msg);
+ sendCallbackMessage(msg);
}
@Override
@@ -345,7 +339,7 @@
b.putBundle("extras", extras);
}
msg.obj = b;
- mListenerHandler.sendMessage(msg);
+ sendCallbackMessage(msg);
}
@Override
@@ -353,7 +347,7 @@
Message msg = Message.obtain();
msg.what = TYPE_PROVIDER_ENABLED;
msg.obj = provider;
- mListenerHandler.sendMessage(msg);
+ sendCallbackMessage(msg);
}
@Override
@@ -361,7 +355,13 @@
Message msg = Message.obtain();
msg.what = TYPE_PROVIDER_DISABLED;
msg.obj = provider;
- mListenerHandler.sendMessage(msg);
+ sendCallbackMessage(msg);
+ }
+
+ private void sendCallbackMessage(Message msg) {
+ if (!mListenerHandler.sendMessage(msg)) {
+ locationCallbackFinished();
+ }
}
private void _handleMessage(Message msg) {
@@ -384,6 +384,10 @@
mListener.onProviderDisabled((String) msg.obj);
break;
}
+ locationCallbackFinished();
+ }
+
+ private void locationCallbackFinished() {
try {
mService.locationCallbackFinished(this);
} catch (RemoteException e) {
diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java
index e245425..1030d9d 100644
--- a/media/java/android/media/AudioFormat.java
+++ b/media/java/android/media/AudioFormat.java
@@ -1083,6 +1083,7 @@
ENCODING_AAC_LC,
ENCODING_DOLBY_TRUEHD,
ENCODING_E_AC3_JOC,
+ ENCODING_AC4,
};
/** @hide */
@@ -1093,7 +1094,8 @@
ENCODING_DTS_HD,
ENCODING_AAC_LC,
ENCODING_DOLBY_TRUEHD,
- ENCODING_E_AC3_JOC }
+ ENCODING_E_AC3_JOC,
+ ENCODING_AC4 }
)
@Retention(RetentionPolicy.SOURCE)
public @interface SurroundSoundEncoding {}
@@ -1105,14 +1107,14 @@
* It is just a default to use if an international name is not available.
*
* @param audioFormat a surround format
- * @return short default name for the format, eg. “AC3” for ENCODING_AC3.
+ * @return short default name for the format.
*/
public static String toDisplayName(@SurroundSoundEncoding int audioFormat) {
switch (audioFormat) {
case ENCODING_AC3:
- return "Dolby Digital (AC3)";
+ return "Dolby Digital";
case ENCODING_E_AC3:
- return "Dolby Digital Plus (E_AC3)";
+ return "Dolby Digital Plus";
case ENCODING_DTS:
return "DTS";
case ENCODING_DTS_HD:
@@ -1122,7 +1124,9 @@
case ENCODING_DOLBY_TRUEHD:
return "Dolby TrueHD";
case ENCODING_E_AC3_JOC:
- return "Dolby Atmos";
+ return "Dolby Atmos in Dolby Digital Plus";
+ case ENCODING_AC4:
+ return "Dolby AC-4";
default:
return "Unknown surround sound format";
}
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 196b886..1a282b2 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -794,6 +794,11 @@
* <p>
* This method should only be used by applications that replace the platform-wide
* management of audio settings or the main telephony application.
+ * <p>This method has no effect if the device implements a fixed volume policy
+ * as indicated by {@link #isVolumeFixed()}.
+ * <p>From N onward, ringer mode adjustments that would toggle Do Not Disturb are not allowed
+ * unless the app has been granted Do Not Disturb Access.
+ * See {@link NotificationManager#isNotificationPolicyAccessGranted()}.
*
* @param streamType The stream type to adjust. One of {@link #STREAM_VOICE_CALL},
* {@link #STREAM_SYSTEM}, {@link #STREAM_RING}, {@link #STREAM_MUSIC},
@@ -804,6 +809,8 @@
* @param flags One or more flags.
* @see #adjustVolume(int, int)
* @see #setStreamVolume(int, int, int)
+ * @throws SecurityException if the adjustment triggers a Do Not Disturb change
+ * and the caller is not granted notification policy access.
*/
public void adjustStreamVolume(int streamType, int direction, int flags) {
final IAudioService service = getService();
@@ -1121,6 +1128,8 @@
* @see #getStreamMaxVolume(int)
* @see #getStreamVolume(int)
* @see #isVolumeFixed()
+ * @throws SecurityException if the volume change triggers a Do Not Disturb change
+ * and the caller is not granted notification policy access.
*/
public void setStreamVolume(int streamType, int index, int flags) {
final IAudioService service = getService();
diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java
index 7c68b55..2a2f4fe 100644
--- a/media/java/android/media/MediaFormat.java
+++ b/media/java/android/media/MediaFormat.java
@@ -143,7 +143,7 @@
* MIME type for HEIF still image data encoded in HEVC.
*
* To decode such an image, {@link MediaCodec} decoder for
- * {@ #MIMETYPE_VIDEO_HEVC} shall be used. The client needs to form
+ * {@link #MIMETYPE_VIDEO_HEVC} shall be used. The client needs to form
* the correct {@link #MediaFormat} based on additional information in
* the track format, and send it to {@link MediaCodec#configure}.
*
diff --git a/media/java/android/media/MediaHTTPConnection.java b/media/java/android/media/MediaHTTPConnection.java
index aae1f51..6bf52bd 100644
--- a/media/java/android/media/MediaHTTPConnection.java
+++ b/media/java/android/media/MediaHTTPConnection.java
@@ -323,8 +323,10 @@
StrictMode.setThreadPolicy(policy);
try {
- if (offset != mCurrentOffset) {
- seekTo(offset);
+ synchronized(this) {
+ if (offset != mCurrentOffset) {
+ seekTo(offset);
+ }
}
int n = mInputStream.read(data, 0, size);
@@ -366,7 +368,7 @@
}
@Override
- public long getSize() {
+ public synchronized long getSize() {
if (mConnection == null) {
try {
seekTo(0);
@@ -379,7 +381,7 @@
}
@Override
- public String getMIMEType() {
+ public synchronized String getMIMEType() {
if (mConnection == null) {
try {
seekTo(0);
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index ada91be..d532e52 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -2510,10 +2510,10 @@
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(mTrackType);
+ dest.writeString(mFormat.getString(MediaFormat.KEY_MIME));
dest.writeString(getLanguage());
if (mTrackType == MEDIA_TRACK_TYPE_SUBTITLE) {
- dest.writeString(mFormat.getString(MediaFormat.KEY_MIME));
dest.writeInt(mFormat.getInteger(MediaFormat.KEY_IS_AUTOSELECT));
dest.writeInt(mFormat.getInteger(MediaFormat.KEY_IS_DEFAULT));
dest.writeInt(mFormat.getInteger(MediaFormat.KEY_IS_FORCED_SUBTITLE));
diff --git a/media/java/android/media/MediaPlayer2.java b/media/java/android/media/MediaPlayer2.java
index dcc872c..61c412d 100644
--- a/media/java/android/media/MediaPlayer2.java
+++ b/media/java/android/media/MediaPlayer2.java
@@ -116,9 +116,9 @@
* these circumstances. Sometimes, due to programming errors, invoking a playback
* control operation in an invalid state may also occur. Under all these
* error conditions, the internal player engine invokes a user supplied
- * MediaPlayer2EventCallback.onError() method if an MediaPlayer2EventCallback has been
+ * EventCallback.onError() method if an EventCallback has been
* registered beforehand via
- * {@link #setMediaPlayer2EventCallback(Executor, MediaPlayer2EventCallback)}.
+ * {@link #setEventCallback(Executor, EventCallback)}.
* <ul>
* <li>It is important to note that once an error occurs, the
* MediaPlayer2 object enters the <em>Error</em> state (except as noted
@@ -159,9 +159,9 @@
* player engine continues working on the rest of preparation work
* until the preparation work completes. When the preparation completes,
* the internal player engine then calls a user supplied callback method,
- * onInfo() of the MediaPlayer2EventCallback interface with {@link #MEDIA_INFO_PREPARED},
- * if an MediaPlayer2EventCallback is registered beforehand via
- * {@link #setMediaPlayer2EventCallback(Executor, MediaPlayer2EventCallback)}.</li>
+ * onInfo() of the EventCallback interface with {@link #MEDIA_INFO_PREPARED},
+ * if an EventCallback is registered beforehand via
+ * {@link #setEventCallback(Executor, EventCallback)}.</li>
* <li>It is important to note that
* the <em>Preparing</em> state is a transient state, and the behavior
* of calling any method with side effect while a MediaPlayer2 object is
@@ -180,10 +180,10 @@
* whether the MediaPlayer2 object is in the <em>Started</em> state.
* <ul>
* <li>While in the <em>Started</em> state, the internal player engine calls
- * a user supplied callback method MediaPlayer2EventCallback.onInfo() with
- * {@link #MEDIA_INFO_BUFFERING_UPDATE} if an MediaPlayer2EventCallback has been
+ * a user supplied callback method EventCallback.onInfo() with
+ * {@link #MEDIA_INFO_BUFFERING_UPDATE} if an EventCallback has been
* registered beforehand via
- * {@link #setMediaPlayer2EventCallback(Executor, MediaPlayer2EventCallback)}.
+ * {@link #setEventCallback(Executor, EventCallback)}.
* This callback allows applications to keep track of the buffering status
* while streaming audio/video.</li>
* <li>Calling {@link #play()} has not effect
@@ -215,10 +215,10 @@
* call returns right away, the actual seek operation may take a while to
* finish, especially for audio/video being streamed. When the actual
* seek operation completes, the internal player engine calls a user
- * supplied MediaPlayer2EventCallback.onCallCompleted() with
+ * supplied EventCallback.onCallCompleted() with
* {@link #CALL_COMPLETED_SEEK_TO}
- * if an MediaPlayer2EventCallback has been registered beforehand via
- * {@link #setMediaPlayer2EventCallback(Executor, MediaPlayer2EventCallback)}.</li>
+ * if an EventCallback has been registered beforehand via
+ * {@link #setEventCallback(Executor, EventCallback)}.</li>
* <li>Please
* note that {@link #seekTo(long, int)} can also be called in the other states,
* such as <em>Prepared</em>, <em>Paused</em> and <em>PlaybackCompleted
@@ -238,9 +238,9 @@
* the MediaPlayer2 object shall remain in the <em>Started</em> state.</li>
* <li>If the looping mode was set to <var>false
* </var>, the player engine calls a user supplied callback method,
- * MediaPlayer2EventCallback.onCompletion(), if an MediaPlayer2EventCallback is
+ * EventCallback.onCompletion(), if an EventCallback is
* registered beforehand via
- * {@link #setMediaPlayer2EventCallback(Executor, MediaPlayer2EventCallback)}.
+ * {@link #setEventCallback(Executor, EventCallback)}.
* The invoke of the callback signals that the object is now in the <em>
* PlaybackCompleted</em> state.</li>
* <li>While in the <em>PlaybackCompleted</em>
@@ -387,7 +387,7 @@
* <td>{} </p></td>
* <td>This method can be called in any state and calling it does not change
* the object state. </p></td></tr>
- * <tr><td>setMediaPlayer2EventCallback </p></td>
+ * <tr><td>setEventCallback </p></td>
* <td>any </p></td>
* <td>{} </p></td>
* <td>This method can be called in any state and calling it does not change
@@ -445,7 +445,7 @@
* possible runtime errors during playback or streaming. Registration for
* these events is done by properly setting the appropriate listeners (via calls
* to
- * {@link #setMediaPlayer2EventCallback(Executor, MediaPlayer2EventCallback)},
+ * {@link #setEventCallback(Executor, EventCallback)},
* {@link #setDrmEventCallback(Executor, DrmEventCallback)}).
* In order to receive the respective callback
* associated with these listeners, applications are required to create
@@ -453,8 +453,8 @@
* thread by default has a Looper running).
*
*/
-public abstract class MediaPlayer2 extends MediaPlayerBase
- implements SubtitleController.Listener
+public abstract class MediaPlayer2 implements SubtitleController.Listener
+ , AutoCloseable
, AudioRouting {
/**
* Create a MediaPlayer2 object.
@@ -514,6 +514,12 @@
public MediaPlayer2() { }
/**
+ * Returns a {@link MediaPlayerBase} implementation which runs based on
+ * this MediaPlayer2 instance.
+ */
+ public abstract MediaPlayerBase getMediaPlayerBase();
+
+ /**
* Releases the resources held by this {@code MediaPlayer2} object.
*
* It is considered good practice to call this method when you're
@@ -549,7 +555,6 @@
*
*/
// This is an asynchronous call.
- @Override
public abstract void play();
/**
@@ -560,21 +565,18 @@
*
*/
// This is an asynchronous call.
- @Override
public abstract void prepare();
/**
* Pauses playback. Call play() to resume.
*/
// This is an asynchronous call.
- @Override
public abstract void pause();
/**
* Tries to play next data source if applicable.
*/
// This is an asynchronous call.
- @Override
public abstract void skipToNext();
/**
@@ -584,7 +586,6 @@
* @param msec the offset in milliseconds from the start to seek to
*/
// This is an asynchronous call.
- @Override
public void seekTo(long msec) {
seekTo(msec, SEEK_PREVIOUS_SYNC /* mode */);
}
@@ -594,7 +595,6 @@
*
* @return the current position in milliseconds
*/
- @Override
public abstract long getCurrentPosition();
/**
@@ -603,7 +603,6 @@
* @return the duration in milliseconds, if no duration is available
* (for example, if streaming live content), -1 is returned.
*/
- @Override
public abstract long getDuration();
/**
@@ -615,25 +614,60 @@
*
* @return the current buffered media source position in milliseconds
*/
- @Override
public abstract long getBufferedPosition();
/**
+ * MediaPlayer2 has not been prepared or just has been reset.
+ * In this state, MediaPlayer2 doesn't fetch data.
+ * @hide
+ */
+ public static final int PLAYER_STATE_IDLE = 1001;
+
+ /**
+ * MediaPlayer2 has been just prepared.
+ * In this state, MediaPlayer2 just fetches data from media source,
+ * but doesn't actively render data.
+ * @hide
+ */
+ public static final int PLAYER_STATE_PREPARED = 1002;
+
+ /**
+ * MediaPlayer2 is paused.
+ * In this state, MediaPlayer2 doesn't actively render data.
+ * @hide
+ */
+ public static final int PLAYER_STATE_PAUSED = 1003;
+
+ /**
+ * MediaPlayer2 is actively playing back data.
+ * @hide
+ */
+ public static final int PLAYER_STATE_PLAYING = 1004;
+
+ /**
+ * MediaPlayer2 has hit some fatal error and cannot continue playback.
+ * @hide
+ */
+ public static final int PLAYER_STATE_ERROR = 1005;
+
+ /**
+ * @hide
+ */
+ @IntDef(flag = false, prefix = "MEDIAPLAYER2_STATE", value = {
+ PLAYER_STATE_IDLE,
+ PLAYER_STATE_PREPARED,
+ PLAYER_STATE_PAUSED,
+ PLAYER_STATE_PLAYING,
+ PLAYER_STATE_ERROR })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface MediaPlayer2State {}
+
+ /**
* Gets the current player state.
*
* @return the current player state.
*/
- @Override
- public abstract @PlayerState int getPlayerState();
-
- /**
- * Gets the current buffering state of the player.
- * During buffering, see {@link #getBufferedPosition()} for the quantifying the amount already
- * buffered.
- * @return the buffering state, one of the following:
- */
- @Override
- public abstract @BuffState int getBufferingState();
+ public abstract @MediaPlayer2State int getState();
/**
* Sets the audio attributes for this MediaPlayer2.
@@ -643,14 +677,12 @@
* @param attributes a non-null set of audio attributes
*/
// This is an asynchronous call.
- @Override
public abstract void setAudioAttributes(@NonNull AudioAttributes attributes);
/**
* Gets the audio attributes for this MediaPlayer2.
* @return attributes a set of audio attributes
*/
- @Override
public abstract @Nullable AudioAttributes getAudioAttributes();
/**
@@ -659,7 +691,6 @@
* @param dsd the descriptor of data source you want to play
*/
// This is an asynchronous call.
- @Override
public abstract void setDataSource(@NonNull DataSourceDesc dsd);
/**
@@ -669,7 +700,6 @@
* @param dsd the descriptor of data source you want to play after current one
*/
// This is an asynchronous call.
- @Override
public abstract void setNextDataSource(@NonNull DataSourceDesc dsd);
/**
@@ -678,7 +708,6 @@
* @param dsds the list of data sources you want to play after current one
*/
// This is an asynchronous call.
- @Override
public abstract void setNextDataSources(@NonNull List<DataSourceDesc> dsds);
/**
@@ -686,7 +715,6 @@
*
* @return the current DataSourceDesc
*/
- @Override
public abstract @NonNull DataSourceDesc getCurrentDataSource();
/**
@@ -694,44 +722,9 @@
* @param loop true if the current data source is meant to loop.
*/
// This is an asynchronous call.
- @Override
public abstract void loopCurrent(boolean loop);
/**
- * Sets the playback speed.
- * A value of 1.0f is the default playback value.
- * A negative value indicates reverse playback, check {@link #isReversePlaybackSupported()}
- * before using negative values.<br>
- * After changing the playback speed, it is recommended to query the actual speed supported
- * by the player, see {@link #getPlaybackSpeed()}.
- * @param speed the desired playback speed
- */
- // This is an asynchronous call.
- @Override
- public abstract void setPlaybackSpeed(float speed);
-
- /**
- * Returns the actual playback speed to be used by the player when playing.
- * Note that it may differ from the speed set in {@link #setPlaybackSpeed(float)}.
- * @return the actual playback speed
- */
- @Override
- public float getPlaybackSpeed() {
- return 1.0f;
- }
-
- /**
- * Indicates whether reverse playback is supported.
- * Reverse playback is indicated by negative playback speeds, see
- * {@link #setPlaybackSpeed(float)}.
- * @return true if reverse playback is supported.
- */
- @Override
- public boolean isReversePlaybackSupported() {
- return false;
- }
-
- /**
* Sets the volume of the audio of the media to play, expressed as a linear multiplier
* on the audio samples.
* Note that this volume is specific to the player, and is separate from stream volume
@@ -741,7 +734,6 @@
* @param volume a value between 0.0f and {@link #getMaxPlayerVolume()}.
*/
// This is an asynchronous call.
- @Override
public abstract void setPlayerVolume(float volume);
/**
@@ -749,36 +741,16 @@
* Note that it does not take into account the associated stream volume.
* @return the player volume.
*/
- @Override
public abstract float getPlayerVolume();
/**
* @return the maximum volume that can be used in {@link #setPlayerVolume(float)}.
*/
- @Override
public float getMaxPlayerVolume() {
return 1.0f;
}
/**
- * Adds a callback to be notified of events for this player.
- * @param e the {@link Executor} to be used for the events.
- * @param cb the callback to receive the events.
- */
- // This is a synchronous call.
- @Override
- public abstract void registerPlayerEventCallback(@NonNull Executor e,
- @NonNull PlayerEventCallback cb);
-
- /**
- * Removes a previously registered callback for player events
- * @param cb the callback to remove
- */
- // This is a synchronous call.
- @Override
- public abstract void unregisterPlayerEventCallback(@NonNull PlayerEventCallback cb);
-
- /**
* Create a request parcel which can be routed to the native media
* player using {@link #invoke(Parcel, Parcel)}. The Parcel
* returned has the proper InterfaceToken set. The caller should
@@ -812,10 +784,10 @@
/**
* Insert a task in the command queue to help the client to identify whether a batch
* of commands has been finished. When this command is processed, a notification
- * {@code MediaPlayer2EventCallback.onCommandLabelReached} will be fired with the
+ * {@code EventCallback.onCommandLabelReached} will be fired with the
* given {@code label}.
*
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCommandLabelReached
+ * @see android.media.MediaPlayer2.EventCallback#onCommandLabelReached
*
* @param label An application specific Object used to help to identify the completeness
* of a batch of commands.
@@ -911,15 +883,6 @@
// This is a synchronous call.
public abstract void clearPendingCommands();
- /**
- * Stops playback after playback has been started or paused.
- *
- * @throws IllegalStateException if the internal player engine has not been
- * initialized.
- * @hide
- */
- public void stop() { }
-
//--------------------------------------------------------------------------
// Explicit Routing
//--------------------
@@ -973,7 +936,8 @@
*/
// This is a synchronous call.
@Override
- public abstract void removeOnRoutingChangedListener(AudioRouting.OnRoutingChangedListener listener);
+ public abstract void removeOnRoutingChangedListener(
+ AudioRouting.OnRoutingChangedListener listener);
/**
* Set the low-level power management behavior for this MediaPlayer2.
@@ -1010,9 +974,9 @@
*
* @return the width of the video, or 0 if there is no video,
* no display surface was set, or the width has not been determined
- * yet. The {@code MediaPlayer2EventCallback} can be registered via
- * {@link #setMediaPlayer2EventCallback(Executor, MediaPlayer2EventCallback)} to provide a
- * notification {@code MediaPlayer2EventCallback.onVideoSizeChanged} when the width
+ * yet. The {@code EventCallback} can be registered via
+ * {@link #setEventCallback(Executor, EventCallback)} to provide a
+ * notification {@code EventCallback.onVideoSizeChanged} when the width
* is available.
*/
public abstract int getVideoWidth();
@@ -1022,9 +986,10 @@
*
* @return the height of the video, or 0 if there is no video,
* no display surface was set, or the height has not been determined
- * yet. The {@code MediaPlayer2EventCallback} can be registered via
- * {@link #setMediaPlayer2EventCallback(Executor, MediaPlayer2EventCallback)} to provide a
- * notification {@code MediaPlayer2EventCallback.onVideoSizeChanged} when the height is available.
+ * yet. The {@code EventCallback} can be registered via
+ * {@link #setEventCallback(Executor, EventCallback)} to provide a
+ * notification {@code EventCallback.onVideoSizeChanged} when the height is
+ * available.
*/
public abstract int getVideoHeight();
@@ -1051,60 +1016,6 @@
public abstract boolean isPlaying();
/**
- * MediaPlayer2 has not been prepared or just has been reset.
- * In this state, MediaPlayer2 doesn't fetch data.
- * @hide
- */
- public static final int MEDIAPLAYER2_STATE_IDLE = 1;
-
- /**
- * MediaPlayer2 has been just prepared.
- * In this state, MediaPlayer2 just fetches data from media source,
- * but doesn't actively render data.
- * @hide
- */
- public static final int MEDIAPLAYER2_STATE_PREPARED = 2;
-
- /**
- * MediaPlayer2 is paused.
- * In this state, MediaPlayer2 doesn't actively render data.
- * @hide
- */
- public static final int MEDIAPLAYER2_STATE_PAUSED = 3;
-
- /**
- * MediaPlayer2 is actively playing back data.
- * @hide
- */
- public static final int MEDIAPLAYER2_STATE_PLAYING = 4;
-
- /**
- * MediaPlayer2 has hit some fatal error and cannot continue playback.
- * @hide
- */
- public static final int MEDIAPLAYER2_STATE_ERROR = 5;
-
- /**
- * @hide
- */
- @IntDef(flag = false, prefix = "MEDIAPLAYER2_STATE", value = {
- MEDIAPLAYER2_STATE_IDLE,
- MEDIAPLAYER2_STATE_PREPARED,
- MEDIAPLAYER2_STATE_PAUSED,
- MEDIAPLAYER2_STATE_PLAYING,
- MEDIAPLAYER2_STATE_ERROR })
- @Retention(RetentionPolicy.SOURCE)
- public @interface MediaPlayer2State {}
-
- /**
- * Gets the current MediaPlayer2 state.
- *
- * @return the current MediaPlayer2 state.
- * @hide
- */
- public abstract @MediaPlayer2State int getMediaPlayer2State();
-
- /**
* Gets the current buffering management params used by the source component.
* Calling it only after {@code setDataSource} has been called.
* Each type of data source might have different set of default params.
@@ -1383,7 +1294,6 @@
* data source and calling prepare().
*/
// This is a synchronous call.
- @Override
public abstract void reset();
/**
@@ -1706,7 +1616,7 @@
* Interface definition for callbacks to be invoked when the player has the corresponding
* events.
*/
- public abstract static class MediaPlayer2EventCallback {
+ public abstract static class EventCallback {
/**
* Called to indicate the video size
*
@@ -1718,7 +1628,8 @@
* @param width the width of the video
* @param height the height of the video
*/
- public void onVideoSizeChanged(MediaPlayer2 mp, DataSourceDesc dsd, int width, int height) { }
+ public void onVideoSizeChanged(
+ MediaPlayer2 mp, DataSourceDesc dsd, int width, int height) { }
/**
* Called to indicate an avaliable timed text
@@ -1794,7 +1705,7 @@
* @param dsd the DataSourceDesc of this data source
* @param timestamp the new media clock.
*/
- public void onMediaTimeChanged(
+ public void onMediaTimeDiscontinuity(
MediaPlayer2 mp, DataSourceDesc dsd, MediaTimestamp timestamp) { }
/**
@@ -1805,58 +1716,48 @@
* {@link #notifyWhenCommandLabelReached(Object)}.
*/
public void onCommandLabelReached(MediaPlayer2 mp, @NonNull Object label) { }
+
+ /**
+ * Called when when a player subtitle track has new subtitle data available.
+ * @param mp the player that reports the new subtitle data
+ * @param dsd the DataSourceDesc of this data source
+ * @param data the subtitle data
+ */
+ public void onSubtitleData(
+ MediaPlayer2 mp, DataSourceDesc dsd, @NonNull SubtitleData data) { }
}
/**
* Sets the callback to be invoked when the media source is ready for playback.
*
- * @param eventCallback the callback that will be run
* @param executor the executor through which the callback should be invoked
+ * @param eventCallback the callback that will be run
*/
// This is a synchronous call.
- public abstract void setMediaPlayer2EventCallback(@NonNull @CallbackExecutor Executor executor,
- @NonNull MediaPlayer2EventCallback eventCallback);
+ public abstract void registerEventCallback(@NonNull @CallbackExecutor Executor executor,
+ @NonNull EventCallback eventCallback);
/**
- * Clears the {@link MediaPlayer2EventCallback}.
+ * Unregisters the {@link EventCallback}.
+ *
+ * @param eventCallback the callback to be unregistered
*/
// This is a synchronous call.
- public abstract void clearMediaPlayer2EventCallback();
-
- /**
- * Interface definition of a callback to be invoked when a
- * track has data available.
- *
- * @hide
- */
- public interface OnSubtitleDataListener
- {
- public void onSubtitleData(MediaPlayer2 mp, SubtitleData data);
- }
-
- /**
- * Register a callback to be invoked when a track has data available.
- *
- * @param listener the callback that will be run
- *
- * @hide
- */
- // This is a synchronous call.
- public void setOnSubtitleDataListener(OnSubtitleDataListener listener) { }
+ public abstract void unregisterEventCallback(EventCallback eventCallback);
/* Do not change these values without updating their counterparts
* in include/media/mediaplayer2.h!
*/
/** Unspecified media player error.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onError
+ * @see android.media.MediaPlayer2.EventCallback#onError
*/
public static final int MEDIA_ERROR_UNKNOWN = 1;
/** The video is streamed and its container is not valid for progressive
* playback i.e the video's index (e.g moov atom) is not at the start of the
* file.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onError
+ * @see android.media.MediaPlayer2.EventCallback#onError
*/
public static final int MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK = 200;
@@ -1872,7 +1773,7 @@
/** Unspecified low-level system error. This value originated from UNKNOWN_ERROR in
* system/core/include/utils/Errors.h
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onError
+ * @see android.media.MediaPlayer2.EventCallback#onError
* @hide
*/
public static final int MEDIA_ERROR_SYSTEM = -2147483648;
@@ -1896,62 +1797,62 @@
* in include/media/mediaplayer2.h!
*/
/** Unspecified media player info.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
+ * @see android.media.MediaPlayer2.EventCallback#onInfo
*/
public static final int MEDIA_INFO_UNKNOWN = 1;
/** The player switched to this datas source because it is the
* next-to-be-played in the playlist.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
+ * @see android.media.MediaPlayer2.EventCallback#onInfo
*/
public static final int MEDIA_INFO_STARTED_AS_NEXT = 2;
/** The player just pushed the very first video frame for rendering.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
+ * @see android.media.MediaPlayer2.EventCallback#onInfo
*/
public static final int MEDIA_INFO_VIDEO_RENDERING_START = 3;
/** The player just rendered the very first audio sample.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
+ * @see android.media.MediaPlayer2.EventCallback#onInfo
*/
public static final int MEDIA_INFO_AUDIO_RENDERING_START = 4;
/** The player just completed the playback of this data source.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
+ * @see android.media.MediaPlayer2.EventCallback#onInfo
*/
public static final int MEDIA_INFO_PLAYBACK_COMPLETE = 5;
/** The player just completed the playback of the full playlist.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
+ * @see android.media.MediaPlayer2.EventCallback#onInfo
*/
public static final int MEDIA_INFO_PLAYLIST_END = 6;
/** The player just prepared a data source.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
+ * @see android.media.MediaPlayer2.EventCallback#onInfo
*/
public static final int MEDIA_INFO_PREPARED = 100;
/** The video is too complex for the decoder: it can't decode frames fast
* enough. Possibly only the audio plays fine at this stage.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
+ * @see android.media.MediaPlayer2.EventCallback#onInfo
*/
public static final int MEDIA_INFO_VIDEO_TRACK_LAGGING = 700;
/** MediaPlayer2 is temporarily pausing playback internally in order to
* buffer more data.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
+ * @see android.media.MediaPlayer2.EventCallback#onInfo
*/
public static final int MEDIA_INFO_BUFFERING_START = 701;
/** MediaPlayer2 is resuming playback after filling buffers.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
+ * @see android.media.MediaPlayer2.EventCallback#onInfo
*/
public static final int MEDIA_INFO_BUFFERING_END = 702;
/** Estimated network bandwidth information (kbps) is available; currently this event fires
* simultaneously as {@link #MEDIA_INFO_BUFFERING_START} and {@link #MEDIA_INFO_BUFFERING_END}
* when playing network files.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
+ * @see android.media.MediaPlayer2.EventCallback#onInfo
* @hide
*/
public static final int MEDIA_INFO_NETWORK_BANDWIDTH = 703;
@@ -1963,26 +1864,26 @@
* has already been played indicates that the next 30 percent of the
* content to play has been buffered.
*
- * The {@code extra} parameter in {@code MediaPlayer2EventCallback.onInfo} is the
+ * The {@code extra} parameter in {@code EventCallback.onInfo} is the
* percentage (0-100) of the content that has been buffered or played thus far.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
+ * @see android.media.MediaPlayer2.EventCallback#onInfo
*/
public static final int MEDIA_INFO_BUFFERING_UPDATE = 704;
/** Bad interleaving means that a media has been improperly interleaved or
* not interleaved at all, e.g has all the video samples first then all the
* audio ones. Video is playing but a lot of disk seeks may be happening.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
+ * @see android.media.MediaPlayer2.EventCallback#onInfo
*/
public static final int MEDIA_INFO_BAD_INTERLEAVING = 800;
/** The media cannot be seeked (e.g live stream)
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
+ * @see android.media.MediaPlayer2.EventCallback#onInfo
*/
public static final int MEDIA_INFO_NOT_SEEKABLE = 801;
/** A new set of metadata is available.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
+ * @see android.media.MediaPlayer2.EventCallback#onInfo
*/
public static final int MEDIA_INFO_METADATA_UPDATE = 802;
@@ -1994,30 +1895,30 @@
/** Informs that audio is not playing. Note that playback of the video
* is not interrupted.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
+ * @see android.media.MediaPlayer2.EventCallback#onInfo
*/
public static final int MEDIA_INFO_AUDIO_NOT_PLAYING = 804;
/** Informs that video is not playing. Note that playback of the audio
* is not interrupted.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
+ * @see android.media.MediaPlayer2.EventCallback#onInfo
*/
public static final int MEDIA_INFO_VIDEO_NOT_PLAYING = 805;
/** Failed to handle timed text track properly.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
+ * @see android.media.MediaPlayer2.EventCallback#onInfo
*
* {@hide}
*/
public static final int MEDIA_INFO_TIMED_TEXT_ERROR = 900;
/** Subtitle track was not supported by the media framework.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
+ * @see android.media.MediaPlayer2.EventCallback#onInfo
*/
public static final int MEDIA_INFO_UNSUPPORTED_SUBTITLE = 901;
/** Reading the subtitle track takes too long.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
+ * @see android.media.MediaPlayer2.EventCallback#onInfo
*/
public static final int MEDIA_INFO_SUBTITLE_TIMED_OUT = 902;
@@ -2052,129 +1953,124 @@
//--------------------------------------------------------------------------
/** The player just completed a call {@link #attachAuxEffect}.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+ * @see android.media.MediaPlayer2.EventCallback#onCallCompleted
*/
public static final int CALL_COMPLETED_ATTACH_AUX_EFFECT = 1;
/** The player just completed a call {@link #deselectTrack}.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+ * @see android.media.MediaPlayer2.EventCallback#onCallCompleted
*/
public static final int CALL_COMPLETED_DESELECT_TRACK = 2;
/** The player just completed a call {@link #loopCurrent}.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+ * @see android.media.MediaPlayer2.EventCallback#onCallCompleted
*/
public static final int CALL_COMPLETED_LOOP_CURRENT = 3;
/** The player just completed a call {@link #pause}.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+ * @see android.media.MediaPlayer2.EventCallback#onCallCompleted
*/
public static final int CALL_COMPLETED_PAUSE = 4;
/** The player just completed a call {@link #play}.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+ * @see android.media.MediaPlayer2.EventCallback#onCallCompleted
*/
public static final int CALL_COMPLETED_PLAY = 5;
/** The player just completed a call {@link #prepare}.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+ * @see android.media.MediaPlayer2.EventCallback#onCallCompleted
*/
public static final int CALL_COMPLETED_PREPARE = 6;
/** The player just completed a call {@link #releaseDrm}.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+ * @see android.media.MediaPlayer2.EventCallback#onCallCompleted
*/
public static final int CALL_COMPLETED_RELEASE_DRM = 12;
/** The player just completed a call {@link #restoreDrmKeys}.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+ * @see android.media.MediaPlayer2.EventCallback#onCallCompleted
*/
public static final int CALL_COMPLETED_RESTORE_DRM_KEYS = 13;
/** The player just completed a call {@link #seekTo}.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+ * @see android.media.MediaPlayer2.EventCallback#onCallCompleted
*/
public static final int CALL_COMPLETED_SEEK_TO = 14;
/** The player just completed a call {@link #selectTrack}.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+ * @see android.media.MediaPlayer2.EventCallback#onCallCompleted
*/
public static final int CALL_COMPLETED_SELECT_TRACK = 15;
/** The player just completed a call {@link #setAudioAttributes}.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+ * @see android.media.MediaPlayer2.EventCallback#onCallCompleted
*/
public static final int CALL_COMPLETED_SET_AUDIO_ATTRIBUTES = 16;
/** The player just completed a call {@link #setAudioSessionId}.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+ * @see android.media.MediaPlayer2.EventCallback#onCallCompleted
*/
public static final int CALL_COMPLETED_SET_AUDIO_SESSION_ID = 17;
/** The player just completed a call {@link #setAuxEffectSendLevel}.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+ * @see android.media.MediaPlayer2.EventCallback#onCallCompleted
*/
public static final int CALL_COMPLETED_SET_AUX_EFFECT_SEND_LEVEL = 18;
/** The player just completed a call {@link #setDataSource}.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+ * @see android.media.MediaPlayer2.EventCallback#onCallCompleted
*/
public static final int CALL_COMPLETED_SET_DATA_SOURCE = 19;
/** The player just completed a call {@link #setNextDataSource}.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+ * @see android.media.MediaPlayer2.EventCallback#onCallCompleted
*/
public static final int CALL_COMPLETED_SET_NEXT_DATA_SOURCE = 22;
/** The player just completed a call {@link #setNextDataSources}.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+ * @see android.media.MediaPlayer2.EventCallback#onCallCompleted
*/
public static final int CALL_COMPLETED_SET_NEXT_DATA_SOURCES = 23;
/** The player just completed a call {@link #setPlaybackParams}.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+ * @see android.media.MediaPlayer2.EventCallback#onCallCompleted
*/
public static final int CALL_COMPLETED_SET_PLAYBACK_PARAMS = 24;
- /** The player just completed a call {@link #setPlaybackSpeed}.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
- */
- public static final int CALL_COMPLETED_SET_PLAYBACK_SPEED = 25;
-
/** The player just completed a call {@link #setPlayerVolume}.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+ * @see android.media.MediaPlayer2.EventCallback#onCallCompleted
*/
public static final int CALL_COMPLETED_SET_PLAYER_VOLUME = 26;
/** The player just completed a call {@link #setSurface}.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+ * @see android.media.MediaPlayer2.EventCallback#onCallCompleted
*/
public static final int CALL_COMPLETED_SET_SURFACE = 27;
/** The player just completed a call {@link #setSyncParams}.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+ * @see android.media.MediaPlayer2.EventCallback#onCallCompleted
*/
public static final int CALL_COMPLETED_SET_SYNC_PARAMS = 28;
/** The player just completed a call {@link #skipToNext}.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+ * @see android.media.MediaPlayer2.EventCallback#onCallCompleted
*/
public static final int CALL_COMPLETED_SKIP_TO_NEXT = 29;
/** The player just completed a call {@link #setBufferingParams}.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+ * @see android.media.MediaPlayer2.EventCallback#onCallCompleted
* @hide
*/
public static final int CALL_COMPLETED_SET_BUFFERING_PARAMS = 1001;
/** The player just completed a call {@code setVideoScalingMode}.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+ * @see android.media.MediaPlayer2.EventCallback#onCallCompleted
* @hide
*/
public static final int CALL_COMPLETED_SET_VIDEO_SCALING_MODE = 1002;
/** The player just completed a call {@code notifyWhenCommandLabelReached}.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCommandLabelReached
+ * @see android.media.MediaPlayer2.EventCallback#onCommandLabelReached
* @hide
*/
public static final int CALL_COMPLETED_NOTIFY_WHEN_COMMAND_LABEL_REACHED = 1003;
@@ -2200,7 +2096,6 @@
CALL_COMPLETED_SET_NEXT_DATA_SOURCE,
CALL_COMPLETED_SET_NEXT_DATA_SOURCES,
CALL_COMPLETED_SET_PLAYBACK_PARAMS,
- CALL_COMPLETED_SET_PLAYBACK_SPEED,
CALL_COMPLETED_SET_PLAYER_VOLUME,
CALL_COMPLETED_SET_SURFACE,
CALL_COMPLETED_SET_SYNC_PARAMS,
@@ -2213,40 +2108,46 @@
public @interface CallCompleted {}
/** Status code represents that call is completed without an error.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+ * @see android.media.MediaPlayer2.EventCallback#onCallCompleted
*/
public static final int CALL_STATUS_NO_ERROR = 0;
/** Status code represents that call is ended with an unknown error.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+ * @see android.media.MediaPlayer2.EventCallback#onCallCompleted
*/
public static final int CALL_STATUS_ERROR_UNKNOWN = Integer.MIN_VALUE;
/** Status code represents that the player is not in valid state for the operation.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+ * @see android.media.MediaPlayer2.EventCallback#onCallCompleted
*/
public static final int CALL_STATUS_INVALID_OPERATION = 1;
/** Status code represents that the argument is illegal.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+ * @see android.media.MediaPlayer2.EventCallback#onCallCompleted
*/
public static final int CALL_STATUS_BAD_VALUE = 2;
/** Status code represents that the operation is not allowed.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+ * @see android.media.MediaPlayer2.EventCallback#onCallCompleted
*/
public static final int CALL_STATUS_PERMISSION_DENIED = 3;
/** Status code represents a file or network related operation error.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+ * @see android.media.MediaPlayer2.EventCallback#onCallCompleted
*/
public static final int CALL_STATUS_ERROR_IO = 4;
+ /** Status code represents that the call has been skipped. For example, a {@link #seekTo}
+ * request may be skipped if it is followed by another {@link #seekTo} request.
+ * @see EventCallback#onCallCompleted
+ */
+ public static final int CALL_STATUS_SKIPPED = 5;
+
/** Status code represents that DRM operation is called before preparing a DRM scheme through
* {@link #prepareDrm}.
- * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+ * @see android.media.MediaPlayer2.EventCallback#onCallCompleted
*/
- public static final int CALL_STATUS_NO_DRM_SCHEME = 5;
+ public static final int CALL_STATUS_NO_DRM_SCHEME = 6;
/**
* @hide
@@ -2258,6 +2159,7 @@
CALL_STATUS_BAD_VALUE,
CALL_STATUS_PERMISSION_DENIED,
CALL_STATUS_ERROR_IO,
+ CALL_STATUS_SKIPPED,
CALL_STATUS_NO_DRM_SCHEME})
@Retention(RetentionPolicy.SOURCE)
public @interface CallStatus {}
diff --git a/media/java/android/media/MediaPlayer2Impl.java b/media/java/android/media/MediaPlayer2Impl.java
index 56423fd..dccfd7a 100644
--- a/media/java/android/media/MediaPlayer2Impl.java
+++ b/media/java/android/media/MediaPlayer2Impl.java
@@ -97,7 +97,6 @@
private long mNativeSurfaceTexture; // accessed by native methods
private int mListenerContext; // accessed by native methods
private SurfaceHolder mSurfaceHolder;
- private EventHandler mEventHandler;
private PowerManager.WakeLock mWakeLock = null;
private boolean mScreenOnWhilePlaying;
private boolean mStayAwake;
@@ -135,7 +134,7 @@
//--- guarded by |mDrmLock| end
private HandlerThread mHandlerThread;
- private final Handler mTaskHandler;
+ private final TaskHandler mTaskHandler;
private final Object mTaskLock = new Object();
@GuardedBy("mTaskLock")
private final List<Task> mPendingTasks = new LinkedList<>();
@@ -149,19 +148,10 @@
* result in an exception.</p>
*/
public MediaPlayer2Impl() {
- Looper looper;
- if ((looper = Looper.myLooper()) != null) {
- mEventHandler = new EventHandler(this, looper);
- } else if ((looper = Looper.getMainLooper()) != null) {
- mEventHandler = new EventHandler(this, looper);
- } else {
- mEventHandler = null;
- }
-
mHandlerThread = new HandlerThread("MediaPlayer2TaskThread");
mHandlerThread.start();
- looper = mHandlerThread.getLooper();
- mTaskHandler = new Handler(looper);
+ Looper looper = mHandlerThread.getLooper();
+ mTaskHandler = new TaskHandler(this, looper);
mTimeProvider = new TimeProvider(this);
mOpenSubtitleSources = new Vector<InputStream>();
@@ -173,6 +163,11 @@
native_setup(new WeakReference<MediaPlayer2Impl>(this));
}
+ @Override
+ public MediaPlayerBase getMediaPlayerBase() {
+ return null;
+ }
+
/**
* Releases the resources held by this {@code MediaPlayer2} object.
*
@@ -208,8 +203,6 @@
* playback will continue from where it was paused. If playback had
* been stopped, or never started before, playback will start at the
* beginning.
- *
- * @throws IllegalStateException if it is called in an invalid state
*/
@Override
public void play() {
@@ -231,8 +224,6 @@
* call prepare(). For streams, you should call prepare(),
* which returns immediately, rather than blocking until enough data has been
* buffered.
- *
- * @throws IllegalStateException if it is called in an invalid state
*/
@Override
public void prepare() {
@@ -248,9 +239,6 @@
/**
* Pauses playback. Call play() to resume.
- *
- * @throws IllegalStateException if the internal player engine has not been
- * initialized.
*/
@Override
public void pause() {
@@ -313,39 +301,11 @@
}
@Override
- public @PlayerState int getPlayerState() {
- int mediaplayer2State = getMediaPlayer2State();
- int playerState;
- switch (mediaplayer2State) {
- case MEDIAPLAYER2_STATE_IDLE:
- playerState = PLAYER_STATE_IDLE;
- break;
- case MEDIAPLAYER2_STATE_PREPARED:
- case MEDIAPLAYER2_STATE_PAUSED:
- playerState = PLAYER_STATE_PAUSED;
- break;
- case MEDIAPLAYER2_STATE_PLAYING:
- playerState = PLAYER_STATE_PLAYING;
- break;
- case MEDIAPLAYER2_STATE_ERROR:
- default:
- playerState = PLAYER_STATE_ERROR;
- break;
- }
-
- return playerState;
+ public @MediaPlayer2State int getState() {
+ return native_getState();
}
- /**
- * Gets the current buffering state of the player.
- * During buffering, see {@link #getBufferedPosition()} for the quantifying the amount already
- * buffered.
- */
- @Override
- public @BuffState int getBufferingState() {
- // TODO: use cached state or call native function.
- return BUFFERING_STATE_UNKNOWN;
- }
+ private native int native_getState();
/**
* Sets the audio attributes for this MediaPlayer2.
@@ -384,23 +344,22 @@
* Sets the data source as described by a DataSourceDesc.
*
* @param dsd the descriptor of data source you want to play
- * @throws IllegalStateException if it is called in an invalid state
- * @throws NullPointerException if dsd is null
*/
@Override
public void setDataSource(@NonNull DataSourceDesc dsd) {
addTask(new Task(CALL_COMPLETED_SET_DATA_SOURCE, false) {
@Override
- void process() {
- Preconditions.checkNotNull(dsd, "the DataSourceDesc cannot be null");
- // TODO: setDataSource could update exist data source
+ void process() throws IOException {
+ Preconditions.checkArgument(dsd != null, "the DataSourceDesc cannot be null");
+ int state = getState();
+ if (state != PLAYER_STATE_ERROR && state != PLAYER_STATE_IDLE) {
+ throw new IllegalStateException("called in wrong state " + state);
+ }
+
synchronized (mSrcLock) {
mCurrentDSD = dsd;
mCurrentSrcId = mSrcIdGenerator++;
- try {
- handleDataSource(true /* isCurrent */, dsd, mCurrentSrcId);
- } catch (IOException e) {
- }
+ handleDataSource(true /* isCurrent */, dsd, mCurrentSrcId);
}
}
});
@@ -419,7 +378,7 @@
addTask(new Task(CALL_COMPLETED_SET_NEXT_DATA_SOURCE, false) {
@Override
void process() {
- Preconditions.checkNotNull(dsd, "the DataSourceDesc cannot be null");
+ Preconditions.checkArgument(dsd != null, "the DataSourceDesc cannot be null");
synchronized (mSrcLock) {
mNextDSDs = new ArrayList<DataSourceDesc>(1);
mNextDSDs.add(dsd);
@@ -427,8 +386,8 @@
mNextSourceState = NEXT_SOURCE_STATE_INIT;
mNextSourcePlayPending = false;
}
- int state = getMediaPlayer2State();
- if (state != MEDIAPLAYER2_STATE_IDLE) {
+ int state = getState();
+ if (state != PLAYER_STATE_IDLE) {
synchronized (mSrcLock) {
prepareNextDataSource_l();
}
@@ -465,8 +424,8 @@
mNextSourceState = NEXT_SOURCE_STATE_INIT;
mNextSourcePlayPending = false;
}
- int state = getMediaPlayer2State();
- if (state != MEDIAPLAYER2_STATE_IDLE) {
+ int state = getState();
+ if (state != PLAYER_STATE_IDLE) {
synchronized (mSrcLock) {
prepareNextDataSource_l();
}
@@ -500,46 +459,6 @@
private native void setLooping(boolean looping);
/**
- * Sets the playback speed.
- * A value of 1.0f is the default playback value.
- * A negative value indicates reverse playback, check {@link #isReversePlaybackSupported()}
- * before using negative values.<br>
- * After changing the playback speed, it is recommended to query the actual speed supported
- * by the player, see {@link #getPlaybackSpeed()}.
- * @param speed the desired playback speed
- */
- @Override
- public void setPlaybackSpeed(float speed) {
- addTask(new Task(CALL_COMPLETED_SET_PLAYBACK_SPEED, false) {
- @Override
- void process() {
- _setPlaybackParams(getPlaybackParams().setSpeed(speed));
- }
- });
- }
-
- /**
- * Returns the actual playback speed to be used by the player when playing.
- * Note that it may differ from the speed set in {@link #setPlaybackSpeed(float)}.
- * @return the actual playback speed
- */
- @Override
- public float getPlaybackSpeed() {
- return getPlaybackParams().getSpeed();
- }
-
- /**
- * Indicates whether reverse playback is supported.
- * Reverse playback is indicated by negative playback speeds, see
- * {@link #setPlaybackSpeed(float)}.
- * @return true if reverse playback is supported.
- */
- @Override
- public boolean isReversePlaybackSupported() {
- return false;
- }
-
- /**
* Sets the volume of the audio of the media to play, expressed as a linear multiplier
* on the audio samples.
* Note that this volume is specific to the player, and is separate from stream volume
@@ -579,25 +498,6 @@
return 1.0f;
}
- /**
- * Adds a callback to be notified of events for this player.
- * @param e the {@link Executor} to be used for the events.
- * @param cb the callback to receive the events.
- */
- @Override
- public void registerPlayerEventCallback(@NonNull Executor e,
- @NonNull PlayerEventCallback cb) {
- }
-
- /**
- * Removes a previously registered callback for player events
- * @param cb the callback to remove
- */
- @Override
- public void unregisterPlayerEventCallback(@NonNull PlayerEventCallback cb) {
- }
-
-
private static final int NEXT_SOURCE_STATE_ERROR = -1;
private static final int NEXT_SOURCE_STATE_INIT = 0;
private static final int NEXT_SOURCE_STATE_PREPARING = 1;
@@ -666,7 +566,7 @@
@Override
void process() {
synchronized (mEventCbLock) {
- for (Pair<Executor, MediaPlayer2EventCallback> cb : mEventCallbackRecords) {
+ for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
cb.first.execute(() -> cb.second.onCommandLabelReached(
MediaPlayer2Impl.this, label));
}
@@ -1016,13 +916,13 @@
mNextSourceState = NEXT_SOURCE_STATE_PREPARING;
handleDataSource(false /* isCurrent */, mNextDSDs.get(0), mNextSrcId);
} catch (Exception e) {
- Message msg2 = mEventHandler.obtainMessage(
+ Message msg2 = mTaskHandler.obtainMessage(
MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, MEDIA_ERROR_UNSUPPORTED, null);
final long nextSrcId = mNextSrcId;
- mEventHandler.post(new Runnable() {
+ mTaskHandler.post(new Runnable() {
@Override
public void run() {
- mEventHandler.handleMessage(msg2, nextSrcId);
+ mTaskHandler.handleMessage(msg2, nextSrcId);
}
});
}
@@ -1049,12 +949,12 @@
try {
nativePlayNextDataSource(srcId);
} catch (Exception e) {
- Message msg2 = mEventHandler.obtainMessage(
+ Message msg2 = mTaskHandler.obtainMessage(
MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, MEDIA_ERROR_UNSUPPORTED, null);
- mEventHandler.post(new Runnable() {
+ mTaskHandler.post(new Runnable() {
@Override
public void run() {
- mEventHandler.handleMessage(msg2, srcId);
+ mTaskHandler.handleMessage(msg2, srcId);
}
});
}
@@ -1080,20 +980,6 @@
private native int _getAudioStreamType() throws IllegalStateException;
- /**
- * Stops playback after playback has been started or paused.
- *
- * @throws IllegalStateException if the internal player engine has not been
- * initialized.
- * #hide
- */
- @Override
- public void stop() {
- stayAwake(false);
- _stop();
- }
-
- private native void _stop() throws IllegalStateException;
//--------------------------------------------------------------------------
// Explicit Routing
@@ -1191,7 +1077,7 @@
enableNativeRoutingCallbacksLocked(true);
mRoutingChangeListeners.put(
listener, new NativeRoutingEventHandlerDelegate(this, listener,
- handler != null ? handler : mEventHandler));
+ handler != null ? handler : mTaskHandler));
}
}
}
@@ -1305,9 +1191,9 @@
*
* @return the width of the video, or 0 if there is no video,
* no display surface was set, or the width has not been determined
- * yet. The {@code MediaPlayer2EventCallback} can be registered via
- * {@link #setMediaPlayer2EventCallback(Executor, MediaPlayer2EventCallback)} to provide a
- * notification {@code MediaPlayer2EventCallback.onVideoSizeChanged} when the width
+ * yet. The {@code EventCallback} can be registered via
+ * {@link #setEventCallback(Executor, EventCallback)} to provide a
+ * notification {@code EventCallback.onVideoSizeChanged} when the width
* is available.
*/
@Override
@@ -1318,9 +1204,9 @@
*
* @return the height of the video, or 0 if there is no video,
* no display surface was set, or the height has not been determined
- * yet. The {@code MediaPlayer2EventCallback} can be registered via
- * {@link #setMediaPlayer2EventCallback(Executor, MediaPlayer2EventCallback)} to provide a
- * notification {@code MediaPlayer2EventCallback.onVideoSizeChanged} when the height
+ * yet. The {@code EventCallback} can be registered via
+ * {@link #setEventCallback(Executor, EventCallback)} to provide a
+ * notification {@code EventCallback.onVideoSizeChanged} when the height
* is available.
*/
@Override
@@ -1355,13 +1241,6 @@
@Override
public native boolean isPlaying();
- @Override
- public @MediaPlayer2State int getMediaPlayer2State() {
- return native_getMediaPlayer2State();
- }
-
- private native int native_getMediaPlayer2State();
-
/**
* Gets the current buffering management params used by the source component.
* Calling it only after {@code setDataSource} has been called.
@@ -1395,7 +1274,7 @@
addTask(new Task(CALL_COMPLETED_SET_BUFFERING_PARAMS, false) {
@Override
void process() {
- Preconditions.checkNotNull(params, "the BufferingParams cannot be null");
+ Preconditions.checkArgument(params != null, "the BufferingParams cannot be null");
_setBufferingParams(params);
}
});
@@ -1459,7 +1338,7 @@
addTask(new Task(CALL_COMPLETED_SET_PLAYBACK_PARAMS, false) {
@Override
void process() {
- Preconditions.checkNotNull(params, "the PlaybackParams cannot be null");
+ Preconditions.checkArgument(params != null, "the PlaybackParams cannot be null");
_setPlaybackParams(params);
}
});
@@ -1492,7 +1371,7 @@
addTask(new Task(CALL_COMPLETED_SET_SYNC_PARAMS, false) {
@Override
void process() {
- Preconditions.checkNotNull(params, "the SyncParams cannot be null");
+ Preconditions.checkArgument(params != null, "the SyncParams cannot be null");
_setSyncParams(params);
}
});
@@ -1722,8 +1601,8 @@
stayAwake(false);
_reset();
// make sure none of the listeners get called anymore
- if (mEventHandler != null) {
- mEventHandler.removeCallbacksAndMessages(null);
+ if (mTaskHandler != null) {
+ mTaskHandler.removeCallbacksAndMessages(null);
}
synchronized (mIndexTrackPairs) {
@@ -2148,9 +2027,9 @@
private int mSelectedSubtitleTrackIndex = -1;
private Vector<InputStream> mOpenSubtitleSources;
- private OnSubtitleDataListener mSubtitleDataListener = new OnSubtitleDataListener() {
+ private EventCallback mSubtitleDataCallback = new EventCallback() {
@Override
- public void onSubtitleData(MediaPlayer2 mp, SubtitleData data) {
+ public void onSubtitleData(MediaPlayer2 mp, DataSourceDesc dsd, SubtitleData data) {
int index = data.getTrackIndex();
synchronized (mIndexTrackPairs) {
for (Pair<Integer, SubtitleTrack> p : mIndexTrackPairs) {
@@ -2174,7 +2053,7 @@
}
mSelectedSubtitleTrackIndex = -1;
}
- setOnSubtitleDataListener(null);
+ unregisterEventCallback(mSubtitleDataCallback);
if (track == null) {
return;
}
@@ -2194,7 +2073,8 @@
selectOrDeselectInbandTrack(mSelectedSubtitleTrackIndex, true);
} catch (IllegalStateException e) {
}
- setOnSubtitleDataListener(mSubtitleDataListener);
+ final Executor executor = (runnable) -> mTaskHandler.post(runnable);
+ registerEventCallback(executor, mSubtitleDataCallback);
}
// no need to select out-of-band tracks
}
@@ -2256,9 +2136,9 @@
public void run() {
int res = addTrack();
- if (mEventHandler != null) {
- Message m = mEventHandler.obtainMessage(MEDIA_INFO, res, 0, null);
- mEventHandler.sendMessage(m);
+ if (mTaskHandler != null) {
+ Message m = mTaskHandler.obtainMessage(MEDIA_INFO, res, 0, null);
+ mTaskHandler.sendMessage(m);
}
thread.getLooper().quitSafely();
}
@@ -2446,7 +2326,7 @@
if (!mSubtitleController.hasRendererFor(fFormat)) {
// test and add not atomic
Context context = ActivityThread.currentApplication();
- mSubtitleController.registerRenderer(new SRTRenderer(context, mEventHandler));
+ mSubtitleController.registerRenderer(new SRTRenderer(context, mTaskHandler));
}
final SubtitleTrack track = mSubtitleController.addTrack(fFormat);
synchronized (mIndexTrackPairs) {
@@ -2499,9 +2379,9 @@
public void run() {
int res = addTrack();
- if (mEventHandler != null) {
- Message m = mEventHandler.obtainMessage(MEDIA_INFO, res, 0, null);
- mEventHandler.sendMessage(m);
+ if (mTaskHandler != null) {
+ Message m = mTaskHandler.obtainMessage(MEDIA_INFO, res, 0, null);
+ mTaskHandler.sendMessage(m);
}
thread.getLooper().quitSafely();
}
@@ -2717,7 +2597,6 @@
mTimeProvider.close();
mTimeProvider = null;
}
- mOnSubtitleDataListener = null;
// Modular DRM clean up
mOnDrmConfigHelper = null;
@@ -2764,10 +2643,10 @@
return mTimeProvider;
}
- private class EventHandler extends Handler {
+ private class TaskHandler extends Handler {
private MediaPlayer2Impl mMediaPlayer;
- public EventHandler(MediaPlayer2Impl mp, Looper looper) {
+ public TaskHandler(MediaPlayer2Impl mp, Looper looper) {
super(looper);
mMediaPlayer = mp;
}
@@ -2820,7 +2699,7 @@
if (dsd != null) {
synchronized (mEventCbLock) {
- for (Pair<Executor, MediaPlayer2EventCallback> cb : mEventCallbackRecords) {
+ for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
cb.first.execute(() -> cb.second.onInfo(
mMediaPlayer, dsd, MEDIA_INFO_PREPARED, 0));
}
@@ -2882,7 +2761,7 @@
}
synchronized (mEventCbLock) {
- for (Pair<Executor, MediaPlayer2EventCallback> cb : mEventCallbackRecords) {
+ for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
cb.first.execute(() -> cb.second.onInfo(
mMediaPlayer, dsd, MEDIA_INFO_PLAYBACK_COMPLETE, 0));
}
@@ -2916,7 +2795,7 @@
synchronized (mEventCbLock) {
if (srcId == mCurrentSrcId) {
mBufferedPercentageCurrent.set(percent);
- for (Pair<Executor, MediaPlayer2EventCallback> cb : mEventCallbackRecords) {
+ for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
cb.first.execute(() -> cb.second.onInfo(
mMediaPlayer, mCurrentDSD, MEDIA_INFO_BUFFERING_UPDATE,
percent));
@@ -2924,7 +2803,7 @@
} else if (srcId == mNextSrcId && !mNextDSDs.isEmpty()) {
mBufferedPercentageNext.set(percent);
DataSourceDesc nextDSD = mNextDSDs.get(0);
- for (Pair<Executor, MediaPlayer2EventCallback> cb : mEventCallbackRecords) {
+ for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
cb.first.execute(() -> cb.second.onInfo(
mMediaPlayer, nextDSD, MEDIA_INFO_BUFFERING_UPDATE,
percent));
@@ -2962,7 +2841,7 @@
final int width = msg.arg1;
final int height = msg.arg2;
synchronized (mEventCbLock) {
- for (Pair<Executor, MediaPlayer2EventCallback> cb : mEventCallbackRecords) {
+ for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
cb.first.execute(() -> cb.second.onVideoSizeChanged(
mMediaPlayer, mCurrentDSD, width, height));
}
@@ -2974,7 +2853,7 @@
{
Log.e(TAG, "Error (" + msg.arg1 + "," + msg.arg2 + ")");
synchronized (mEventCbLock) {
- for (Pair<Executor, MediaPlayer2EventCallback> cb : mEventCallbackRecords) {
+ for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
cb.first.execute(() -> cb.second.onError(
mMediaPlayer, mCurrentDSD, what, extra));
cb.first.execute(() -> cb.second.onInfo(
@@ -3027,7 +2906,7 @@
}
synchronized (mEventCbLock) {
- for (Pair<Executor, MediaPlayer2EventCallback> cb : mEventCallbackRecords) {
+ for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
cb.first.execute(() -> cb.second.onInfo(
mMediaPlayer, mCurrentDSD, what, extra));
}
@@ -3057,8 +2936,9 @@
}
synchronized (mEventCbLock) {
- for (Pair<Executor, MediaPlayer2EventCallback> cb : mEventCallbackRecords) {
- cb.first.execute(() -> cb.second.onTimedText(mMediaPlayer, mCurrentDSD, text));
+ for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
+ cb.first.execute(() -> cb.second.onTimedText(
+ mMediaPlayer, mCurrentDSD, text));
}
}
return;
@@ -3066,15 +2946,16 @@
case MEDIA_SUBTITLE_DATA:
{
- OnSubtitleDataListener onSubtitleDataListener = mOnSubtitleDataListener;
- if (onSubtitleDataListener == null) {
- return;
- }
if (msg.obj instanceof Parcel) {
Parcel parcel = (Parcel) msg.obj;
SubtitleData data = new SubtitleData(parcel);
parcel.recycle();
- onSubtitleDataListener.onSubtitleData(mMediaPlayer, data);
+ synchronized (mEventCbLock) {
+ for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
+ cb.first.execute(() -> cb.second.onSubtitleData(
+ mMediaPlayer, mCurrentDSD, data));
+ }
+ }
}
return;
}
@@ -3091,7 +2972,7 @@
}
synchronized (mEventCbLock) {
- for (Pair<Executor, MediaPlayer2EventCallback> cb : mEventCallbackRecords) {
+ for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
cb.first.execute(() -> cb.second.onTimedMetaDataAvailable(
mMediaPlayer, mCurrentDSD, data));
}
@@ -3127,7 +3008,7 @@
/*
* Called from native code when an interesting event happens. This method
- * just uses the EventHandler system to post the event back to the main app thread.
+ * just uses the TaskHandler system to post the event back to the main app thread.
* We use a weak reference to the original MediaPlayer2 object so that the native
* code is safe from the object disappearing from underneath it. (This is
* the cookie passed to native_setup().)
@@ -3156,7 +3037,7 @@
case MEDIA_DRM_INFO:
// We need to derive mDrmInfoImpl before prepare() returns so processing it here
- // before the notification is sent to EventHandler below. EventHandler runs in the
+ // before the notification is sent to TaskHandler below. TaskHandler runs in the
// notification looper so its handleMessage might process the event after prepare()
// has returned.
Log.v(TAG, "postEventFromNative MEDIA_DRM_INFO");
@@ -3183,21 +3064,21 @@
}
- if (mp.mEventHandler != null) {
- Message m = mp.mEventHandler.obtainMessage(what, arg1, arg2, obj);
+ if (mp.mTaskHandler != null) {
+ Message m = mp.mTaskHandler.obtainMessage(what, arg1, arg2, obj);
- mp.mEventHandler.post(new Runnable() {
+ mp.mTaskHandler.post(new Runnable() {
@Override
public void run() {
- mp.mEventHandler.handleMessage(m, srcId);
+ mp.mTaskHandler.handleMessage(m, srcId);
}
});
}
}
private final Object mEventCbLock = new Object();
- private ArrayList<Pair<Executor, MediaPlayer2EventCallback> > mEventCallbackRecords
- = new ArrayList<Pair<Executor, MediaPlayer2EventCallback> >();
+ private ArrayList<Pair<Executor, EventCallback> > mEventCallbackRecords
+ = new ArrayList<Pair<Executor, EventCallback> >();
/**
* Register a callback to be invoked when the media source is ready
@@ -3207,14 +3088,14 @@
* @param executor the executor through which the callback should be invoked
*/
@Override
- public void setMediaPlayer2EventCallback(@NonNull @CallbackExecutor Executor executor,
- @NonNull MediaPlayer2EventCallback eventCallback) {
+ public void registerEventCallback(@NonNull @CallbackExecutor Executor executor,
+ @NonNull EventCallback eventCallback) {
if (eventCallback == null) {
- throw new IllegalArgumentException("Illegal null MediaPlayer2EventCallback");
+ throw new IllegalArgumentException("Illegal null EventCallback");
}
if (executor == null) {
throw new IllegalArgumentException(
- "Illegal null Executor for the MediaPlayer2EventCallback");
+ "Illegal null Executor for the EventCallback");
}
synchronized (mEventCbLock) {
mEventCallbackRecords.add(new Pair(executor, eventCallback));
@@ -3222,30 +3103,19 @@
}
/**
- * Clears the {@link MediaPlayer2EventCallback}.
+ * Clears the {@link EventCallback}.
*/
@Override
- public void clearMediaPlayer2EventCallback() {
+ public void unregisterEventCallback(EventCallback eventCallback) {
synchronized (mEventCbLock) {
- mEventCallbackRecords.clear();
+ for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
+ if (cb.second == eventCallback) {
+ mEventCallbackRecords.remove(cb);
+ }
+ }
}
}
- /**
- * Register a callback to be invoked when a track has data available.
- *
- * @param listener the callback that will be run
- *
- * @hide
- */
- @Override
- public void setOnSubtitleDataListener(OnSubtitleDataListener listener) {
- mOnSubtitleDataListener = listener;
- }
-
- private OnSubtitleDataListener mOnSubtitleDataListener;
-
-
// Modular DRM begin
/**
@@ -3281,11 +3151,11 @@
public void setDrmEventCallback(@NonNull @CallbackExecutor Executor executor,
@NonNull DrmEventCallback eventCallback) {
if (eventCallback == null) {
- throw new IllegalArgumentException("Illegal null MediaPlayer2EventCallback");
+ throw new IllegalArgumentException("Illegal null EventCallback");
}
if (executor == null) {
throw new IllegalArgumentException(
- "Illegal null Executor for the MediaPlayer2EventCallback");
+ "Illegal null Executor for the EventCallback");
}
synchronized (mDrmEventCbLock) {
mDrmEventCallbackRecords.add(new Pair(executor, eventCallback));
@@ -4749,7 +4619,12 @@
public void run() {
int status = CALL_STATUS_NO_ERROR;
try {
- process();
+ if (mMediaCallType != CALL_COMPLETED_NOTIFY_WHEN_COMMAND_LABEL_REACHED
+ && getState() == PLAYER_STATE_ERROR) {
+ status = CALL_STATUS_INVALID_OPERATION;
+ } else {
+ process();
+ }
} catch (IllegalStateException e) {
status = CALL_STATUS_INVALID_OPERATION;
} catch (IllegalArgumentException e) {
@@ -4760,6 +4635,8 @@
status = CALL_STATUS_ERROR_IO;
} catch (NoDrmSchemeException e) {
status = CALL_STATUS_NO_DRM_SCHEME;
+ } catch (CommandSkippedException e) {
+ status = CALL_STATUS_SKIPPED;
} catch (Exception e) {
status = CALL_STATUS_ERROR_UNKNOWN;
}
@@ -4786,11 +4663,17 @@
return;
}
synchronized (mEventCbLock) {
- for (Pair<Executor, MediaPlayer2EventCallback> cb : mEventCallbackRecords) {
+ for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
cb.first.execute(() -> cb.second.onCallCompleted(
MediaPlayer2Impl.this, mDSD, mMediaCallType, status));
}
}
}
};
+
+ private final class CommandSkippedException extends RuntimeException {
+ public CommandSkippedException(String detailMessage) {
+ super(detailMessage);
+ }
+ };
}
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index 143182f..ec2d9ba 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -888,6 +888,8 @@
if (token != null) {
session = new Session(token, channel, mService, mUserId, seq,
mSessionCallbackRecordMap);
+ } else {
+ mSessionCallbackRecordMap.delete(seq);
}
record.postSessionCreated(session);
}
@@ -2487,7 +2489,7 @@
}
}
synchronized (mSessionCallbackRecordMap) {
- mSessionCallbackRecordMap.remove(mSeq);
+ mSessionCallbackRecordMap.delete(mSeq);
}
}
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index ddbbaa4..bf80c57 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -90,9 +90,7 @@
srcs: [
"android_media_Media2HTTPConnection.cpp",
"android_media_Media2HTTPService.cpp",
- "android_media_MediaCrypto.cpp",
"android_media_Media2DataSource.cpp",
- "android_media_MediaDrm.cpp",
"android_media_MediaPlayer2.cpp",
"android_media_SyncParams.cpp",
],
@@ -100,19 +98,24 @@
shared_libs: [
"android.hardware.cas@1.0", // for CasManager. VNDK???
"android.hardware.cas.native@1.0", // CasManager. VNDK???
- "libandroid", // NDK
"libandroid_runtime", // ???
"libaudioclient", // for use of AudioTrack, AudioSystem. to be removed
- "liblog", // NDK
+ "libbinder",
"libdrmframework", // for FileSource, MediaHTTP
"libgui", // for VideoFrameScheduler
"libhidlallocatorutils",
"libhidlbase", // VNDK???
- "libmediandk", // NDK
"libpowermanager", // for JWakeLock. to be removed
"libutils", // Have to use shared lib to make libandroid_runtime behave correctly.
// Otherwise, AndroidRuntime::getJNIEnv() will return NULL.
+
+ // NDK or NDK-compliant
+ "libandroid",
+ "libmediandk",
+ "libnativehelper_compat_libc++",
+ "liblog",
+ "libz",
],
header_libs: ["libhardware_headers"],
@@ -120,7 +123,6 @@
static_libs: [
"libbacktrace",
"libbase",
- "libbinder",
"libc_malloc_debug_backtrace",
"libcrypto",
"libcutils",
@@ -133,7 +135,6 @@
"libmediametrics",
"libmediaplayer2",
"libmediautils",
- "libnativehelper",
"libnetd_client",
"libstagefright_esds",
"libstagefright_foundation",
@@ -146,7 +147,6 @@
"libstagefright_timedtext",
"libunwindstack",
"libutilscallstack",
- "libz",
"libziparchive",
],
@@ -168,7 +168,7 @@
"-Wunreachable-code",
],
- ldflags: ["-Wl,--exclude-libs=ALL"],
+ ldflags: ["-Wl,--exclude-libs=ALL,-error-limit=0"],
}
subdirs = [
diff --git a/media/jni/android_media_ImageReader.cpp b/media/jni/android_media_ImageReader.cpp
index c36858a..a45aa90 100644
--- a/media/jni/android_media_ImageReader.cpp
+++ b/media/jni/android_media_ImageReader.cpp
@@ -612,8 +612,12 @@
Image_setBufferItem(env, image, buffer);
env->SetLongField(image, gSurfaceImageClassInfo.mTimestamp,
static_cast<jlong>(buffer->mTimestamp));
+ auto transform = buffer->mTransform;
+ if (buffer->mTransformToDisplayInverse) {
+ transform |= NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY;
+ }
env->SetIntField(image, gSurfaceImageClassInfo.mTransform,
- static_cast<jint>(buffer->mTransform));
+ static_cast<jint>(transform));
env->SetIntField(image, gSurfaceImageClassInfo.mScalingMode,
static_cast<jint>(buffer->mScalingMode));
diff --git a/media/jni/android_media_Media2DataSource.cpp b/media/jni/android_media_Media2DataSource.cpp
index bc3f6bd..b3434e9 100644
--- a/media/jni/android_media_Media2DataSource.cpp
+++ b/media/jni/android_media_Media2DataSource.cpp
@@ -20,12 +20,12 @@
#include "android_media_Media2DataSource.h"
-#include "android_runtime/AndroidRuntime.h"
-#include "android_runtime/Log.h"
+#include "log/log.h"
#include "jni.h"
#include <nativehelper/JNIHelp.h>
#include <drm/drm_framework_common.h>
+#include <mediaplayer2/JavaVMHelper.h>
#include <media/stagefright/foundation/ADebug.h>
#include <nativehelper/ScopedLocalRef.h>
@@ -56,7 +56,7 @@
}
JMedia2DataSource::~JMedia2DataSource() {
- JNIEnv* env = AndroidRuntime::getJNIEnv();
+ JNIEnv* env = JavaVMHelper::getJNIEnv();
env->DeleteGlobalRef(mMedia2DataSourceObj);
env->DeleteGlobalRef(mByteArrayObj);
}
@@ -75,12 +75,12 @@
size = kBufferSize;
}
- JNIEnv* env = AndroidRuntime::getJNIEnv();
+ JNIEnv* env = JavaVMHelper::getJNIEnv();
jint numread = env->CallIntMethod(mMedia2DataSourceObj, mReadAtMethod,
(jlong)offset, mByteArrayObj, (jint)0, (jint)size);
if (env->ExceptionCheck()) {
ALOGW("An exception occurred in readAt()");
- LOGW_EX(env);
+ jniLogException(env, ANDROID_LOG_WARN, LOG_TAG);
env->ExceptionClear();
mJavaObjStatus = UNKNOWN_ERROR;
return -1;
@@ -117,11 +117,11 @@
return OK;
}
- JNIEnv* env = AndroidRuntime::getJNIEnv();
+ JNIEnv* env = JavaVMHelper::getJNIEnv();
*size = env->CallLongMethod(mMedia2DataSourceObj, mGetSizeMethod);
if (env->ExceptionCheck()) {
ALOGW("An exception occurred in getSize()");
- LOGW_EX(env);
+ jniLogException(env, ANDROID_LOG_WARN, LOG_TAG);
env->ExceptionClear();
// After returning an error, size shouldn't be used by callers.
*size = UNKNOWN_ERROR;
@@ -142,7 +142,7 @@
void JMedia2DataSource::close() {
Mutex::Autolock lock(mLock);
- JNIEnv* env = AndroidRuntime::getJNIEnv();
+ JNIEnv* env = JavaVMHelper::getJNIEnv();
env->CallVoidMethod(mMedia2DataSourceObj, mCloseMethod);
// The closed state is effectively the same as an error state.
mJavaObjStatus = UNKNOWN_ERROR;
diff --git a/media/jni/android_media_Media2HTTPConnection.cpp b/media/jni/android_media_Media2HTTPConnection.cpp
index 60176e3..d02ee06 100644
--- a/media/jni/android_media_Media2HTTPConnection.cpp
+++ b/media/jni/android_media_Media2HTTPConnection.cpp
@@ -18,14 +18,14 @@
#define LOG_TAG "Media2HTTPConnection-JNI"
#include <utils/Log.h>
+#include <mediaplayer2/JavaVMHelper.h>
#include <media/stagefright/foundation/ADebug.h>
#include <nativehelper/ScopedLocalRef.h>
#include "android_media_Media2HTTPConnection.h"
#include "android_util_Binder.h"
-#include "android_runtime/AndroidRuntime.h"
-#include "android_runtime/Log.h"
+#include "log/log.h"
#include "jni.h"
#include <nativehelper/JNIHelp.h>
@@ -84,7 +84,7 @@
}
JMedia2HTTPConnection::~JMedia2HTTPConnection() {
- JNIEnv *env = AndroidRuntime::getJNIEnv();
+ JNIEnv *env = JavaVMHelper::getJNIEnv();
env->DeleteGlobalRef(mMedia2HTTPConnectionObj);
env->DeleteGlobalRef(mByteArrayObj);
}
@@ -101,7 +101,7 @@
}
}
- JNIEnv* env = AndroidRuntime::getJNIEnv();
+ JNIEnv* env = JavaVMHelper::getJNIEnv();
jstring juri = env->NewStringUTF(uri);
jstring jheaders = env->NewStringUTF(tmp.string());
@@ -115,12 +115,12 @@
}
void JMedia2HTTPConnection::disconnect() {
- JNIEnv* env = AndroidRuntime::getJNIEnv();
+ JNIEnv* env = JavaVMHelper::getJNIEnv();
env->CallVoidMethod(mMedia2HTTPConnectionObj, mDisconnectMethod);
}
ssize_t JMedia2HTTPConnection::readAt(off64_t offset, void *data, size_t size) {
- JNIEnv* env = AndroidRuntime::getJNIEnv();
+ JNIEnv* env = JavaVMHelper::getJNIEnv();
if (size > kBufferSize) {
size = kBufferSize;
@@ -141,12 +141,12 @@
}
off64_t JMedia2HTTPConnection::getSize() {
- JNIEnv* env = AndroidRuntime::getJNIEnv();
+ JNIEnv* env = JavaVMHelper::getJNIEnv();
return (off64_t)(env->CallLongMethod(mMedia2HTTPConnectionObj, mGetSizeMethod));
}
status_t JMedia2HTTPConnection::getMIMEType(String8 *mimeType) {
- JNIEnv* env = AndroidRuntime::getJNIEnv();
+ JNIEnv* env = JavaVMHelper::getJNIEnv();
jstring jmime = (jstring)env->CallObjectMethod(mMedia2HTTPConnectionObj, mGetMIMETypeMethod);
jboolean flag = env->ExceptionCheck();
if (flag) {
@@ -165,7 +165,7 @@
}
status_t JMedia2HTTPConnection::getUri(String8 *uri) {
- JNIEnv* env = AndroidRuntime::getJNIEnv();
+ JNIEnv* env = JavaVMHelper::getJNIEnv();
jstring juri = (jstring)env->CallObjectMethod(mMedia2HTTPConnectionObj, mGetUriMethod);
jboolean flag = env->ExceptionCheck();
if (flag) {
diff --git a/media/jni/android_media_Media2HTTPService.cpp b/media/jni/android_media_Media2HTTPService.cpp
index 382f099..1c63889 100644
--- a/media/jni/android_media_Media2HTTPService.cpp
+++ b/media/jni/android_media_Media2HTTPService.cpp
@@ -21,11 +21,11 @@
#include "android_media_Media2HTTPConnection.h"
#include "android_media_Media2HTTPService.h"
-#include "android_runtime/AndroidRuntime.h"
-#include "android_runtime/Log.h"
+#include "log/log.h"
#include "jni.h"
#include <nativehelper/JNIHelp.h>
+#include <mediaplayer2/JavaVMHelper.h>
#include <media/stagefright/foundation/ADebug.h>
#include <nativehelper/ScopedLocalRef.h>
@@ -46,12 +46,12 @@
}
JMedia2HTTPService::~JMedia2HTTPService() {
- JNIEnv *env = AndroidRuntime::getJNIEnv();
+ JNIEnv *env = JavaVMHelper::getJNIEnv();
env->DeleteGlobalRef(mMedia2HTTPServiceObj);
}
sp<MediaHTTPConnection> JMedia2HTTPService::makeHTTPConnection() {
- JNIEnv* env = AndroidRuntime::getJNIEnv();
+ JNIEnv* env = JavaVMHelper::getJNIEnv();
jobject media2HTTPConnectionObj =
env->CallObjectMethod(mMedia2HTTPServiceObj, mMakeHTTPConnectionMethod);
diff --git a/media/jni/android_media_MediaPlayer2.cpp b/media/jni/android_media_MediaPlayer2.cpp
index 6546cf0..4265987 100644
--- a/media/jni/android_media_MediaPlayer2.cpp
+++ b/media/jni/android_media_MediaPlayer2.cpp
@@ -29,6 +29,7 @@
#include <media/stagefright/Utils.h>
#include <media/stagefright/foundation/ByteUtils.h> // for FOURCC definition
#include <mediaplayer2/JAudioTrack.h>
+#include <mediaplayer2/JavaVMHelper.h>
#include <mediaplayer2/mediaplayer2.h>
#include <stdio.h>
#include <assert.h>
@@ -39,7 +40,7 @@
#include "jni.h"
#include <nativehelper/JNIHelp.h>
#include "android/native_window_jni.h"
-#include "android_runtime/Log.h"
+#include "log/log.h"
#include "utils/Errors.h" // for status_t
#include "utils/KeyedVector.h"
#include "utils/String8.h"
@@ -184,14 +185,14 @@
JNIMediaPlayer2Listener::~JNIMediaPlayer2Listener()
{
// remove global references
- JNIEnv *env = AndroidRuntime::getJNIEnv();
+ JNIEnv *env = JavaVMHelper::getJNIEnv();
env->DeleteGlobalRef(mObject);
env->DeleteGlobalRef(mClass);
}
void JNIMediaPlayer2Listener::notify(int64_t srcId, int msg, int ext1, int ext2, const Parcel *obj)
{
- JNIEnv *env = AndroidRuntime::getJNIEnv();
+ JNIEnv *env = JavaVMHelper::getJNIEnv();
if (obj && obj->dataSize() > 0) {
jobject jParcel = createJavaParcelObject(env);
if (jParcel != NULL) {
@@ -207,7 +208,7 @@
}
if (env->ExceptionCheck()) {
ALOGW("An exception occurred while notifying an event.");
- LOGW_EX(env);
+ jniLogException(env, ANDROID_LOG_WARN, LOG_TAG);
env->ExceptionClear();
}
}
@@ -568,18 +569,6 @@
}
static void
-android_media_MediaPlayer2_stop(JNIEnv *env, jobject thiz)
-{
- ALOGV("stop");
- sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
- if (mp == NULL ) {
- jniThrowException(env, "java/lang/IllegalStateException", NULL);
- return;
- }
- process_media_player_call( env, thiz, mp->stop(), NULL, NULL );
-}
-
-static void
android_media_MediaPlayer2_pause(JNIEnv *env, jobject thiz)
{
ALOGV("pause");
@@ -786,13 +775,13 @@
}
static jint
-android_media_MediaPlayer2_getMediaPlayer2State(JNIEnv *env, jobject thiz)
+android_media_MediaPlayer2_getState(JNIEnv *env, jobject thiz)
{
sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
if (mp == NULL) {
return MEDIAPLAYER2_STATE_IDLE;
}
- return (jint)mp->getMediaPlayer2State();
+ return (jint)mp->getState();
}
static jint
@@ -1501,8 +1490,7 @@
{"_setBufferingParams", "(Landroid/media/BufferingParams;)V", (void *)android_media_MediaPlayer2_setBufferingParams},
{"_prepare", "()V", (void *)android_media_MediaPlayer2_prepare},
{"_start", "()V", (void *)android_media_MediaPlayer2_start},
- {"_stop", "()V", (void *)android_media_MediaPlayer2_stop},
- {"native_getMediaPlayer2State", "()I", (void *)android_media_MediaPlayer2_getMediaPlayer2State},
+ {"native_getState", "()I", (void *)android_media_MediaPlayer2_getState},
{"getVideoWidth", "()I", (void *)android_media_MediaPlayer2_getVideoWidth},
{"getVideoHeight", "()I", (void *)android_media_MediaPlayer2_getVideoHeight},
{"native_getMetrics", "()Landroid/os/PersistableBundle;", (void *)android_media_MediaPlayer2_native_getMetrics},
@@ -1552,8 +1540,7 @@
// This function only registers the native methods
static int register_android_media_MediaPlayer2Impl(JNIEnv *env)
{
- return AndroidRuntime::registerNativeMethods(env,
- "android/media/MediaPlayer2Impl", gMethods, NELEM(gMethods));
+ return jniRegisterNativeMethods(env, "android/media/MediaPlayer2Impl", gMethods, NELEM(gMethods));
}
jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
@@ -1572,6 +1559,8 @@
goto bail;
}
+ JavaVMHelper::setJavaVM(vm);
+
/* success -- return valid version number */
result = JNI_VERSION_1_4;
diff --git a/media/native/midi/include/midi.h b/media/native/midi/include/midi.h
index 780d8a7..755d09f 100644
--- a/media/native/midi/include/midi.h
+++ b/media/native/midi/include/midi.h
@@ -71,7 +71,7 @@
* @see AMEDIA_ERROR_UNKNOWN {@link AMEDIA_ERROR_UNKNOWN} - an unknown error occurred.
*/
media_status_t AMIDI_API AMidiDevice_fromJava(
- JNIEnv *env, jobject midiDeviceObj, AMidiDevice **outDevicePtrPtr);
+ JNIEnv *env, jobject midiDeviceObj, AMidiDevice **outDevicePtrPtr) __INTRODUCED_IN(29);
/**
* Disconnects the native Midi Device Object from the associated Java MidiDevice object.
@@ -88,7 +88,7 @@
* - the JNI interface initialization to the associated java MidiDevice failed.
* @see AMEDIA_ERROR_UNKNOWN {@link AMEDIA_ERROR_UNKNOWN} - couldn't retrieve the device info.
*/
-media_status_t AMIDI_API AMidiDevice_release(const AMidiDevice *midiDevice);
+media_status_t AMIDI_API AMidiDevice_release(const AMidiDevice *midiDevice) __INTRODUCED_IN(29);
/**
* Gets the MIDI device type.
@@ -104,7 +104,7 @@
* parameter is NULL.
* @see AMEDIA_ERROR_UNKNOWN {@link AMEDIA_ERROR_UNKNOWN} - Unknown error.
*/
-int32_t AMIDI_API AMidiDevice_getType(const AMidiDevice *device);
+int32_t AMIDI_API AMidiDevice_getType(const AMidiDevice *device) __INTRODUCED_IN(29);
/**
* Gets the number of input (sending) ports available on the specified MIDI device.
@@ -117,7 +117,7 @@
* parameter is NULL.
* @see AMEDIA_ERROR_UNKNOWN {@link AMEDIA_ERROR_UNKNOWN} - couldn't retrieve the device info.
*/
-ssize_t AMIDI_API AMidiDevice_getNumInputPorts(const AMidiDevice *device);
+ssize_t AMIDI_API AMidiDevice_getNumInputPorts(const AMidiDevice *device) __INTRODUCED_IN(29);
/**
* Gets the number of output (receiving) ports available on the specified MIDI device.
@@ -130,7 +130,7 @@
* parameter is NULL.
* @see AMEDIA_ERROR_UNKNOWN {@link AMEDIA_ERROR_UNKNOWN}- couldn't retrieve the device info.
*/
-ssize_t AMIDI_API AMidiDevice_getNumOutputPorts(const AMidiDevice *device);
+ssize_t AMIDI_API AMidiDevice_getNumOutputPorts(const AMidiDevice *device) __INTRODUCED_IN(29);
/*
* API for receiving data from the Output port of a device.
@@ -149,14 +149,14 @@
* @see AMEDIA_ERROR_UNKNOWN {@link AMEDIA_ERROR_UNKNOWN} - Unknown Error.
*/
media_status_t AMIDI_API AMidiOutputPort_open(const AMidiDevice *device, int32_t portNumber,
- AMidiOutputPort **outOutputPortPtr);
+ AMidiOutputPort **outOutputPortPtr) __INTRODUCED_IN(29);
/**
* Closes the output port.
*
* @param outputPort The native API port identifier of the port.
*/
-void AMIDI_API AMidiOutputPort_close(const AMidiOutputPort *outputPort);
+void AMIDI_API AMidiOutputPort_close(const AMidiOutputPort *outputPort) __INTRODUCED_IN(29);
/**
* Receives the next pending MIDI message. To retrieve all pending messages, the client should
@@ -177,7 +177,7 @@
* @see AMEDIA_ERROR_UNKNOWN {@link AMEDIA_ERROR_UNKNOWN} - Unknown Error.
*/
ssize_t AMIDI_API AMidiOutputPort_receive(const AMidiOutputPort *outputPort, int32_t *opcodePtr,
- uint8_t *buffer, size_t maxBytes, size_t* numBytesReceivedPtr, int64_t *outTimestampPtr);
+ uint8_t *buffer, size_t maxBytes, size_t* numBytesReceivedPtr, int64_t *outTimestampPtr) __INTRODUCED_IN(29);
/*
* API for sending data to the Input port of a device.
@@ -196,7 +196,7 @@
* @see AMEDIA_ERROR_UNKNOWN {@link AMEDIA_ERROR_UNKNOWN} - Unknown Error.
*/
media_status_t AMIDI_API AMidiInputPort_open(const AMidiDevice *device, int32_t portNumber,
- AMidiInputPort **outInputPortPtr);
+ AMidiInputPort **outInputPortPtr) __INTRODUCED_IN(29);
/**
* Sends data to the specified input port.
@@ -210,7 +210,7 @@
* was NULL, the specified buffer was NULL.
*/
ssize_t AMIDI_API AMidiInputPort_send(const AMidiInputPort *inputPort, const uint8_t *buffer,
- size_t numBytes);
+ size_t numBytes) __INTRODUCED_IN(29);
/**
* Sends data to the specified input port with a timestamp.
@@ -225,7 +225,7 @@
* was NULL, the specified buffer was NULL.
*/
ssize_t AMIDI_API AMidiInputPort_sendWithTimestamp(const AMidiInputPort *inputPort,
- const uint8_t *buffer, size_t numBytes, int64_t timestamp);
+ const uint8_t *buffer, size_t numBytes, int64_t timestamp) __INTRODUCED_IN(29);
/**
* Sends a message with a 'MIDI flush command code' to the specified port. This should cause
@@ -239,14 +239,14 @@
* @see AMEDIA_ERROR_UNSUPPORTED {@link AMEDIA_ERROR_UNSUPPORTED} The FLUSH command couldn't
* be sent.
*/
-media_status_t AMIDI_API AMidiInputPort_sendFlush(const AMidiInputPort *inputPort);
+media_status_t AMIDI_API AMidiInputPort_sendFlush(const AMidiInputPort *inputPort) __INTRODUCED_IN(29);
/**
* Closes the input port.
*
* @param inputPort Identifies the input (sending) port to close.
*/
-void AMIDI_API AMidiInputPort_close(const AMidiInputPort *inputPort);
+void AMIDI_API AMidiInputPort_close(const AMidiInputPort *inputPort) __INTRODUCED_IN(29);
#ifdef __cplusplus
}
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/CameraTestUtils.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/CameraTestUtils.java
index b2be464..5ab5092 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/CameraTestUtils.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/CameraTestUtils.java
@@ -43,6 +43,8 @@
import android.hardware.camera2.TotalCaptureResult;
import android.hardware.camera2.params.InputConfiguration;
import android.hardware.camera2.params.MeteringRectangle;
+import android.hardware.camera2.params.OutputConfiguration;
+import android.hardware.camera2.params.SessionConfiguration;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.location.Location;
import android.location.LocationManager;
@@ -75,6 +77,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.Executor;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
@@ -694,6 +697,38 @@
}
/**
+ * Configure a new camera session with output surfaces and initial session parameters.
+ *
+ * @param camera The CameraDevice to be configured.
+ * @param outputSurfaces The surface list that used for camera output.
+ * @param listener The callback CameraDevice will notify when session is available.
+ * @param handler The handler used to notify callbacks.
+ * @param initialRequest Initial request settings to use as session parameters.
+ */
+ public static CameraCaptureSession configureCameraSessionWithParameters(CameraDevice camera,
+ List<Surface> outputSurfaces, BlockingSessionCallback listener,
+ Handler handler, CaptureRequest initialRequest) throws CameraAccessException {
+ List<OutputConfiguration> outConfigurations = new ArrayList<>(outputSurfaces.size());
+ for (Surface surface : outputSurfaces) {
+ outConfigurations.add(new OutputConfiguration(surface));
+ }
+ SessionConfiguration sessionConfig = new SessionConfiguration(
+ SessionConfiguration.SESSION_REGULAR, outConfigurations,
+ new HandlerExecutor(handler), listener);
+ sessionConfig.setSessionParameters(initialRequest);
+ camera.createCaptureSession(sessionConfig);
+
+ CameraCaptureSession session = listener.waitAndGetSession(SESSION_CONFIGURE_TIMEOUT_MS);
+ assertFalse("Camera session should not be a reprocessable session",
+ session.isReprocessable());
+ assertFalse("Capture session type must be regular",
+ CameraConstrainedHighSpeedCaptureSession.class.isAssignableFrom(
+ session.getClass()));
+
+ return session;
+ }
+
+ /**
* Configure a new camera session with output surfaces and type.
*
* @param camera The CameraDevice to be configured.
@@ -1334,6 +1369,20 @@
}
}
+ public static class HandlerExecutor implements Executor {
+ private final Handler mHandler;
+
+ public HandlerExecutor(Handler handler) {
+ assertNotNull("handler must be valid", handler);
+ mHandler = handler;
+ }
+
+ @Override
+ public void execute(Runnable runCmd) {
+ mHandler.post(runCmd);
+ }
+ }
+
/**
* Provide a mock for {@link CameraDevice.StateCallback}.
*
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/Camera2RecordingTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/Camera2RecordingTest.java
index ddb05f0..8f27fef 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/Camera2RecordingTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/Camera2RecordingTest.java
@@ -58,6 +58,7 @@
import static com.android.mediaframeworktest.helpers.CameraTestUtils.SimpleCaptureCallback;
import static com.android.mediaframeworktest.helpers.CameraTestUtils.SimpleImageReaderListener;
import static com.android.mediaframeworktest.helpers.CameraTestUtils.configureCameraSession;
+import static com.android.mediaframeworktest.helpers.CameraTestUtils.configureCameraSessionWithParameters;
import static com.android.mediaframeworktest.helpers.CameraTestUtils.getSupportedVideoSizes;
import static com.android.mediaframeworktest.helpers.CameraTestUtils.getValueNotNull;
@@ -872,7 +873,6 @@
outputSurfaces.add(mReaderSurface);
}
mSessionListener = new BlockingSessionCallback();
- mSession = configureCameraSession(mCamera, outputSurfaces, mSessionListener, mHandler);
CaptureRequest.Builder recordingRequestBuilder =
mCamera.createCaptureRequest(CameraDevice.TEMPLATE_RECORD);
@@ -885,7 +885,10 @@
}
recordingRequestBuilder.addTarget(mRecordingSurface);
recordingRequestBuilder.addTarget(mPreviewSurface);
- mSession.setRepeatingRequest(recordingRequestBuilder.build(), listener, mHandler);
+ CaptureRequest recordingRequest = recordingRequestBuilder.build();
+ mSession = configureCameraSessionWithParameters(mCamera, outputSurfaces, mSessionListener,
+ mHandler, recordingRequest);
+ mSession.setRepeatingRequest(recordingRequest, listener, mHandler);
if (useMediaRecorder) {
mMediaRecorder.start();
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/ImageReaderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/ImageReaderTest.java
index f6cd990..2cb5704 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/ImageReaderTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/ImageReaderTest.java
@@ -41,6 +41,7 @@
private Image mImage3;
@Override
+ @SuppressWarnings("CheckReturnValue")
protected void setUp() throws Exception {
super.setUp();
diff --git a/packages/BackupRestoreConfirmation/res/values-pt-rBR/strings.xml b/packages/BackupRestoreConfirmation/res/values-pt-rBR/strings.xml
index baa4867..d7c56b8 100644
--- a/packages/BackupRestoreConfirmation/res/values-pt-rBR/strings.xml
+++ b/packages/BackupRestoreConfirmation/res/values-pt-rBR/strings.xml
@@ -28,7 +28,7 @@
<string name="device_encryption_restore_text" msgid="1570864916855208992">"Insira sua senha de criptografia do dispositivo abaixo."</string>
<string name="device_encryption_backup_text" msgid="5866590762672844664">"Insira sua senha de criptografia do dispositivo abaixo. Ela também será usada para criptografar o arquivo de backup."</string>
<string name="backup_enc_password_text" msgid="4981585714795233099">"Digite uma senha para usar para criptografar os dados de backup por completo. Se isso for deixado em branco, sua senha atual de backup será usada:"</string>
- <string name="backup_enc_password_optional" msgid="1350137345907579306">"Se você deseja criptografar os dados de backup por completo, digite uma senha abaixo:"</string>
+ <string name="backup_enc_password_optional" msgid="1350137345907579306">"Se você quer criptografar os dados de backup por completo, digite uma senha abaixo:"</string>
<string name="backup_enc_password_required" msgid="7889652203371654149">"Como seu dispositivo está criptografado, é necessário criptografar seu backup. Insira uma senha abaixo:"</string>
<string name="restore_enc_password_text" msgid="6140898525580710823">"Se os dados restaurados forem criptografada, digite a senha abaixo:"</string>
<string name="toast_backup_started" msgid="550354281452756121">"Iniciando backup..."</string>
diff --git a/packages/BackupRestoreConfirmation/res/values-pt/strings.xml b/packages/BackupRestoreConfirmation/res/values-pt/strings.xml
index baa4867..d7c56b8 100644
--- a/packages/BackupRestoreConfirmation/res/values-pt/strings.xml
+++ b/packages/BackupRestoreConfirmation/res/values-pt/strings.xml
@@ -28,7 +28,7 @@
<string name="device_encryption_restore_text" msgid="1570864916855208992">"Insira sua senha de criptografia do dispositivo abaixo."</string>
<string name="device_encryption_backup_text" msgid="5866590762672844664">"Insira sua senha de criptografia do dispositivo abaixo. Ela também será usada para criptografar o arquivo de backup."</string>
<string name="backup_enc_password_text" msgid="4981585714795233099">"Digite uma senha para usar para criptografar os dados de backup por completo. Se isso for deixado em branco, sua senha atual de backup será usada:"</string>
- <string name="backup_enc_password_optional" msgid="1350137345907579306">"Se você deseja criptografar os dados de backup por completo, digite uma senha abaixo:"</string>
+ <string name="backup_enc_password_optional" msgid="1350137345907579306">"Se você quer criptografar os dados de backup por completo, digite uma senha abaixo:"</string>
<string name="backup_enc_password_required" msgid="7889652203371654149">"Como seu dispositivo está criptografado, é necessário criptografar seu backup. Insira uma senha abaixo:"</string>
<string name="restore_enc_password_text" msgid="6140898525580710823">"Se os dados restaurados forem criptografada, digite a senha abaixo:"</string>
<string name="toast_backup_started" msgid="550354281452756121">"Iniciando backup..."</string>
diff --git a/packages/CarSystemUI/Android.mk b/packages/CarSystemUI/Android.mk
new file mode 100644
index 0000000..0d40b7f
--- /dev/null
+++ b/packages/CarSystemUI/Android.mk
@@ -0,0 +1,86 @@
+# LOCAL_PATH is not the current directory of this file.
+# If you need the local path, use CAR_SYSUI_PATH.
+# This tweak ensures that the resource overlay that is device-specific still works
+# which requires that LOCAL_PATH match the original path (which must be frameworks/base/packages/SystemUI).
+#
+# For clarity, we also define SYSTEM_UI_AOSP_PATH to frameworks/base/packages/SystemUI and refer to that
+SYSTEM_UI_AOSP_PATH := frameworks/base/packages/SystemUI
+SYSTEM_UI_CAR_PATH := frameworks/base/packages/CarSystemUI
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_USE_AAPT2 := true
+
+LOCAL_MODULE_TAGS := optional
+
+# The same as SYSTEM_UI_AOSP_PATH but based on the value of LOCAL_PATH which is
+# frameworks/base/packages/CarSystemUI.
+RELATIVE_SYSTEM_UI_AOSP_PATH := ../../../../$(SYSTEM_UI_AOSP_PATH)
+
+
+LOCAL_SRC_FILES := \
+ $(call all-java-files-under, src) \
+ $(call all-Iaidl-files-under, src) \
+ $(call all-java-files-under, $(RELATIVE_SYSTEM_UI_AOSP_PATH)/src) \
+ $(call all-Iaidl-files-under, $(RELATIVE_SYSTEM_UI_AOSP_PATH)/src)
+
+LOCAL_STATIC_ANDROID_LIBRARIES := \
+ SystemUIPluginLib \
+ SystemUISharedLib \
+ androidx.car_car \
+ androidx.legacy_legacy-support-v4 \
+ androidx.recyclerview_recyclerview \
+ androidx.preference_preference \
+ androidx.appcompat_appcompat \
+ androidx.mediarouter_mediarouter \
+ androidx.palette_palette \
+ androidx.legacy_legacy-preference-v14 \
+ androidx.leanback_leanback \
+ androidx.slice_slice-core \
+ androidx.slice_slice-view \
+ androidx.slice_slice-builders \
+ androidx.arch.core_core-runtime \
+ androidx.lifecycle_lifecycle-extensions \
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ SystemUI-tags \
+ SystemUI-proto
+
+LOCAL_JAVA_LIBRARIES := telephony-common \
+ android.car
+
+LOCAL_FULL_LIBS_MANIFEST_FILES := $(SYSTEM_UI_AOSP_PATH)/AndroidManifest.xml
+LOCAL_MANIFEST_FILE := AndroidManifest.xml
+
+LOCAL_MODULE_OWNER := google
+LOCAL_PACKAGE_NAME := CarSystemUI
+LOCAL_PRIVATE_PLATFORM_APIS := true
+LOCAL_CERTIFICATE := platform
+LOCAL_PRIVILEGED_MODULE := true
+
+LOCAL_PROGUARD_FLAG_FILES := $(RELATIVE_SYSTEM_UI_AOSP_PATH)/proguard.flags \
+ proguard.flags
+
+LOCAL_RESOURCE_DIR := \
+ $(LOCAL_PATH)/res \
+ $(SYSTEM_UI_AOSP_PATH)/res-keyguard \
+ $(SYSTEM_UI_AOSP_PATH)/res
+
+ifneq ($(INCREMENTAL_BUILDS),)
+ LOCAL_PROGUARD_ENABLED := disabled
+ LOCAL_JACK_ENABLED := incremental
+endif
+
+LOCAL_DX_FLAGS := --multi-dex
+LOCAL_JACK_FLAGS := --multi-dex native
+
+include frameworks/base/packages/SettingsLib/common.mk
+
+LOCAL_OVERRIDES_PACKAGES := SystemUI
+
+LOCAL_AAPT_FLAGS := --extra-packages com.android.keyguard
+
+include $(BUILD_PACKAGE)
+
+include $(call all-makefiles-under, $(SYSTEM_UI_CAR_PATH))
diff --git a/packages/CarSystemUI/AndroidManifest.xml b/packages/CarSystemUI/AndroidManifest.xml
new file mode 100644
index 0000000..4e8a3a3
--- /dev/null
+++ b/packages/CarSystemUI/AndroidManifest.xml
@@ -0,0 +1,24 @@
+<?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"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ package="com.android.systemui"
+ android:sharedUserId="android.uid.systemui"
+ coreApp="true">
+
+
+</manifest>
diff --git a/packages/CarSystemUI/proguard.flags b/packages/CarSystemUI/proguard.flags
new file mode 100644
index 0000000..ceb037c
--- /dev/null
+++ b/packages/CarSystemUI/proguard.flags
@@ -0,0 +1 @@
+-keep class com.android.systemui.CarSystemUIFactory
diff --git a/packages/CarSystemUI/res/drawable/car_ic_apps.xml b/packages/CarSystemUI/res/drawable/car_ic_apps.xml
new file mode 100644
index 0000000..13b543b
--- /dev/null
+++ b/packages/CarSystemUI/res/drawable/car_ic_apps.xml
@@ -0,0 +1,28 @@
+<?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
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="33dp"
+ android:height="33dp"
+ android:viewportHeight="33.0"
+ android:viewportWidth="33.0">
+ <path
+ android:fillColor="@color/car_nav_icon_fill_color"
+ android:fillType="evenOdd"
+ android:pathData="M1,20L12,20C12.55,20 13,20.45 13,21L13,32C13,32.55 12.55,33 12,33L1,33C0.45,33 0,32.55 0,32L0,21C0,20.45 0.45,20 1,20ZM21,20L32,20C32.55,20 33,20.45 33,21L33,32C33,32.55 32.55,33 32,33L21,33C20.45,33 20,32.55 20,32L20,21C20,20.45 20.45,20 21,20ZM1,0L12,0C12.55,0 13,0.45 13,1L13,12C13,12.55 12.55,13 12,13L1,13C0.45,13 0,12.55 0,12L0,1C0,0.45 0.45,0 1,0ZM21,0L32,0C32.55,0 33,0.45 33,1L33,12C33,12.55 32.55,13 32,13L21,13C20.45,13 20,12.55 20,12L20,1C20,0.45 20.45,0 21,0Z"
+ android:strokeColor="@color/car_nav_icon_fill_color"
+ android:strokeWidth="1"/>
+</vector>
diff --git a/packages/CarSystemUI/res/drawable/car_ic_apps_black.xml b/packages/CarSystemUI/res/drawable/car_ic_apps_black.xml
new file mode 100644
index 0000000..498d366
--- /dev/null
+++ b/packages/CarSystemUI/res/drawable/car_ic_apps_black.xml
@@ -0,0 +1,28 @@
+<?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
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="33dp"
+ android:height="33dp"
+ android:viewportHeight="33.0"
+ android:viewportWidth="33.0">
+ <path
+ android:fillColor="#FF000000"
+ android:fillType="evenOdd"
+ android:pathData="M1,20L12,20C12.55,20 13,20.45 13,21L13,32C13,32.55 12.55,33 12,33L1,33C0.45,33 0,32.55 0,32L0,21C0,20.45 0.45,20 1,20ZM21,20L32,20C32.55,20 33,20.45 33,21L33,32C33,32.55 32.55,33 32,33L21,33C20.45,33 20,32.55 20,32L20,21C20,20.45 20.45,20 21,20ZM1,0L12,0C12.55,0 13,0.45 13,1L13,12C13,12.55 12.55,13 12,13L1,13C0.45,13 0,12.55 0,12L0,1C0,0.45 0.45,0 1,0ZM21,0L32,0C32.55,0 33,0.45 33,1L33,12C33,12.55 32.55,13 32,13L21,13C20.45,13 20,12.55 20,12L20,1C20,0.45 20.45,0 21,0Z"
+ android:strokeColor="#00000000"
+ android:strokeWidth="1"/>
+</vector>
diff --git a/packages/CarSystemUI/res/drawable/car_ic_apps_selected.xml b/packages/CarSystemUI/res/drawable/car_ic_apps_selected.xml
new file mode 100644
index 0000000..250bfe8
--- /dev/null
+++ b/packages/CarSystemUI/res/drawable/car_ic_apps_selected.xml
@@ -0,0 +1,20 @@
+<?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
+ -->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:drawable="@drawable/car_ic_selection_bg" android:gravity="center"/>
+ <item android:drawable="@drawable/car_ic_apps_black" android:gravity="center"/>
+</layer-list>
diff --git a/packages/CarSystemUI/res/drawable/car_ic_music.xml b/packages/CarSystemUI/res/drawable/car_ic_music.xml
new file mode 100644
index 0000000..8c15494
--- /dev/null
+++ b/packages/CarSystemUI/res/drawable/car_ic_music.xml
@@ -0,0 +1,34 @@
+<?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
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="34dp"
+ android:height="39dp"
+ android:viewportHeight="39.0"
+ android:viewportWidth="34.0">
+ <path
+ android:fillColor="@color/car_nav_icon_fill_color"
+ android:fillType="evenOdd"
+ android:pathData="M23,25L31,23L31,9L12,12L12,38C12,38.45 11.45,39 11,39L1,39C0.55,39 0.01,38.87 0,38.3C0,38.3 0,35.87 0,31C0,30.42 0.62,30.08 1,30L9,28L9,4.85C9,4.36 9.36,3.94 9.84,3.87L32.84,0.19C33.39,0.1 33.9,0.47 33.99,1.01C34,1.07 34,1.12 34,1.17L34,33.06C34,33.51 33.45,34 33,34L23,34C22.55,34 22,33.51 22,33.06L22,26C22,25.61 22.16,25.18 23,25Z"
+ android:strokeColor="@color/car_nav_icon_fill_color"
+ android:strokeWidth="1"/>
+ <path
+ android:fillColor="@color/car_nav_icon_fill_color"
+ android:fillType="evenOdd"
+ android:pathData="M23.57,26.48L23.57,32.42L32.42,32.42L32.42,24.27L23.57,26.48ZM32.42,7.18L32.42,1.85L10.57,5.34L10.57,10.63L32.42,7.18ZM10.42,29.27L9.38,29.53L1.58,31.48C1.58,34.35 1.58,34.45 1.57,36.48C1.58,36.89 1.58,37.2 1.58,37.43L10.42,37.43L10.42,29.27Z"
+ android:strokeColor="@color/car_nav_icon_fill_color"
+ android:strokeWidth="3.15"/>
+</vector>
diff --git a/packages/CarSystemUI/res/drawable/car_ic_music_black.xml b/packages/CarSystemUI/res/drawable/car_ic_music_black.xml
new file mode 100644
index 0000000..64287a5
--- /dev/null
+++ b/packages/CarSystemUI/res/drawable/car_ic_music_black.xml
@@ -0,0 +1,34 @@
+<?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
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="34dp"
+ android:height="39dp"
+ android:viewportHeight="39.0"
+ android:viewportWidth="34.0">
+ <path
+ android:fillColor="#FF000000"
+ android:fillType="evenOdd"
+ android:pathData="M23,25L31,23L31,9L12,12L12,38C12,38.45 11.45,39 11,39L1,39C0.55,39 0.01,38.87 0,38.3C0,38.3 0,35.87 0,31C0,30.42 0.62,30.08 1,30L9,28L9,4.85C9,4.36 9.36,3.94 9.84,3.87L32.84,0.19C33.39,0.1 33.9,0.47 33.99,1.01C34,1.07 34,1.12 34,1.17L34,33.06C34,33.51 33.45,34 33,34L23,34C22.55,34 22,33.51 22,33.06L22,26C22,25.61 22.16,25.18 23,25Z"
+ android:strokeColor="#FF000000"
+ android:strokeWidth="1"/>
+ <path
+ android:fillColor="#FF000000"
+ android:fillType="evenOdd"
+ android:pathData="M23.57,26.48L23.57,32.42L32.42,32.42L32.42,24.27L23.57,26.48ZM32.42,7.18L32.42,1.85L10.57,5.34L10.57,10.63L32.42,7.18ZM10.42,29.27L9.38,29.53L1.58,31.48C1.58,34.35 1.58,34.45 1.57,36.48C1.58,36.89 1.58,37.2 1.58,37.43L10.42,37.43L10.42,29.27Z"
+ android:strokeColor="#FF000000"
+ android:strokeWidth="3.15"/>
+</vector>
diff --git a/packages/CarSystemUI/res/drawable/car_ic_music_selected.xml b/packages/CarSystemUI/res/drawable/car_ic_music_selected.xml
new file mode 100644
index 0000000..9703a8c
--- /dev/null
+++ b/packages/CarSystemUI/res/drawable/car_ic_music_selected.xml
@@ -0,0 +1,20 @@
+<?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
+ -->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:drawable="@drawable/car_ic_selection_bg" android:gravity="center"/>
+ <item android:drawable="@drawable/car_ic_music_black" android:gravity="center"/>
+</layer-list>
diff --git a/packages/CarSystemUI/res/drawable/car_ic_navigation.xml b/packages/CarSystemUI/res/drawable/car_ic_navigation.xml
new file mode 100644
index 0000000..58667be
--- /dev/null
+++ b/packages/CarSystemUI/res/drawable/car_ic_navigation.xml
@@ -0,0 +1,28 @@
+<?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
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="32dp"
+ android:height="37dp"
+ android:viewportHeight="37.0"
+ android:viewportWidth="32.0">
+ <path
+ android:fillColor="@color/car_nav_icon_fill_color"
+ android:fillType="evenOdd"
+ android:pathData="M16.62,0.61L31.33,35.21C31.55,35.72 31.31,36.3 30.8,36.52C30.48,36.66 30.12,36.62 29.83,36.42L15.7,26.44L1.58,36.42C1.13,36.73 0.5,36.63 0.18,36.18C-0.02,35.89 -0.06,35.53 0.08,35.21L14.78,0.61C15,0.1 15.59,-0.14 16.1,0.08C16.33,0.18 16.52,0.37 16.62,0.61Z"
+ android:strokeColor="@color/car_nav_icon_fill_color"
+ android:strokeWidth="1"/>
+</vector>
diff --git a/packages/CarSystemUI/res/drawable/car_ic_navigation_black.xml b/packages/CarSystemUI/res/drawable/car_ic_navigation_black.xml
new file mode 100644
index 0000000..145d4c9
--- /dev/null
+++ b/packages/CarSystemUI/res/drawable/car_ic_navigation_black.xml
@@ -0,0 +1,28 @@
+<?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
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="32dp"
+ android:height="37dp"
+ android:viewportHeight="37.0"
+ android:viewportWidth="32.0">
+ <path
+ android:fillColor="#FF000000"
+ android:fillType="evenOdd"
+ android:pathData="M16.62,0.61L31.33,35.21C31.55,35.72 31.31,36.3 30.8,36.52C30.48,36.66 30.12,36.62 29.83,36.42L15.7,26.44L1.58,36.42C1.13,36.73 0.5,36.63 0.18,36.18C-0.02,35.89 -0.06,35.53 0.08,35.21L14.78,0.61C15,0.1 15.59,-0.14 16.1,0.08C16.33,0.18 16.52,0.37 16.62,0.61Z"
+ android:strokeColor="#00000000"
+ android:strokeWidth="1"/>
+</vector>
diff --git a/packages/CarSystemUI/res/drawable/car_ic_navigation_selected.xml b/packages/CarSystemUI/res/drawable/car_ic_navigation_selected.xml
new file mode 100644
index 0000000..f0174db
--- /dev/null
+++ b/packages/CarSystemUI/res/drawable/car_ic_navigation_selected.xml
@@ -0,0 +1,21 @@
+<?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
+ -->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:drawable="@drawable/car_ic_selection_bg" android:gravity="center"/>
+ <item android:drawable="@drawable/car_ic_navigation_black" android:gravity="center"/>
+</layer-list>
\ No newline at end of file
diff --git a/packages/CarSystemUI/res/drawable/car_ic_notification.xml b/packages/CarSystemUI/res/drawable/car_ic_notification.xml
new file mode 100644
index 0000000..f96b050
--- /dev/null
+++ b/packages/CarSystemUI/res/drawable/car_ic_notification.xml
@@ -0,0 +1,28 @@
+<?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
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="32dp"
+ android:height="38dp"
+ android:viewportHeight="38.0"
+ android:viewportWidth="32.0">
+ <path
+ android:fillColor="@color/car_nav_icon_fill_color"
+ android:fillType="evenOdd"
+ android:pathData="M20.62,3.98C25.51,5.85 28.98,10.61 28.98,16.18L28.98,24.95L32,24.95L32,31.22L0,31.22L0,24.95L3.02,24.95L3.02,16.18C3.02,10.61 6.49,5.85 11.38,3.98C11.72,1.73 13.66,0 16,0C18.34,0 20.28,1.73 20.62,3.98ZM11.33,33.3L20.67,33.3C20.67,35.9 18.58,38 16,38C13.42,38 11.33,35.9 11.33,33.3Z"
+ android:strokeColor="@color/car_nav_icon_fill_color"
+ android:strokeWidth="1"/>
+</vector>
diff --git a/packages/CarSystemUI/res/drawable/car_ic_notification_black.xml b/packages/CarSystemUI/res/drawable/car_ic_notification_black.xml
new file mode 100644
index 0000000..5f8df88
--- /dev/null
+++ b/packages/CarSystemUI/res/drawable/car_ic_notification_black.xml
@@ -0,0 +1,28 @@
+<?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
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="32dp"
+ android:height="38dp"
+ android:viewportHeight="38.0"
+ android:viewportWidth="32.0">
+ <path
+ android:fillColor="#000000"
+ android:fillType="evenOdd"
+ android:pathData="M20.62,3.98C25.51,5.85 28.98,10.61 28.98,16.18L28.98,24.95L32,24.95L32,31.22L0,31.22L0,24.95L3.02,24.95L3.02,16.18C3.02,10.61 6.49,5.85 11.38,3.98C11.72,1.73 13.66,0 16,0C18.34,0 20.28,1.73 20.62,3.98ZM11.33,33.3L20.67,33.3C20.67,35.9 18.58,38 16,38C13.42,38 11.33,35.9 11.33,33.3Z"
+ android:strokeColor="#00000000"
+ android:strokeWidth="1"/>
+</vector>
diff --git a/packages/CarSystemUI/res/drawable/car_ic_notification_selected.xml b/packages/CarSystemUI/res/drawable/car_ic_notification_selected.xml
new file mode 100644
index 0000000..02eec93
--- /dev/null
+++ b/packages/CarSystemUI/res/drawable/car_ic_notification_selected.xml
@@ -0,0 +1,21 @@
+<?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
+ -->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:drawable="@drawable/car_ic_selection_bg" android:gravity="center"/>
+ <item android:drawable="@drawable/car_ic_notification_black" android:gravity="center"/>
+</layer-list>
\ No newline at end of file
diff --git a/packages/CarSystemUI/res/drawable/car_ic_overview.xml b/packages/CarSystemUI/res/drawable/car_ic_overview.xml
new file mode 100644
index 0000000..590109a
--- /dev/null
+++ b/packages/CarSystemUI/res/drawable/car_ic_overview.xml
@@ -0,0 +1,28 @@
+<?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
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="39dp"
+ android:height="40dp"
+ android:viewportHeight="40.0"
+ android:viewportWidth="39.0">
+ <path
+ android:fillColor="@color/car_nav_icon_fill_color"
+ android:fillType="evenOdd"
+ android:pathData="M33.4,22.74L33.4,37.74C33.4,38.84 32.51,39.74 31.4,39.74L24.4,39.74L24.4,24.74L14.4,24.74L14.4,39.74L7.4,39.74C6.3,39.74 5.4,38.84 5.4,37.74L5.4,22.74L0.5,22.74C0.22,22.74 0,22.51 0,22.24C0,22.12 0.04,22 0.12,21.91L19.03,0.17C19.21,-0.04 19.52,-0.06 19.73,0.12C19.75,0.14 19.76,0.15 19.78,0.17L38.68,21.91C38.86,22.12 38.84,22.43 38.63,22.62C38.54,22.69 38.43,22.74 38.31,22.74L33.4,22.74Z"
+ android:strokeColor="@color/car_nav_icon_fill_color"
+ android:strokeWidth="1"/>
+</vector>
diff --git a/packages/CarSystemUI/res/drawable/car_ic_overview_black.xml b/packages/CarSystemUI/res/drawable/car_ic_overview_black.xml
new file mode 100644
index 0000000..48cff97
--- /dev/null
+++ b/packages/CarSystemUI/res/drawable/car_ic_overview_black.xml
@@ -0,0 +1,28 @@
+<?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
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="39dp"
+ android:height="40dp"
+ android:viewportHeight="40.0"
+ android:viewportWidth="39.0">
+ <path
+ android:fillColor="#FF000000"
+ android:fillType="evenOdd"
+ android:pathData="M33.4,22.74L33.4,37.74C33.4,38.84 32.51,39.74 31.4,39.74L24.4,39.74L24.4,24.74L14.4,24.74L14.4,39.74L7.4,39.74C6.3,39.74 5.4,38.84 5.4,37.74L5.4,22.74L0.5,22.74C0.22,22.74 0,22.51 0,22.24C0,22.12 0.04,22 0.12,21.91L19.03,0.17C19.21,-0.04 19.52,-0.06 19.73,0.12C19.75,0.14 19.76,0.15 19.78,0.17L38.68,21.91C38.86,22.12 38.84,22.43 38.63,22.62C38.54,22.69 38.43,22.74 38.31,22.74L33.4,22.74Z"
+ android:strokeColor="#00000000"
+ android:strokeWidth="1"/>
+</vector>
diff --git a/packages/CarSystemUI/res/drawable/car_ic_overview_selected.xml b/packages/CarSystemUI/res/drawable/car_ic_overview_selected.xml
new file mode 100644
index 0000000..18ebf0c
--- /dev/null
+++ b/packages/CarSystemUI/res/drawable/car_ic_overview_selected.xml
@@ -0,0 +1,20 @@
+<?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
+ -->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:drawable="@drawable/car_ic_selection_bg" android:gravity="center"/>
+ <item android:drawable="@drawable/car_ic_overview_black" android:gravity="center"/>
+</layer-list>
diff --git a/packages/CarSystemUI/res/drawable/car_ic_phone.xml b/packages/CarSystemUI/res/drawable/car_ic_phone.xml
new file mode 100644
index 0000000..7091670
--- /dev/null
+++ b/packages/CarSystemUI/res/drawable/car_ic_phone.xml
@@ -0,0 +1,28 @@
+<?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
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="40dp"
+ android:height="40dp"
+ android:viewportHeight="40.0"
+ android:viewportWidth="40.0">
+ <path
+ android:fillColor="@color/car_nav_icon_fill_color"
+ android:fillType="nonZero"
+ android:pathData="M21.31,35.75L21.3,35.76L3.46,17.92L0.03,6.25C-0.16,5.32 0.53,3.88 1.41,3.51C6.36,1.25 8.88,0.1 8.95,0.08C10.02,-0.23 11.39,0.39 11.7,1.45L13.76,10.37C13.97,11.1 13.62,11.91 13.07,12.43L8.24,17.25L22.31,31.32L27.13,26.49C27.65,25.94 28.47,25.6 29.19,25.81L38.11,27.87C39.18,28.17 39.79,29.55 39.49,30.61C39.46,30.69 38.32,33.2 36.05,38.16C35.68,39.04 34.25,39.73 33.31,39.53L21.65,36.1C21.51,35.96 21.4,35.84 21.31,35.75Z"
+ android:strokeColor="@color/car_nav_icon_fill_color"
+ android:strokeWidth="1"/>
+</vector>
diff --git a/packages/CarSystemUI/res/drawable/car_ic_phone_black.xml b/packages/CarSystemUI/res/drawable/car_ic_phone_black.xml
new file mode 100644
index 0000000..087eebb
--- /dev/null
+++ b/packages/CarSystemUI/res/drawable/car_ic_phone_black.xml
@@ -0,0 +1,28 @@
+<?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
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="40dp"
+ android:height="40dp"
+ android:viewportHeight="40.0"
+ android:viewportWidth="40.0">
+ <path
+ android:fillColor="#FF000000"
+ android:fillType="nonZero"
+ android:pathData="M21.31,35.75L21.3,35.76L3.46,17.92L0.03,6.25C-0.16,5.32 0.53,3.88 1.41,3.51C6.36,1.25 8.88,0.1 8.95,0.08C10.02,-0.23 11.39,0.39 11.7,1.45L13.76,10.37C13.97,11.1 13.62,11.91 13.07,12.43L8.24,17.25L22.31,31.32L27.13,26.49C27.65,25.94 28.47,25.6 29.19,25.81L38.11,27.87C39.18,28.17 39.79,29.55 39.49,30.61C39.46,30.69 38.32,33.2 36.05,38.16C35.68,39.04 34.25,39.73 33.31,39.53L21.65,36.1C21.51,35.96 21.4,35.84 21.31,35.75Z"
+ android:strokeColor="#00000000"
+ android:strokeWidth="1"/>
+</vector>
diff --git a/packages/CarSystemUI/res/drawable/car_ic_phone_selected.xml b/packages/CarSystemUI/res/drawable/car_ic_phone_selected.xml
new file mode 100644
index 0000000..197eac0
--- /dev/null
+++ b/packages/CarSystemUI/res/drawable/car_ic_phone_selected.xml
@@ -0,0 +1,20 @@
+<?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
+ -->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:drawable="@drawable/car_ic_selection_bg" android:gravity="center"/>
+ <item android:drawable="@drawable/car_ic_phone_black" android:gravity="center"/>
+</layer-list>
diff --git a/packages/CarSystemUI/res/drawable/car_ic_selection_bg.xml b/packages/CarSystemUI/res/drawable/car_ic_selection_bg.xml
new file mode 100644
index 0000000..781beaf
--- /dev/null
+++ b/packages/CarSystemUI/res/drawable/car_ic_selection_bg.xml
@@ -0,0 +1,28 @@
+<?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
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="70dp"
+ android:height="70dp"
+ android:viewportHeight="70.0"
+ android:viewportWidth="70.0">
+ <path
+ android:fillColor="@color/car_accent"
+ android:fillType="evenOdd"
+ android:pathData="M4,0L66,0A4,4 0,0 1,70 4L70,66A4,4 0,0 1,66 70L4,70A4,4 0,0 1,0 66L0,4A4,4 0,0 1,4 0z"
+ android:strokeColor="#00000000"
+ android:strokeWidth="1"/>
+</vector>
diff --git a/packages/CarSystemUI/res/drawable/car_seekbar_thumb.xml b/packages/CarSystemUI/res/drawable/car_seekbar_thumb.xml
new file mode 100644
index 0000000..1a9b8a5
--- /dev/null
+++ b/packages/CarSystemUI/res/drawable/car_seekbar_thumb.xml
@@ -0,0 +1,37 @@
+<?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
+ -->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item>
+ <shape android:shape="oval">
+ <padding
+ android:bottom="@dimen/car_padding_1"
+ android:left="@dimen/car_padding_1"
+ android:right="@dimen/car_padding_1"
+ android:top="@dimen/car_padding_1"/>
+ <solid android:color="@android:color/black"/>
+ </shape>
+ </item>
+ <item>
+ <shape android:shape="oval">
+ <solid android:color="@color/car_accent"/>
+ <size
+ android:width="@dimen/car_seekbar_thumb_size"
+ android:height="@dimen/car_seekbar_thumb_size"/>
+ </shape>
+ </item>
+</layer-list>
diff --git a/packages/CarSystemUI/res/drawable/ic_mic_white.xml b/packages/CarSystemUI/res/drawable/ic_mic_white.xml
new file mode 100644
index 0000000..f5a91b5
--- /dev/null
+++ b/packages/CarSystemUI/res/drawable/ic_mic_white.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0">
+ <path
+ android:pathData="M24,28c3.31,0 5.98,-2.69 5.98,-6L30,10c0,-3.32 -2.68,-6 -6,-6 -3.31,0 -6,2.68 -6,6v12c0,3.31 2.69,6 6,6zM34.6,22c0,6 -5.07,10.2 -10.6,10.2 -5.52,0 -10.6,-4.2 -10.6,-10.2L10,22c0,6.83 5.44,12.47 12,13.44L22,42h4v-6.56c6.56,-0.97 12,-6.61 12,-13.44h-3.4z"
+ android:fillColor="#FFFFFF"/>
+</vector>
diff --git a/packages/CarSystemUI/res/drawable/nav_button_background.xml b/packages/CarSystemUI/res/drawable/nav_button_background.xml
new file mode 100644
index 0000000..376347c
--- /dev/null
+++ b/packages/CarSystemUI/res/drawable/nav_button_background.xml
@@ -0,0 +1,26 @@
+<?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
+ -->
+
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="@color/nav_bar_ripple_background_color">
+ <item android:id="@android:id/mask">
+ <shape android:shape="rectangle">
+ <solid android:color="?android:colorAccent"/>
+ <corners android:radius="6dp"/>
+ </shape>
+ </item>
+</ripple>
diff --git a/packages/CarSystemUI/res/drawable/notification_material_bg.xml b/packages/CarSystemUI/res/drawable/notification_material_bg.xml
new file mode 100644
index 0000000..03746c8
--- /dev/null
+++ b/packages/CarSystemUI/res/drawable/notification_material_bg.xml
@@ -0,0 +1,26 @@
+<?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
+ -->
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="@color/notification_ripple_untinted_color">
+ <item>
+ <shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <solid android:color="@color/notification_material_background_color"/>
+ <corners
+ android:radius="@dimen/notification_shadow_radius"/>
+ </shape>
+ </item>
+</ripple>
diff --git a/packages/CarSystemUI/res/drawable/notification_material_bg_dim.xml b/packages/CarSystemUI/res/drawable/notification_material_bg_dim.xml
new file mode 100644
index 0000000..03746c8
--- /dev/null
+++ b/packages/CarSystemUI/res/drawable/notification_material_bg_dim.xml
@@ -0,0 +1,26 @@
+<?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
+ -->
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="@color/notification_ripple_untinted_color">
+ <item>
+ <shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <solid android:color="@color/notification_material_background_color"/>
+ <corners
+ android:radius="@dimen/notification_shadow_radius"/>
+ </shape>
+ </item>
+</ripple>
diff --git a/packages/CarSystemUI/res/drawable/volume_dialog_background.xml b/packages/CarSystemUI/res/drawable/volume_dialog_background.xml
new file mode 100644
index 0000000..fa3ca8f
--- /dev/null
+++ b/packages/CarSystemUI/res/drawable/volume_dialog_background.xml
@@ -0,0 +1,26 @@
+<?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
+ -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <solid android:color="?android:attr/colorBackgroundFloating"/>
+ <padding
+ android:bottom="5dp"
+ android:left="5dp"
+ android:right="5dp"
+ android:top="5dp"/>
+ <corners android:bottomLeftRadius="20dp"
+ android:bottomRightRadius="20dp"/>
+</shape>
diff --git a/packages/CarSystemUI/res/layout/car_navigation_bar.xml b/packages/CarSystemUI/res/layout/car_navigation_bar.xml
new file mode 100644
index 0000000..b67ce15
--- /dev/null
+++ b/packages/CarSystemUI/res/layout/car_navigation_bar.xml
@@ -0,0 +1,145 @@
+<?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
+ -->
+
+<com.android.systemui.statusbar.car.CarNavigationBarView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:systemui="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@android:color/black"
+ android:orientation="vertical">
+ <LinearLayout
+ android:id="@id/nav_buttons"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:paddingStart="20dp"
+ android:paddingEnd="20dp"
+ android:gravity="center">
+
+ <com.android.systemui.statusbar.car.CarFacetButton
+ android:id="@+id/home"
+ style="@style/NavigationBarButton"
+ systemui:componentNames="com.android.car.carlauncher/.CarLauncher"
+ systemui:icon="@drawable/car_ic_overview"
+ systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;launchFlags=0x14000000;end"
+ systemui:longIntent="intent:#Intent;action=com.google.android.demandspace.START;end"
+ systemui:selectedIcon="@drawable/car_ic_overview_selected"
+ systemui:useMoreIcon="false"
+ />
+
+ <Space
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"/>
+
+ <com.android.systemui.statusbar.car.CarFacetButton
+ android:id="@+id/maps_nav"
+ style="@style/NavigationBarButton"
+ systemui:categories="android.intent.category.APP_MAPS"
+ systemui:icon="@drawable/car_ic_navigation"
+ systemui:intent="intent:#Intent;component=com.android.car.carlauncher/.CarLauncher;category=android.intent.category.APP_MAPS;launchFlags=0x24000000;end"
+ systemui:selectedIcon="@drawable/car_ic_navigation_selected"
+ systemui:useMoreIcon="false"
+ />
+
+ <Space
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"/>
+
+ <com.android.systemui.statusbar.car.CarFacetButton
+ android:id="@+id/music_nav"
+ style="@style/NavigationBarButton"
+ systemui:icon="@drawable/car_ic_music"
+ systemui:intent="intent:#Intent;component=com.android.car.media/.MediaActivity;launchFlags=0x14000000;end"
+ systemui:packages="com.android.car.media"
+ systemui:selectedIcon="@drawable/car_ic_music_selected"
+ systemui:useMoreIcon="false"
+ />
+
+ <Space
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"/>
+
+ <com.android.systemui.statusbar.car.CarFacetButton
+ android:id="@+id/phone_nav"
+ style="@style/NavigationBarButton"
+ systemui:componentNames="com.android.car.dialer/.TelecomActivity"
+ systemui:icon="@drawable/car_ic_phone"
+ systemui:intent="intent:#Intent;component=com.android.car.dialer/.TelecomActivity;launchFlags=0x14000000;end"
+ systemui:selectedIcon="@drawable/car_ic_phone_selected"
+ systemui:useMoreIcon="false"
+ />
+
+ <Space
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"/>
+
+ <com.android.systemui.statusbar.car.CarFacetButton
+ android:id="@+id/grid_nav"
+ style="@style/NavigationBarButton"
+ systemui:componentNames="com.android.car.carlauncher/.AppGridActivity"
+ systemui:icon="@drawable/car_ic_apps"
+ systemui:intent="intent:#Intent;component=com.android.car.carlauncher/.AppGridActivity;launchFlags=0x24000000;end"
+ systemui:selectedIcon="@drawable/car_ic_apps_selected"
+ systemui:useMoreIcon="false"
+ />
+
+ <Space
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"/>
+
+ <com.android.systemui.statusbar.car.CarFacetButton
+ android:id="@+id/note"
+ style="@style/NavigationBarButton"
+ systemui:icon="@drawable/car_ic_notification"
+ systemui:intent="intent:#Intent;component=com.android.car.notification/.CarNotificationCenterActivity;launchFlags=0x14000000;end"
+ systemui:packages="com.android.car.notification"
+ systemui:selectedIcon="@drawable/car_ic_notification_selected"
+ systemui:useMoreIcon="false"
+ />
+
+ <Space
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"/>
+
+ <com.android.systemui.statusbar.car.CarFacetButton
+ android:id="@+id/assist"
+ style="@style/NavigationBarButton"
+ systemui:icon="@drawable/ic_mic_white"
+ systemui:intent="intent:#Intent;action=com.google.android.demandspace.START;end"
+ systemui:useMoreIcon="false"
+ />
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/lock_screen_nav_buttons"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:paddingStart="@dimen/car_keyline_1"
+ android:paddingEnd="@dimen/car_keyline_1"
+ android:gravity="center"
+ android:visibility="gone">
+ </LinearLayout>
+
+</com.android.systemui.statusbar.car.CarNavigationBarView>
diff --git a/packages/CarSystemUI/res/layout/car_navigation_bar_unprovisioned.xml b/packages/CarSystemUI/res/layout/car_navigation_bar_unprovisioned.xml
new file mode 100644
index 0000000..46e60db
--- /dev/null
+++ b/packages/CarSystemUI/res/layout/car_navigation_bar_unprovisioned.xml
@@ -0,0 +1,44 @@
+<?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
+ -->
+
+<com.android.systemui.statusbar.car.CarNavigationBarView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:systemui="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@android:color/black"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:id="@+id/nav_buttons"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:paddingStart="@dimen/car_padding_5"
+ android:paddingEnd="@dimen/car_padding_5">
+
+ <com.android.systemui.statusbar.car.CarNavigationButton
+ android:id="@+id/home"
+ android:layout_width="@dimen/car_touch_target_size"
+ android:layout_height="match_parent"
+ android:background="?android:attr/selectableItemBackground"
+ android:src="@drawable/car_ic_overview"
+ systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;launchFlags=0x14000000;end"
+ />
+ </LinearLayout>
+</com.android.systemui.statusbar.car.CarNavigationBarView>
+
diff --git a/packages/CarSystemUI/res/layout/car_status_bar_header.xml b/packages/CarSystemUI/res/layout/car_status_bar_header.xml
new file mode 100644
index 0000000..e446072
--- /dev/null
+++ b/packages/CarSystemUI/res/layout/car_status_bar_header.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
+ -->
+<!-- Extends LinearLayout -->
+<com.android.systemui.qs.car.CarStatusBarHeader
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/header"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/status_bar_height">
+
+ <include layout="@layout/car_top_navigation_bar"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ />
+</com.android.systemui.qs.car.CarStatusBarHeader>
diff --git a/packages/CarSystemUI/res/layout/car_top_navigation_bar.xml b/packages/CarSystemUI/res/layout/car_top_navigation_bar.xml
new file mode 100644
index 0000000..55d50a0
--- /dev/null
+++ b/packages/CarSystemUI/res/layout/car_top_navigation_bar.xml
@@ -0,0 +1,151 @@
+<?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
+ -->
+
+<com.android.systemui.statusbar.car.CarNavigationBarView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:systemui="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/car_top_bar"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@android:color/black"
+ android:orientation="vertical">
+
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1">
+
+ <FrameLayout
+ android:id="@+id/left_hvac_container"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_alignParentStart="true"
+ >
+
+ <com.android.systemui.statusbar.car.CarNavigationButton
+ android:id="@+id/hvacleft"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@null"
+ systemui:broadcast="true"
+ systemui:intent="intent:#Intent;action=android.car.intent.action.TOGGLE_HVAC_CONTROLS;end"
+ />
+
+ <com.android.systemui.statusbar.hvac.AnimatedTemperatureView
+ android:id="@+id/lefttext"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:paddingStart="@dimen/car_padding_4"
+ android:paddingEnd="16dp"
+ android:gravity="center_vertical|start"
+ android:minEms="4"
+ android:textAppearance="@style/TextAppearance.Car.Status"
+ systemui:hvacAreaId="1"
+ systemui:hvacMaxText="@string/hvac_max_text"
+ systemui:hvacMaxValue="@dimen/hvac_max_value"
+ systemui:hvacMinText="@string/hvac_min_text"
+ systemui:hvacMinValue="@dimen/hvac_min_value"
+ systemui:hvacPivotOffset="60dp"
+ systemui:hvacPropertyId="358614275"
+ systemui:hvacTempFormat="%.0f\u00B0"
+ />
+ </FrameLayout>
+
+ <FrameLayout
+ android:id="@+id/clock_container"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_centerInParent="true"
+ >
+ <com.android.systemui.statusbar.car.CarNavigationButton
+ android:id="@+id/qs"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@null"
+ systemui:intent="intent:#Intent;component=com.android.car.settings/.common.CarSettingActivity;launchFlags=0x14008000;end"
+ />
+ <com.android.systemui.statusbar.policy.Clock
+ android:id="@+id/clock"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:elevation="5dp"
+ android:singleLine="true"
+ android:textAppearance="@style/TextAppearance.StatusBar.Clock"
+ />
+ </FrameLayout>
+
+ <LinearLayout
+ android:id="@+id/system_icon_area"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_centerHorizontal="true"
+ android:layout_centerVertical="true"
+ android:layout_toEndOf="@+id/clock_container"
+ android:paddingStart="@dimen/car_padding_1"
+ android:gravity="center_vertical"
+ android:orientation="horizontal"
+ >
+
+ <include
+ layout="@layout/system_icons"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:paddingStart="4dp"
+ android:gravity="center_vertical"
+ />
+ </LinearLayout>
+
+ <FrameLayout
+ android:id="@+id/right_hvac_container"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_alignParentEnd="true"
+ >
+
+ <com.android.systemui.statusbar.car.CarNavigationButton
+ android:id="@+id/hvacright"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@null"
+ systemui:broadcast="true"
+ systemui:intent="intent:#Intent;action=android.car.intent.action.TOGGLE_HVAC_CONTROLS;end"
+ />
+
+ <com.android.systemui.statusbar.hvac.AnimatedTemperatureView
+ android:id="@+id/righttext"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:paddingStart="16dp"
+ android:paddingEnd="@dimen/car_padding_4"
+ android:gravity="center_vertical|end"
+ android:minEms="4"
+ android:textAppearance="@style/TextAppearance.Car.Status"
+ systemui:hvacAreaId="4"
+ systemui:hvacMaxText="@string/hvac_max_text"
+ systemui:hvacMaxValue="@dimen/hvac_max_value"
+ systemui:hvacMinText="@string/hvac_min_text"
+ systemui:hvacMinValue="@dimen/hvac_min_value"
+ systemui:hvacPivotOffset="60dp"
+ systemui:hvacPropertyId="358614275"
+ systemui:hvacTempFormat="%.0f\u00B0"
+ />
+ </FrameLayout>
+ </RelativeLayout>
+
+</com.android.systemui.statusbar.car.CarNavigationBarView>
diff --git a/packages/CarSystemUI/res/layout/car_volume_dialog.xml b/packages/CarSystemUI/res/layout/car_volume_dialog.xml
new file mode 100644
index 0000000..c98740e
--- /dev/null
+++ b/packages/CarSystemUI/res/layout/car_volume_dialog.xml
@@ -0,0 +1,30 @@
+<?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
+ -->
+<androidx.car.widget.PagedListView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/volume_list"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="@android:color/black"
+ android:minWidth="@dimen/volume_dialog_panel_width"
+ android:theme="@style/Theme.Car.DialogListView"
+ app:dividerEndMargin="@dimen/car_keyline_1"
+ app:dividerStartMargin="@dimen/car_keyline_1"
+ app:gutter="none"
+ app:scrollBarEnabled="false"
+ app:showPagedListViewDivider="true"/>
diff --git a/packages/CarSystemUI/res/layout/status_bar_wifi_group.xml b/packages/CarSystemUI/res/layout/status_bar_wifi_group.xml
new file mode 100644
index 0000000..2793e56
--- /dev/null
+++ b/packages/CarSystemUI/res/layout/status_bar_wifi_group.xml
@@ -0,0 +1,85 @@
+<?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
+ -->
+<com.android.systemui.statusbar.StatusBarWifiView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:systemui="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/wifi_combo"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:gravity="center_vertical">
+
+ <com.android.keyguard.AlphaOptimizedLinearLayout
+ android:id="@+id/wifi_group"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:paddingStart="2dp"
+ android:gravity="center_vertical"
+ >
+ <FrameLayout
+ android:id="@+id/inout_container"
+ android:layout_width="wrap_content"
+ android:layout_height="17dp"
+ android:gravity="center_vertical">
+ <ImageView
+ android:id="@+id/wifi_in"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingEnd="2dp"
+ android:src="@drawable/ic_activity_down"
+ android:visibility="gone"
+ />
+ <ImageView
+ android:id="@+id/wifi_out"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingEnd="2dp"
+ android:src="@drawable/ic_activity_up"
+ android:visibility="gone"
+ />
+ </FrameLayout>
+ <FrameLayout
+ android:id="@+id/wifi_combo"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="center_vertical">
+ <com.android.systemui.statusbar.AlphaOptimizedImageView
+ android:id="@+id/wifi_signal"
+ android:layout_width="@dimen/status_bar_icon_size"
+ android:layout_height="@dimen/status_bar_icon_size"
+ android:theme="?attr/lightIconTheme"/>
+ </FrameLayout>
+
+ <View
+ android:id="@+id/wifi_signal_spacer"
+ android:layout_width="@dimen/status_bar_wifi_signal_spacer_width"
+ android:layout_height="4dp"
+ android:visibility="gone"/>
+
+ <ViewStub
+ android:id="@+id/connected_device_signals_stub"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout="@layout/connected_device_signal"/>
+
+ <View
+ android:id="@+id/wifi_airplane_spacer"
+ android:layout_width="@dimen/status_bar_airplane_spacer_width"
+ android:layout_height="4dp"
+ android:visibility="gone"
+ />
+ </com.android.keyguard.AlphaOptimizedLinearLayout>
+</com.android.systemui.statusbar.StatusBarWifiView>
diff --git a/packages/CarSystemUI/res/layout/super_status_bar.xml b/packages/CarSystemUI/res/layout/super_status_bar.xml
new file mode 100644
index 0000000..0594dce
--- /dev/null
+++ b/packages/CarSystemUI/res/layout/super_status_bar.xml
@@ -0,0 +1,91 @@
+<?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
+ -->
+
+<!-- This is the combined status bar / notification panel window. -->
+<com.android.systemui.statusbar.phone.StatusBarWindowView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:sysui="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:fitsSystemWindows="true">
+
+ <com.android.systemui.statusbar.BackDropView
+ android:id="@+id/backdrop"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:visibility="gone"
+ sysui:ignoreRightInset="true"
+ >
+ <ImageView android:id="@+id/backdrop_back"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:scaleType="centerCrop"/>
+ <ImageView android:id="@+id/backdrop_front"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:scaleType="centerCrop"
+ android:visibility="invisible"/>
+ </com.android.systemui.statusbar.BackDropView>
+
+ <com.android.systemui.statusbar.ScrimView
+ android:id="@+id/scrim_behind"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:importantForAccessibility="no"
+ sysui:ignoreRightInset="true"
+ />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/status_bar_height"
+ android:orientation="vertical"
+ >
+ <FrameLayout
+ android:id="@+id/status_bar_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:visibility="gone"
+ />
+
+ <include layout="@layout/car_top_navigation_bar"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ />
+ </LinearLayout>
+
+ <include layout="@layout/brightness_mirror"/>
+
+ <ViewStub android:id="@+id/fullscreen_user_switcher_stub"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout="@layout/car_fullscreen_user_switcher"/>
+
+ <include layout="@layout/status_bar_expanded"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:visibility="invisible"/>
+
+ <com.android.systemui.statusbar.ScrimView
+ android:id="@+id/scrim_in_front"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:importantForAccessibility="no"
+ sysui:ignoreRightInset="true"
+ />
+
+</com.android.systemui.statusbar.phone.StatusBarWindowView>
diff --git a/packages/CarSystemUI/res/layout/system_icons.xml b/packages/CarSystemUI/res/layout/system_icons.xml
new file mode 100644
index 0000000..a7dd65e
--- /dev/null
+++ b/packages/CarSystemUI/res/layout/system_icons.xml
@@ -0,0 +1,40 @@
+<?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
+ -->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/system_icons"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:gravity="center_vertical">
+
+ <com.android.systemui.statusbar.phone.StatusIconContainer
+ android:id="@+id/statusIcons"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:paddingEnd="4dp"
+ android:gravity="center_vertical"
+ android:orientation="horizontal"
+ />
+
+ <com.android.systemui.BatteryMeterView
+ android:id="@+id/battery"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ />
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/CarSystemUI/res/values-night/colors.xml b/packages/CarSystemUI/res/values-night/colors.xml
new file mode 100644
index 0000000..dad94a8
--- /dev/null
+++ b/packages/CarSystemUI/res/values-night/colors.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2018 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+ <color name="car_accent">#356FE5</color>
+ <color name="status_bar_background_color">#ff000000</color>
+ <color name="system_bar_background_opaque">#ff0c1013</color>
+
+ <!-- The color of the ripples on the untinted notifications -->
+ <color name="notification_ripple_untinted_color">@color/ripple_material_dark</color>
+</resources>
diff --git a/packages/CarSystemUI/res/values/attrs.xml b/packages/CarSystemUI/res/values/attrs.xml
new file mode 100644
index 0000000..6178738
--- /dev/null
+++ b/packages/CarSystemUI/res/values/attrs.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+
+<resources>
+
+ <!-- Custom attributes to configure hvac values -->
+ <declare-styleable name="AnimatedTemperatureView">
+ <attr name="hvacAreaId" format="integer"/>
+ <attr name="hvacPropertyId" format="integer"/>
+ <attr name="hvacTempFormat" format="string"/>
+ <!-- how far away the animations should center around -->
+ <attr name="hvacPivotOffset" format="dimension"/>
+ <attr name="hvacMinValue" format="float"/>
+ <attr name="hvacMaxValue" format="float"/>
+ <attr name="hvacMinText" format="string|reference"/>
+ <attr name="hvacMaxText" format="string|reference"/>
+ <attr name="android:gravity"/>
+ <attr name="android:minEms"/>
+ <attr name="android:textAppearance"/>
+ </declare-styleable>
+</resources>
diff --git a/packages/CarSystemUI/res/values/colors.xml b/packages/CarSystemUI/res/values/colors.xml
new file mode 100644
index 0000000..e9ddbaf
--- /dev/null
+++ b/packages/CarSystemUI/res/values/colors.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2018 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+ <color name="nav_bar_ripple_background_color">#40ffffff</color>
+ <color name="car_accent">#356FE5</color>
+ <!-- colors for user switcher -->
+ <color name="car_user_switcher_background_color">#000000</color>
+ <color name="car_user_switcher_name_text_color">@color/car_title2_light</color>
+ <color name="car_user_switcher_add_user_background_color">#131313</color>
+ <color name="car_nav_icon_fill_color">@color/car_grey_50</color>
+ <!-- colors for seekbar -->
+ <color name="car_seekbar_track_background">#131315</color>
+ <color name="car_seekbar_track_secondary_progress">@color/car_accent</color>
+ <!-- colors for volume dialog tint -->
+ <color name="car_volume_dialog_tint">@color/car_tint_light</color>
+
+ <!-- System ui can't depend on car libs so redefine. -->
+ <color name="car_grey_50">#fffafafa</color>
+
+ <color name="docked_divider_background">@color/car_grey_50</color>
+ <color name="system_bar_background_opaque">#ff172026</color>
+
+ <color name="status_bar_background_color">#33000000</color>
+ <drawable name="system_bar_background">@color/status_bar_background_color</drawable>
+
+ <!-- The scrim color for the background of the notifications shade. -->
+ <color name="scrim_behind_color">#172026</color>
+
+ <!-- The color of the dividing line between grouped notifications. -->
+ <color name="notification_divider_color">@*android:color/notification_action_list</color>
+
+ <!-- The color of the ripples on the untinted notifications -->
+ <color name="notification_ripple_untinted_color">@color/ripple_material_light</color>
+
+ <color name="car_teal_700">#ff00796b</color>
+ <color name="car_grey_300">#ffe0e0e0</color>
+ <color name="car_grey_900">#ff212121</color>
+
+ <color name="keyguard_button_text_color">@android:color/black</color>
+</resources>
diff --git a/packages/CarSystemUI/res/values/config.xml b/packages/CarSystemUI/res/values/config.xml
new file mode 100644
index 0000000..452d61d
--- /dev/null
+++ b/packages/CarSystemUI/res/values/config.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2018 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<resources>
+ <string name="config_statusBarComponent" translatable="false">
+ com.android.systemui.statusbar.car.CarStatusBar
+ </string>
+ <string name="config_systemUIFactoryComponent" translatable="false">
+ com.android.systemui.CarSystemUIFactory
+ </string>
+ <bool name="config_enableFullscreenUserSwitcher">true</bool>
+
+ <!-- configure which system ui bars should be displayed -->
+ <bool name="config_enableLeftNavigationBar">false</bool>
+ <bool name="config_enableRightNavigationBar">false</bool>
+ <bool name="config_enableBottomNavigationBar">true</bool>
+
+</resources>
diff --git a/packages/CarSystemUI/res/values/dimens.xml b/packages/CarSystemUI/res/values/dimens.xml
new file mode 100644
index 0000000..3829aa3
--- /dev/null
+++ b/packages/CarSystemUI/res/values/dimens.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2018 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<resources>
+ <!--
+ Note: status bar height and navigation bar heights are defined
+ in frameworks/base/core package and thus will have no effect if
+ set here. See car_product overlay for car specific defaults-->
+
+ <dimen name="status_bar_icon_drawing_size_dark">36dp</dimen>
+ <dimen name="status_bar_icon_drawing_size">36dp</dimen>
+ <dimen name="car_qs_header_system_icons_area_height">96dp</dimen>
+ <!-- The amount by which to scale up the status bar icons. -->
+ <item name="status_bar_icon_scale_factor" format="float" type="dimen">1.75</item>
+
+ <dimen name="car_primary_icon_size">36dp</dimen>
+
+ <!-- dimensions for the car user switcher -->
+ <dimen name="car_user_switcher_name_text_size">@dimen/car_title2_size</dimen>
+ <dimen name="car_user_switcher_vertical_spacing_between_users">124dp</dimen>
+
+ <!--These values represent MIN and MAX for hvac-->
+ <item name="hvac_min_value" format="float" type="dimen">0</item>
+ <item name="hvac_max_value" format="float" type="dimen">126</item>
+
+ <!-- Largest size an avatar might need to be drawn in the user picker, status bar, or
+ quick settings header -->
+ <dimen name="max_avatar_size">128dp</dimen>
+
+ <!-- Standard image button size for volume dialog buttons -->
+ <dimen name="volume_button_size">84dp</dimen>
+ <!-- The maximum width allowed for the volume dialog. For auto, we allow this to span a good
+ deal of the screen. This value accounts for the side margins. -->
+ <dimen name="volume_dialog_panel_width">1920dp</dimen>
+ <dimen name="volume_dialog_side_margin">@dimen/side_margin</dimen>
+
+ <dimen name="volume_dialog_elevation">6dp</dimen>
+
+ <dimen name="volume_dialog_row_margin_end">@dimen/car_keyline_3</dimen>
+
+ <dimen name="volume_dialog_row_padding_end">0dp</dimen>
+
+ <dimen name="line_item_height">128dp</dimen>
+ <dimen name="volume_icon_size">96dp</dimen>
+ <dimen name="side_margin">148dp</dimen>
+ <dimen name="car_keyline_1">24dp</dimen>
+ <dimen name="car_keyline_2">96dp</dimen>
+ <dimen name="car_keyline_3">128dp</dimen>
+</resources>
diff --git a/packages/CarSystemUI/res/values/integers.xml b/packages/CarSystemUI/res/values/integers.xml
new file mode 100644
index 0000000..8b87c74
--- /dev/null
+++ b/packages/CarSystemUI/res/values/integers.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (c) 2018, The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<resources>
+ <integer name="user_fullscreen_switcher_num_col">2</integer>
+</resources>
diff --git a/packages/CarSystemUI/res/values/strings.xml b/packages/CarSystemUI/res/values/strings.xml
new file mode 100644
index 0000000..0368e61
--- /dev/null
+++ b/packages/CarSystemUI/res/values/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (c) 2018 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources>
+ <!-- String to represent lowest setting of an HVAC system [CHAR LIMIT=5]-->
+ <string name="hvac_min_text">Min</string>
+ <!-- String to represent largest setting of an HVAC system [CHAR LIMIT=5]-->
+ <string name="hvac_max_text">Max</string>
+</resources>
diff --git a/packages/CarSystemUI/res/values/styles.xml b/packages/CarSystemUI/res/values/styles.xml
new file mode 100644
index 0000000..22a699c
--- /dev/null
+++ b/packages/CarSystemUI/res/values/styles.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2018 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <!-- The style for the volume icons in the volume dialog. This style makes the icon scale to
+ fit its container since auto wants the icon to be larger. The padding is added to make it
+ so the icon does not press along the edges of the dialog. -->
+ <style name="VolumeButtons" parent="@android:style/Widget.Material.Button.Borderless">
+ <item name="android:background">@drawable/btn_borderless_rect</item>
+ <item name="android:scaleType">fitCenter</item>
+ <item name="android:padding">22dp</item>
+ </style>
+
+ <style name="TextAppearance.StatusBar.Clock"
+ parent="@*android:style/TextAppearance.StatusBar.Icon">
+ <item name="android:textSize">42sp</item>
+ <item name="android:fontFamily">sans-serif-regular</item>
+ <item name="android:textColor">@color/car_grey_50</item>
+ </style>
+
+ <style name="TextAppearance.Car.Status">
+ <item name="android:textSize">@dimen/car_body2_size</item>
+ <item name="android:textColor">@color/car_grey_50</item>
+ </style>
+
+ <style name="CarNavigationBarButtonTheme">
+ <item name="android:colorControlHighlight">@color/nav_bar_ripple_background_color</item>
+ </style>
+
+ <style name="Theme.Car.DialogListView" parent="@style/Theme.Car.NoActionBar">
+ <item name="listItemBackgroundColor">@android:color/black</item>
+ </style>
+
+ <style name="NavigationBarButton">
+ <item name="android:layout_height">96dp</item>
+ <item name="android:layout_width">96dp</item>
+ <item name="android:background">@drawable/nav_button_background</item>
+ </style>
+
+</resources>
diff --git a/packages/CarSystemUI/res/xml/car_volume_items.xml b/packages/CarSystemUI/res/xml/car_volume_items.xml
new file mode 100644
index 0000000..8715946
--- /dev/null
+++ b/packages/CarSystemUI/res/xml/car_volume_items.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+
+<!--
+ Defines all possible items on car volume settings UI, keyed by usage.
+-->
+<carVolumeItems xmlns:car="http://schemas.android.com/apk/res-auto">
+ <item car:usage="unknown"
+ car:icon="@drawable/car_ic_music"/>
+ <item car:usage="media"
+ car:icon="@drawable/car_ic_music"/>
+ <item car:usage="voice_communication"
+ car:icon="@*android:drawable/ic_audio_ring_notif"/>
+ <item car:usage="voice_communication_signalling"
+ car:icon="@*android:drawable/ic_audio_ring_notif"/>
+ <item car:usage="alarm"
+ car:icon="@drawable/ic_volume_alarm"/>
+ <item car:usage="notification"
+ car:icon="@drawable/car_ic_notification"/>
+ <item car:usage="notification_ringtone"
+ car:icon="@drawable/car_ic_notification"/>
+ <item car:usage="notification_communication_request"
+ car:icon="@drawable/car_ic_notification"/>
+ <item car:usage="notification_communication_instant"
+ car:icon="@drawable/car_ic_notification"/>
+ <item car:usage="notification_communication_delayed"
+ car:icon="@drawable/car_ic_notification"/>
+ <item car:usage="notification_event"
+ car:icon="@drawable/car_ic_notification"/>
+ <item car:usage="assistance_accessibility"
+ car:icon="@drawable/car_ic_notification"/>
+ <item car:usage="assistance_navigation_guidance"
+ car:icon="@drawable/car_ic_navigation"/>
+ <item car:usage="assistance_sonification"
+ car:icon="@drawable/car_ic_notification"/>
+ <item car:usage="game"
+ car:icon="@drawable/car_ic_music"/>
+ <item car:usage="assistant"
+ car:icon="@drawable/car_ic_music"/>
+</carVolumeItems>
+
diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java
new file mode 100644
index 0000000..dfe5704
--- /dev/null
+++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.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;
+
+import android.content.Context;
+import android.util.ArrayMap;
+
+import com.android.internal.widget.LockPatternUtils;
+import com.android.keyguard.ViewMediatorCallback;
+import com.android.systemui.Dependency.DependencyProvider;
+import com.android.systemui.car.CarNotificationEntryManager;
+import com.android.systemui.statusbar.car.CarFacetButtonController;
+import com.android.systemui.statusbar.car.CarStatusBarKeyguardViewManager;
+import com.android.systemui.statusbar.car.hvac.HvacController;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
+
+/**
+ * Class factory to provide car specific SystemUI components.
+ */
+public class CarSystemUIFactory extends SystemUIFactory {
+
+ public StatusBarKeyguardViewManager createStatusBarKeyguardViewManager(Context context,
+ ViewMediatorCallback viewMediatorCallback, LockPatternUtils lockPatternUtils) {
+ return new CarStatusBarKeyguardViewManager(context, viewMediatorCallback, lockPatternUtils);
+ }
+
+ @Override
+ public void injectDependencies(ArrayMap<Object, DependencyProvider> providers,
+ Context context) {
+ super.injectDependencies(providers, context);
+ providers.put(NotificationEntryManager.class,
+ () -> new CarNotificationEntryManager(context));
+ providers.put(CarFacetButtonController.class, () -> new CarFacetButtonController(context));
+ providers.put(HvacController.class, () -> new HvacController(context));
+ }
+}
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/hvac/AnimatedTemperatureView.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/hvac/AnimatedTemperatureView.java
new file mode 100644
index 0000000..27d3106
--- /dev/null
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/hvac/AnimatedTemperatureView.java
@@ -0,0 +1,276 @@
+/*
+ * 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.hvac;
+
+import android.animation.ObjectAnimator;
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Color;
+import android.graphics.Rect;
+import android.graphics.drawable.ColorDrawable;
+import com.android.systemui.statusbar.car.hvac.TemperatureView;
+import com.android.systemui.statusbar.car.hvac.HvacController;
+import android.util.AttributeSet;
+import android.util.Property;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.TextSwitcher;
+import android.widget.TextView;
+
+import com.android.systemui.Dependency;
+import com.android.systemui.R;
+
+/**
+ * Simple text display of HVAC properties, It is designed to show mTemperature and is configured in
+ * the XML.
+ * XML properties:
+ * hvacPropertyId - Example: CarHvacManager.ID_ZONED_TEMP_SETPOINT (16385)
+ * hvacAreaId - Example: VehicleSeat.SEAT_ROW_1_LEFT (1)
+ * hvacTempFormat - Example: "%.1f\u00B0" (1 decimal and the degree symbol)
+ * hvacOrientaion = Example: left
+ * <p>
+ * Note: It registers itself with {@link HvacController}
+ */
+public class AnimatedTemperatureView extends FrameLayout implements TemperatureView {
+
+ private static final float TEMPERATURE_EQUIVALENT_DELTA = .01f;
+ private static final Property<ColorDrawable, Integer> COLOR_PROPERTY =
+ new Property<ColorDrawable, Integer>(Integer.class, "color") {
+
+ @Override
+ public Integer get(ColorDrawable object) {
+ return object.getColor();
+ }
+
+ @Override
+ public void set(ColorDrawable object, Integer value) {
+ object.setColor(value);
+ }
+ };
+
+ static boolean isHorizontal(int gravity) {
+ return Gravity.isHorizontal(gravity)
+ && (gravity & Gravity.HORIZONTAL_GRAVITY_MASK) != Gravity.CENTER_HORIZONTAL;
+ }
+
+ @SuppressLint("RtlHardcoded")
+ static boolean isLeft(int gravity, int layoutDirection) {
+ return Gravity
+ .getAbsoluteGravity(gravity & Gravity.HORIZONTAL_GRAVITY_MASK, layoutDirection)
+ == Gravity.LEFT;
+ }
+
+ static boolean isVertical(int gravity) {
+ return Gravity.isVertical(gravity)
+ && (gravity & Gravity.VERTICAL_GRAVITY_MASK) != Gravity.CENTER_VERTICAL;
+ }
+
+ static boolean isTop(int gravity) {
+ return (gravity & Gravity.VERTICAL_GRAVITY_MASK) == Gravity.TOP;
+ }
+
+ private final int mAreaId;
+ private final int mPropertyId;
+ private final int mPivotOffset;
+ private final int mGravity;
+ private final int mTextAppearanceRes;
+ private final int mMinEms;
+ private final Rect mPaddingRect;
+ private final float mMinValue;
+ private final float mMaxValue;
+
+ private final ColorDrawable mBackgroundColor;
+
+ private final TemperatureColorStore mColorStore = new TemperatureColorStore();
+ private final TemperatureBackgroundAnimator mBackgroundAnimator;
+ private final TemperatureTextAnimator mTextAnimator;
+
+ public AnimatedTemperatureView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ TypedArray typedArray = context.obtainStyledAttributes(attrs,
+ R.styleable.AnimatedTemperatureView);
+ mAreaId = typedArray.getInt(R.styleable.AnimatedTemperatureView_hvacAreaId, -1);
+ mPropertyId = typedArray.getInt(R.styleable.AnimatedTemperatureView_hvacPropertyId, -1);
+ mPivotOffset =
+ typedArray.getDimensionPixelOffset(
+ R.styleable.AnimatedTemperatureView_hvacPivotOffset, 0);
+ mGravity = typedArray.getInt(R.styleable.AnimatedTemperatureView_android_gravity,
+ Gravity.START);
+ mTextAppearanceRes =
+ typedArray.getResourceId(R.styleable.AnimatedTemperatureView_android_textAppearance,
+ 0);
+ mMinEms = typedArray.getInteger(R.styleable.AnimatedTemperatureView_android_minEms, 0);
+ mMinValue = typedArray.getFloat(R.styleable.AnimatedTemperatureView_hvacMinValue,
+ Float.NaN);
+ mMaxValue = typedArray.getFloat(R.styleable.AnimatedTemperatureView_hvacMaxValue,
+ Float.NaN);
+
+
+ mPaddingRect =
+ new Rect(getPaddingLeft(), getPaddingTop(), getPaddingRight(), getPaddingBottom());
+ setPadding(0, 0, 0, 0);
+
+ setClipChildren(false);
+ setClipToPadding(false);
+
+ // init Views
+ TextSwitcher textSwitcher = new TextSwitcher(context);
+ textSwitcher.setFactory(this::generateTextView);
+ ImageView background = new ImageView(context);
+ mBackgroundColor = new ColorDrawable(Color.TRANSPARENT);
+ background.setImageDrawable(mBackgroundColor);
+ background.setVisibility(View.GONE);
+
+ mBackgroundAnimator = new TemperatureBackgroundAnimator(this, background);
+
+
+ String format = typedArray.getString(R.styleable.AnimatedTemperatureView_hvacTempFormat);
+ format = (format == null) ? "%.1f\u00B0" : format;
+ CharSequence minText = typedArray.getString(
+ R.styleable.AnimatedTemperatureView_hvacMinText);
+ CharSequence maxText = typedArray.getString(
+ R.styleable.AnimatedTemperatureView_hvacMaxText);
+ mTextAnimator = new TemperatureTextAnimator(this, textSwitcher, format, mPivotOffset,
+ minText, maxText);
+
+ addView(background, ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT);
+ addView(textSwitcher, ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT);
+
+ typedArray.recycle();
+
+ // register with controller
+ HvacController hvacController = Dependency.get(HvacController.class);
+ hvacController.addHvacTextView(this);
+ }
+
+ private TextView generateTextView() {
+ TextView textView = new TextView(getContext());
+ textView.setTextAppearance(mTextAppearanceRes);
+ textView.setAllCaps(true);
+ textView.setMinEms(mMinEms);
+ textView.setGravity(mGravity);
+ textView.setPadding(mPaddingRect.left, mPaddingRect.top, mPaddingRect.right,
+ mPaddingRect.bottom);
+ textView.getViewTreeObserver()
+ .addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
+ @Override
+ public boolean onPreDraw() {
+ if (isHorizontal(mGravity)) {
+ if (isLeft(mGravity, getLayoutDirection())) {
+ textView.setPivotX(-mPivotOffset);
+ } else {
+ textView.setPivotX(textView.getWidth() + mPivotOffset);
+ }
+ }
+ textView.getViewTreeObserver().removeOnPreDrawListener(this);
+ return false;
+ }
+ });
+ textView.setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT));
+
+ return textView;
+ }
+
+ /**
+ * Formats the float for display
+ *
+ * @param temp - The current temp or NaN
+ */
+ @Override
+ public void setTemp(float temp) {
+ mTextAnimator.setTemp(temp);
+ if (Float.isNaN(temp)) {
+ mBackgroundAnimator.hideCircle();
+ return;
+ }
+ int color;
+ if (isMinValue(temp)) {
+ color = mColorStore.getMinColor();
+ } else if (isMaxValue(temp)) {
+ color = mColorStore.getMaxColor();
+ } else {
+ color = mColorStore.getColorForTemperature(temp);
+ }
+ if (mBackgroundAnimator.isOpen()) {
+ ObjectAnimator colorAnimator =
+ ObjectAnimator.ofInt(mBackgroundColor, COLOR_PROPERTY, color);
+ colorAnimator.setEvaluator((fraction, startValue, endValue) -> mColorStore
+ .lerpColor(fraction, (int) startValue, (int) endValue));
+ colorAnimator.start();
+ } else {
+ mBackgroundColor.setColor(color);
+ }
+
+ mBackgroundAnimator.animateOpen();
+ }
+
+ boolean isMinValue(float temp) {
+ return !Float.isNaN(mMinValue) && isApproxEqual(temp, mMinValue);
+ }
+
+ boolean isMaxValue(float temp) {
+ return !Float.isNaN(mMaxValue) && isApproxEqual(temp, mMaxValue);
+ }
+
+ private boolean isApproxEqual(float left, float right) {
+ return Math.abs(left - right) <= TEMPERATURE_EQUIVALENT_DELTA;
+ }
+
+ int getGravity() {
+ return mGravity;
+ }
+
+ int getPivotOffset() {
+ return mPivotOffset;
+ }
+
+ Rect getPaddingRect() {
+ return mPaddingRect;
+ }
+
+ /**
+ * @return propertiyId Example: CarHvacManager.ID_ZONED_TEMP_SETPOINT (358614275)
+ */
+ @Override
+ public int getPropertyId() {
+ return mPropertyId;
+ }
+
+ /**
+ * @return hvac AreaId - Example: VehicleSeat.SEAT_ROW_1_LEFT (1)
+ */
+ @Override
+ public int getAreaId() {
+ return mAreaId;
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ mBackgroundAnimator.stopAnimations();
+ }
+
+}
+
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/hvac/TemperatureBackgroundAnimator.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/hvac/TemperatureBackgroundAnimator.java
new file mode 100644
index 0000000..0bc94b5
--- /dev/null
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/hvac/TemperatureBackgroundAnimator.java
@@ -0,0 +1,338 @@
+/*
+ * 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.hvac;
+
+import static com.android.systemui.statusbar.hvac.AnimatedTemperatureView.isHorizontal;
+import static com.android.systemui.statusbar.hvac.AnimatedTemperatureView.isLeft;
+import static com.android.systemui.statusbar.hvac.AnimatedTemperatureView.isTop;
+import static com.android.systemui.statusbar.hvac.AnimatedTemperatureView.isVertical;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.annotation.IntDef;
+import android.graphics.Rect;
+import android.view.View;
+import android.view.ViewAnimationUtils;
+import android.view.animation.AnticipateInterpolator;
+import android.widget.ImageView;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Controls circular reveal animation of temperature background
+ */
+class TemperatureBackgroundAnimator {
+
+ private static final AnticipateInterpolator ANTICIPATE_INTERPOLATOR =
+ new AnticipateInterpolator();
+ private static final float MAX_OPACITY = .6f;
+
+ private final View mAnimatedView;
+
+ private int mPivotX;
+ private int mPivotY;
+ private int mGoneRadius;
+ private int mOvershootRadius;
+ private int mRestingRadius;
+ private int mBumpRadius;
+
+ @CircleState
+ private int mCircleState;
+
+ private Animator mCircularReveal;
+ private boolean mAnimationsReady;
+
+ @IntDef({CircleState.GONE, CircleState.ENTERING, CircleState.OVERSHOT, CircleState.RESTING,
+ CircleState.RESTED, CircleState.BUMPING, CircleState.BUMPED, CircleState.EXITING})
+ private @interface CircleState {
+ int GONE = 0;
+ int ENTERING = 1;
+ int OVERSHOT = 2;
+ int RESTING = 3;
+ int RESTED = 4;
+ int BUMPING = 5;
+ int BUMPED = 6;
+ int EXITING = 7;
+ }
+
+ TemperatureBackgroundAnimator(
+ AnimatedTemperatureView parent,
+ ImageView animatedView) {
+ mAnimatedView = animatedView;
+ mAnimatedView.setAlpha(0);
+
+ parent.addOnLayoutChangeListener(
+ (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) ->
+ setupAnimations(parent.getGravity(), parent.getPivotOffset(),
+ parent.getPaddingRect(), parent.getWidth(), parent.getHeight()));
+ }
+
+ private void setupAnimations(int gravity, int pivotOffset, Rect paddingRect,
+ int width, int height) {
+ int padding;
+ if (isHorizontal(gravity)) {
+ mGoneRadius = pivotOffset;
+ if (isLeft(gravity, mAnimatedView.getLayoutDirection())) {
+ mPivotX = -pivotOffset;
+ padding = paddingRect.right;
+ } else {
+ mPivotX = width + pivotOffset;
+ padding = paddingRect.left;
+ }
+ mPivotY = height / 2;
+ mOvershootRadius = pivotOffset + width;
+ } else if (isVertical(gravity)) {
+ mGoneRadius = pivotOffset;
+ if (isTop(gravity)) {
+ mPivotY = -pivotOffset;
+ padding = paddingRect.bottom;
+ } else {
+ mPivotY = height + pivotOffset;
+ padding = paddingRect.top;
+ }
+ mPivotX = width / 2;
+ mOvershootRadius = pivotOffset + height;
+ } else {
+ mPivotX = width / 2;
+ mPivotY = height / 2;
+ mGoneRadius = 0;
+ if (width > height) {
+ mOvershootRadius = height;
+ padding = Math.max(paddingRect.top, paddingRect.bottom);
+ } else {
+ mOvershootRadius = width;
+ padding = Math.max(paddingRect.left, paddingRect.right);
+ }
+ }
+ mRestingRadius = mOvershootRadius - padding;
+ mBumpRadius = mOvershootRadius - padding / 3;
+ mAnimationsReady = true;
+ }
+
+ boolean isOpen() {
+ return mCircleState != CircleState.GONE;
+ }
+
+ void animateOpen() {
+ if (!mAnimationsReady || mCircleState == CircleState.ENTERING) {
+ return;
+ }
+
+ AnimatorSet set = new AnimatorSet();
+ List<Animator> animators = new ArrayList<>();
+ switch (mCircleState) {
+ case CircleState.ENTERING:
+ throw new AssertionError("Should not be able to reach this statement");
+ case CircleState.GONE: {
+ Animator startCircle = createEnterAnimator();
+ markState(startCircle, CircleState.ENTERING);
+ animators.add(startCircle);
+ Animator holdOvershoot = ViewAnimationUtils
+ .createCircularReveal(mAnimatedView, mPivotX, mPivotY, mOvershootRadius,
+ mOvershootRadius);
+ holdOvershoot.setDuration(50);
+ markState(holdOvershoot, CircleState.OVERSHOT);
+ animators.add(holdOvershoot);
+ Animator rest = ViewAnimationUtils
+ .createCircularReveal(mAnimatedView, mPivotX, mPivotY, mOvershootRadius,
+ mRestingRadius);
+ markState(rest, CircleState.RESTING);
+ animators.add(rest);
+ Animator holdRest = ViewAnimationUtils
+ .createCircularReveal(mAnimatedView, mPivotX, mPivotY, mRestingRadius,
+ mRestingRadius);
+ markState(holdRest, CircleState.RESTED);
+ holdRest.setDuration(1000);
+ animators.add(holdRest);
+ Animator exit = createExitAnimator(mRestingRadius);
+ markState(exit, CircleState.EXITING);
+ animators.add(exit);
+ }
+ break;
+ case CircleState.RESTED:
+ case CircleState.RESTING:
+ case CircleState.EXITING:
+ case CircleState.OVERSHOT:
+ int startRadius =
+ mCircleState == CircleState.OVERSHOT ? mOvershootRadius : mRestingRadius;
+ Animator bump = ViewAnimationUtils
+ .createCircularReveal(mAnimatedView, mPivotX, mPivotY, startRadius,
+ mBumpRadius);
+ bump.setDuration(50);
+ markState(bump, CircleState.BUMPING);
+ animators.add(bump);
+ // fallthrough intentional
+ case CircleState.BUMPED:
+ case CircleState.BUMPING:
+ Animator holdBump = ViewAnimationUtils
+ .createCircularReveal(mAnimatedView, mPivotX, mPivotY, mBumpRadius,
+ mBumpRadius);
+ holdBump.setDuration(100);
+ markState(holdBump, CircleState.BUMPED);
+ animators.add(holdBump);
+ Animator rest = ViewAnimationUtils
+ .createCircularReveal(mAnimatedView, mPivotX, mPivotY, mBumpRadius,
+ mRestingRadius);
+ markState(rest, CircleState.RESTING);
+ animators.add(rest);
+ Animator holdRest = ViewAnimationUtils
+ .createCircularReveal(mAnimatedView, mPivotX, mPivotY, mRestingRadius,
+ mRestingRadius);
+ holdRest.setDuration(1000);
+ markState(holdRest, CircleState.RESTED);
+ animators.add(holdRest);
+ Animator exit = createExitAnimator(mRestingRadius);
+ markState(exit, CircleState.EXITING);
+ animators.add(exit);
+ break;
+ }
+ set.playSequentially(animators);
+ set.addListener(new AnimatorListenerAdapter() {
+ private boolean mCanceled = false;
+
+ @Override
+ public void onAnimationStart(Animator animation) {
+ if (mCircularReveal != null) {
+ mCircularReveal.cancel();
+ }
+ mCircularReveal = animation;
+ mAnimatedView.setVisibility(View.VISIBLE);
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ mCanceled = true;
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (mCanceled) {
+ return;
+ }
+ mCircularReveal = null;
+ mCircleState = CircleState.GONE;
+ mAnimatedView.setVisibility(View.GONE);
+ }
+ });
+
+ set.start();
+ }
+
+ private Animator createEnterAnimator() {
+ AnimatorSet animatorSet = new AnimatorSet();
+ Animator circularReveal = ViewAnimationUtils
+ .createCircularReveal(mAnimatedView, mPivotX, mPivotY, mGoneRadius,
+ mOvershootRadius);
+ Animator fade = ObjectAnimator.ofFloat(mAnimatedView, View.ALPHA, MAX_OPACITY);
+ animatorSet.playTogether(circularReveal, fade);
+ return animatorSet;
+ }
+
+ private Animator createExitAnimator(int startRadius) {
+ AnimatorSet animatorSet = new AnimatorSet();
+ Animator circularHide = ViewAnimationUtils
+ .createCircularReveal(mAnimatedView, mPivotX, mPivotY, startRadius,
+ (mGoneRadius + startRadius) / 2);
+ circularHide.setInterpolator(ANTICIPATE_INTERPOLATOR);
+ Animator fade = ObjectAnimator.ofFloat(mAnimatedView, View.ALPHA, 0);
+ fade.setStartDelay(50);
+ animatorSet.playTogether(circularHide, fade);
+ return animatorSet;
+ }
+
+ void hideCircle() {
+ if (!mAnimationsReady || mCircleState == CircleState.GONE
+ || mCircleState == CircleState.EXITING) {
+ return;
+ }
+
+ int startRadius;
+ switch (mCircleState) {
+ // Unreachable, but here to exhaust switch cases
+ //noinspection ConstantConditions
+ case CircleState.EXITING:
+ //noinspection ConstantConditions
+ case CircleState.GONE:
+ throw new AssertionError("Should not be able to reach this statement");
+ case CircleState.BUMPED:
+ case CircleState.BUMPING:
+ startRadius = mBumpRadius;
+ break;
+ case CircleState.OVERSHOT:
+ startRadius = mOvershootRadius;
+ break;
+ case CircleState.ENTERING:
+ case CircleState.RESTED:
+ case CircleState.RESTING:
+ startRadius = mRestingRadius;
+ break;
+ default:
+ throw new IllegalStateException("Unknown CircleState: " + mCircleState);
+ }
+
+ Animator hideAnimation = createExitAnimator(startRadius);
+ if (startRadius == mRestingRadius) {
+ hideAnimation.setInterpolator(ANTICIPATE_INTERPOLATOR);
+ }
+ hideAnimation.addListener(new AnimatorListenerAdapter() {
+ private boolean mCanceled = false;
+
+ @Override
+ public void onAnimationStart(Animator animation) {
+ mCircleState = CircleState.EXITING;
+ if (mCircularReveal != null) {
+ mCircularReveal.cancel();
+ }
+ mCircularReveal = animation;
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ mCanceled = true;
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (mCanceled) {
+ return;
+ }
+ mCircularReveal = null;
+ mCircleState = CircleState.GONE;
+ mAnimatedView.setVisibility(View.GONE);
+ }
+ });
+ hideAnimation.start();
+ }
+
+ void stopAnimations() {
+ if (mCircularReveal != null) {
+ mCircularReveal.end();
+ }
+ }
+
+ private void markState(Animator animator, @CircleState int startState) {
+ animator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ mCircleState = startState;
+ }
+ });
+ }
+}
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/hvac/TemperatureColorStore.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/hvac/TemperatureColorStore.java
new file mode 100644
index 0000000..a40ffaf
--- /dev/null
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/hvac/TemperatureColorStore.java
@@ -0,0 +1,202 @@
+/*
+ * 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.hvac;
+
+import android.graphics.Color;
+
+/**
+ * Contains the logic for mapping colors to temperatures
+ */
+class TemperatureColorStore {
+
+ private static class TemperatureColorValue {
+ final float mTemperature;
+ final int mColor;
+
+ private TemperatureColorValue(float temperature, int color) {
+ this.mTemperature = temperature;
+ this.mColor = color;
+ }
+
+ float getTemperature() {
+ return mTemperature;
+ }
+
+ int getColor() {
+ return mColor;
+ }
+ }
+
+ private static TemperatureColorValue tempToColor(float temperature, int color) {
+ return new TemperatureColorValue(temperature, color);
+ }
+
+ private static final int COLOR_COLDEST = 0xFF406DFF;
+ private static final int COLOR_COLD = 0xFF4094FF;
+ private static final int COLOR_NEUTRAL = 0xFFF4F4F4;
+ private static final int COLOR_WARM = 0xFFFF550F;
+ private static final int COLOR_WARMEST = 0xFFFF0000;
+ // must be sorted by temperature
+ private static final TemperatureColorValue[] sTemperatureColorValues =
+ {
+ // Celsius
+ tempToColor(19, COLOR_COLDEST),
+ tempToColor(21, COLOR_COLD),
+ tempToColor(23, COLOR_NEUTRAL),
+ tempToColor(25, COLOR_WARM),
+ tempToColor(27, COLOR_WARMEST),
+
+ // Switch over
+ tempToColor(45, COLOR_WARMEST),
+ tempToColor(45.00001f, COLOR_COLDEST),
+
+ // Farenheight
+ tempToColor(66, COLOR_COLDEST),
+ tempToColor(70, COLOR_COLD),
+ tempToColor(74, COLOR_NEUTRAL),
+ tempToColor(76, COLOR_WARM),
+ tempToColor(80, COLOR_WARMEST)
+ };
+
+ private static final int COLOR_UNSET = Color.BLACK;
+
+ private final float[] mTempHsv1 = new float[3];
+ private final float[] mTempHsv2 = new float[3];
+ private final float[] mTempHsv3 = new float[3];
+
+ int getMinColor() {
+ return COLOR_COLDEST;
+ }
+
+ int getMaxColor() {
+ return COLOR_WARMEST;
+ }
+
+ int getColorForTemperature(float temperature) {
+ if (Float.isNaN(temperature)) {
+ return COLOR_UNSET;
+ }
+ TemperatureColorValue bottomValue = sTemperatureColorValues[0];
+ if (temperature <= bottomValue.getTemperature()) {
+ return bottomValue.getColor();
+ }
+ TemperatureColorValue topValue =
+ sTemperatureColorValues[sTemperatureColorValues.length - 1];
+ if (temperature >= topValue.getTemperature()) {
+ return topValue.getColor();
+ }
+
+ int index = binarySearch(temperature);
+ if (index >= 0) {
+ return sTemperatureColorValues[index].getColor();
+ }
+
+ index = -index - 1; // move to the insertion point
+
+ TemperatureColorValue startValue = sTemperatureColorValues[index - 1];
+ TemperatureColorValue endValue = sTemperatureColorValues[index];
+ float fraction = (temperature - startValue.getTemperature()) / (endValue.getTemperature()
+ - startValue.getTemperature());
+ return lerpColor(fraction, startValue.getColor(), endValue.getColor());
+ }
+
+ int lerpColor(float fraction, int startColor, int endColor) {
+ float[] startHsv = mTempHsv1;
+ Color.colorToHSV(startColor, startHsv);
+ float[] endHsv = mTempHsv2;
+ Color.colorToHSV(endColor, endHsv);
+
+ // If a target color is white/gray, it should use the same hue as the other target
+ if (startHsv[1] == 0) {
+ startHsv[0] = endHsv[0];
+ }
+ if (endHsv[1] == 0) {
+ endHsv[0] = startHsv[0];
+ }
+
+ float[] outColor = mTempHsv3;
+ outColor[0] = hueLerp(fraction, startHsv[0], endHsv[0]);
+ outColor[1] = lerp(fraction, startHsv[1], endHsv[1]);
+ outColor[2] = lerp(fraction, startHsv[2], endHsv[2]);
+
+ return Color.HSVToColor(outColor);
+ }
+
+ private float hueLerp(float fraction, float start, float end) {
+ // If in flat part of curve, no interpolation necessary
+ if (start == end) {
+ return start;
+ }
+
+ // If the hues are more than 180 degrees apart, go the other way around the color wheel
+ // by moving the smaller value above 360
+ if (Math.abs(start - end) > 180f) {
+ if (start < end) {
+ start += 360f;
+ } else {
+ end += 360f;
+ }
+ }
+ // Lerp and ensure the final output is within [0, 360)
+ return lerp(fraction, start, end) % 360f;
+
+ }
+
+ private float lerp(float fraction, float start, float end) {
+ // If in flat part of curve, no interpolation necessary
+ if (start == end) {
+ return start;
+ }
+
+ // If outside bounds, use boundary value
+ if (fraction >= 1) {
+ return end;
+ }
+ if (fraction <= 0) {
+ return start;
+ }
+
+ return (end - start) * fraction + start;
+ }
+
+ private int binarySearch(float temperature) {
+ int low = 0;
+ int high = sTemperatureColorValues.length;
+
+ while (low <= high) {
+ int mid = (low + high) >>> 1;
+ float midVal = sTemperatureColorValues[mid].getTemperature();
+
+ if (midVal < temperature) {
+ low = mid + 1; // Neither val is NaN, thisVal is smaller
+ } else if (midVal > temperature) {
+ high = mid - 1; // Neither val is NaN, thisVal is larger
+ } else {
+ int midBits = Float.floatToIntBits(midVal);
+ int keyBits = Float.floatToIntBits(temperature);
+ if (midBits == keyBits) { // Values are equal
+ return mid; // Key found
+ } else if (midBits < keyBits) { // (-0.0, 0.0) or (!NaN, NaN)
+ low = mid + 1;
+ } else { /* (0.0, -0.0) or (NaN, !NaN)*/
+ high = mid - 1;
+ }
+ }
+ }
+ return -(low + 1); // key not found.
+ }
+}
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/hvac/TemperatureTextAnimator.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/hvac/TemperatureTextAnimator.java
new file mode 100644
index 0000000..8ee5ef6
--- /dev/null
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/hvac/TemperatureTextAnimator.java
@@ -0,0 +1,164 @@
+/*
+ * 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.hvac;
+
+import static com.android.systemui.statusbar.hvac.AnimatedTemperatureView.isHorizontal;
+import static com.android.systemui.statusbar.hvac.AnimatedTemperatureView.isLeft;
+
+import android.annotation.NonNull;
+import android.view.animation.AccelerateDecelerateInterpolator;
+import android.view.animation.AlphaAnimation;
+import android.view.animation.Animation;
+import android.view.animation.AnimationSet;
+import android.view.animation.DecelerateInterpolator;
+import android.view.animation.RotateAnimation;
+import android.view.animation.TranslateAnimation;
+import android.widget.TextSwitcher;
+
+/**
+ * Controls animating TemperatureView's text
+ */
+class TemperatureTextAnimator {
+
+ private static final DecelerateInterpolator DECELERATE_INTERPOLATOR =
+ new DecelerateInterpolator();
+ private static final AccelerateDecelerateInterpolator ACCELERATE_DECELERATE_INTERPOLATOR =
+ new AccelerateDecelerateInterpolator();
+
+ private static final int ROTATION_DEGREES = 15;
+ private static final int DURATION_MILLIS = 200;
+
+ private AnimatedTemperatureView mParent;
+ private final TextSwitcher mTextSwitcher;
+ private final String mTempFormat;
+ private final int mPivotOffset;
+ private final CharSequence mMinText;
+ private final CharSequence mMaxText;
+
+ private Animation mTextInAnimationUp;
+ private Animation mTextOutAnimationUp;
+ private Animation mTextInAnimationDown;
+ private Animation mTextOutAnimationDown;
+ private Animation mTextFadeInAnimation;
+ private Animation mTextFadeOutAnimation;
+
+ private float mLastTemp = Float.NaN;
+
+ TemperatureTextAnimator(AnimatedTemperatureView parent, TextSwitcher textSwitcher,
+ String tempFormat, int pivotOffset,
+ CharSequence minText, CharSequence maxText) {
+ mParent = parent;
+ mTextSwitcher = textSwitcher;
+ mTempFormat = tempFormat;
+ mPivotOffset = pivotOffset;
+ mMinText = minText;
+ mMaxText = maxText;
+
+ mParent.addOnLayoutChangeListener(
+ (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) ->
+ setupAnimations(mParent.getGravity()));
+ }
+
+ void setTemp(float temp) {
+ if (Float.isNaN(temp)) {
+ mTextSwitcher.setInAnimation(mTextFadeInAnimation);
+ mTextSwitcher.setOutAnimation(mTextFadeOutAnimation);
+ mTextSwitcher.setText("--");
+ mLastTemp = temp;
+ return;
+ }
+ boolean isMinValue = mParent.isMinValue(temp);
+ boolean isMaxValue = mParent.isMaxValue(temp);
+ if (Float.isNaN(mLastTemp)) {
+ mTextSwitcher.setInAnimation(mTextFadeInAnimation);
+ mTextSwitcher.setOutAnimation(mTextFadeOutAnimation);
+ } else if (!isMinValue && (isMaxValue || temp > mLastTemp)) {
+ mTextSwitcher.setInAnimation(mTextInAnimationUp);
+ mTextSwitcher.setOutAnimation(mTextOutAnimationUp);
+ } else {
+ mTextSwitcher.setInAnimation(mTextInAnimationDown);
+ mTextSwitcher.setOutAnimation(mTextOutAnimationDown);
+ }
+ CharSequence text;
+ if (isMinValue) {
+ text = mMinText;
+ } else if (isMaxValue) {
+ text = mMaxText;
+ } else {
+ text = String.format(mTempFormat, temp);
+ }
+ mTextSwitcher.setText(text);
+ mLastTemp = temp;
+ }
+
+ private void setupAnimations(int gravity) {
+ mTextFadeInAnimation = createFadeAnimation(true);
+ mTextFadeOutAnimation = createFadeAnimation(false);
+ if (!isHorizontal(gravity)) {
+ mTextInAnimationUp = createTranslateFadeAnimation(true, true);
+ mTextOutAnimationUp = createTranslateFadeAnimation(false, true);
+ mTextInAnimationDown = createTranslateFadeAnimation(true, false);
+ mTextOutAnimationDown = createTranslateFadeAnimation(false, false);
+ } else {
+ boolean isLeft = isLeft(gravity, mTextSwitcher.getLayoutDirection());
+ mTextInAnimationUp = createRotateFadeAnimation(true, isLeft, true);
+ mTextOutAnimationUp = createRotateFadeAnimation(false, isLeft, true);
+ mTextInAnimationDown = createRotateFadeAnimation(true, isLeft, false);
+ mTextOutAnimationDown = createRotateFadeAnimation(false, isLeft, false);
+ }
+ }
+
+ @NonNull
+ private Animation createFadeAnimation(boolean in) {
+ AnimationSet set = new AnimationSet(true);
+ AlphaAnimation alphaAnimation = new AlphaAnimation(in ? 0 : 1, in ? 1 : 0);
+ alphaAnimation.setDuration(DURATION_MILLIS);
+ set.addAnimation(new RotateAnimation(0, 0)); // Undo any previous rotation
+ set.addAnimation(alphaAnimation);
+ return set;
+ }
+
+ @NonNull
+ private Animation createTranslateFadeAnimation(boolean in, boolean up) {
+ AnimationSet set = new AnimationSet(true);
+ set.setInterpolator(ACCELERATE_DECELERATE_INTERPOLATOR);
+ set.setDuration(DURATION_MILLIS);
+ int fromYDelta = in ? (up ? 1 : -1) : 0;
+ int toYDelta = in ? 0 : (up ? -1 : 1);
+ set.addAnimation(
+ new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, 0,
+ Animation.RELATIVE_TO_SELF, fromYDelta, Animation.RELATIVE_TO_SELF,
+ toYDelta));
+ set.addAnimation(new AlphaAnimation(in ? 0 : 1, in ? 1 : 0));
+ return set;
+ }
+
+ @NonNull
+ private Animation createRotateFadeAnimation(boolean in, boolean isLeft, boolean up) {
+ AnimationSet set = new AnimationSet(true);
+ set.setInterpolator(DECELERATE_INTERPOLATOR);
+ set.setDuration(DURATION_MILLIS);
+
+ float degrees = isLeft == up ? -ROTATION_DEGREES : ROTATION_DEGREES;
+ int pivotX = isLeft ? -mPivotOffset : mParent.getWidth() + mPivotOffset;
+ set.addAnimation(
+ new RotateAnimation(in ? -degrees : 0f, in ? 0f : degrees, Animation.ABSOLUTE,
+ pivotX, Animation.ABSOLUTE, 0f));
+ set.addAnimation(new AlphaAnimation(in ? 0 : 1, in ? 1 : 0));
+ return set;
+ }
+}
diff --git a/packages/CarrierDefaultApp/res/values-el/strings.xml b/packages/CarrierDefaultApp/res/values-el/strings.xml
index a13f634..016e68f 100644
--- a/packages/CarrierDefaultApp/res/values-el/strings.xml
+++ b/packages/CarrierDefaultApp/res/values-el/strings.xml
@@ -6,7 +6,7 @@
<string name="portal_notification_id" msgid="5155057562457079297">"Τα δεδομένα κινητής τηλεφωνίας εξαντλήθηκαν"</string>
<string name="no_data_notification_id" msgid="668400731803969521">"Τα δεδομένα κινητής τηλεφωνίας έχουν απενεργοποιηθεί"</string>
<string name="portal_notification_detail" msgid="2295729385924660881">"Πατήστε για να επισκεφτείτε τον ιστότοπο %s"</string>
- <string name="no_data_notification_detail" msgid="3112125343857014825">"Επικοινωνήστε με τον παροχέα υπηρεσιών σας %s"</string>
+ <string name="no_data_notification_detail" msgid="3112125343857014825">"Επικοινωνήστε με τον πάροχο υπηρεσιών σας %s"</string>
<string name="no_mobile_data_connection_title" msgid="7449525772416200578">"Δεν υπάρχει σύνδεση δεδομένων κινητής τηλεφωνίας"</string>
<string name="no_mobile_data_connection" msgid="544980465184147010">"Προσθήκη δεδομένων ή προγράμματος περιαγωγής μέσω του %s"</string>
<string name="mobile_data_status_notification_channel_name" msgid="833999690121305708">"Κατάσταση δεδομένων κινητής τηλεφωνίας"</string>
diff --git a/packages/EasterEgg/Android.bp b/packages/EasterEgg/Android.bp
new file mode 100644
index 0000000..43ed810
--- /dev/null
+++ b/packages/EasterEgg/Android.bp
@@ -0,0 +1,33 @@
+//
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+android_app {
+ // the build system in pi-dev can't quite handle R.java in kt
+ // so we will have a mix of java and kotlin files
+ srcs: ["src/**/*.java", "src/**/*.kt"],
+
+ resource_dirs: ["res"],
+
+ name: "EasterEgg",
+
+ certificate: "platform",
+
+ sdk_version: "current",
+
+ optimize: {
+ enabled: false,
+ }
+}
diff --git a/packages/EasterEgg/Android.mk b/packages/EasterEgg/Android.mk
deleted file mode 100644
index 8fa4d6a..0000000
--- a/packages/EasterEgg/Android.mk
+++ /dev/null
@@ -1,29 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
- jsr305
-
-LOCAL_STATIC_ANDROID_LIBRARIES := \
- androidx.legacy_legacy-support-v4 \
- androidx.legacy_legacy-support-v13 \
- androidx.dynamicanimation_dynamicanimation \
- androidx.recyclerview_recyclerview \
- androidx.preference_preference \
- androidx.appcompat_appcompat \
- androidx.legacy_legacy-preference-v14
-
-LOCAL_USE_AAPT2 := true
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-
-LOCAL_PACKAGE_NAME := EasterEgg
-LOCAL_PRIVATE_PLATFORM_APIS := true
-LOCAL_CERTIFICATE := platform
-
-include $(BUILD_PACKAGE)
-
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/packages/EasterEgg/AndroidManifest.xml b/packages/EasterEgg/AndroidManifest.xml
index e8ea32b..c7dd40d 100644
--- a/packages/EasterEgg/AndroidManifest.xml
+++ b/packages/EasterEgg/AndroidManifest.xml
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
-Copyright (C) 2016 The Android Open Source Project
+ Copyright (C) 2018 The Android Open Source Project
- Licensed under the Apache License, Version 2.0 (the "License");
+ Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
@@ -15,85 +15,28 @@
limitations under the License.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.egg"
- android:versionCode="1"
- android:versionName="1.0">
+ package="com.android.egg"
+ android:versionCode="1"
+ android:versionName="1.0">
- <uses-sdk android:minSdkVersion="26" />
+ <uses-sdk android:minSdkVersion="28" />
- <uses-permission android:name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME" />
- <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+ <application
+ android:icon="@drawable/icon"
+ android:label="@string/app_name">
- <application android:label="@string/app_name" android:icon="@drawable/icon">
-
- <activity android:name=".octo.Ocquarium"
- android:theme="@android:style/Theme.DeviceDefault.NoActionBar.Fullscreen"
- android:label="@string/app_name">
+ <activity
+ android:name=".paint.PaintActivity"
+ android:configChanges="orientation|keyboardHidden|screenSize|uiMode"
+ android:label="@string/app_name"
+ android:theme="@style/AppTheme">
<intent-filter>
- <action android:name="android.intent.action.MAIN"/>
+ <action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.DEFAULT" />
+ <!--<category android:name="android.intent.category.LAUNCHER" />-->
<category android:name="com.android.internal.category.PLATLOGO" />
</intent-filter>
</activity>
-
- <!-- Android N lives on inside Android O... -->
-
- <!-- Long press the QS tile to get here -->
- <activity android:name=".neko.NekoLand"
- android:theme="@android:style/Theme.Material.NoActionBar"
- android:label="@string/app_name">
- <intent-filter>
- <action android:name="android.service.quicksettings.action.QS_TILE_PREFERENCES" />
- <action android:name="android.intent.action.MAIN" />
- </intent-filter>
- </activity>
-
- <!-- This is where the magic happens -->
- <service
- android:name=".neko.NekoService"
- android:enabled="true"
- android:permission="android.permission.BIND_JOB_SERVICE"
- android:exported="true" >
- </service>
-
- <!-- Used to show over lock screen -->
- <activity android:name=".neko.NekoLockedActivity"
- android:excludeFromRecents="true"
- android:theme="@android:style/Theme.Material.Light.Dialog.NoActionBar"
- android:showOnLockScreen="true" />
-
- <!-- Used to enable easter egg -->
- <activity android:name=".neko.NekoActivationActivity"
- android:excludeFromRecents="true"
- android:theme="@android:style/Theme.NoDisplay"
- >
- <intent-filter>
- <action android:name="android.intent.action.MAIN"/>
- <category android:name="android.intent.category.DEFAULT" />
- </intent-filter>
- </activity>
-
- <!-- The quick settings tile, disabled by default -->
- <service
- android:name=".neko.NekoTile"
- android:permission="android.permission.BIND_QUICK_SETTINGS_TILE"
- android:icon="@drawable/stat_icon"
- android:enabled="false"
- android:label="@string/default_tile_name">
- <intent-filter>
- <action android:name="android.service.quicksettings.action.QS_TILE" />
- </intent-filter>
- </service>
-
- <!-- FileProvider for sending pictures -->
- <provider
- android:name="androidx.core.content.FileProvider"
- android:authorities="com.android.egg.fileprovider"
- android:grantUriPermissions="true"
- android:exported="false">
- <meta-data
- android:name="android.support.FILE_PROVIDER_PATHS"
- android:resource="@xml/filepaths" />
- </provider>
</application>
+
</manifest>
diff --git a/packages/EasterEgg/res/values/dimens.xml b/packages/EasterEgg/res/color-night/toolbar_icon_color.xml
similarity index 63%
copy from packages/EasterEgg/res/values/dimens.xml
copy to packages/EasterEgg/res/color-night/toolbar_icon_color.xml
index e9dcebd..c0a8152 100644
--- a/packages/EasterEgg/res/values/dimens.xml
+++ b/packages/EasterEgg/res/color-night/toolbar_icon_color.xml
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
-Copyright (C) 2016 The Android Open Source Project
+ Copyright (C) 2018 The Android Open Source Project
- Licensed under the Apache License, Version 2.0 (the "License");
+ Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
@@ -14,6 +14,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<resources xmlns:android="http://schemas.android.com/apk/res/android">
- <dimen name="neko_display_size">64dp</dimen>
-</resources>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:color="#FFFF3333" android:state_selected="true" />
+ <item android:color="#FFFFFFFF" />
+</selector>
\ No newline at end of file
diff --git a/packages/EasterEgg/res/values/dimens.xml b/packages/EasterEgg/res/color/toolbar_icon_color.xml
similarity index 71%
rename from packages/EasterEgg/res/values/dimens.xml
rename to packages/EasterEgg/res/color/toolbar_icon_color.xml
index e9dcebd..d3247e4 100644
--- a/packages/EasterEgg/res/values/dimens.xml
+++ b/packages/EasterEgg/res/color/toolbar_icon_color.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
-Copyright (C) 2016 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.
@@ -14,6 +14,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<resources xmlns:android="http://schemas.android.com/apk/res/android">
- <dimen name="neko_display_size">64dp</dimen>
-</resources>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:color="#FFCC0000" android:state_selected="true" />
+ <item android:color="#FF000000" />
+</selector>
\ No newline at end of file
diff --git a/packages/EasterEgg/res/drawable/back.xml b/packages/EasterEgg/res/drawable/back.xml
deleted file mode 100644
index b55d65c..0000000
--- a/packages/EasterEgg/res/drawable/back.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<!--
-Copyright (C) 2016 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="48dp"
- android:height="48dp"
- android:viewportWidth="48.0"
- android:viewportHeight="48.0">
- <path android:name="back" android:fillColor="#FF000000" android:pathData="M37.1,22c-1.1,0 -1.9,0.8 -1.9,1.9v5.6c0,1.1 0.8,1.9 1.9,1.9H39v-1.9v-5.6V22H37.1z"/>
-</vector>
diff --git a/packages/EasterEgg/res/drawable/belly.xml b/packages/EasterEgg/res/drawable/belly.xml
deleted file mode 100644
index 8b0e9af..0000000
--- a/packages/EasterEgg/res/drawable/belly.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<!--
-Copyright (C) 2016 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="48dp"
- android:height="48dp"
- android:viewportWidth="48.0"
- android:viewportHeight="48.0">
- <path android:name="belly" android:fillColor="#FF000000" android:pathData="M20.5,25c-3.6,0 -6.5,2.9 -6.5,6.5V38h13v-6.5C27,27.9 24.1,25 20.5,25z"/>
-</vector>
diff --git a/packages/EasterEgg/res/drawable/body.xml b/packages/EasterEgg/res/drawable/body.xml
deleted file mode 100644
index 8608720..0000000
--- a/packages/EasterEgg/res/drawable/body.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<!--
-Copyright (C) 2016 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="48dp"
- android:height="48dp"
- android:viewportWidth="48.0"
- android:viewportHeight="48.0">
- <path android:name="body" android:fillColor="#FF000000" android:pathData="M9,20h30v18h-30z"/>
-</vector>
diff --git a/packages/EasterEgg/res/drawable/bowtie.xml b/packages/EasterEgg/res/drawable/bowtie.xml
deleted file mode 100644
index 33fa921..0000000
--- a/packages/EasterEgg/res/drawable/bowtie.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<!--
-Copyright (C) 2016 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="48dp"
- android:height="48dp"
- android:viewportWidth="48.0"
- android:viewportHeight="48.0">
- <path android:name="bowtie" android:fillColor="#FF000000" android:pathData="M29,16.8l-10,5l0,-5l10,5z"/>
-</vector>
diff --git a/packages/EasterEgg/res/drawable/cap.xml b/packages/EasterEgg/res/drawable/cap.xml
deleted file mode 100644
index d8b4cc5..0000000
--- a/packages/EasterEgg/res/drawable/cap.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<!--
-Copyright (C) 2016 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="48dp"
- android:height="48dp"
- android:viewportWidth="48.0"
- android:viewportHeight="48.0">
- <path android:name="cap" android:fillColor="#FF000000" android:pathData="M27.2,3.8c-1,-0.2 -2.1,-0.3 -3.2,-0.3s-2.1,0.1 -3.2,0.3c0.2,1.3 1.5,2.2 3.2,2.2C25.6,6.1 26.9,5.1 27.2,3.8z"/>
-</vector>
diff --git a/packages/EasterEgg/res/drawable/collar.xml b/packages/EasterEgg/res/drawable/collar.xml
deleted file mode 100644
index 5e4d0fd..0000000
--- a/packages/EasterEgg/res/drawable/collar.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<!--
-Copyright (C) 2016 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="48dp"
- android:height="48dp"
- android:viewportWidth="48.0"
- android:viewportHeight="48.0">
- <path android:name="collar" android:fillColor="#FF000000" android:pathData="M9,18.4h30v1.7h-30z"/>
-</vector>
diff --git a/packages/EasterEgg/res/drawable/face_spot.xml b/packages/EasterEgg/res/drawable/face_spot.xml
deleted file mode 100644
index a89fb4f..0000000
--- a/packages/EasterEgg/res/drawable/face_spot.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<!--
-Copyright (C) 2016 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="48dp"
- android:height="48dp"
- android:viewportWidth="48.0"
- android:viewportHeight="48.0">
- <path android:name="face_spot" android:fillColor="#FF000000" android:pathData="M19.5,15.2a4.5,3.2 0,1 0,9 0a4.5,3.2 0,1 0,-9 0z"/>
-</vector>
diff --git a/packages/EasterEgg/res/drawable/food_bits.xml b/packages/EasterEgg/res/drawable/food_bits.xml
deleted file mode 100644
index 1b2bb6f..0000000
--- a/packages/EasterEgg/res/drawable/food_bits.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-<!--
-Copyright (C) 2015 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="48dp"
- android:height="48dp"
- android:viewportWidth="48.0"
- android:viewportHeight="48.0">
- <path
- android:fillColor="#FF000000"
- android:pathData="M19.1,34l-3.5,1.3c-1,0.4,-2.2,-0.1,-2.6,-1.1l-1.2,-3c-0.4,-1,0.1,-2.2,1.1,-2.6l3.5,-1.3c1,-0.4,2.2,0.1,2.6,1.1l1.2,3 C20.6,32.4,20.1,33.6,19.1,34z"/>
- <path
- android:fillColor="#FF000000"
- android:pathData="M25.2,28.1L22.9,28c-0.8,0,-1.5,-0.7,-1.4,-1.6l0.1,-2c0,-0.8,0.7,-1.5,1.6,-1.4l2.4,0.1c0.8,0,1.5,0.7,1.4,1.6l-0.1,2 C26.8,27.5,26.1,28.1,25.2,28.1z"/>
- <path
- android:fillColor="#FF000000"
- android:pathData="M18.7,23.1L16.5,23c-0.5,0,-0.9,-0.4,-0.8,-0.9l0.1,-2.2c0,-0.5,0.4,-0.9,0.9,-0.8l2.2,0.1c0.5,0,0.9,0.4,0.8,0.9 l-0.1,2.2C19.6,22.8,19.2,23.1,18.7,23.1z"/>
- <path
- android:fillColor="#FF000000"
- android:pathData="M32.2,35.3l-3.6,-1.8c-1,-0.5,-1.4,-1.7,-0.9,-2.7l1.6,-3.1c0.5,-1,1.7,-1.4,2.7,-0.9l3.6,1.8c1,0.5,1.4,1.7,0.9,2.7 l-1.6,3.1C34.4,35.4,33.2,35.7,32.2,35.3z"/>
-</vector>
diff --git a/packages/EasterEgg/res/drawable/food_chicken.xml b/packages/EasterEgg/res/drawable/food_chicken.xml
deleted file mode 100644
index 95b2fb5..0000000
--- a/packages/EasterEgg/res/drawable/food_chicken.xml
+++ /dev/null
@@ -1,39 +0,0 @@
-<!--
-Copyright (C) 2015 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="48dp"
- android:height="48dp"
- android:viewportWidth="48.0"
- android:viewportHeight="48.0">
- <path
- android:fillColor="#FF000000"
- android:pathData="M9,12v14h10V11H9z M11.7,16.3c-0.7,0,-1.3,-0.6,-1.3,-1.3s0.6,-1.3,1.3,-1.3S13,14.3,13,15S12.4,16.3,11.7,16.3z"/>
- <path
- android:fillColor="#FF000000"
- android:pathData="M5.7,20.1l1.6,-3.0l-1.6,-3.0l4.4,3.0z"/>
- <path
- android:fillColor="#FF000000"
- android:pathData="M19.0,6.0l-2.3,2.3l-2.7,-2.6l-2.7,2.6l-2.3,-2.3l0.0,4.0l10.0,0.0z"/>
- <path
- android:fillColor="#FF000000"
- android:pathData="M9,25c0,8.3,6.7,15,15,15s15,-6.7,15,-15H9z M29.9,31.5h-11v-1h12L29.9,31.5z M31.9,29.5h-13v-1h14L31.9,29.5z M33.9,27.5 h-15v-1h16L33.9,27.5z"/>
- <path
- android:fillColor="#FF000000"
- android:pathData="M27.0,38.6h2.0v6.0h-2.0z"/>
- <path
- android:fillColor="#FF000000"
- android:pathData="M17.4,44.6l-2.1999998,0.0l4.4000006,-6.0l2.1999989,0.0z"/>
-</vector>
diff --git a/packages/EasterEgg/res/drawable/food_cookie.xml b/packages/EasterEgg/res/drawable/food_cookie.xml
deleted file mode 100644
index 74dd134..0000000
--- a/packages/EasterEgg/res/drawable/food_cookie.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.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportWidth="24"
- android:viewportHeight="24">
- <group>
- <path
- android:fillColor="#55FFFFFF"
- android:fillType="evenOdd"
- android:pathData="M5.71 18.29A8.99 8.99 0 0 0 22 13c0-3-1.46-5.65-3.71-7.29A8.99 8.99 0 0 0 2 11c0 3 1.46 5.65 3.71 7.29z"/>
- <path
- android:fillColor="#FFFFFFFF"
- android:fillType="evenOdd"
- android:pathData="M7.25 19.18A8.5 8.5 0 0 0 19.19 7.24 9 9 0 0 1 7.24 19.19z"/>
- <path
- android:fillColor="#55FFFFFF"
- android:pathData="M10.5 3a0.5 0.5 0 1 1 1 0v2.05a0.5 0.5 0 1 1-1 0V3zm3.1 0.42a0.5 0.5 0 0 1 0.93 0.39l-0.8 1.88A0.5 0.5 0 1 1 12.8 5.3l0.8-1.88zm2.7 1.57a0.5 0.5 0 1 1 0.71 0.7l-1.45 1.46a0.5 0.5 0 0 1-0.7-0.71l1.44-1.45zm1.9 2.5a0.5 0.5 0 0 1 0.38 0.92l-1.9 0.77a0.5 0.5 0 0 1-0.37-0.93l1.9-0.77zM19 10.5a0.5 0.5 0 1 1 0 1h-2.05a0.5 0.5 0 0 1 0-1H19zm-0.42 3.1a0.5 0.5 0 0 1-0.39 0.93l-1.88-0.8a0.5 0.5 0 1 1 0.39-0.92l1.88 0.8zm-1.57 2.7a0.5 0.5 0 1 1-0.7 0.71l-1.46-1.45a0.5 0.5 0 0 1 0.71-0.7l1.45 1.44zm-2.5 1.9a0.5 0.5 0 1 1-0.92 0.38l-0.77-1.9a0.5 0.5 0 0 1 0.93-0.37l0.77 1.9zM11.5 19a0.5 0.5 0 1 1-1 0v-2.05a0.5 0.5 0 0 1 1 0V19zm-3.1-0.42a0.5 0.5 0 0 1-0.93-0.39l0.8-1.88A0.5 0.5 0 0 1 9.2 16.7l-0.8 1.88zm-2.7-1.57a0.5 0.5 0 1 1-0.71-0.7l1.45-1.46a0.5 0.5 0 0 1 0.7 0.71L5.7 17.01zm-1.9-2.48a0.5 0.5 0 0 1-0.38-0.92l1.88-0.8a0.5 0.5 0 0 1 0.4 0.92l-1.9 0.8zM3 11.5a0.5 0.5 0 1 1 0-1h2.05a0.5 0.5 0 1 1 0 1H3zm0.42-3.1A0.5 0.5 0 0 1 3.8 7.46l1.88 0.8A0.5 0.5 0 1 1 5.3 9.2L3.42 8.4zm1.57-2.7a0.5 0.5 0 1 1 0.7-0.71l1.46 1.45a0.5 0.5 0 0 1-0.71 0.7L4.99 5.7zm2.5-1.9A0.5 0.5 0 0 1 8.4 3.41l0.77 1.9a0.5 0.5 0 0 1-0.93 0.37L7.48 3.8z"/>
- </group>
-</vector>
\ No newline at end of file
diff --git a/packages/EasterEgg/res/drawable/food_dish.xml b/packages/EasterEgg/res/drawable/food_dish.xml
deleted file mode 100644
index 3fff6a9..0000000
--- a/packages/EasterEgg/res/drawable/food_dish.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<!--
-Copyright (C) 2015 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="48dp"
- android:height="48dp"
- android:viewportWidth="48.0"
- android:viewportHeight="48.0">
- <path
- android:fillColor="#FF000000"
- android:pathData="M24,13.8C11.3,13.8,1,18.3,1,24c0,5.7,10.3,10.2,23,10.2S47,29.7,47,24C47,18.3,36.7,13.8,24,13.8z M33.7,26.6 c1.1,-0.6,1.8,-1.3,1.8,-2c0,-2.1,-5.2,-3.8,-11.7,-3.8s-11.7,1.7,-11.7,3.8c0,0.6,0.4,1.2,1.2,1.7c-1.7,-0.8,-2.8,-1.7,-2.8,-2.8 c0,-2.5,6,-4.5,13.4,-4.5s13.4,2,13.4,4.5C37.4,24.7,36,25.8,33.7,26.6z"/>
-</vector>
diff --git a/packages/EasterEgg/res/drawable/food_donut.xml b/packages/EasterEgg/res/drawable/food_donut.xml
deleted file mode 100644
index eaf831e..0000000
--- a/packages/EasterEgg/res/drawable/food_donut.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<!--
-Copyright (C) 2015 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="48dp"
- android:height="48dp"
- android:viewportWidth="48.0"
- android:viewportHeight="48.0">
- <path
- android:fillColor="#FF000000"
- android:pathData="M24,4.5c-10.5,0,-19,8.5,-19,19s8.5,19,19,19s19,-8.5,19,-19S34.5,4.5,24,4.5z M35.2,15.5l1.6,-1.1 c0.3,-0.2,0.6,-0.1,0.8,0.1l0.1,0.1c0.2,0.3,0.1,0.6,-0.1,0.8l-1.6,1.1c-0.3,0.2,-0.6,0.1,-0.8,-0.1l-0.1,-0.1 C34.9,16.1,35,15.7,35.2,15.5z M32.7,10.7c0,-0.3,0.3,-0.5,0.6,-0.5l0.1,0c0.3,0,0.5,0.3,0.5,0.6l-0.2,2c0,0.3,-0.3,0.5,-0.6,0.5l-0.1,0 c-0.3,0,-0.5,-0.3,-0.5,-0.6L32.7,10.7z M31.7,15.1l1.5,-0.2c0.2,0,0.5,0.1,0.5,0.4l0,0.1c0,0.2,-0.1,0.5,-0.4,0.5l-1.5,0.2 c-0.2,0,-0.5,-0.1,-0.5,-0.4l0,-0.1C31.3,15.4,31.5,15.2,31.7,15.1z M28.8,10.6l1.6,-1.1c0.3,-0.2,0.6,-0.1,0.8,0.1l0.1,0.1 c0.2,0.3,0.1,0.6,-0.1,0.8l-1.6,1.1c-0.3,0.2,-0.6,0.1,-0.8,-0.1l-0.1,-0.1C28.4,11.1,28.5,10.8,28.8,10.6z M25.8,6 c0,-0.3,0.3,-0.5,0.6,-0.5l0.1,0c0.3,0,0.5,0.3,0.5,0.6l-0.2,2c0,0.3,-0.3,0.5,-0.6,0.5l-0.1,0c-0.3,0,-0.5,-0.3,-0.5,-0.6L25.8,6z M20.7,6.5l1.9,-0.7c0.3,-0.1,0.6,0,0.7,0.3l0,0.1c0.1,0.3,0,0.6,-0.3,0.7l-1.9,0.7c-0.3,0.1,-0.6,0,-0.7,-0.3l0,-0.1 C20.3,6.9,20.4,6.6,20.7,6.5z M19.9,10.9l1.5,-0.2c0.2,0,0.5,0.1,0.5,0.4l0,0.1c0,0.2,-0.1,0.5,-0.4,0.5l-1.5,0.2 c-0.2,0,-0.5,-0.1,-0.5,-0.4l0,-0.1C19.5,11.1,19.7,10.9,19.9,10.9z M16,10.9L16,10.9c0.2,-0.3,0.4,-0.4,0.6,-0.3l1.3,0.7 c0.2,0.1,0.3,0.4,0.2,0.6L18,12c-0.1,0.2,-0.4,0.3,-0.6,0.2l-1.3,-0.7C15.9,11.4,15.8,11.1,16,10.9z M15.8,18.5c0.2,0,0.4,0.1,0.5,0.4 l0,0.1c0,0.2,-0.1,0.4,-0.4,0.5l-1.5,0.2c-0.2,0,-0.4,-0.1,-0.5,-0.4l0,-0.1c0,-0.2,0.1,-0.4,0.4,-0.5L15.8,18.5z M14,21.8l-1.6,1.1 c-0.3,0.2,-0.6,0.1,-0.8,-0.1l-0.1,-0.1c-0.2,-0.3,-0.1,-0.6,0.1,-0.8l1.6,-1.1c0.3,-0.2,0.6,-0.1,0.8,0.1l0.1,0.1 C14.3,21.3,14.3,21.6,14,21.8z M12.4,12L12.4,12c0.3,-0.2,0.5,-0.2,0.7,-0.1l1,1.1c0.2,0.2,0.2,0.4,0,0.6L14,13.7 c-0.2,0.2,-0.4,0.2,-0.6,0l-1,-1.1C12.2,12.4,12.2,12.1,12.4,12z M8.3,24.5c0,0.3,-0.3,0.5,-0.6,0.5l-0.1,0c-0.3,0,-0.5,-0.3,-0.5,-0.6 l0.2,-2c0,-0.3,0.3,-0.5,0.6,-0.5l0.1,0c0.3,0,0.5,0.3,0.5,0.6L8.3,24.5z M8.5,16.2v-0.1c0,-0.3,0.2,-0.6,0.6,-0.6h2 c0.3,0,0.6,0.2,0.6,0.6v0.1c0,0.3,-0.2,0.6,-0.6,0.6H9C8.7,16.7,8.5,16.5,8.5,16.2z M10.3,20.7c-0.3,0.2,-0.6,0.1,-0.8,-0.1l-0.1,-0.1 c-0.2,-0.3,-0.1,-0.6,0.1,-0.8l1.6,-1.1c0.3,-0.2,0.6,-0.1,0.8,0.1l0.1,0.1c0.2,0.3,0.1,0.6,-0.1,0.8L10.3,20.7z M11.3,28.3l0,-0.1 c-0.1,-0.3,0,-0.6,0.3,-0.7l1.9,-0.7c0.3,-0.1,0.6,0,0.7,0.3l0,0.1c0.1,0.3,0,0.6,-0.3,0.7L12,28.6C11.7,28.7,11.4,28.6,11.3,28.3z M14.4,33c0,0.2,-0.2,0.4,-0.4,0.4h-1.5c-0.2,0,-0.4,-0.2,-0.4,-0.4v-0.1c0,-0.2,0.2,-0.4,0.4,-0.4H14c0.2,0,0.4,0.2,0.4,0.4V33z M17.9,35.2 l-1.6,1.1c-0.3,0.2,-0.6,0.1,-0.8,-0.1l-0.1,-0.1c-0.2,-0.3,-0.1,-0.6,0.1,-0.8l1.6,-1.1c0.3,-0.2,0.6,-0.1,0.8,0.1l0.1,0.1 C18.2,34.7,18.2,35.1,17.9,35.2z M20.7,33.8l-0.1,0.1c-0.1,0.3,-0.5,0.4,-0.8,0.2l-1.7,-1c-0.3,-0.1,-0.4,-0.5,-0.2,-0.8l0.1,-0.1 c0.1,-0.3,0.5,-0.4,0.8,-0.2l1.7,1C20.7,33.2,20.8,33.5,20.7,33.8z M17.5,23.5c0,-3.6,2.9,-6.5,6.5,-6.5s6.5,2.9,6.5,6.5 c0,3.6,-2.9,6.5,-6.5,6.5S17.5,27.1,17.5,23.5z M27.4,35.7l-1.9,0.7c-0.3,0.1,-0.6,0,-0.7,-0.3l0,-0.1c-0.1,-0.3,0,-0.6,0.3,-0.7l1.9,-0.7 c0.3,-0.1,0.6,0,0.7,0.3l0,0.1C27.9,35.3,27.7,35.6,27.4,35.7z M29.7,32.7l-1.4,0.5c-0.2,0.1,-0.5,0,-0.5,-0.3l0,-0.1 c-0.1,-0.2,0,-0.5,0.3,-0.5l1.4,-0.5c0.2,-0.1,0.5,0,0.5,0.3l0,0.1C30,32.3,29.9,32.6,29.7,32.7z M32.8,35.5l-0.1,0.1 c-0.1,0.3,-0.5,0.4,-0.8,0.2l-1.7,-1c-0.3,-0.1,-0.4,-0.5,-0.2,-0.8l0.1,-0.1c0.1,-0.3,0.5,-0.4,0.8,-0.2l1.7,1C32.8,34.9,32.9,35.2,32.8,35.5z M33.7,30.9c0,0.2,-0.2,0.4,-0.5,0.4l-0.1,0c-0.2,0,-0.4,-0.2,-0.4,-0.5l0.1,-1.5c0,-0.2,0.2,-0.4,0.5,-0.4l0.1,0c0.2,0,0.4,0.2,0.4,0.5 L33.7,30.9z M34.5,26.5l-1.3,0.9c-0.2,0.1,-0.5,0.1,-0.6,-0.1l-0.1,-0.1c-0.1,-0.2,-0.1,-0.5,0.1,-0.6l1.3,-0.9c0.2,-0.1,0.5,-0.1,0.6,0.1 l0.1,0.1C34.8,26.1,34.7,26.3,34.5,26.5z M35.6,20.6l-1.7,-1c-0.3,-0.1,-0.4,-0.5,-0.2,-0.8l0.1,-0.1c0.1,-0.3,0.5,-0.4,0.8,-0.2l1.7,1 c0.3,0.1,0.4,0.5,0.2,0.8l-0.1,0.1C36.2,20.6,35.8,20.7,35.6,20.6z M38.6,27.1l-1.6,1.1c-0.3,0.2,-0.6,0.1,-0.8,-0.1L36.1,28 c-0.2,-0.3,-0.1,-0.6,0.1,-0.8l1.6,-1.1c0.3,-0.2,0.6,-0.1,0.8,0.1l0.1,0.1C38.9,26.6,38.8,27,38.6,27.1z M39,19.4l-1.5,0.2 c-0.2,0,-0.5,-0.1,-0.5,-0.4l0,-0.1c0,-0.2,0.1,-0.5,0.4,-0.5l1.5,-0.2c0.2,0,0.5,0.1,0.5,0.4l0,0.1C39.4,19.1,39.2,19.3,39,19.4z"/>
-</vector>
diff --git a/packages/EasterEgg/res/drawable/food_sysuituna.xml b/packages/EasterEgg/res/drawable/food_sysuituna.xml
deleted file mode 100644
index 28cf4a2..0000000
--- a/packages/EasterEgg/res/drawable/food_sysuituna.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<!--
-Copyright (C) 2015 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="48dp"
- android:height="48dp"
- android:viewportWidth="48.0"
- android:viewportHeight="48.0">
- <path
- android:fillColor="#FF000000"
- android:pathData="M46,18.4l-5.8,4.6c-3.9,-3.2,-8.9,-5.6,-14.6,-6.3l1.2,-6l-7.3,5.9C12.5,17.2,6.4,20,2,24.3l7.2,1.4L2,27 c4.3,4.2,10.4,7.1,17.3,7.6l3.1,2.5L22,34.8c7.1,0,13.5,-2.5,18.2,-6.5l5.8,4.6l-1.4,-7.2L46,18.4z M14.3,24.8l-0.6,0.6l-1.1,-1.1 l-1.1,1.1l-0.6,-0.6l1.1,-1.1l-1.1,-1.1l0.6,-0.6l1.1,1.1l1.1,-1.1l0.6,0.6l-1.1,1.1L14.3,24.8z M18.8,29.1c0.7,-0.8,1.1,-2.2,1.1,-3.8 c0,-1.6,-0.4,-3,-1.1,-3.8c1.1,0.5,1.9,2,1.9,3.8S19.9,28.5,18.8,29.1z M20.7,29.1c0.7,-0.8,1.1,-2.2,1.1,-3.8c0,-1.6,-0.4,-3,-1.1,-3.8 c1.1,0.5,1.9,2,1.9,3.8S21.8,28.5,20.7,29.1z"/>
-</vector>
diff --git a/packages/EasterEgg/res/drawable/foot1.xml b/packages/EasterEgg/res/drawable/foot1.xml
deleted file mode 100644
index 0d90859..0000000
--- a/packages/EasterEgg/res/drawable/foot1.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<!--
-Copyright (C) 2016 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="48dp"
- android:height="48dp"
- android:viewportWidth="48.0"
- android:viewportHeight="48.0">
- <path android:name="foot1" android:fillColor="#FF000000" android:pathData="M11.5,43m-2.5,0a2.5,2.5 0,1 1,5 0a2.5,2.5 0,1 1,-5 0"/>
-</vector>
diff --git a/packages/EasterEgg/res/drawable/foot2.xml b/packages/EasterEgg/res/drawable/foot2.xml
deleted file mode 100644
index 364ba0c..0000000
--- a/packages/EasterEgg/res/drawable/foot2.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<!--
-Copyright (C) 2016 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="48dp"
- android:height="48dp"
- android:viewportWidth="48.0"
- android:viewportHeight="48.0">
- <path android:name="foot2" android:fillColor="#FF000000" android:pathData="M18.5,43m-2.5,0a2.5,2.5 0,1 1,5 0a2.5,2.5 0,1 1,-5 0"/>
-</vector>
diff --git a/packages/EasterEgg/res/drawable/foot3.xml b/packages/EasterEgg/res/drawable/foot3.xml
deleted file mode 100644
index e3a512a..0000000
--- a/packages/EasterEgg/res/drawable/foot3.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<!--
-Copyright (C) 2016 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="48dp"
- android:height="48dp"
- android:viewportWidth="48.0"
- android:viewportHeight="48.0">
- <path android:name="foot3" android:fillColor="#FF000000" android:pathData="M29.5,43m-2.5,0a2.5,2.5 0,1 1,5 0a2.5,2.5 0,1 1,-5 0"/>
-</vector>
diff --git a/packages/EasterEgg/res/drawable/foot4.xml b/packages/EasterEgg/res/drawable/foot4.xml
deleted file mode 100644
index 66b78fa..0000000
--- a/packages/EasterEgg/res/drawable/foot4.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<!--
-Copyright (C) 2016 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="48dp"
- android:height="48dp"
- android:viewportWidth="48.0"
- android:viewportHeight="48.0">
- <path android:name="foot4" android:fillColor="#FF000000" android:pathData="M36.5,43m-2.5,0a2.5,2.5 0,1 1,5 0a2.5,2.5 0,1 1,-5 0"/>
-</vector>
diff --git a/packages/EasterEgg/res/drawable/head.xml b/packages/EasterEgg/res/drawable/head.xml
deleted file mode 100644
index df600a8..0000000
--- a/packages/EasterEgg/res/drawable/head.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<!--
-Copyright (C) 2016 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="48dp"
- android:height="48dp"
- android:viewportWidth="48.0"
- android:viewportHeight="48.0">
- <path android:name="head" android:fillColor="#FF000000" android:pathData="M9,18.5c0,-8.3 6.8,-15 15,-15s15,6.7 15,15H9z"/>
-</vector>
diff --git a/packages/EasterEgg/res/drawable/ic_clear.xml b/packages/EasterEgg/res/drawable/ic_clear.xml
new file mode 100644
index 0000000..489dcd2
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/ic_clear.xml
@@ -0,0 +1,27 @@
+<!--
+Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M19,6.41l-1.41,-1.41l-5.59,5.59l-5.59,-5.59l-1.41,1.41l5.59,5.59l-5.59,5.59l1.41,1.41l5.59,-5.59l5.59,5.59l1.41,-1.41l-5.59,-5.59z"
+ android:strokeWidth="1"
+ android:fillColor="#000000"
+ android:fillType="nonZero"
+ android:strokeColor="#00000000"/>
+</vector>
diff --git a/packages/EasterEgg/res/drawable/ic_close.xml b/packages/EasterEgg/res/drawable/ic_close.xml
deleted file mode 100644
index 60ea36b..0000000
--- a/packages/EasterEgg/res/drawable/ic_close.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<!--
- Copyright (C) 2016 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<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="#FFFFFFFF"
- android:pathData="M19.0,6.41L17.59,5.0 12.0,10.59 6.41,5.0 5.0,6.41 10.59,12.0 5.0,17.59 6.41,19.0 12.0,13.41 17.59,19.0 19.0,17.59 13.41,12.0z"/>
-</vector>
diff --git a/packages/EasterEgg/res/drawable/ic_dropper.xml b/packages/EasterEgg/res/drawable/ic_dropper.xml
new file mode 100644
index 0000000..2307309
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/ic_dropper.xml
@@ -0,0 +1,39 @@
+<!--
+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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M13.6789,5.6997L3,16.3784L3,20L4,21L7.6216,21L18.3004,10.3212L13.6789,5.6997ZM7,19L5,19L5,17L13.788,8.344L15.6561,10.212L7,19Z"
+ android:strokeWidth="1"
+ android:fillColor="#000000"
+ android:fillType="nonZero"
+ android:strokeColor="#00000000"/>
+ <path
+ android:pathData="M20.9983,2.4982L21.5018,3.0017C22.0876,3.5875 22.0876,4.5373 21.5018,5.1231L18.1231,8.5018C17.5373,9.0876 16.5875,9.0876 16.0017,8.5018L15.4982,7.9983C14.9124,7.4125 14.9124,6.4627 15.4982,5.8769L18.8769,2.4982C19.4627,1.9124 20.4125,1.9124 20.9983,2.4982Z"
+ android:strokeWidth="1"
+ android:fillColor="#000000"
+ android:fillType="evenOdd"
+ android:strokeColor="#00000000"/>
+ <path
+ android:pathData="M13.8284,3l7.0711,7.0711l-2.8284,2.8284l-7.0711,-7.0711z"
+ android:strokeWidth="1"
+ android:fillColor="#000000"
+ android:fillType="evenOdd"
+ android:strokeColor="#00000000"/>
+</vector>
diff --git a/packages/EasterEgg/res/drawable/ic_hourglass.xml b/packages/EasterEgg/res/drawable/ic_hourglass.xml
new file mode 100644
index 0000000..fe4b9c4
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/ic_hourglass.xml
@@ -0,0 +1,27 @@
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M12.5,11L16,7.5L16,4L8,4L8,7.5L11.5,11L11.5,13L8,16.5L8,20L16,20L16,16.5L12.5,13L12.5,11ZM6,2L18,2L18,8L17.99,8L18,8.01L14,12L18,16L17.99,16.01L18,16.01L18,22L6,22L6,16.01L6.01,16.01L6,16L10,12L6,8.01L6.01,8L6,8L6,2Z"
+ android:strokeWidth="1"
+ android:fillColor="#000000"
+ android:fillType="nonZero"
+ android:strokeColor="#00000000"/>
+</vector>
diff --git a/packages/EasterEgg/res/drawable/ic_share.xml b/packages/EasterEgg/res/drawable/ic_share.xml
deleted file mode 100644
index 8cebc7e..0000000
--- a/packages/EasterEgg/res/drawable/ic_share.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<!--
- Copyright (C) 2016 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<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="#FFFFFFFF"
- android:pathData="M18.0,16.08c-0.76,0.0 -1.4,0.3 -1.9,0.77L8.91,12.7c0.05,-0.2 0.09,-0.4 0.09,-0.7s-0.04,-0.47 -0.09,-0.7l7.05,-4.11c0.5,0.5 1.2,0.81 2.0,0.81 1.66,0.0 3.0,-1.34 3.0,-3.0s-1.34,-3.0 -3.0,-3.0 -3.0,1.34 -3.0,3.0c0.0,0.2 0.0,0.4 0.0,0.7L8.04,9.81C7.5,9.31 6.79,9.0 6.0,9.0c-1.66,0.0 -3.0,1.34 -3.0,3.0s1.34,3.0 3.0,3.0c0.79,0.0 1.5,-0.31 2.04,-0.81l7.12,4.16c0.0,0.21 0.0,0.43 0.0,0.65 0.0,1.61 1.31,2.92 2.92,2.92 1.61,0.0 2.92,-1.31 2.92,-2.92s-1.31,-2.92 -2.92,-2.92z"/>
-</vector>
diff --git a/packages/EasterEgg/res/drawable/icon.xml b/packages/EasterEgg/res/drawable/icon.xml
index 5ce9e51..2306b7b 100644
--- a/packages/EasterEgg/res/drawable/icon.xml
+++ b/packages/EasterEgg/res/drawable/icon.xml
@@ -1,7 +1,7 @@
<!--
-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");
+ Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
@@ -13,28 +13,7 @@
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="48dp"
- android:height="48dp"
- android:viewportWidth="48.0"
- android:viewportHeight="48.0">
- <path
- android:pathData="M25.0,25.0m-20.5,0.0a20.5,20.5,0,1,1,41.0,0.0a20.5,20.5,0,1,1,-41.0,0.0"
- android:fillAlpha="0.066"
- android:fillColor="#000000"/>
- <path
- android:pathData="M24.0,24.0m-20.0,0.0a20.0,20.0,0,1,1,40.0,0.0a20.0,20.0,0,1,1,-40.0,0.0"
- android:fillColor="#283593"/>
- <path
- android:pathData="M44,24.2010101 L33.9004889,14.101499 L14.101499,33.9004889 L24.2010101,44 C29.2525804,43.9497929 34.2887564,41.9975027 38.1431296,38.1431296 C41.9975027,34.2887564 43.9497929,29.2525804 44,24.2010101 Z"
- android:fillColor="#1a237e"/>
- <path
- android:pathData="M24.0,24.0m-14.0,0.0a14.0,14.0,0,1,1,28.0,0.0a14.0,14.0,0,1,1,-28.0,0.0"
- android:fillColor="#5c6bc0"/>
- <path
- android:pathData="M37.7829445,26.469236 L29.6578482,18.3441397 L18.3441397,29.6578482 L26.469236,37.7829445 C29.1911841,37.2979273 31.7972024,36.0037754 33.9004889,33.9004889 C36.0037754,31.7972024 37.2979273,29.1911841 37.7829445,26.469236 Z"
- android:fillColor="#3f51b5"/>
- <path
- android:pathData="M24.0,24.0m-8.0,0.0a8.0,8.0,0,1,1,16.0,0.0a8.0,8.0,0,1,1,-16.0,0.0"
- android:fillColor="#FFFFFF"/>
-</vector>
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+ <background android:drawable="@drawable/icon_bg"/>
+ <foreground android:drawable="@drawable/p"/>
+</adaptive-icon>
diff --git a/packages/EasterEgg/res/xml/filepaths.xml b/packages/EasterEgg/res/drawable/icon_bg.xml
similarity index 70%
rename from packages/EasterEgg/res/xml/filepaths.xml
rename to packages/EasterEgg/res/drawable/icon_bg.xml
index 2130025..c1553ce 100644
--- a/packages/EasterEgg/res/xml/filepaths.xml
+++ b/packages/EasterEgg/res/drawable/icon_bg.xml
@@ -1,8 +1,8 @@
<?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");
+ Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
@@ -14,6 +14,5 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<paths>
- <external-path name="cats" path="Pictures/Cats" />
-</paths>
\ No newline at end of file
+<color xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="#C5E1A5" />
\ No newline at end of file
diff --git a/packages/EasterEgg/res/drawable/left_ear.xml b/packages/EasterEgg/res/drawable/left_ear.xml
deleted file mode 100644
index 2b98736..0000000
--- a/packages/EasterEgg/res/drawable/left_ear.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<!--
-Copyright (C) 2016 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="48dp"
- android:height="48dp"
- android:viewportWidth="48.0"
- android:viewportHeight="48.0">
- <path android:name="left_ear" android:fillColor="#FF000000" android:pathData="M15.4,1l5.1000004,5.3l-6.3,2.8000002z"/>
-</vector>
diff --git a/packages/EasterEgg/res/drawable/left_ear_inside.xml b/packages/EasterEgg/res/drawable/left_ear_inside.xml
deleted file mode 100644
index 1d947ed..0000000
--- a/packages/EasterEgg/res/drawable/left_ear_inside.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<!--
-Copyright (C) 2016 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="48dp"
- android:height="48dp"
- android:viewportWidth="48.0"
- android:viewportHeight="48.0">
- <path android:name="left_ear_inside" android:fillColor="#FF000000" android:pathData="M15.4,1l3.5,6.2l-4.7,1.9z"/>
-</vector>
diff --git a/packages/EasterEgg/res/drawable/left_eye.xml b/packages/EasterEgg/res/drawable/left_eye.xml
deleted file mode 100644
index 4dde1b6..0000000
--- a/packages/EasterEgg/res/drawable/left_eye.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<!--
-Copyright (C) 2016 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="48dp"
- android:height="48dp"
- android:viewportWidth="48.0"
- android:viewportHeight="48.0">
- <path android:name="left_eye" android:fillColor="#FF000000" android:pathData="M20.5,11c0,1.7 -3,1.7 -3,0C17.5,9.3 20.5,9.3 20.5,11z"/>
-</vector>
diff --git a/packages/EasterEgg/res/drawable/leg1.xml b/packages/EasterEgg/res/drawable/leg1.xml
deleted file mode 100644
index d72c746..0000000
--- a/packages/EasterEgg/res/drawable/leg1.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<!--
-Copyright (C) 2016 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="48dp"
- android:height="48dp"
- android:viewportWidth="48.0"
- android:viewportHeight="48.0">
- <path android:name="leg1" android:fillColor="#FF000000" android:pathData="M9,37h5v6h-5z"/>
-</vector>
diff --git a/packages/EasterEgg/res/drawable/leg2.xml b/packages/EasterEgg/res/drawable/leg2.xml
deleted file mode 100644
index a772a87..0000000
--- a/packages/EasterEgg/res/drawable/leg2.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<!--
-Copyright (C) 2016 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="48dp"
- android:height="48dp"
- android:viewportWidth="48.0"
- android:viewportHeight="48.0">
- <path android:name="leg2" android:fillColor="#FF000000" android:pathData="M16,37h5v6h-5z"/>
-</vector>
diff --git a/packages/EasterEgg/res/drawable/leg2_shadow.xml b/packages/EasterEgg/res/drawable/leg2_shadow.xml
deleted file mode 100644
index b01bd69..0000000
--- a/packages/EasterEgg/res/drawable/leg2_shadow.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<!--
-Copyright (C) 2016 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="48dp"
- android:height="48dp"
- android:viewportWidth="48.0"
- android:viewportHeight="48.0">
- <path android:name="leg2_shadow" android:fillColor="#FF000000" android:pathData="M16,37h5v3h-5z"/>
-</vector>
diff --git a/packages/EasterEgg/res/drawable/leg3.xml b/packages/EasterEgg/res/drawable/leg3.xml
deleted file mode 100644
index d471236..0000000
--- a/packages/EasterEgg/res/drawable/leg3.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<!--
-Copyright (C) 2016 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="48dp"
- android:height="48dp"
- android:viewportWidth="48.0"
- android:viewportHeight="48.0">
- <path android:name="leg3" android:fillColor="#FF000000" android:pathData="M27,37h5v6h-5z"/>
-</vector>
diff --git a/packages/EasterEgg/res/drawable/leg4.xml b/packages/EasterEgg/res/drawable/leg4.xml
deleted file mode 100644
index e5868eb..0000000
--- a/packages/EasterEgg/res/drawable/leg4.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<!--
-Copyright (C) 2016 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="48dp"
- android:height="48dp"
- android:viewportWidth="48.0"
- android:viewportHeight="48.0">
- <path android:name="leg4" android:fillColor="#FF000000" android:pathData="M34,37h5v6h-5z"/>
-</vector>
diff --git a/packages/EasterEgg/res/drawable/mouth.xml b/packages/EasterEgg/res/drawable/mouth.xml
deleted file mode 100644
index ddcf2e8..0000000
--- a/packages/EasterEgg/res/drawable/mouth.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<!--
-Copyright (C) 2016 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="48dp"
- android:height="48dp"
- android:viewportWidth="48.0"
- android:viewportHeight="48.0">
- <path android:name="mouth"
- android:strokeColor="#FF000000"
- android:strokeWidth="1.2"
- android:strokeLineCap="round"
- android:pathData="M29,14.3c-0.4,0.8 -1.3,1.4 -2.3,1.4c-1.4,0 -2.7,-1.3 -2.7,-2.7
- M24,13c0,1.5 -1.2,2.7 -2.7,2.7c-1,0 -1.9,-0.5 -2.3,-1.4"/>
-</vector>
diff --git a/packages/EasterEgg/res/drawable/nose.xml b/packages/EasterEgg/res/drawable/nose.xml
deleted file mode 100644
index d403cd1..0000000
--- a/packages/EasterEgg/res/drawable/nose.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<!--
-Copyright (C) 2016 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="48dp"
- android:height="48dp"
- android:viewportWidth="48.0"
- android:viewportHeight="48.0">
- <path android:name="nose" android:fillColor="#FF000000" android:pathData="M25.2,13c0,1.3 -2.3,1.3 -2.3,0S25.2,11.7 25.2,13z"/>
-</vector>
diff --git a/packages/EasterEgg/res/drawable/octo_bg.xml b/packages/EasterEgg/res/drawable/octo_bg.xml
deleted file mode 100644
index 1e46cf4..0000000
--- a/packages/EasterEgg/res/drawable/octo_bg.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<shape xmlns:android="http://schemas.android.com/apk/res/android">
- <gradient android:angle="-90"
- android:startColor="#FF205090"
- android:endColor="#FF001040"
- android:type="linear"
- />
-</shape>
\ No newline at end of file
diff --git a/packages/EasterEgg/res/drawable/p.xml b/packages/EasterEgg/res/drawable/p.xml
new file mode 100644
index 0000000..596b782
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/p.xml
@@ -0,0 +1,33 @@
+<!--
+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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="108dp"
+ android:height="108dp"
+ android:viewportWidth="108"
+ android:viewportHeight="108">
+ <path
+ android:pathData="M49,65L54,65C60.075,65 65,60.075 65,54C65,47.925 60.075,43 54,43C47.925,43 43,47.925 43,54L43,108"
+ android:strokeWidth="16"
+ android:fillColor="#00000000"
+ android:strokeColor="#7CB342"
+ android:fillType="evenOdd"/>
+ <path
+ android:pathData="M51,65L54,65C60.075,65 65,60.075 65,54C65,47.925 60.075,43 54,43C47.925,43 43,47.925 43,54L43,108"
+ android:strokeWidth="8"
+ android:fillColor="#00000000"
+ android:strokeColor="#FFFFFF"
+ android:fillType="evenOdd"/>
+</vector>
diff --git a/packages/EasterEgg/res/drawable/right_ear.xml b/packages/EasterEgg/res/drawable/right_ear.xml
deleted file mode 100644
index b9fb4d1..0000000
--- a/packages/EasterEgg/res/drawable/right_ear.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<!--
-Copyright (C) 2016 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="48dp"
- android:height="48dp"
- android:viewportWidth="48.0"
- android:viewportHeight="48.0">
- <path android:name="right_ear" android:fillColor="#FF000000" android:pathData="M32.6,1l-5.0999985,5.3l6.299999,2.8000002z"/>
-</vector>
diff --git a/packages/EasterEgg/res/drawable/right_ear_inside.xml b/packages/EasterEgg/res/drawable/right_ear_inside.xml
deleted file mode 100644
index 86b6e34..0000000
--- a/packages/EasterEgg/res/drawable/right_ear_inside.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<!--
-Copyright (C) 2016 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="48dp"
- android:height="48dp"
- android:viewportWidth="48.0"
- android:viewportHeight="48.0">
-
- <path android:name="right_ear_inside" android:fillColor="#FF000000" android:pathData="M33.8,9.1l-4.7,-1.9l3.5,-6.2z"/>
-</vector>
diff --git a/packages/EasterEgg/res/drawable/right_eye.xml b/packages/EasterEgg/res/drawable/right_eye.xml
deleted file mode 100644
index a1871a6..0000000
--- a/packages/EasterEgg/res/drawable/right_eye.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<!--
-Copyright (C) 2016 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="48dp"
- android:height="48dp"
- android:viewportWidth="48.0"
- android:viewportHeight="48.0">
- <path android:name="right_eye" android:fillColor="#FF000000" android:pathData="M30.5,11c0,1.7 -3,1.7 -3,0C27.5,9.3 30.5,9.3 30.5,11z"/>
-</vector>
diff --git a/packages/EasterEgg/res/drawable/stat_icon.xml b/packages/EasterEgg/res/drawable/stat_icon.xml
deleted file mode 100644
index 608cb20..0000000
--- a/packages/EasterEgg/res/drawable/stat_icon.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<!--
-Copyright (C) 2015 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportWidth="24.0"
- android:viewportHeight="24.0">
- <path
- android:fillColor="#FF000000"
- android:pathData="M12,2C6.5,2 2,6.5 2,12c0,5.5 4.5,10 10,10s10,-4.5 10,-10C22,6.5 17.5,2 12,2zM5.5,11c0,-1.6 3,-1.6 3,0C8.5,12.7 5.5,12.7 5.5,11zM17.5,14.6c-0.6,1 -1.7,1.7 -2.9,1.7c-1.1,0 -2,-0.6 -2.6,-1.4c-0.6,0.9 -1.6,1.4 -2.7,1.4c-1.3,0 -2.3,-0.7 -2.9,-1.8c-0.2,-0.3 0,-0.7 0.3,-0.8c0.3,-0.2 0.7,0 0.8,0.3c0.3,0.7 1,1.1 1.8,1.1c0.9,0 1.6,-0.5 1.9,-1.3c-0.2,-0.2 -0.4,-0.4 -0.4,-0.7c0,-1.3 2.3,-1.3 2.3,0c0,0.3 -0.2,0.6 -0.4,0.7c0.3,0.8 1.1,1.3 1.9,1.3c0.8,0 1.5,-0.6 1.8,-1.1c0.2,-0.3 0.6,-0.4 0.9,-0.2C17.6,13.9 17.7,14.3 17.5,14.6zM15.5,11c0,-1.6 3,-1.6 3,0C18.5,12.7 15.5,12.7 15.5,11z"/>
- <path
- android:fillColor="#FF000000"
- android:pathData="M5.2,1.0l4.1000004,4.2l-5.0,2.1000004z"/>
- <path
- android:fillColor="#FF000000"
- android:pathData="M18.8,1.0l-4.0999994,4.2l5.000001,2.1000004z"/>
-</vector>
diff --git a/packages/EasterEgg/res/drawable/tail.xml b/packages/EasterEgg/res/drawable/tail.xml
deleted file mode 100644
index 0cca23c..0000000
--- a/packages/EasterEgg/res/drawable/tail.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<!--
-Copyright (C) 2016 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="48dp"
- android:height="48dp"
- android:viewportWidth="48.0"
- android:viewportHeight="48.0">
- <path android:name="tail"
- android:strokeColor="#FF000000"
- android:strokeWidth="5"
- android:strokeLineCap="round"
- android:pathData="M35,35.5h5.9c2.1,0 3.8,-1.7 3.8,-3.8v-6.2"/>
-</vector>
diff --git a/packages/EasterEgg/res/drawable/tail_cap.xml b/packages/EasterEgg/res/drawable/tail_cap.xml
deleted file mode 100644
index b82f6f9..0000000
--- a/packages/EasterEgg/res/drawable/tail_cap.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<!--
-Copyright (C) 2016 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="48dp"
- android:height="48dp"
- android:viewportWidth="48.0"
- android:viewportHeight="48.0">
- <path android:name="tail_cap" android:fillColor="#FF000000" android:pathData="M42.2,25.5c0,-1.4 1.1,-2.5 2.5,-2.5s2.5,1.1 2.5,2.5H42.2z"/>
-</vector>
diff --git a/packages/EasterEgg/res/drawable/tail_shadow.xml b/packages/EasterEgg/res/drawable/tail_shadow.xml
deleted file mode 100644
index bb1ff12..0000000
--- a/packages/EasterEgg/res/drawable/tail_shadow.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<!--
-Copyright (C) 2016 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="48dp"
- android:height="48dp"
- android:viewportWidth="48.0"
- android:viewportHeight="48.0">
- <path android:name="tail_shadow" android:fillColor="#FF000000" android:pathData="M40,38l0,-5l-1,0l0,5z"/>
-</vector>
diff --git a/packages/EasterEgg/res/values/dimens.xml b/packages/EasterEgg/res/drawable/toolbar_bg.xml
similarity index 65%
copy from packages/EasterEgg/res/values/dimens.xml
copy to packages/EasterEgg/res/drawable/toolbar_bg.xml
index e9dcebd..0f0e702 100644
--- a/packages/EasterEgg/res/values/dimens.xml
+++ b/packages/EasterEgg/res/drawable/toolbar_bg.xml
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
-Copyright (C) 2016 The Android Open Source Project
+ Copyright (C) 2018 The Android Open Source Project
- Licensed under the Apache License, Version 2.0 (the "License");
+ Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
@@ -14,6 +14,6 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<resources xmlns:android="http://schemas.android.com/apk/res/android">
- <dimen name="neko_display_size">64dp</dimen>
-</resources>
+<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
+ <solid android:color="@color/toolbar_bg_color" />
+</shape>
\ No newline at end of file
diff --git a/packages/EasterEgg/res/drawable/toolbar_button_bg.xml b/packages/EasterEgg/res/drawable/toolbar_button_bg.xml
new file mode 100644
index 0000000..1b6a53e
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/toolbar_button_bg.xml
@@ -0,0 +1,25 @@
+<?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.
+-->
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="?android:attr/colorControlHighlight">
+ <item android:id="@android:id/mask">
+ <shape android:shape="rectangle">
+ <solid android:color="@android:color/black" />
+ <corners android:radius="4dp" />
+ </shape>
+ </item>
+</ripple>
diff --git a/packages/EasterEgg/res/layout/activity_paint.xml b/packages/EasterEgg/res/layout/activity_paint.xml
new file mode 100644
index 0000000..a4c17af
--- /dev/null
+++ b/packages/EasterEgg/res/layout/activity_paint.xml
@@ -0,0 +1,48 @@
+<?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.
+-->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ xmlns:app="http://schemas.android.com/apk/res/com.android.egg"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="#666"
+ tools:context=".paint.PaintActivity"
+ android:id="@+id/contentView" >
+
+ <include layout="@layout/toolbar"
+ android:id="@+id/toolbar"
+ android:layout_width="match_parent"
+ android:layout_height="50dp"
+ android:layout_gravity="top"
+ />
+ <include layout="@layout/colors"
+ android:id="@+id/colors"
+ android:layout_width="match_parent"
+ android:layout_height="48dp"
+ android:layout_gravity="top"
+ android:visibility="gone"
+ />
+ <include layout="@layout/brushes"
+ android:id="@+id/brushes"
+ android:layout_width="match_parent"
+ android:layout_height="48dp"
+ android:layout_gravity="top"
+ android:visibility="gone"
+ />
+
+
+</FrameLayout>
\ No newline at end of file
diff --git a/packages/EasterEgg/res/layout/brushes.xml b/packages/EasterEgg/res/layout/brushes.xml
new file mode 100644
index 0000000..0c4b849
--- /dev/null
+++ b/packages/EasterEgg/res/layout/brushes.xml
@@ -0,0 +1,25 @@
+<?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.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:gravity="left"
+ android:background="@drawable/toolbar_bg"
+ android:elevation="10dp"
+ >
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/EasterEgg/res/layout/cat_view.xml b/packages/EasterEgg/res/layout/cat_view.xml
deleted file mode 100644
index 85b494d..0000000
--- a/packages/EasterEgg/res/layout/cat_view.xml
+++ /dev/null
@@ -1,82 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- 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.
- -->
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:minHeight="?android:attr/listPreferredItemHeightSmall"
- android:paddingStart="?android:attr/listPreferredItemPaddingStart"
- android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
- android:paddingTop="8dp"
- android:paddingBottom="8dp"
- android:background="?android:attr/selectableItemBackgroundBorderless"
- android:gravity="center_horizontal"
- android:clipToPadding="false">
-
- <FrameLayout
- android:layout_width="96dp"
- android:layout_height="wrap_content">
-
- <ImageView
- android:id="@android:id/icon"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:padding="10dp"
- android:layout_gravity="center"
- android:scaleType="fitCenter" />
-
- <LinearLayout
- android:id="@+id/contextGroup"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:visibility="invisible"
- android:layout_gravity="bottom">
-
- <ImageView
- android:id="@android:id/shareText"
- android:layout_width="40dp"
- android:layout_height="40dp"
- android:padding="8dp"
- android:src="@drawable/ic_share"
- android:scaleType="fitCenter"
- android:background="#40000000"/>
-
- <Space
- android:layout_width="0dp"
- android:layout_height="0dp"
- android:layout_weight="1" />
-
- <ImageView
- android:id="@android:id/closeButton"
- android:layout_width="40dp"
- android:layout_height="40dp"
- android:padding="4dp"
- android:src="@drawable/ic_close"
- android:scaleType="fitCenter"
- android:background="#40000000"/>
-
- </LinearLayout>
-
- </FrameLayout>
-
- <TextView
- android:id="@android:id/title"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textAppearance="?android:attr/textAppearanceListItem"
- android:gravity="center"/>
-</LinearLayout>
-
diff --git a/packages/EasterEgg/res/layout/colors.xml b/packages/EasterEgg/res/layout/colors.xml
new file mode 100644
index 0000000..b90f4d7
--- /dev/null
+++ b/packages/EasterEgg/res/layout/colors.xml
@@ -0,0 +1,26 @@
+<?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.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:gravity="left"
+ android:background="@drawable/toolbar_bg"
+ android:elevation="10dp"
+ >
+
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/EasterEgg/res/layout/edit_text.xml b/packages/EasterEgg/res/layout/edit_text.xml
deleted file mode 100644
index 9f7ac802..0000000
--- a/packages/EasterEgg/res/layout/edit_text.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- 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.
- -->
-
-<FrameLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:paddingStart="20dp"
- android:paddingEnd="20dp">
-
- <EditText
- android:id="@android:id/edit"
- android:maxLines="1"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"/>
-
-</FrameLayout>
\ No newline at end of file
diff --git a/packages/EasterEgg/res/layout/food_layout.xml b/packages/EasterEgg/res/layout/food_layout.xml
deleted file mode 100644
index d0ca0c8..0000000
--- a/packages/EasterEgg/res/layout/food_layout.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- 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.
- -->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:background="?android:attr/selectableItemBackgroundBorderless"
- android:paddingLeft="4dp" android:paddingRight="4dp"
- android:paddingBottom="6dp" android:paddingTop="6dp">
- <ImageView
- android:layout_width="64dp"
- android:layout_height="64dp"
- android:id="@+id/icon"
- android:tint="?android:attr/colorControlNormal"/>
- <TextView android:layout_width="64dp" android:layout_height="wrap_content"
- android:gravity="top|center_horizontal"
- android:id="@+id/text" />
-</LinearLayout>
diff --git a/packages/EasterEgg/res/layout/neko_activity.xml b/packages/EasterEgg/res/layout/neko_activity.xml
deleted file mode 100644
index c258137..0000000
--- a/packages/EasterEgg/res/layout/neko_activity.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-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.
--->
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
- <androidx.recyclerview.widget.RecyclerView
- android:id="@+id/holder"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"/>
-</FrameLayout>
\ No newline at end of file
diff --git a/packages/EasterEgg/res/layout/toolbar.xml b/packages/EasterEgg/res/layout/toolbar.xml
new file mode 100644
index 0000000..9a5a9c5
--- /dev/null
+++ b/packages/EasterEgg/res/layout/toolbar.xml
@@ -0,0 +1,111 @@
+<?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.
+-->
+<com.android.egg.paint.CutoutAvoidingToolbar
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="50dp"
+ android:orientation="horizontal"
+ android:gravity="left"
+ android:background="@drawable/toolbar_bg"
+ android:elevation="20dp"
+ >
+
+ <Space
+ android:tag="cutoutLeft"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ />
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:tag="beforeCutout"
+ android:layout_height="match_parent"
+ android:orientation="horizontal"
+ android:layout_weight="1"
+ tools:ignore="UselessParent">
+
+ <ImageButton
+ android:id="@+id/btnBrush"
+ android:layout_width="48dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:tint="@color/toolbar_icon_color"
+ android:background="@drawable/toolbar_button_bg"
+ />
+
+ <ImageButton
+ android:id="@+id/btnColor"
+ android:layout_width="48dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:tint="@color/toolbar_icon_color"
+ android:background="@drawable/toolbar_button_bg"
+ />
+
+ <ImageButton
+ android:id="@+id/btnSample"
+ android:layout_width="48dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:background="@drawable/toolbar_button_bg"
+ android:tint="@color/toolbar_icon_color"
+ android:src="@drawable/ic_dropper" />
+
+ </LinearLayout>
+
+ <Space
+ android:tag="cutoutCenter"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ />
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:tag="afterCutout"
+ android:layout_height="match_parent"
+ android:orientation="horizontal"
+ android:layout_weight="1"
+ tools:ignore="UselessParent">
+
+ <ImageButton
+ android:id="@+id/btnZen"
+ android:layout_width="48dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:background="@drawable/toolbar_button_bg"
+ android:tint="@color/toolbar_icon_color"
+ android:src="@drawable/ic_hourglass" />
+
+ <ImageButton
+ android:id="@+id/btnClear"
+ android:layout_width="48dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:background="@drawable/toolbar_button_bg"
+ android:tint="@color/toolbar_icon_color"
+ android:src="@drawable/ic_clear" />
+
+ </LinearLayout>
+
+ <Space
+ android:tag="cutoutRight"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ />
+
+</com.android.egg.paint.CutoutAvoidingToolbar>
\ No newline at end of file
diff --git a/packages/EasterEgg/res/values/dimens.xml b/packages/EasterEgg/res/values-night/colors.xml
similarity index 65%
copy from packages/EasterEgg/res/values/dimens.xml
copy to packages/EasterEgg/res/values-night/colors.xml
index e9dcebd..7c188f7 100644
--- a/packages/EasterEgg/res/values/dimens.xml
+++ b/packages/EasterEgg/res/values-night/colors.xml
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
-Copyright (C) 2016 The Android Open Source Project
+ Copyright (C) 2018 The Android Open Source Project
- Licensed under the Apache License, Version 2.0 (the "License");
+ Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
@@ -14,6 +14,8 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<resources xmlns:android="http://schemas.android.com/apk/res/android">
- <dimen name="neko_display_size">64dp</dimen>
-</resources>
+<resources>
+ <color name="toolbar_bg_color">#FF333333</color>
+ <color name="paper_color">#FF000000</color>
+ <color name="paint_color">#FFFFFFFF</color>
+</resources>
\ No newline at end of file
diff --git a/packages/EasterEgg/res/values-night/styles.xml b/packages/EasterEgg/res/values-night/styles.xml
new file mode 100644
index 0000000..4edf692
--- /dev/null
+++ b/packages/EasterEgg/res/values-night/styles.xml
@@ -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.
+-->
+<resources>
+ <style name="AppTheme" parent="@android:style/Theme.DeviceDefault.NoActionBar.Fullscreen">
+ <item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
+ <item name="android:windowLightNavigationBar">false</item>
+ </style>
+</resources>
diff --git a/packages/EasterEgg/res/xml/filepaths.xml b/packages/EasterEgg/res/values/attrs_toolbar_view.xml
similarity index 68%
copy from packages/EasterEgg/res/xml/filepaths.xml
copy to packages/EasterEgg/res/values/attrs_toolbar_view.xml
index 2130025..ed1360f 100644
--- a/packages/EasterEgg/res/xml/filepaths.xml
+++ b/packages/EasterEgg/res/values/attrs_toolbar_view.xml
@@ -1,8 +1,7 @@
-<?xml version="1.0" encoding="utf-8"?>
<!--
-Copyright (C) 2017 The Android Open Source Project
+ Copyright (C) 2018 The Android Open Source Project
- Licensed under the Apache License, Version 2.0 (the "License");
+ Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
@@ -14,6 +13,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<paths>
- <external-path name="cats" path="Pictures/Cats" />
-</paths>
\ No newline at end of file
+<resources>
+ <declare-styleable name="ToolbarView">
+ </declare-styleable>
+</resources>
diff --git a/packages/EasterEgg/res/values/dimens.xml b/packages/EasterEgg/res/values/colors.xml
similarity index 66%
copy from packages/EasterEgg/res/values/dimens.xml
copy to packages/EasterEgg/res/values/colors.xml
index e9dcebd..1a5388b 100644
--- a/packages/EasterEgg/res/values/dimens.xml
+++ b/packages/EasterEgg/res/values/colors.xml
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
-Copyright (C) 2016 The Android Open Source Project
+ Copyright (C) 2018 The Android Open Source Project
- Licensed under the Apache License, Version 2.0 (the "License");
+ Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
@@ -14,6 +14,8 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<resources xmlns:android="http://schemas.android.com/apk/res/android">
- <dimen name="neko_display_size">64dp</dimen>
-</resources>
+<resources>
+ <color name="toolbar_bg_color">#FFDDDDDD</color>
+ <color name="paper_color">#FFFFFFFF</color>
+ <color name="paint_color">#FF000000</color>
+</resources>
\ No newline at end of file
diff --git a/packages/EasterEgg/res/values/strings.xml b/packages/EasterEgg/res/values/strings.xml
index 61e3834..32dbc97 100644
--- a/packages/EasterEgg/res/values/strings.xml
+++ b/packages/EasterEgg/res/values/strings.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
-Copyright (C) 2016 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.
@@ -15,40 +15,5 @@
limitations under the License.
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android">
- <string name="app_name" translatable="false">Android Easter Egg</string>
- <string name="notification_name" translatable="false">Android Neko</string>
- <string name="notification_channel_name" translatable="false">New cats</string>
- <string name="default_tile_name" translatable="false">\????</string>
- <string name="notification_title" translatable="false">A cat is here.</string>
- <string name="default_cat_name" translatable="false">Cat #%s</string>
- <string name="directory_name" translatable="false">Cats</string>
- <string name="confirm_delete" translatable="false">Forget %s?</string>
- <string-array name="food_names" translatable="false">
- <item>Empty dish</item>
- <item>Bits</item>
- <item>Fish</item>
- <item>Chicken</item>
- <item>Treat</item>
- </string-array>
- <array name="food_icons">
- <item>@drawable/food_dish</item>
- <item>@drawable/food_bits</item>
- <item>@drawable/food_sysuituna</item>
- <item>@drawable/food_chicken</item>
- <item>@drawable/food_cookie</item>
- </array>
- <integer-array name="food_intervals">
- <item>0</item>
- <item>15</item>
- <item>30</item>
- <item>60</item>
- <item>120</item>
- </integer-array>
- <integer-array name="food_new_cat_prob">
- <item>0</item>
- <item>5</item>
- <item>35</item>
- <item>65</item>
- <item>90</item>
- </integer-array>
+ <string name="app_name" translatable="false">PAINT.APK</string>
</resources>
diff --git a/packages/EasterEgg/res/values/styles.xml b/packages/EasterEgg/res/values/styles.xml
new file mode 100644
index 0000000..44e2ce5
--- /dev/null
+++ b/packages/EasterEgg/res/values/styles.xml
@@ -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.
+-->
+<resources>
+
+ <style name="AppTheme" parent="@android:style/Theme.DeviceDefault.Light.NoActionBar.Fullscreen">
+ <item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
+ <item name="android:windowLightNavigationBar">true</item>
+ </style>
+
+</resources>
diff --git a/packages/EasterEgg/src/com/android/egg/neko/Cat.java b/packages/EasterEgg/src/com/android/egg/neko/Cat.java
deleted file mode 100644
index dd1bd07..0000000
--- a/packages/EasterEgg/src/com/android/egg/neko/Cat.java
+++ /dev/null
@@ -1,434 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the
- * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the specific language governing
- * permissions and limitations under the License.
- */
-
-package com.android.egg.neko;
-
-import android.app.Notification;
-import android.app.PendingIntent;
-import android.content.Context;
-import android.content.Intent;
-import android.content.res.Resources;
-import android.graphics.*;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.Icon;
-import android.os.Bundle;
-
-import java.io.ByteArrayOutputStream;
-import java.util.Random;
-import java.util.concurrent.ThreadLocalRandom;
-
-import com.android.egg.R;
-import com.android.internal.logging.MetricsLogger;
-
-import static com.android.egg.neko.NekoLand.CHAN_ID;
-
-public class Cat extends Drawable {
- public static final long[] PURR = {0, 40, 20, 40, 20, 40, 20, 40, 20, 40, 20, 40};
-
- private Random mNotSoRandom;
- private Bitmap mBitmap;
- private long mSeed;
- private String mName;
- private int mBodyColor;
- private int mFootType;
- private boolean mBowTie;
-
- private synchronized Random notSoRandom(long seed) {
- if (mNotSoRandom == null) {
- mNotSoRandom = new Random();
- mNotSoRandom.setSeed(seed);
- }
- return mNotSoRandom;
- }
-
- public static final float frandrange(Random r, float a, float b) {
- return (b-a)*r.nextFloat() + a;
- }
-
- public static final Object choose(Random r, Object...l) {
- return l[r.nextInt(l.length)];
- }
-
- public static final int chooseP(Random r, int[] a) {
- int pct = r.nextInt(1000);
- final int stop = a.length-2;
- int i=0;
- while (i<stop) {
- pct -= a[i];
- if (pct < 0) break;
- i+=2;
- }
- return a[i+1];
- }
-
- public static final int getColorIndex(int q, int[] a) {
- for(int i = 1; i < a.length; i+=2) {
- if (a[i] == q) {
- return i/2;
- }
- }
- return -1;
- }
-
- public static final int[] P_BODY_COLORS = {
- 180, 0xFF212121, // black
- 180, 0xFFFFFFFF, // white
- 140, 0xFF616161, // gray
- 140, 0xFF795548, // brown
- 100, 0xFF90A4AE, // steel
- 100, 0xFFFFF9C4, // buff
- 100, 0xFFFF8F00, // orange
- 5, 0xFF29B6F6, // blue..?
- 5, 0xFFFFCDD2, // pink!?
- 5, 0xFFCE93D8, // purple?!?!?
- 4, 0xFF43A047, // yeah, why not green
- 1, 0, // ?!?!?!
- };
-
- public static final int[] P_COLLAR_COLORS = {
- 250, 0xFFFFFFFF,
- 250, 0xFF000000,
- 250, 0xFFF44336,
- 50, 0xFF1976D2,
- 50, 0xFFFDD835,
- 50, 0xFFFB8C00,
- 50, 0xFFF48FB1,
- 50, 0xFF4CAF50,
- };
-
- public static final int[] P_BELLY_COLORS = {
- 750, 0,
- 250, 0xFFFFFFFF,
- };
-
- public static final int[] P_DARK_SPOT_COLORS = {
- 700, 0,
- 250, 0xFF212121,
- 50, 0xFF6D4C41,
- };
-
- public static final int[] P_LIGHT_SPOT_COLORS = {
- 700, 0,
- 300, 0xFFFFFFFF,
- };
-
- private CatParts D;
-
- public static void tint(int color, Drawable ... ds) {
- for (Drawable d : ds) {
- if (d != null) {
- d.mutate().setTint(color);
- }
- }
- }
-
- public static boolean isDark(int color) {
- final int r = (color & 0xFF0000) >> 16;
- final int g = (color & 0x00FF00) >> 8;
- final int b = color & 0x0000FF;
- return (r + g + b) < 0x80;
- }
-
- public Cat(Context context, long seed) {
- D = new CatParts(context);
- mSeed = seed;
-
- setName(context.getString(R.string.default_cat_name,
- String.valueOf(mSeed % 1000)));
-
- final Random nsr = notSoRandom(seed);
-
- // body color
- mBodyColor = chooseP(nsr, P_BODY_COLORS);
- if (mBodyColor == 0) mBodyColor = Color.HSVToColor(new float[] {
- nsr.nextFloat()*360f, frandrange(nsr,0.5f,1f), frandrange(nsr,0.5f, 1f)});
-
- tint(mBodyColor, D.body, D.head, D.leg1, D.leg2, D.leg3, D.leg4, D.tail,
- D.leftEar, D.rightEar, D.foot1, D.foot2, D.foot3, D.foot4, D.tailCap);
- tint(0x20000000, D.leg2Shadow, D.tailShadow);
- if (isDark(mBodyColor)) {
- tint(0xFFFFFFFF, D.leftEye, D.rightEye, D.mouth, D.nose);
- }
- tint(isDark(mBodyColor) ? 0xFFEF9A9A : 0x20D50000, D.leftEarInside, D.rightEarInside);
-
- tint(chooseP(nsr, P_BELLY_COLORS), D.belly);
- tint(chooseP(nsr, P_BELLY_COLORS), D.back);
- final int faceColor = chooseP(nsr, P_BELLY_COLORS);
- tint(faceColor, D.faceSpot);
- if (!isDark(faceColor)) {
- tint(0xFF000000, D.mouth, D.nose);
- }
-
- mFootType = 0;
- if (nsr.nextFloat() < 0.25f) {
- mFootType = 4;
- tint(0xFFFFFFFF, D.foot1, D.foot2, D.foot3, D.foot4);
- } else {
- if (nsr.nextFloat() < 0.25f) {
- mFootType = 2;
- tint(0xFFFFFFFF, D.foot1, D.foot3);
- } else if (nsr.nextFloat() < 0.25f) {
- mFootType = 3; // maybe -2 would be better? meh.
- tint(0xFFFFFFFF, D.foot2, D.foot4);
- } else if (nsr.nextFloat() < 0.1f) {
- mFootType = 1;
- tint(0xFFFFFFFF, (Drawable) choose(nsr, D.foot1, D.foot2, D.foot3, D.foot4));
- }
- }
-
- tint(nsr.nextFloat() < 0.333f ? 0xFFFFFFFF : mBodyColor, D.tailCap);
-
- final int capColor = chooseP(nsr, isDark(mBodyColor) ? P_LIGHT_SPOT_COLORS : P_DARK_SPOT_COLORS);
- tint(capColor, D.cap);
- //tint(chooseP(nsr, isDark(bodyColor) ? P_LIGHT_SPOT_COLORS : P_DARK_SPOT_COLORS), D.nose);
-
- final int collarColor = chooseP(nsr, P_COLLAR_COLORS);
- tint(collarColor, D.collar);
- mBowTie = nsr.nextFloat() < 0.1f;
- tint(mBowTie ? collarColor : 0, D.bowtie);
- }
-
- public static Cat create(Context context) {
- return new Cat(context, Math.abs(ThreadLocalRandom.current().nextInt()));
- }
-
- public Notification.Builder buildNotification(Context context) {
- final Bundle extras = new Bundle();
- extras.putString("android.substName", context.getString(R.string.notification_name));
- final Intent intent = new Intent(Intent.ACTION_MAIN)
- .setClass(context, NekoLand.class)
- .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- return new Notification.Builder(context)
- .setSmallIcon(Icon.createWithResource(context, R.drawable.stat_icon))
- .setLargeIcon(createNotificationLargeIcon(context))
- .setColor(getBodyColor())
- .setPriority(Notification.PRIORITY_LOW)
- .setContentTitle(context.getString(R.string.notification_title))
- .setShowWhen(true)
- .setCategory(Notification.CATEGORY_STATUS)
- .setContentText(getName())
- .setContentIntent(PendingIntent.getActivity(context, 0, intent, 0))
- .setAutoCancel(true)
- .setChannel(CHAN_ID)
- .setVibrate(PURR)
- .addExtras(extras);
- }
-
- public long getSeed() {
- return mSeed;
- }
-
- @Override
- public void draw(Canvas canvas) {
- final int w = Math.min(canvas.getWidth(), canvas.getHeight());
- final int h = w;
-
- if (mBitmap == null || mBitmap.getWidth() != w || mBitmap.getHeight() != h) {
- mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
- final Canvas bitCanvas = new Canvas(mBitmap);
- slowDraw(bitCanvas, 0, 0, w, h);
- }
- canvas.drawBitmap(mBitmap, 0, 0, null);
- }
-
- private void slowDraw(Canvas canvas, int x, int y, int w, int h) {
- for (int i = 0; i < D.drawingOrder.length; i++) {
- final Drawable d = D.drawingOrder[i];
- if (d != null) {
- d.setBounds(x, y, x+w, y+h);
- d.draw(canvas);
- }
- }
-
- }
-
- public Bitmap createBitmap(int w, int h) {
- if (mBitmap != null && mBitmap.getWidth() == w && mBitmap.getHeight() == h) {
- return mBitmap.copy(mBitmap.getConfig(), true);
- }
- Bitmap result = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
- slowDraw(new Canvas(result), 0, 0, w, h);
- return result;
- }
-
- public static Icon recompressIcon(Icon bitmapIcon) {
- if (bitmapIcon.getType() != Icon.TYPE_BITMAP) return bitmapIcon;
- final Bitmap bits = bitmapIcon.getBitmap();
- final ByteArrayOutputStream ostream = new ByteArrayOutputStream(
- bits.getWidth() * bits.getHeight() * 2); // guess 50% compression
- final boolean ok = bits.compress(Bitmap.CompressFormat.PNG, 100, ostream);
- if (!ok) return null;
- return Icon.createWithData(ostream.toByteArray(), 0, ostream.size());
- }
-
- public Icon createNotificationLargeIcon(Context context) {
- final Resources res = context.getResources();
- final int w = 2*res.getDimensionPixelSize(android.R.dimen.notification_large_icon_width);
- final int h = 2*res.getDimensionPixelSize(android.R.dimen.notification_large_icon_height);
- return recompressIcon(createIcon(context, w, h));
- }
-
- public Icon createIcon(Context context, int w, int h) {
- Bitmap result = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
- final Canvas canvas = new Canvas(result);
- final Paint pt = new Paint();
- float[] hsv = new float[3];
- Color.colorToHSV(mBodyColor, hsv);
- hsv[2] = (hsv[2]>0.5f)
- ? (hsv[2] - 0.25f)
- : (hsv[2] + 0.25f);
- pt.setColor(Color.HSVToColor(hsv));
- float r = w/2;
- canvas.drawCircle(r, r, r, pt);
- int m = w/10;
-
- slowDraw(canvas, m, m, w-m-m, h-m-m);
-
- return Icon.createWithBitmap(result);
- }
-
- @Override
- public void setAlpha(int i) {
-
- }
-
- @Override
- public void setColorFilter(ColorFilter colorFilter) {
-
- }
-
- @Override
- public int getOpacity() {
- return PixelFormat.TRANSLUCENT;
- }
-
- public String getName() {
- return mName;
- }
-
- public void setName(String name) {
- this.mName = name;
- }
-
- public int getBodyColor() {
- return mBodyColor;
- }
-
- public void logAdd(Context context) {
- logCatAction(context, "egg_neko_add");
- }
-
- public void logRename(Context context) {
- logCatAction(context, "egg_neko_rename");
- }
-
- public void logRemove(Context context) {
- logCatAction(context, "egg_neko_remove");
- }
-
- public void logShare(Context context) {
- logCatAction(context, "egg_neko_share");
- }
-
- private void logCatAction(Context context, String prefix) {
- MetricsLogger.count(context, prefix, 1);
- MetricsLogger.histogram(context, prefix +"_color",
- getColorIndex(mBodyColor, P_BODY_COLORS));
- MetricsLogger.histogram(context, prefix + "_bowtie", mBowTie ? 1 : 0);
- MetricsLogger.histogram(context, prefix + "_feet", mFootType);
- }
-
- public static class CatParts {
- public Drawable leftEar;
- public Drawable rightEar;
- public Drawable rightEarInside;
- public Drawable leftEarInside;
- public Drawable head;
- public Drawable faceSpot;
- public Drawable cap;
- public Drawable mouth;
- public Drawable body;
- public Drawable foot1;
- public Drawable leg1;
- public Drawable foot2;
- public Drawable leg2;
- public Drawable foot3;
- public Drawable leg3;
- public Drawable foot4;
- public Drawable leg4;
- public Drawable tail;
- public Drawable leg2Shadow;
- public Drawable tailShadow;
- public Drawable tailCap;
- public Drawable belly;
- public Drawable back;
- public Drawable rightEye;
- public Drawable leftEye;
- public Drawable nose;
- public Drawable bowtie;
- public Drawable collar;
- public Drawable[] drawingOrder;
-
- public CatParts(Context context) {
- body = context.getDrawable(R.drawable.body);
- head = context.getDrawable(R.drawable.head);
- leg1 = context.getDrawable(R.drawable.leg1);
- leg2 = context.getDrawable(R.drawable.leg2);
- leg3 = context.getDrawable(R.drawable.leg3);
- leg4 = context.getDrawable(R.drawable.leg4);
- tail = context.getDrawable(R.drawable.tail);
- leftEar = context.getDrawable(R.drawable.left_ear);
- rightEar = context.getDrawable(R.drawable.right_ear);
- rightEarInside = context.getDrawable(R.drawable.right_ear_inside);
- leftEarInside = context.getDrawable(R.drawable.left_ear_inside);
- faceSpot = context.getDrawable(R.drawable.face_spot);
- cap = context.getDrawable(R.drawable.cap);
- mouth = context.getDrawable(R.drawable.mouth);
- foot4 = context.getDrawable(R.drawable.foot4);
- foot3 = context.getDrawable(R.drawable.foot3);
- foot1 = context.getDrawable(R.drawable.foot1);
- foot2 = context.getDrawable(R.drawable.foot2);
- leg2Shadow = context.getDrawable(R.drawable.leg2_shadow);
- tailShadow = context.getDrawable(R.drawable.tail_shadow);
- tailCap = context.getDrawable(R.drawable.tail_cap);
- belly = context.getDrawable(R.drawable.belly);
- back = context.getDrawable(R.drawable.back);
- rightEye = context.getDrawable(R.drawable.right_eye);
- leftEye = context.getDrawable(R.drawable.left_eye);
- nose = context.getDrawable(R.drawable.nose);
- collar = context.getDrawable(R.drawable.collar);
- bowtie = context.getDrawable(R.drawable.bowtie);
- drawingOrder = getDrawingOrder();
- }
- private Drawable[] getDrawingOrder() {
- return new Drawable[] {
- collar,
- leftEar, leftEarInside, rightEar, rightEarInside,
- head,
- faceSpot,
- cap,
- leftEye, rightEye,
- nose, mouth,
- tail, tailCap, tailShadow,
- foot1, leg1,
- foot2, leg2,
- foot3, leg3,
- foot4, leg4,
- leg2Shadow,
- body, belly,
- bowtie
- };
- }
- }
-}
diff --git a/packages/EasterEgg/src/com/android/egg/neko/Food.java b/packages/EasterEgg/src/com/android/egg/neko/Food.java
deleted file mode 100644
index 5c0f12e..0000000
--- a/packages/EasterEgg/src/com/android/egg/neko/Food.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the
- * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the specific language governing
- * permissions and limitations under the License.
- */
-
-package com.android.egg.neko;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.Icon;
-
-import com.android.egg.R;
-
-public class Food {
- private final int mType;
-
- private static int[] sIcons;
- private static String[] sNames;
-
- public Food(int type) {
- mType = type;
- }
-
- public Icon getIcon(Context context) {
- if (sIcons == null) {
- TypedArray icons = context.getResources().obtainTypedArray(R.array.food_icons);
- sIcons = new int[icons.length()];
- for (int i = 0; i < sIcons.length; i++) {
- sIcons[i] = icons.getResourceId(i, 0);
- }
- icons.recycle();
- }
- return Icon.createWithResource(context, sIcons[mType]);
- }
-
- public String getName(Context context) {
- if (sNames == null) {
- sNames = context.getResources().getStringArray(R.array.food_names);
- }
- return sNames[mType];
- }
-
- public long getInterval(Context context) {
- return context.getResources().getIntArray(R.array.food_intervals)[mType];
- }
-
- public int getType() {
- return mType;
- }
-}
diff --git a/packages/EasterEgg/src/com/android/egg/neko/NekoActivationActivity.java b/packages/EasterEgg/src/com/android/egg/neko/NekoActivationActivity.java
deleted file mode 100644
index c0b725c..0000000
--- a/packages/EasterEgg/src/com/android/egg/neko/NekoActivationActivity.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the
- * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the specific language governing
- * permissions and limitations under the License.
- */
-
-package com.android.egg.neko;
-
-import android.app.Activity;
-import android.content.ComponentName;
-import android.content.pm.PackageManager;
-import android.util.Log;
-import android.widget.Toast;
-
-import com.android.internal.logging.MetricsLogger;
-
-public class NekoActivationActivity extends Activity {
- private void toastUp(String s) {
- Toast toast = Toast.makeText(this, s, Toast.LENGTH_SHORT);
- toast.getView().setBackgroundDrawable(null);
- toast.show();
- }
-
- @Override
- public void onStart() {
- super.onStart();
-
- final PackageManager pm = getPackageManager();
- final ComponentName cn = new ComponentName(this, NekoTile.class);
- if (pm.getComponentEnabledSetting(cn) == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
- if (NekoLand.DEBUG) {
- Log.v("Neko", "Disabling tile.");
- }
- pm.setComponentEnabledSetting(cn, PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
- PackageManager.DONT_KILL_APP);
- MetricsLogger.histogram(this, "egg_neko_enable", 0);
- toastUp("\uD83D\uDEAB");
- } else {
- if (NekoLand.DEBUG) {
- Log.v("Neko", "Enabling tile.");
- }
- pm.setComponentEnabledSetting(cn, PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
- PackageManager.DONT_KILL_APP);
- MetricsLogger.histogram(this, "egg_neko_enable", 1);
- toastUp("\uD83D\uDC31");
- }
- finish();
- }
-}
diff --git a/packages/EasterEgg/src/com/android/egg/neko/NekoDialog.java b/packages/EasterEgg/src/com/android/egg/neko/NekoDialog.java
deleted file mode 100644
index ee89041..0000000
--- a/packages/EasterEgg/src/com/android/egg/neko/NekoDialog.java
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the
- * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the specific language governing
- * permissions and limitations under the License.
- */
-
-package com.android.egg.neko;
-
-import androidx.annotation.NonNull;
-import android.app.Dialog;
-import android.content.Context;
-import androidx.recyclerview.widget.GridLayoutManager;
-import androidx.recyclerview.widget.RecyclerView;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import com.android.egg.R;
-import com.android.internal.logging.MetricsLogger;
-
-import java.util.ArrayList;
-
-public class NekoDialog extends Dialog {
-
- private final Adapter mAdapter;
-
- public NekoDialog(@NonNull Context context) {
- super(context, android.R.style.Theme_Material_Dialog_NoActionBar);
- RecyclerView view = new RecyclerView(getContext());
- mAdapter = new Adapter(getContext());
- view.setLayoutManager(new GridLayoutManager(getContext(), 2));
- view.setAdapter(mAdapter);
- final float dp = context.getResources().getDisplayMetrics().density;
- final int pad = (int)(16*dp);
- view.setPadding(pad, pad, pad, pad);
- setContentView(view);
- }
-
- private void onFoodSelected(Food food) {
- PrefState prefs = new PrefState(getContext());
- int currentState = prefs.getFoodState();
- if (currentState == 0 && food.getType() != 0) {
- NekoService.registerJob(getContext(), food.getInterval(getContext()));
- }
- MetricsLogger.histogram(getContext(), "egg_neko_offered_food", food.getType());
- prefs.setFoodState(food.getType());
- dismiss();
- }
-
- private class Adapter extends RecyclerView.Adapter<Holder> {
-
- private final Context mContext;
- private final ArrayList<Food> mFoods = new ArrayList<>();
-
- public Adapter(Context context) {
- mContext = context;
- int[] foods = context.getResources().getIntArray(R.array.food_names);
- // skip food 0, you can't choose it
- for (int i=1; i<foods.length; i++) {
- mFoods.add(new Food(i));
- }
- }
-
- @Override
- public Holder onCreateViewHolder(ViewGroup parent, int viewType) {
- return new Holder(LayoutInflater.from(parent.getContext())
- .inflate(R.layout.food_layout, parent, false));
- }
-
- @Override
- public void onBindViewHolder(final Holder holder, int position) {
- final Food food = mFoods.get(position);
- ((ImageView) holder.itemView.findViewById(R.id.icon))
- .setImageIcon(food.getIcon(mContext));
- ((TextView) holder.itemView.findViewById(R.id.text))
- .setText(food.getName(mContext));
- holder.itemView.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- onFoodSelected(mFoods.get(holder.getAdapterPosition()));
- }
- });
- }
-
- @Override
- public int getItemCount() {
- return mFoods.size();
- }
- }
-
- public static class Holder extends RecyclerView.ViewHolder {
-
- public Holder(View itemView) {
- super(itemView);
- }
- }
-}
diff --git a/packages/EasterEgg/src/com/android/egg/neko/NekoLand.java b/packages/EasterEgg/src/com/android/egg/neko/NekoLand.java
deleted file mode 100644
index ceb2170..0000000
--- a/packages/EasterEgg/src/com/android/egg/neko/NekoLand.java
+++ /dev/null
@@ -1,338 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the
- * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the specific language governing
- * permissions and limitations under the License.
- */
-
-package com.android.egg.neko;
-
-import android.Manifest;
-import android.app.ActionBar;
-import android.app.Activity;
-import android.app.AlertDialog;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.DialogInterface.OnClickListener;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.graphics.Bitmap;
-import android.graphics.Color;
-import android.graphics.drawable.Drawable;
-import android.media.MediaScannerConnection;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.Environment;
-import android.provider.MediaStore.Images;
-import androidx.core.content.FileProvider;
-import androidx.recyclerview.widget.GridLayoutManager;
-import androidx.recyclerview.widget.RecyclerView;
-import android.util.Log;
-import android.view.ContextThemeWrapper;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.View.OnLongClickListener;
-import android.view.ViewGroup;
-import android.widget.EditText;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import com.android.egg.R;
-import com.android.egg.neko.PrefState.PrefsListener;
-import com.android.internal.logging.MetricsLogger;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
-
-public class NekoLand extends Activity implements PrefsListener {
- public static String CHAN_ID = "EGG";
-
- public static boolean DEBUG = false;
- public static boolean DEBUG_NOTIFICATIONS = false;
-
- private static final int EXPORT_BITMAP_SIZE = 600;
-
- private static final int STORAGE_PERM_REQUEST = 123;
-
- private static boolean CAT_GEN = false;
- private PrefState mPrefs;
- private CatAdapter mAdapter;
- private Cat mPendingShareCat;
-
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.neko_activity);
- final ActionBar actionBar = getActionBar();
- if (actionBar != null) {
- actionBar.setLogo(Cat.create(this));
- actionBar.setDisplayUseLogoEnabled(false);
- actionBar.setDisplayShowHomeEnabled(true);
- }
-
- mPrefs = new PrefState(this);
- mPrefs.setListener(this);
- final RecyclerView recyclerView = findViewById(R.id.holder);
- mAdapter = new CatAdapter();
- recyclerView.setAdapter(mAdapter);
- recyclerView.setLayoutManager(new GridLayoutManager(this, 3));
- int numCats = updateCats();
- MetricsLogger.histogram(this, "egg_neko_visit_gallery", numCats);
- }
-
- @Override
- protected void onDestroy() {
- super.onDestroy();
- mPrefs.setListener(null);
- }
-
- private int updateCats() {
- Cat[] cats;
- if (CAT_GEN) {
- cats = new Cat[50];
- for (int i = 0; i < cats.length; i++) {
- cats[i] = Cat.create(this);
- }
- } else {
- final float[] hsv = new float[3];
- List<Cat> list = mPrefs.getCats();
- Collections.sort(list, new Comparator<Cat>() {
- @Override
- public int compare(Cat cat, Cat cat2) {
- Color.colorToHSV(cat.getBodyColor(), hsv);
- float bodyH1 = hsv[0];
- Color.colorToHSV(cat2.getBodyColor(), hsv);
- float bodyH2 = hsv[0];
- return Float.compare(bodyH1, bodyH2);
- }
- });
- cats = list.toArray(new Cat[0]);
- }
- mAdapter.setCats(cats);
- return cats.length;
- }
-
- private void onCatClick(Cat cat) {
- if (CAT_GEN) {
- mPrefs.addCat(cat);
- new AlertDialog.Builder(NekoLand.this)
- .setTitle("Cat added")
- .setPositiveButton(android.R.string.ok, null)
- .show();
- } else {
- showNameDialog(cat);
- }
-// noman.notify(1, cat.buildNotification(NekoLand.this).build());
- }
-
- private void onCatRemove(Cat cat) {
- cat.logRemove(this);
- mPrefs.removeCat(cat);
- }
-
- private void showNameDialog(final Cat cat) {
- final Context context = new ContextThemeWrapper(this,
- android.R.style.Theme_Material_Light_Dialog_NoActionBar);
- // TODO: Move to XML, add correct margins.
- View view = LayoutInflater.from(context).inflate(R.layout.edit_text, null);
- final EditText text = (EditText) view.findViewById(android.R.id.edit);
- text.setText(cat.getName());
- text.setSelection(cat.getName().length());
- final int size = context.getResources()
- .getDimensionPixelSize(android.R.dimen.app_icon_size);
- Drawable catIcon = cat.createIcon(this, size, size).loadDrawable(this);
- new AlertDialog.Builder(context)
- .setTitle(" ")
- .setIcon(catIcon)
- .setView(view)
- .setPositiveButton(android.R.string.ok, new OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- cat.logRename(context);
- cat.setName(text.getText().toString().trim());
- mPrefs.addCat(cat);
- }
- }).show();
- }
-
- @Override
- public void onPrefsChanged() {
- updateCats();
- }
-
- private class CatAdapter extends RecyclerView.Adapter<CatHolder> {
-
- private Cat[] mCats;
-
- public void setCats(Cat[] cats) {
- mCats = cats;
- notifyDataSetChanged();
- }
-
- @Override
- public CatHolder onCreateViewHolder(ViewGroup parent, int viewType) {
- return new CatHolder(LayoutInflater.from(parent.getContext())
- .inflate(R.layout.cat_view, parent, false));
- }
-
- private void setContextGroupVisible(final CatHolder holder, boolean vis) {
- final View group = holder.contextGroup;
- if (vis && group.getVisibility() != View.VISIBLE) {
- group.setAlpha(0);
- group.setVisibility(View.VISIBLE);
- group.animate().alpha(1.0f).setDuration(333);
- Runnable hideAction = new Runnable() {
- @Override
- public void run() {
- setContextGroupVisible(holder, false);
- }
- };
- group.setTag(hideAction);
- group.postDelayed(hideAction, 5000);
- } else if (!vis && group.getVisibility() == View.VISIBLE) {
- group.removeCallbacks((Runnable) group.getTag());
- group.animate().alpha(0f).setDuration(250).withEndAction(new Runnable() {
- @Override
- public void run() {
- group.setVisibility(View.INVISIBLE);
- }
- });
- }
- }
-
- @Override
- public void onBindViewHolder(final CatHolder holder, int position) {
- Context context = holder.itemView.getContext();
- final int size = context.getResources().getDimensionPixelSize(R.dimen.neko_display_size);
- holder.imageView.setImageIcon(mCats[position].createIcon(context, size, size));
- holder.textView.setText(mCats[position].getName());
- holder.itemView.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- onCatClick(mCats[holder.getAdapterPosition()]);
- }
- });
- holder.itemView.setOnLongClickListener(new OnLongClickListener() {
- @Override
- public boolean onLongClick(View v) {
- setContextGroupVisible(holder, true);
- return true;
- }
- });
- holder.delete.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- setContextGroupVisible(holder, false);
- new AlertDialog.Builder(NekoLand.this)
- .setTitle(getString(R.string.confirm_delete, mCats[position].getName()))
- .setNegativeButton(android.R.string.cancel, null)
- .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- onCatRemove(mCats[holder.getAdapterPosition()]);
- }
- })
- .show();
- }
- });
- holder.share.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- setContextGroupVisible(holder, false);
- Cat cat = mCats[holder.getAdapterPosition()];
- if (checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)
- != PackageManager.PERMISSION_GRANTED) {
- mPendingShareCat = cat;
- requestPermissions(
- new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
- STORAGE_PERM_REQUEST);
- return;
- }
- shareCat(cat);
- }
- });
- }
-
- @Override
- public int getItemCount() {
- return mCats.length;
- }
- }
-
- private void shareCat(Cat cat) {
- final File dir = new File(
- Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES),
- getString(R.string.directory_name));
- if (!dir.exists() && !dir.mkdirs()) {
- Log.e("NekoLand", "save: error: can't create Pictures directory");
- return;
- }
- final File png = new File(dir, cat.getName().replaceAll("[/ #:]+", "_") + ".png");
- Bitmap bitmap = cat.createBitmap(EXPORT_BITMAP_SIZE, EXPORT_BITMAP_SIZE);
- if (bitmap != null) {
- try {
- OutputStream os = new FileOutputStream(png);
- bitmap.compress(Bitmap.CompressFormat.PNG, 0, os);
- os.close();
- MediaScannerConnection.scanFile(
- this,
- new String[] {png.toString()},
- new String[] {"image/png"},
- null);
- Log.v("Neko", "cat file: " + png);
- Uri uri = FileProvider.getUriForFile(this, "com.android.egg.fileprovider", png);
- Log.v("Neko", "cat uri: " + uri);
- Intent intent = new Intent(Intent.ACTION_SEND);
- intent.putExtra(Intent.EXTRA_STREAM, uri);
- intent.putExtra(Intent.EXTRA_SUBJECT, cat.getName());
- intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
- intent.setType("image/png");
- startActivity(Intent.createChooser(intent, null));
- cat.logShare(this);
- } catch (IOException e) {
- Log.e("NekoLand", "save: error: " + e);
- }
- }
- }
-
- @Override
- public void onRequestPermissionsResult(int requestCode,
- String permissions[], int[] grantResults) {
- if (requestCode == STORAGE_PERM_REQUEST) {
- if (mPendingShareCat != null) {
- shareCat(mPendingShareCat);
- mPendingShareCat = null;
- }
- }
- }
-
- private static class CatHolder extends RecyclerView.ViewHolder {
- private final ImageView imageView;
- private final TextView textView;
- private final View contextGroup;
- private final View delete;
- private final View share;
-
- public CatHolder(View itemView) {
- super(itemView);
- imageView = (ImageView) itemView.findViewById(android.R.id.icon);
- textView = (TextView) itemView.findViewById(android.R.id.title);
- contextGroup = itemView.findViewById(R.id.contextGroup);
- delete = itemView.findViewById(android.R.id.closeButton);
- share = itemView.findViewById(android.R.id.shareText);
- }
- }
-}
diff --git a/packages/EasterEgg/src/com/android/egg/neko/NekoLockedActivity.java b/packages/EasterEgg/src/com/android/egg/neko/NekoLockedActivity.java
deleted file mode 100644
index e0876a4..0000000
--- a/packages/EasterEgg/src/com/android/egg/neko/NekoLockedActivity.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the
- * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the specific language governing
- * permissions and limitations under the License.
- */
-
-package com.android.egg.neko;
-
-import androidx.annotation.Nullable;
-import android.app.Activity;
-import android.content.DialogInterface;
-import android.content.DialogInterface.OnDismissListener;
-import android.os.Bundle;
-import android.view.WindowManager;
-
-public class NekoLockedActivity extends Activity implements OnDismissListener {
-
- private NekoDialog mDialog;
-
- @Override
- public void onCreate(@Nullable Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
- getWindow().addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
- getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
- getWindow().addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
-
- mDialog = new NekoDialog(this);
- mDialog.setOnDismissListener(this);
- mDialog.show();
- }
-
- @Override
- public void onDismiss(DialogInterface dialog) {
- finish();
- }
-}
diff --git a/packages/EasterEgg/src/com/android/egg/neko/NekoService.java b/packages/EasterEgg/src/com/android/egg/neko/NekoService.java
deleted file mode 100644
index 42506e6..0000000
--- a/packages/EasterEgg/src/com/android/egg/neko/NekoService.java
+++ /dev/null
@@ -1,165 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the
- * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the specific language governing
- * permissions and limitations under the License.
- */
-
-package com.android.egg.neko;
-
-import android.app.Notification;
-import android.app.NotificationChannel;
-import android.app.NotificationManager;
-import android.app.job.JobInfo;
-import android.app.job.JobParameters;
-import android.app.job.JobScheduler;
-import android.app.job.JobService;
-import android.content.ComponentName;
-import android.content.Context;
-import android.net.Uri;
-import android.os.Bundle;
-
-import java.util.List;
-import android.util.Log;
-
-import com.android.egg.R;
-
-import java.util.Random;
-
-import static com.android.egg.neko.Cat.PURR;
-import static com.android.egg.neko.NekoLand.CHAN_ID;
-
-public class NekoService extends JobService {
-
- private static final String TAG = "NekoService";
-
- public static int JOB_ID = 42;
-
- public static int CAT_NOTIFICATION = 1;
- public static int DEBUG_NOTIFICATION = 1234;
-
- public static float CAT_CAPTURE_PROB = 1.0f; // generous
-
- public static long SECONDS = 1000;
- public static long MINUTES = 60 * SECONDS;
-
- public static long INTERVAL_FLEX = 5 * MINUTES;
-
- public static float INTERVAL_JITTER_FRAC = 0.25f;
-
- private static void setupNotificationChannels(Context context) {
- NotificationManager noman = context.getSystemService(NotificationManager.class);
- NotificationChannel eggChan = new NotificationChannel(CHAN_ID,
- context.getString(R.string.notification_channel_name),
- NotificationManager.IMPORTANCE_DEFAULT);
- eggChan.setSound(Uri.EMPTY, Notification.AUDIO_ATTRIBUTES_DEFAULT); // cats are quiet
- eggChan.setVibrationPattern(PURR); // not totally quiet though
- eggChan.setBlockableSystem(true); // unlike a real cat, you can push this one off your lap
- eggChan.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC); // cats sit in the window
- noman.createNotificationChannel(eggChan);
- }
-
- @Override
- public boolean onStartJob(JobParameters params) {
- Log.v(TAG, "Starting job: " + String.valueOf(params));
-
- NotificationManager noman = getSystemService(NotificationManager.class);
- if (NekoLand.DEBUG_NOTIFICATIONS) {
- final Bundle extras = new Bundle();
- extras.putString("android.substName", getString(R.string.notification_name));
- final int size = getResources()
- .getDimensionPixelSize(android.R.dimen.notification_large_icon_width);
- final Cat cat = Cat.create(this);
- final Notification.Builder builder
- = cat.buildNotification(this)
- .setContentTitle("DEBUG")
- .setChannel(NekoLand.CHAN_ID)
- .setContentText("Ran job: " + params);
- noman.notify(DEBUG_NOTIFICATION, builder.build());
- }
-
- final PrefState prefs = new PrefState(this);
- int food = prefs.getFoodState();
- if (food != 0) {
- prefs.setFoodState(0); // nom
- final Random rng = new Random();
- if (rng.nextFloat() <= CAT_CAPTURE_PROB) {
- Cat cat;
- List<Cat> cats = prefs.getCats();
- final int[] probs = getResources().getIntArray(R.array.food_new_cat_prob);
- final float new_cat_prob = (float)((food < probs.length) ? probs[food] : 50) / 100f;
-
- if (cats.size() == 0 || rng.nextFloat() <= new_cat_prob) {
- cat = Cat.create(this);
- prefs.addCat(cat);
- cat.logAdd(this);
- Log.v(TAG, "A new cat is here: " + cat.getName());
- } else {
- cat = cats.get(rng.nextInt(cats.size()));
- Log.v(TAG, "A cat has returned: " + cat.getName());
- }
-
- final Notification.Builder builder = cat.buildNotification(this);
- noman.notify(CAT_NOTIFICATION, builder.build());
- }
- }
- cancelJob(this);
- return false;
- }
-
- @Override
- public boolean onStopJob(JobParameters jobParameters) {
- return false;
- }
-
- public static void registerJobIfNeeded(Context context, long intervalMinutes) {
- JobScheduler jss = context.getSystemService(JobScheduler.class);
- JobInfo info = jss.getPendingJob(JOB_ID);
- if (info == null) {
- registerJob(context, intervalMinutes);
- }
- }
-
- public static void registerJob(Context context, long intervalMinutes) {
- setupNotificationChannels(context);
-
- JobScheduler jss = context.getSystemService(JobScheduler.class);
- jss.cancel(JOB_ID);
- long interval = intervalMinutes * MINUTES;
- long jitter = (long)(INTERVAL_JITTER_FRAC * interval);
- interval += (long)(Math.random() * (2 * jitter)) - jitter;
- final JobInfo jobInfo = new JobInfo.Builder(JOB_ID,
- new ComponentName(context, NekoService.class))
- .setPeriodic(interval, INTERVAL_FLEX)
- .build();
-
- Log.v(TAG, "A cat will visit in " + interval + "ms: " + String.valueOf(jobInfo));
- jss.schedule(jobInfo);
-
- if (NekoLand.DEBUG_NOTIFICATIONS) {
- NotificationManager noman = context.getSystemService(NotificationManager.class);
- noman.notify(DEBUG_NOTIFICATION, new Notification.Builder(context)
- .setSmallIcon(R.drawable.stat_icon)
- .setContentTitle(String.format("Job scheduled in %d min", (interval / MINUTES)))
- .setContentText(String.valueOf(jobInfo))
- .setPriority(Notification.PRIORITY_MIN)
- .setCategory(Notification.CATEGORY_SERVICE)
- .setChannel(NekoLand.CHAN_ID)
- .setShowWhen(true)
- .build());
- }
- }
-
- public static void cancelJob(Context context) {
- JobScheduler jss = context.getSystemService(JobScheduler.class);
- Log.v(TAG, "Canceling job");
- jss.cancel(JOB_ID);
- }
-}
diff --git a/packages/EasterEgg/src/com/android/egg/neko/NekoTile.java b/packages/EasterEgg/src/com/android/egg/neko/NekoTile.java
deleted file mode 100644
index 159b40a..0000000
--- a/packages/EasterEgg/src/com/android/egg/neko/NekoTile.java
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the
- * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the specific language governing
- * permissions and limitations under the License.
- */
-
-package com.android.egg.neko;
-
-import android.content.Intent;
-import android.service.quicksettings.Tile;
-import android.service.quicksettings.TileService;
-import android.util.Log;
-
-import com.android.egg.neko.PrefState.PrefsListener;
-import com.android.internal.logging.MetricsLogger;
-
-public class NekoTile extends TileService implements PrefsListener {
-
- private static final String TAG = "NekoTile";
-
- private PrefState mPrefs;
-
- @Override
- public void onCreate() {
- super.onCreate();
- mPrefs = new PrefState(this);
- }
-
- @Override
- public void onStartListening() {
- super.onStartListening();
- mPrefs.setListener(this);
- updateState();
- }
-
- @Override
- public void onStopListening() {
- super.onStopListening();
- mPrefs.setListener(null);
- }
-
- @Override
- public void onTileAdded() {
- super.onTileAdded();
- MetricsLogger.count(this, "egg_neko_tile_added", 1);
- }
-
- @Override
- public void onTileRemoved() {
- super.onTileRemoved();
- MetricsLogger.count(this, "egg_neko_tile_removed", 1);
- }
-
- @Override
- public void onPrefsChanged() {
- updateState();
- }
-
- private void updateState() {
- Tile tile = getQsTile();
- int foodState = mPrefs.getFoodState();
- Food food = new Food(foodState);
- if (foodState != 0) {
- NekoService.registerJobIfNeeded(this, food.getInterval(this));
- }
- tile.setIcon(food.getIcon(this));
- tile.setLabel(food.getName(this));
- tile.setState(foodState != 0 ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE);
- tile.updateTile();
- }
-
- @Override
- public void onClick() {
- if (mPrefs.getFoodState() != 0) {
- // there's already food loaded, let's empty it
- MetricsLogger.count(this, "egg_neko_empty_food", 1);
- mPrefs.setFoodState(0);
- NekoService.cancelJob(this);
- } else {
- // time to feed the cats
- if (isLocked()) {
- if (isSecure()) {
- Log.d(TAG, "startActivityAndCollapse");
- Intent intent = new Intent(this, NekoLockedActivity.class);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- startActivityAndCollapse(intent);
- } else {
- unlockAndRun(new Runnable() {
- @Override
- public void run() {
- showNekoDialog();
- }
- });
- }
- } else {
- showNekoDialog();
- }
- }
- }
-
- private void showNekoDialog() {
- Log.d(TAG, "showNekoDialog");
- MetricsLogger.count(this, "egg_neko_select_food", 1);
- showDialog(new NekoDialog(this));
- }
-}
diff --git a/packages/EasterEgg/src/com/android/egg/neko/PrefState.java b/packages/EasterEgg/src/com/android/egg/neko/PrefState.java
deleted file mode 100644
index bf71b19..0000000
--- a/packages/EasterEgg/src/com/android/egg/neko/PrefState.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the
- * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the specific language governing
- * permissions and limitations under the License.
- */
-
-package com.android.egg.neko;
-
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-
-public class PrefState implements OnSharedPreferenceChangeListener {
-
- private static final String FILE_NAME = "mPrefs";
-
- private static final String FOOD_STATE = "food";
-
- private static final String CAT_KEY_PREFIX = "cat:";
-
- private final Context mContext;
- private final SharedPreferences mPrefs;
- private PrefsListener mListener;
-
- public PrefState(Context context) {
- mContext = context;
- mPrefs = mContext.getSharedPreferences(FILE_NAME, 0);
- }
-
- // Can also be used for renaming.
- public void addCat(Cat cat) {
- mPrefs.edit()
- .putString(CAT_KEY_PREFIX + String.valueOf(cat.getSeed()), cat.getName())
- .apply();
- }
-
- public void removeCat(Cat cat) {
- mPrefs.edit().remove(CAT_KEY_PREFIX + String.valueOf(cat.getSeed())).apply();
- }
-
- public List<Cat> getCats() {
- ArrayList<Cat> cats = new ArrayList<>();
- Map<String, ?> map = mPrefs.getAll();
- for (String key : map.keySet()) {
- if (key.startsWith(CAT_KEY_PREFIX)) {
- long seed = Long.parseLong(key.substring(CAT_KEY_PREFIX.length()));
- Cat cat = new Cat(mContext, seed);
- cat.setName(String.valueOf(map.get(key)));
- cats.add(cat);
- }
- }
- return cats;
- }
-
- public int getFoodState() {
- return mPrefs.getInt(FOOD_STATE, 0);
- }
-
- public void setFoodState(int foodState) {
- mPrefs.edit().putInt(FOOD_STATE, foodState).apply();
- }
-
- public void setListener(PrefsListener listener) {
- mListener = listener;
- if (mListener != null) {
- mPrefs.registerOnSharedPreferenceChangeListener(this);
- } else {
- mPrefs.unregisterOnSharedPreferenceChangeListener(this);
- }
- }
-
- @Override
- public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
- mListener.onPrefsChanged();
- }
-
- public interface PrefsListener {
- void onPrefsChanged();
- }
-}
diff --git a/packages/EasterEgg/src/com/android/egg/octo/Ocquarium.java b/packages/EasterEgg/src/com/android/egg/octo/Ocquarium.java
deleted file mode 100644
index 8a06cc6..0000000
--- a/packages/EasterEgg/src/com/android/egg/octo/Ocquarium.java
+++ /dev/null
@@ -1,89 +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.egg.octo;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-
-import com.android.egg.R;
-
-public class Ocquarium extends Activity {
- ImageView mImageView;
- private OctopusDrawable mOcto;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- final float dp = getResources().getDisplayMetrics().density;
-
- getWindow().setBackgroundDrawableResource(R.drawable.octo_bg);
-
- FrameLayout bg = new FrameLayout(this);
- setContentView(bg);
- bg.setAlpha(0f);
- bg.animate().setStartDelay(500).setDuration(5000).alpha(1f).start();
-
- mImageView = new ImageView(this);
- bg.addView(mImageView, new FrameLayout.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
-
- mOcto = new OctopusDrawable(getApplicationContext());
- mOcto.setSizePx((int) (OctopusDrawable.randfrange(40f,180f) * dp));
- mImageView.setImageDrawable(mOcto);
-
- mImageView.setOnTouchListener(new View.OnTouchListener() {
- boolean touching;
- @Override
- public boolean onTouch(View view, MotionEvent motionEvent) {
- switch (motionEvent.getActionMasked()) {
- case MotionEvent.ACTION_DOWN:
- if (mOcto.hitTest(motionEvent.getX(), motionEvent.getY())) {
- touching = true;
- mOcto.stopDrift();
- }
- break;
- case MotionEvent.ACTION_MOVE:
- if (touching) {
- mOcto.moveTo(motionEvent.getX(), motionEvent.getY());
- }
- break;
- case MotionEvent.ACTION_UP:
- case MotionEvent.ACTION_CANCEL:
- touching = false;
- mOcto.startDrift();
- break;
- }
- return true;
- }
- });
- }
-
- @Override
- protected void onPause() {
- mOcto.stopDrift();
- super.onPause();
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- mOcto.startDrift();
- }
-}
diff --git a/packages/EasterEgg/src/com/android/egg/octo/OctopusDrawable.java b/packages/EasterEgg/src/com/android/egg/octo/OctopusDrawable.java
deleted file mode 100644
index 4f8c11b..0000000
--- a/packages/EasterEgg/src/com/android/egg/octo/OctopusDrawable.java
+++ /dev/null
@@ -1,436 +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.egg.octo;
-
-import android.animation.TimeAnimator;
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.ColorFilter;
-import android.graphics.DashPathEffect;
-import android.graphics.Matrix;
-import android.graphics.Paint;
-import android.graphics.Path;
-import android.graphics.PixelFormat;
-import android.graphics.PointF;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import androidx.dynamicanimation.animation.DynamicAnimation;
-import androidx.dynamicanimation.animation.SpringForce;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.dynamicanimation.animation.SpringAnimation;
-import androidx.dynamicanimation.animation.FloatValueHolder;
-
-public class OctopusDrawable extends Drawable {
- private static float BASE_SCALE = 100f;
- public static boolean PATH_DEBUG = false;
-
- private static int BODY_COLOR = 0xFF101010;
- private static int ARM_COLOR = 0xFF101010;
- private static int ARM_COLOR_BACK = 0xFF000000;
- private static int EYE_COLOR = 0xFF808080;
-
- private static int[] BACK_ARMS = {1, 3, 4, 6};
- private static int[] FRONT_ARMS = {0, 2, 5, 7};
-
- private Paint mPaint = new Paint();
- private Arm[] mArms = new Arm[8];
- final PointF point = new PointF();
- private int mSizePx = 100;
- final Matrix M = new Matrix();
- final Matrix M_inv = new Matrix();
- private TimeAnimator mDriftAnimation;
- private boolean mBlinking;
- private float[] ptmp = new float[2];
- private float[] scaledBounds = new float[2];
-
- public static float randfrange(float a, float b) {
- return (float) (Math.random()*(b-a) + a);
- }
- public static float clamp(float v, float a, float b) {
- return v<a?a:v>b?b:v;
- }
-
- public OctopusDrawable(Context context) {
- float dp = context.getResources().getDisplayMetrics().density;
- setSizePx((int) (100*dp));
- mPaint.setAntiAlias(true);
- for (int i=0; i<mArms.length; i++) {
- final float bias = (float)i/(mArms.length-1) - 0.5f;
- mArms[i] = new Arm(
- 0,0, // arm will be repositioned on moveTo
- 10f*bias + randfrange(0,20f), randfrange(20f,50f),
- 40f*bias+randfrange(-60f,60f), randfrange(30f, 80f),
- randfrange(-40f,40f), randfrange(-80f,40f),
- 14f, 2f);
- }
- }
-
- public void setSizePx(int size) {
- mSizePx = size;
- M.setScale(mSizePx/BASE_SCALE, mSizePx/BASE_SCALE);
- // TaperedPathStroke.setMinStep(20f*BASE_SCALE/mSizePx); // nice little floaty circles
- TaperedPathStroke.setMinStep(8f*BASE_SCALE/mSizePx); // classic tentacles
- M.invert(M_inv);
- }
-
- public void startDrift() {
- if (mDriftAnimation == null) {
- mDriftAnimation = new TimeAnimator();
- mDriftAnimation.setTimeListener(new TimeAnimator.TimeListener() {
- float MAX_VY = 35f;
- float JUMP_VY = -100f;
- float MAX_VX = 15f;
- private float ax = 0f, ay = 30f;
- private float vx, vy;
- long nextjump = 0;
- long unblink = 0;
- @Override
- public void onTimeUpdate(TimeAnimator timeAnimator, long t, long dt) {
- float t_sec = 0.001f * t;
- float dt_sec = 0.001f * dt;
- if (t > nextjump) {
- vy = JUMP_VY;
- nextjump = t + (long) randfrange(5000, 10000);
- }
- if (unblink > 0 && t > unblink) {
- setBlinking(false);
- unblink = 0;
- } else if (Math.random() < 0.001f) {
- setBlinking(true);
- unblink = t + 200;
- }
-
- ax = (float) (MAX_VX * Math.sin(t_sec*.25f));
-
- vx = clamp(vx + dt_sec * ax, -MAX_VX, MAX_VX);
- vy = clamp(vy + dt_sec * ay, -100*MAX_VY, MAX_VY);
-
- // oob check
- if (point.y - BASE_SCALE/2 > scaledBounds[1]) {
- vy = JUMP_VY;
- } else if (point.y + BASE_SCALE < 0) {
- vy = MAX_VY;
- }
-
- point.x = clamp(point.x + dt_sec * vx, 0, scaledBounds[0]);
- point.y = point.y + dt_sec * vy;
-
- repositionArms();
- }
- });
- }
- mDriftAnimation.start();
- }
-
- public void stopDrift() {
- mDriftAnimation.cancel();
- }
-
- @Override
- public void onBoundsChange(Rect bounds) {
- final float w = bounds.width();
- final float h = bounds.height();
-
- lockArms(true);
- moveTo(w/2, h/2);
- lockArms(false);
-
- scaledBounds[0] = w;
- scaledBounds[1] = h;
- M_inv.mapPoints(scaledBounds);
- }
-
- // real pixel coordinates
- public void moveTo(float x, float y) {
- point.x = x;
- point.y = y;
- mapPointF(M_inv, point);
- repositionArms();
- }
-
- public boolean hitTest(float x, float y) {
- ptmp[0] = x;
- ptmp[1] = y;
- M_inv.mapPoints(ptmp);
- return Math.hypot(ptmp[0] - point.x, ptmp[1] - point.y) < BASE_SCALE/2;
- }
-
- private void lockArms(boolean l) {
- for (Arm arm : mArms) {
- arm.setLocked(l);
- }
- }
- private void repositionArms() {
- for (int i=0; i<mArms.length; i++) {
- final float bias = (float)i/(mArms.length-1) - 0.5f;
- mArms[i].setAnchor(
- point.x+bias*30f,point.y+26f);
- }
- invalidateSelf();
- }
-
- private void drawPupil(Canvas canvas, float x, float y, float size, boolean open,
- Paint pt) {
- final float r = open ? size*.33f : size * .1f;
- canvas.drawRoundRect(x - size, y - r, x + size, y + r, r, r, pt);
- }
-
- @Override
- public void draw(@NonNull Canvas canvas) {
- canvas.save();
- {
- canvas.concat(M);
-
- // arms behind
- mPaint.setColor(ARM_COLOR_BACK);
- for (int i : BACK_ARMS) {
- mArms[i].draw(canvas, mPaint);
- }
-
- // head/body/thing
- mPaint.setColor(EYE_COLOR);
- canvas.drawCircle(point.x, point.y, 36f, mPaint);
- mPaint.setColor(BODY_COLOR);
- canvas.save();
- {
- canvas.clipOutRect(point.x - 61f, point.y + 8f,
- point.x + 61f, point.y + 12f);
- canvas.drawOval(point.x-40f,point.y-60f,point.x+40f,point.y+40f, mPaint);
- }
- canvas.restore();
-
- // eyes
- mPaint.setColor(EYE_COLOR);
- if (mBlinking) {
- drawPupil(canvas, point.x - 16f, point.y - 12f, 6f, false, mPaint);
- drawPupil(canvas, point.x + 16f, point.y - 12f, 6f, false, mPaint);
- } else {
- canvas.drawCircle(point.x - 16f, point.y - 12f, 6f, mPaint);
- canvas.drawCircle(point.x + 16f, point.y - 12f, 6f, mPaint);
- }
-
- // too much?
- if (false) {
- mPaint.setColor(0xFF000000);
- drawPupil(canvas, point.x - 16f, point.y - 12f, 5f, true, mPaint);
- drawPupil(canvas, point.x + 16f, point.y - 12f, 5f, true, mPaint);
- }
-
- // arms in front
- mPaint.setColor(ARM_COLOR);
- for (int i : FRONT_ARMS) {
- mArms[i].draw(canvas, mPaint);
- }
-
- if (PATH_DEBUG) for (Arm arm : mArms) {
- arm.drawDebug(canvas);
- }
- }
- canvas.restore();
- }
-
- public void setBlinking(boolean b) {
- mBlinking = b;
- invalidateSelf();
- }
-
- @Override
- public void setAlpha(int i) {
- }
-
- @Override
- public void setColorFilter(@Nullable ColorFilter colorFilter) {
-
- }
-
- @Override
- public int getOpacity() {
- return PixelFormat.TRANSLUCENT;
- }
-
- static Path pathMoveTo(Path p, PointF pt) {
- p.moveTo(pt.x, pt.y);
- return p;
- }
- static Path pathQuadTo(Path p, PointF p1, PointF p2) {
- p.quadTo(p1.x, p1.y, p2.x, p2.y);
- return p;
- }
-
- static void mapPointF(Matrix m, PointF point) {
- float[] p = new float[2];
- p[0] = point.x;
- p[1] = point.y;
- m.mapPoints(p);
- point.x = p[0];
- point.y = p[1];
- }
-
- private class Link // he come to town
- implements DynamicAnimation.OnAnimationUpdateListener {
- final FloatValueHolder[] coords = new FloatValueHolder[2];
- final SpringAnimation[] anims = new SpringAnimation[coords.length];
- private float dx, dy;
- private boolean locked = false;
- Link next;
-
- Link(int index, float x1, float y1, float dx, float dy) {
- coords[0] = new FloatValueHolder(x1);
- coords[1] = new FloatValueHolder(y1);
- this.dx = dx;
- this.dy = dy;
- for (int i=0; i<coords.length; i++) {
- anims[i] = new SpringAnimation(coords[i]);
- anims[i].setSpring(new SpringForce()
- .setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY)
- .setStiffness(
- index == 0 ? SpringForce.STIFFNESS_LOW
- : index == 1 ? SpringForce.STIFFNESS_VERY_LOW
- : SpringForce.STIFFNESS_VERY_LOW/2)
- .setFinalPosition(0f));
- anims[i].addUpdateListener(this);
- }
- }
- public void setLocked(boolean locked) {
- this.locked = locked;
- }
- public PointF start() {
- return new PointF(coords[0].getValue(), coords[1].getValue());
- }
- public PointF end() {
- return new PointF(coords[0].getValue()+dx,coords[1].getValue()+dy);
- }
- public PointF mid() {
- return new PointF(
- 0.5f*dx+(coords[0].getValue()),
- 0.5f*dy+(coords[1].getValue()));
- }
- public void animateTo(PointF target) {
- if (locked) {
- setStart(target.x, target.y);
- } else {
- anims[0].animateToFinalPosition(target.x);
- anims[1].animateToFinalPosition(target.y);
- }
- }
- @Override
- public void onAnimationUpdate(DynamicAnimation dynamicAnimation, float v, float v1) {
- if (next != null) {
- next.animateTo(end());
- }
- OctopusDrawable.this.invalidateSelf();
- }
-
- public void setStart(float x, float y) {
- coords[0].setValue(x);
- coords[1].setValue(y);
- onAnimationUpdate(null, 0, 0);
- }
- }
-
- private class Arm {
- final Link link1, link2, link3;
- float max, min;
-
- public Arm(float x, float y, float dx1, float dy1, float dx2, float dy2, float dx3, float dy3,
- float max, float min) {
- link1 = new Link(0, x, y, dx1, dy1);
- link2 = new Link(1, x+dx1, y+dy1, dx2, dy2);
- link3 = new Link(2, x+dx1+dx2, y+dy1+dy2, dx3, dy3);
- link1.next = link2;
- link2.next = link3;
-
- link1.setLocked(true);
- link2.setLocked(false);
- link3.setLocked(false);
-
- this.max = max;
- this.min = min;
- }
-
- // when the arm is locked, it moves rigidly, without physics
- public void setLocked(boolean locked) {
- link2.setLocked(locked);
- link3.setLocked(locked);
- }
-
- private void setAnchor(float x, float y) {
- link1.setStart(x,y);
- }
-
- public Path getPath() {
- Path p = new Path();
- pathMoveTo(p, link1.start());
- pathQuadTo(p, link2.start(), link2.mid());
- pathQuadTo(p, link2.end(), link3.end());
- return p;
- }
-
- public void draw(@NonNull Canvas canvas, Paint pt) {
- final Path p = getPath();
- TaperedPathStroke.drawPath(canvas, p, max, min, pt);
- }
-
- private final Paint dpt = new Paint();
- public void drawDebug(Canvas canvas) {
- dpt.setStyle(Paint.Style.STROKE);
- dpt.setStrokeWidth(0.75f);
- dpt.setStrokeCap(Paint.Cap.ROUND);
-
- dpt.setAntiAlias(true);
- dpt.setColor(0xFF336699);
-
- final Path path = getPath();
- canvas.drawPath(path, dpt);
-
- dpt.setColor(0xFFFFFF00);
-
- dpt.setPathEffect(new DashPathEffect(new float[] {2f, 2f}, 0f));
-
- canvas.drawLines(new float[] {
- link1.end().x, link1.end().y,
- link2.start().x, link2.start().y,
-
- link2.end().x, link2.end().y,
- link3.start().x, link3.start().y,
- }, dpt);
- dpt.setPathEffect(null);
-
- dpt.setColor(0xFF00CCFF);
-
- canvas.drawLines(new float[] {
- link1.start().x, link1.start().y,
- link1.end().x, link1.end().y,
-
- link2.start().x, link2.start().y,
- link2.end().x, link2.end().y,
-
- link3.start().x, link3.start().y,
- link3.end().x, link3.end().y,
- }, dpt);
-
- dpt.setColor(0xFFCCEEFF);
- canvas.drawCircle(link2.start().x, link2.start().y, 2f, dpt);
- canvas.drawCircle(link3.start().x, link3.start().y, 2f, dpt);
-
- dpt.setStyle(Paint.Style.FILL_AND_STROKE);
- canvas.drawCircle(link1.start().x, link1.start().y, 2f, dpt);
- canvas.drawCircle(link2.mid().x, link2.mid().y, 2f, dpt);
- canvas.drawCircle(link3.end().x, link3.end().y, 2f, dpt);
- }
-
- }
-}
diff --git a/packages/EasterEgg/src/com/android/egg/octo/TaperedPathStroke.java b/packages/EasterEgg/src/com/android/egg/octo/TaperedPathStroke.java
deleted file mode 100644
index e014fbc..0000000
--- a/packages/EasterEgg/src/com/android/egg/octo/TaperedPathStroke.java
+++ /dev/null
@@ -1,55 +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.egg.octo;
-
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.Path;
-import android.graphics.PathMeasure;
-import android.os.Debug;
-
-import java.util.Arrays;
-
-public class TaperedPathStroke {
- static float sMinStepPx = 4f;
- static PathMeasure pm = new PathMeasure();
- static float[] pos = {0,0};
- static float[] tan = {0,0};
- static float lerp(float t, float a, float b) {
- return a + t*(b-a);
- }
- public static void setMinStep(float px) {
- sMinStepPx = px;
- }
-
- // it's the variable-width brush algorithm from the Markers app, basically
- public static void drawPath(Canvas c, Path p, float r1, float r2, Paint pt) {
- pm.setPath(p,false);
- final float len = pm.getLength();
- float t=0;
- boolean last=false;
- while (true) {
- if (t>=len) {
- t=len;
- last=true;
- }
- pm.getPosTan(t, pos, tan);
- float r = len > 0 ? lerp(t/len, r1, r2) : r1;
- c.drawCircle(pos[0], pos[1], r, pt);
- t += Math.max(r*0.25f, sMinStepPx); // walk forward 1/4 radius, not too small though
- if (last) break;
- }
- }
-}
diff --git a/packages/EasterEgg/src/com/android/egg/paint/BrushPropertyDrawable.kt b/packages/EasterEgg/src/com/android/egg/paint/BrushPropertyDrawable.kt
new file mode 100644
index 0000000..4a02ee6
--- /dev/null
+++ b/packages/EasterEgg/src/com/android/egg/paint/BrushPropertyDrawable.kt
@@ -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 com.android.egg.paint
+
+import android.content.Context
+import android.graphics.*
+import android.graphics.PixelFormat.TRANSLUCENT
+import android.graphics.drawable.Drawable
+import android.util.DisplayMetrics
+
+class BrushPropertyDrawable : Drawable {
+ val framePaint = Paint(Paint.ANTI_ALIAS_FLAG).also {
+ it.color = Color.BLACK
+ it.style = Paint.Style.FILL
+ }
+ val wellPaint = Paint(Paint.ANTI_ALIAS_FLAG).also {
+ it.color = Color.RED
+ it.style = Paint.Style.FILL
+ }
+
+ constructor(context: Context) {
+ _size = (24 * context.resources.displayMetrics.density).toInt()
+ }
+
+ private var _size = 24
+ private var _scale = 1f
+
+ fun setFrameColor(color: Int) {
+ framePaint.color = color
+ invalidateSelf()
+ }
+
+ fun setWellColor(color: Int) {
+ wellPaint.color = color
+ invalidateSelf()
+ }
+
+ fun setWellScale(scale: Float) {
+ _scale = scale
+ invalidateSelf()
+ }
+
+ override fun getIntrinsicWidth(): Int {
+ return _size
+ }
+
+ override fun getIntrinsicHeight(): Int {
+ return _size
+ }
+
+ override fun draw(c: Canvas?) {
+ c?.let {
+ val w = bounds.width().toFloat()
+ val h = bounds.height().toFloat()
+ val inset = _size / 12 // 2dp in a 24x24 icon
+ val r = Math.min(w, h) / 2
+
+ c.drawCircle(w/2, h/2, (r - inset) * _scale + 1 , wellPaint)
+
+ val p = Path()
+ p.addCircle(w/2, h/2, r, Path.Direction.CCW)
+ p.addCircle(w/2, h/2, r - inset, Path.Direction.CW)
+ c.drawPath(p, framePaint)
+ }
+ }
+
+ override fun setAlpha(p0: Int) {
+ //
+ }
+
+ override fun getOpacity(): Int {
+ return TRANSLUCENT
+ }
+
+ override fun setColorFilter(p0: ColorFilter?) {
+ //
+ }
+
+}
\ No newline at end of file
diff --git a/packages/EasterEgg/src/com/android/egg/paint/CutoutAvoidingToolbar.kt b/packages/EasterEgg/src/com/android/egg/paint/CutoutAvoidingToolbar.kt
new file mode 100644
index 0000000..164fc5a
--- /dev/null
+++ b/packages/EasterEgg/src/com/android/egg/paint/CutoutAvoidingToolbar.kt
@@ -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.egg.paint
+
+import android.content.Context
+import android.util.AttributeSet
+import android.view.*
+import android.view.ViewGroup.LayoutParams.MATCH_PARENT
+import android.widget.LinearLayout
+
+class CutoutAvoidingToolbar : LinearLayout {
+ private var _insets: WindowInsets? = null
+
+ constructor(context: Context) : super(context) {
+ init(null, 0)
+ }
+
+ constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
+ init(attrs, 0)
+ }
+
+ constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(context, attrs, defStyle) {
+ init(attrs, defStyle)
+ }
+
+ override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
+ super.onSizeChanged(w, h, oldw, oldh)
+ adjustLayout()
+ }
+
+ override fun onApplyWindowInsets(insets: WindowInsets?): WindowInsets {
+ _insets = insets
+ adjustLayout()
+ return super.onApplyWindowInsets(insets)
+ }
+
+ fun adjustLayout() {
+ _insets?.displayCutout?.boundingRects?.let {
+ var cutoutCenter = 0
+ var cutoutLeft = 0
+ var cutoutRight = 0
+
+ // collect at most three cutouts
+ for (r in it) {
+ if (r.top > 0) continue
+
+ if (r.left == left) {
+ cutoutLeft = r.width()
+ } else if (r.right == right) {
+ cutoutRight = r.width()
+ } else {
+ cutoutCenter = r.width()
+ }
+ }
+
+ // apply to layout
+ (findViewWithTag("cutoutLeft") as View?)?.let {
+ it.layoutParams = LayoutParams(cutoutLeft, MATCH_PARENT)
+ }
+ (findViewWithTag("cutoutCenter") as View?)?.let {
+ it.layoutParams = LayoutParams(cutoutCenter, MATCH_PARENT)
+ }
+ (findViewWithTag("cutoutRight") as View?)?.let {
+ it.layoutParams = LayoutParams(cutoutRight, MATCH_PARENT)
+ }
+
+ requestLayout()
+ }
+ }
+
+ private fun init(attrs: AttributeSet?, defStyle: Int) {
+ }
+
+}
diff --git a/packages/EasterEgg/src/com/android/egg/paint/PaintActivity.java b/packages/EasterEgg/src/com/android/egg/paint/PaintActivity.java
new file mode 100644
index 0000000..ac47fbd
--- /dev/null
+++ b/packages/EasterEgg/src/com/android/egg/paint/PaintActivity.java
@@ -0,0 +1,351 @@
+/*
+ * 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.egg.paint;
+
+import static android.view.MotionEvent.ACTION_CANCEL;
+import static android.view.MotionEvent.ACTION_DOWN;
+import static android.view.MotionEvent.ACTION_MOVE;
+import static android.view.MotionEvent.ACTION_UP;
+
+import android.app.Activity;
+import android.content.res.Configuration;
+import android.graphics.Bitmap;
+import android.graphics.Color;
+import android.os.Bundle;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.view.animation.OvershootInterpolator;
+import android.widget.FrameLayout;
+import android.widget.ImageButton;
+import android.widget.LinearLayout;
+import android.widget.Magnifier;
+
+import com.android.egg.R;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.stream.IntStream;
+
+public class PaintActivity extends Activity {
+ private static final float MAX_BRUSH_WIDTH_DP = 100f;
+ private static final float MIN_BRUSH_WIDTH_DP = 1f;
+
+ private static final int NUM_BRUSHES = 6;
+ private static final int NUM_COLORS = 6;
+
+ private Painting painting = null;
+ private CutoutAvoidingToolbar toolbar = null;
+ private LinearLayout brushes = null;
+ private LinearLayout colors = null;
+ private Magnifier magnifier = null;
+ private boolean sampling = false;
+
+ private View.OnClickListener buttonHandler = new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ switch (view.getId()) {
+ case R.id.btnBrush:
+ view.setSelected(true);
+ hideToolbar(colors);
+ toggleToolbar(brushes);
+ break;
+ case R.id.btnColor:
+ view.setSelected(true);
+ hideToolbar(brushes);
+ toggleToolbar(colors);
+ break;
+ case R.id.btnClear:
+ painting.clear();
+ break;
+ case R.id.btnSample:
+ sampling = true;
+ view.setSelected(true);
+ break;
+ case R.id.btnZen:
+ painting.setZenMode(!painting.getZenMode());
+ view.animate()
+ .setStartDelay(200)
+ .setInterpolator(new OvershootInterpolator())
+ .rotation(painting.getZenMode() ? 0f : 90f);
+ break;
+ }
+ }
+ };
+
+ private void showToolbar(View bar) {
+ if (bar.getVisibility() != View.GONE) return;
+ bar.setVisibility(View.VISIBLE);
+ bar.setTranslationY(toolbar.getHeight()/2);
+ bar.animate()
+ .translationY(toolbar.getHeight())
+ .alpha(1f)
+ .setDuration(220)
+ .start();
+ }
+
+ private void hideToolbar(View bar) {
+ if (bar.getVisibility() != View.VISIBLE) return;
+ bar.animate()
+ .translationY(toolbar.getHeight()/2)
+ .alpha(0f)
+ .setDuration(150)
+ .withEndAction(new Runnable() {
+ @Override
+ public void run() {
+ bar.setVisibility(View.GONE);
+ }
+ })
+ .start();
+ }
+
+ private void toggleToolbar(View bar) {
+ if (bar.getVisibility() == View.VISIBLE) {
+ hideToolbar(bar);
+ } else {
+ showToolbar(bar);
+ }
+ }
+
+ private BrushPropertyDrawable widthButtonDrawable;
+ private BrushPropertyDrawable colorButtonDrawable;
+ private float maxBrushWidth, minBrushWidth;
+ private int nightMode = Configuration.UI_MODE_NIGHT_UNDEFINED;
+
+ static final float lerp(float f, float a, float b) {
+ return a + (b-a) * f;
+ }
+
+ void setupViews(Painting oldPainting) {
+ setContentView(R.layout.activity_paint);
+
+ painting = oldPainting != null ? oldPainting : new Painting(this);
+ ((FrameLayout) findViewById(R.id.contentView)).addView(painting,
+ new FrameLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT));
+
+ painting.setPaperColor(getColor(R.color.paper_color));
+ painting.setPaintColor(getColor(R.color.paint_color));
+
+ toolbar = findViewById(R.id.toolbar);
+ brushes = findViewById(R.id.brushes);
+ colors = findViewById(R.id.colors);
+
+ magnifier = new Magnifier(painting);
+
+ painting.setOnTouchListener(
+ new View.OnTouchListener() {
+ @Override
+ public boolean onTouch(View view, MotionEvent event) {
+ switch (event.getActionMasked()) {
+ case ACTION_DOWN:
+ case ACTION_MOVE:
+ if (sampling) {
+ magnifier.show(event.getX(), event.getY());
+ colorButtonDrawable.setWellColor(
+ painting.sampleAt(event.getX(), event.getY()));
+ return true;
+ }
+ break;
+ case ACTION_CANCEL:
+ if (sampling) {
+ findViewById(R.id.btnSample).setSelected(false);
+ sampling = false;
+ magnifier.dismiss();
+ }
+ break;
+ case ACTION_UP:
+ if (sampling) {
+ findViewById(R.id.btnSample).setSelected(false);
+ sampling = false;
+ magnifier.dismiss();
+ painting.setPaintColor(
+ painting.sampleAt(event.getX(), event.getY()));
+ refreshBrushAndColor();
+ }
+ break;
+ }
+ return false; // allow view to continue handling
+ }
+ });
+
+ findViewById(R.id.btnBrush).setOnClickListener(buttonHandler);
+ findViewById(R.id.btnColor).setOnClickListener(buttonHandler);
+ findViewById(R.id.btnClear).setOnClickListener(buttonHandler);
+ findViewById(R.id.btnSample).setOnClickListener(buttonHandler);
+ findViewById(R.id.btnZen).setOnClickListener(buttonHandler);
+
+ findViewById(R.id.btnColor).setOnLongClickListener(new View.OnLongClickListener() {
+ @Override
+ public boolean onLongClick(View view) {
+ colors.removeAllViews();
+ showToolbar(colors);
+ refreshBrushAndColor();
+ return true;
+ }
+ });
+
+ findViewById(R.id.btnClear).setOnLongClickListener(new View.OnLongClickListener() {
+ @Override
+ public boolean onLongClick(View view) {
+ painting.invertContents();
+ return true;
+ }
+ });
+
+ widthButtonDrawable = new BrushPropertyDrawable(this);
+ widthButtonDrawable.setFrameColor(getColor(R.color.toolbar_icon_color));
+ colorButtonDrawable = new BrushPropertyDrawable(this);
+ colorButtonDrawable.setFrameColor(getColor(R.color.toolbar_icon_color));
+
+ ((ImageButton) findViewById(R.id.btnBrush)).setImageDrawable(widthButtonDrawable);
+ ((ImageButton) findViewById(R.id.btnColor)).setImageDrawable(colorButtonDrawable);
+
+ refreshBrushAndColor();
+ }
+
+ private void refreshBrushAndColor() {
+ final LinearLayout.LayoutParams button_lp = new LinearLayout.LayoutParams(
+ 0, ViewGroup.LayoutParams.WRAP_CONTENT);
+ button_lp.weight = 1f;
+ if (brushes.getChildCount() == 0) {
+ for (int i = 0; i < NUM_BRUSHES; i++) {
+ final BrushPropertyDrawable icon = new BrushPropertyDrawable(this);
+ icon.setFrameColor(getColor(R.color.toolbar_icon_color));
+ // exponentially increasing brush size
+ final float width = lerp(
+ (float) Math.pow((float) i / NUM_BRUSHES, 2f), minBrushWidth,
+ maxBrushWidth);
+ icon.setWellScale(width / maxBrushWidth);
+ icon.setWellColor(getColor(R.color.toolbar_icon_color));
+ final ImageButton button = new ImageButton(this);
+ button.setImageDrawable(icon);
+ button.setBackground(getDrawable(R.drawable.toolbar_button_bg));
+ button.setOnClickListener(
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ brushes.setSelected(false);
+ hideToolbar(brushes);
+ painting.setBrushWidth(width);
+ refreshBrushAndColor();
+ }
+ });
+ brushes.addView(button, button_lp);
+ }
+ }
+
+ if (colors.getChildCount() == 0) {
+ final Palette pal = new Palette(NUM_COLORS);
+ for (final int c : IntStream.concat(
+ IntStream.of(Color.BLACK, Color.WHITE),
+ Arrays.stream(pal.getColors())
+ ).toArray()) {
+ final BrushPropertyDrawable icon = new BrushPropertyDrawable(this);
+ icon.setFrameColor(getColor(R.color.toolbar_icon_color));
+ icon.setWellColor(c);
+ final ImageButton button = new ImageButton(this);
+ button.setImageDrawable(icon);
+ button.setBackground(getDrawable(R.drawable.toolbar_button_bg));
+ button.setOnClickListener(
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ colors.setSelected(false);
+ hideToolbar(colors);
+ painting.setPaintColor(c);
+ refreshBrushAndColor();
+ }
+ });
+ colors.addView(button, button_lp);
+ }
+ }
+
+ widthButtonDrawable.setWellScale(painting.getBrushWidth() / maxBrushWidth);
+ widthButtonDrawable.setWellColor(painting.getPaintColor());
+ colorButtonDrawable.setWellColor(painting.getPaintColor());
+ }
+
+ private void refreshNightMode(Configuration config) {
+ int newNightMode =
+ (config.uiMode & Configuration.UI_MODE_NIGHT_MASK);
+ if (nightMode != newNightMode) {
+ if (nightMode != Configuration.UI_MODE_NIGHT_UNDEFINED) {
+ painting.invertContents();
+
+ ((ViewGroup) painting.getParent()).removeView(painting);
+ setupViews(painting);
+
+ final View decorView = getWindow().getDecorView();
+ int decorSUIV = decorView.getSystemUiVisibility();
+
+ if (newNightMode == Configuration.UI_MODE_NIGHT_YES) {
+ decorView.setSystemUiVisibility(
+ decorSUIV & ~View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR);
+ } else {
+ decorView.setSystemUiVisibility(
+ decorSUIV | View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR);
+ }
+ }
+ nightMode = newNightMode;
+ }
+ }
+
+ public PaintActivity() {
+
+ }
+
+ @Override
+ public void onTrimMemory(int level) {
+ super.onTrimMemory(level);
+
+ painting.onTrimMemory();
+ }
+
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+
+ refreshNightMode(newConfig);
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ WindowManager.LayoutParams lp = getWindow().getAttributes();
+ lp.flags = lp.flags
+ | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+ | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
+ getWindow().setAttributes(lp);
+
+ maxBrushWidth = MAX_BRUSH_WIDTH_DP * getResources().getDisplayMetrics().density;
+ minBrushWidth = MIN_BRUSH_WIDTH_DP * getResources().getDisplayMetrics().density;
+
+ setupViews(null);
+ refreshNightMode(getResources().getConfiguration());
+ }
+
+ @Override
+ public void onPostResume() {
+ super.onPostResume();
+ }
+
+}
diff --git a/packages/EasterEgg/src/com/android/egg/paint/Painting.kt b/packages/EasterEgg/src/com/android/egg/paint/Painting.kt
new file mode 100644
index 0000000..a4a3d3d
--- /dev/null
+++ b/packages/EasterEgg/src/com/android/egg/paint/Painting.kt
@@ -0,0 +1,358 @@
+/*
+ * 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.egg.paint
+
+import android.content.Context
+import android.content.res.Resources
+import android.graphics.*
+import android.provider.Settings
+import android.util.AttributeSet
+import android.util.DisplayMetrics
+import android.view.MotionEvent
+import android.view.View
+import android.view.WindowInsets
+import java.util.concurrent.TimeUnit
+import android.util.Log
+import android.provider.Settings.System
+
+import org.json.JSONObject
+
+fun hypot(x: Float, y: Float): Float {
+ return Math.hypot(x.toDouble(), y.toDouble()).toFloat()
+}
+
+fun invlerp(x: Float, a: Float, b: Float): Float {
+ return if (b > a) {
+ (x - a) / (b - a)
+ } else 1.0f
+}
+
+public class Painting : View, SpotFilter.Plotter {
+ companion object {
+ val FADE_MINS = TimeUnit.MINUTES.toMillis(3) // about how long a drawing should last
+ val ZEN_RATE = TimeUnit.SECONDS.toMillis(2) // how often to apply the fade
+ val ZEN_FADE = Math.max(1f, ZEN_RATE / FADE_MINS * 255f)
+
+ val FADE_TO_WHITE_CF = ColorMatrixColorFilter(ColorMatrix(floatArrayOf(
+ 1f, 0f, 0f, 0f, ZEN_FADE,
+ 0f, 1f, 0f, 0f, ZEN_FADE,
+ 0f, 0f, 1f, 0f, ZEN_FADE,
+ 0f, 0f, 0f, 1f, 0f
+ )))
+
+ val FADE_TO_BLACK_CF = ColorMatrixColorFilter(ColorMatrix(floatArrayOf(
+ 1f, 0f, 0f, 0f, -ZEN_FADE,
+ 0f, 1f, 0f, 0f, -ZEN_FADE,
+ 0f, 0f, 1f, 0f, -ZEN_FADE,
+ 0f, 0f, 0f, 1f, 0f
+ )))
+
+ val INVERT_CF = ColorMatrixColorFilter(ColorMatrix(floatArrayOf(
+ -1f, 0f, 0f, 0f, 255f,
+ 0f, -1f, 0f, 0f, 255f,
+ 0f, 0f, -1f, 0f, 255f,
+ 0f, 0f, 0f, 1f, 0f
+ )))
+
+ val TOUCH_STATS = "touch.stats" // Settings.System key
+ }
+
+ var devicePressureMin = 0f; // ideal value
+ var devicePressureMax = 1f; // ideal value
+
+ var zenMode = true
+ set(value) {
+ if (field != value) {
+ field = value
+ removeCallbacks(fadeRunnable)
+ if (value && isAttachedToWindow) {
+ handler.postDelayed(fadeRunnable, ZEN_RATE)
+ }
+ }
+ }
+
+ var bitmap: Bitmap? = null
+ var paperColor : Int = 0xFFFFFFFF.toInt()
+
+ private var _paintCanvas: Canvas? = null
+ private val _bitmapLock = Object()
+
+ private var _drawPaint = Paint(Paint.ANTI_ALIAS_FLAG)
+ private var _lastX = 0f
+ private var _lastY = 0f
+ private var _lastR = 0f
+ private var _insets: WindowInsets? = null
+
+ private var _brushWidth = 100f
+
+ private var _filter = SpotFilter(10, 0.5f, 0.9f, this)
+
+ private val fadeRunnable = object : Runnable {
+ private val pt = Paint()
+ override fun run() {
+ val c = _paintCanvas
+ if (c != null) {
+ pt.colorFilter =
+ if (paperColor.and(0xFF) > 0x80)
+ FADE_TO_WHITE_CF
+ else
+ FADE_TO_BLACK_CF
+
+ synchronized(_bitmapLock) {
+ c.drawBitmap(bitmap, 0f, 0f, pt)
+ }
+ invalidate()
+ }
+ postDelayed(this, ZEN_RATE)
+ }
+ }
+
+ constructor(context: Context) : super(context) {
+ init(null, 0)
+ }
+
+ constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
+ init(attrs, 0)
+ }
+
+ constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(context, attrs, defStyle) {
+ init(attrs, defStyle)
+ }
+
+ private fun init(attrs: AttributeSet?, defStyle: Int) {
+ loadDevicePressureData()
+ }
+
+ override fun onAttachedToWindow() {
+ super.onAttachedToWindow()
+
+ setupBitmaps()
+
+ if (zenMode) {
+ handler.postDelayed(fadeRunnable, ZEN_RATE)
+ }
+ }
+
+ override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
+ super.onSizeChanged(w, h, oldw, oldh)
+ setupBitmaps()
+ }
+
+ override fun onDetachedFromWindow() {
+ if (zenMode) {
+ removeCallbacks(fadeRunnable)
+ }
+ super.onDetachedFromWindow()
+ }
+
+ fun onTrimMemory() {
+ }
+
+ override fun onApplyWindowInsets(insets: WindowInsets?): WindowInsets {
+ _insets = insets
+ if (insets != null && _paintCanvas == null) {
+ setupBitmaps()
+ }
+ return super.onApplyWindowInsets(insets)
+ }
+
+ private fun powf(a: Float, b: Float): Float {
+ return Math.pow(a.toDouble(), b.toDouble()).toFloat()
+ }
+
+ override fun plot(s: MotionEvent.PointerCoords) {
+ val c = _paintCanvas
+ if (c == null) return
+ synchronized(_bitmapLock) {
+ var x = _lastX
+ var y = _lastY
+ var r = _lastR
+ val newR = Math.max(1f, powf(adjustPressure(s.pressure), 2f).toFloat() * _brushWidth)
+
+ if (r >= 0) {
+ val d = hypot(s.x - x, s.y - y)
+ if (d > 1f && (r + newR) > 1f) {
+ val N = (2 * d / Math.min(4f, r + newR)).toInt()
+
+ val stepX = (s.x - x) / N
+ val stepY = (s.y - y) / N
+ val stepR = (newR - r) / N
+ for (i in 0 until N - 1) { // we will draw the last circle below
+ x += stepX
+ y += stepY
+ r += stepR
+ c.drawCircle(x, y, r, _drawPaint)
+ }
+ }
+ }
+
+ c.drawCircle(s.x, s.y, newR, _drawPaint)
+ _lastX = s.x
+ _lastY = s.y
+ _lastR = newR
+ }
+ }
+
+ private fun loadDevicePressureData() {
+ try {
+ val touchDataJson = Settings.System.getString(context.contentResolver, TOUCH_STATS)
+ val touchData = JSONObject(
+ if (touchDataJson != null) touchDataJson else "{}")
+ if (touchData.has("min")) devicePressureMin = touchData.getDouble("min").toFloat()
+ if (touchData.has("max")) devicePressureMax = touchData.getDouble("max").toFloat()
+ if (devicePressureMin < 0) devicePressureMin = 0f
+ if (devicePressureMax < devicePressureMin) devicePressureMax = devicePressureMin + 1f
+ } catch (e: Exception) {
+ }
+ }
+
+ private fun adjustPressure(pressure: Float): Float {
+ if (pressure > devicePressureMax) devicePressureMax = pressure
+ if (pressure < devicePressureMin) devicePressureMin = pressure
+ return invlerp(pressure, devicePressureMin, devicePressureMax)
+ }
+
+ override fun onTouchEvent(event: MotionEvent?): Boolean {
+ val c = _paintCanvas
+ if (event == null || c == null) return super.onTouchEvent(event)
+
+ /*
+ val pt = Paint(Paint.ANTI_ALIAS_FLAG)
+ pt.style = Paint.Style.STROKE
+ pt.color = 0x800000FF.toInt()
+ _paintCanvas?.drawCircle(event.x, event.y, 20f, pt)
+ */
+
+ when (event.actionMasked) {
+ MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
+ _filter.add(event)
+ _filter.finish()
+ invalidate()
+ }
+
+ MotionEvent.ACTION_DOWN -> {
+ _lastR = -1f
+ _filter.add(event)
+ invalidate()
+ }
+
+ MotionEvent.ACTION_MOVE -> {
+ _filter.add(event)
+
+ invalidate()
+ }
+ }
+
+ return true
+ }
+
+ override fun onDraw(canvas: Canvas) {
+ super.onDraw(canvas)
+
+ bitmap?.let {
+ canvas.drawBitmap(bitmap, 0f, 0f, _drawPaint);
+ }
+ }
+
+ // public api
+ fun clear() {
+ bitmap = null
+ setupBitmaps()
+ invalidate()
+ }
+
+ fun sampleAt(x: Float, y: Float): Int {
+ val localX = (x - left).toInt()
+ val localY = (y - top).toInt()
+ return bitmap?.getPixel(localX, localY) ?: Color.BLACK
+ }
+
+ fun setPaintColor(color: Int) {
+ _drawPaint.color = color
+ }
+
+ fun getPaintColor(): Int {
+ return _drawPaint.color
+ }
+
+ fun setBrushWidth(w: Float) {
+ _brushWidth = w
+ }
+
+ fun getBrushWidth(): Float {
+ return _brushWidth
+ }
+
+ private fun setupBitmaps() {
+ val dm = DisplayMetrics()
+ display.getRealMetrics(dm)
+ val w = dm.widthPixels
+ val h = dm.heightPixels
+ val oldBits = bitmap
+ var bits = oldBits
+ if (bits == null || bits.width != w || bits.height != h) {
+ bits = Bitmap.createBitmap(
+ w,
+ h,
+ Bitmap.Config.ARGB_8888
+ )
+ }
+ if (bits == null) return
+
+ val c = Canvas(bits)
+
+ if (oldBits != null) {
+ if (oldBits.width < oldBits.height != bits.width < bits.height) {
+ // orientation change. let's rotate things so they fit better
+ val matrix = Matrix()
+ if (bits.width > bits.height) {
+ // now landscape
+ matrix.postRotate(-90f)
+ matrix.postTranslate(0f, bits.height.toFloat())
+ } else {
+ // now portrait
+ matrix.postRotate(90f)
+ matrix.postTranslate(bits.width.toFloat(), 0f)
+ }
+ if (bits.width != oldBits.height || bits.height != oldBits.width) {
+ matrix.postScale(
+ bits.width.toFloat()/oldBits.height,
+ bits.height.toFloat()/oldBits.width)
+ }
+ c.matrix = matrix
+ }
+ // paint the old artwork atop the new
+ c.drawBitmap(oldBits, 0f, 0f, _drawPaint)
+ c.matrix = Matrix()
+ } else {
+ c.drawColor(paperColor)
+ }
+
+ bitmap = bits
+ _paintCanvas = c
+ }
+
+ fun invertContents() {
+ val invertPaint = Paint()
+ invertPaint.colorFilter = INVERT_CF
+ synchronized(_bitmapLock) {
+ _paintCanvas?.drawBitmap(bitmap, 0f, 0f, invertPaint)
+ }
+ invalidate()
+ }
+}
+
diff --git a/packages/EasterEgg/src/com/android/egg/paint/Palette.kt b/packages/EasterEgg/src/com/android/egg/paint/Palette.kt
new file mode 100644
index 0000000..7043efe
--- /dev/null
+++ b/packages/EasterEgg/src/com/android/egg/paint/Palette.kt
@@ -0,0 +1,83 @@
+/*
+ * 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.egg.paint
+
+import android.graphics.Color
+
+class Palette {
+ var colors : IntArray
+ var lightest = 0
+ var darkest = 0
+
+ /**
+ * rough luminance calculation
+ * https://www.w3.org/TR/AERT/#color-contrast
+ */
+ private fun lum(rgb: Int): Float {
+ return (Color.red(rgb) * 299f + Color.green(rgb) * 587f + Color.blue(rgb) * 114f) / 1000f
+ }
+
+ /**
+ * create a random evenly-spaced color palette
+ * guaranteed to contrast!
+ */
+ fun randomize(S: Float, V: Float) {
+ val hsv = floatArrayOf((Math.random() * 360f).toFloat(), S, V)
+ val count = colors.size
+ colors[0] = Color.HSVToColor(hsv)
+ lightest = 0
+ darkest = 0
+
+ for (i in 0 until count) {
+ hsv[0] = (hsv[0] + 360f / count).rem(360f)
+ val color = Color.HSVToColor(hsv)
+ colors[i] = color
+
+ val lum = lum(colors[i])
+ if (lum < lum(colors[darkest])) darkest = i
+ if (lum > lum(colors[lightest])) lightest = i
+ }
+ }
+
+ override fun toString() : String {
+ val str = StringBuilder("Palette{ ")
+ for (c in colors) {
+ str.append(String.format("#%08x ", c))
+ }
+ str.append("}")
+ return str.toString()
+ }
+
+ constructor(count: Int) {
+ colors = IntArray(count)
+ randomize(1f, 1f)
+ }
+
+ constructor(count: Int, S: Float, V: Float) {
+ colors = IntArray(count)
+ randomize(S, V)
+ }
+
+ constructor(_colors: IntArray) {
+ colors = _colors
+ for (i in 0 until colors.size) {
+ val lum = lum(colors[i])
+ if (lum < lum(colors[darkest])) darkest = i
+ if (lum > lum(colors[lightest])) lightest = i
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/EasterEgg/src/com/android/egg/paint/SpotFilter.kt b/packages/EasterEgg/src/com/android/egg/paint/SpotFilter.kt
new file mode 100644
index 0000000..2c15c0d
--- /dev/null
+++ b/packages/EasterEgg/src/com/android/egg/paint/SpotFilter.kt
@@ -0,0 +1,124 @@
+/*
+ * 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.egg.paint
+
+import java.util.LinkedList
+
+import android.view.MotionEvent
+
+class SpotFilter(internal var mBufSize: Int, posDecay: Float, pressureDecay: Float, internal var mPlotter: Plotter) {
+ val spots = LinkedList<MotionEvent.PointerCoords>() // newest at front
+ val tmpSpot = MotionEvent.PointerCoords()
+ var lastTool = MotionEvent.TOOL_TYPE_UNKNOWN
+
+ val posDecay: Float
+ val pressureDecay: Float
+
+ interface Plotter {
+ fun plot(s: MotionEvent.PointerCoords)
+ }
+
+ init {
+ this.posDecay = if (posDecay in 0f..1f) posDecay else 1f
+ this.pressureDecay = if (pressureDecay in 0f..1f) pressureDecay else 1f
+ }
+
+ fun filterInto(out: MotionEvent.PointerCoords, tool: Int): MotionEvent.PointerCoords {
+ lastTool = tool
+
+ var wi = 1f // weight for ith component (position)
+ var w = 0f // total weight
+ var wi_press = 1f // weight for ith component (pressure)
+ var w_press = 0f // total weight (pressure)
+
+ var x = 0f
+ var y = 0f
+ var pressure = 0f
+ var size = 0f
+ for (pi in spots) {
+ x += pi.x * wi
+ y += pi.y * wi
+
+ pressure += pi.pressure * wi_press
+ size += pi.size * wi_press
+
+ w += wi
+ wi *= posDecay // exponential backoff
+
+ w_press += wi_press
+ wi_press *= pressureDecay
+
+ if (PRECISE_STYLUS_INPUT && tool == MotionEvent.TOOL_TYPE_STYLUS) {
+ // just take the newest one, no need to average
+ break
+ }
+ }
+
+ out.x = x / w
+ out.y = y / w
+ out.pressure = pressure / w_press
+ out.size = size / w_press
+ return out
+ }
+
+ protected fun addInternal(c: MotionEvent.PointerCoords, tool: Int) {
+ val coord =
+ if (spots.size == mBufSize) {
+ spots.removeLast()
+ } else {
+ MotionEvent.PointerCoords()
+ }
+ coord.copyFrom(c)
+
+ spots.add(0, coord)
+
+ filterInto(tmpSpot, tool)
+ mPlotter.plot(tmpSpot)
+ }
+
+ fun add(cv: List<MotionEvent.PointerCoords>, tool: Int) {
+ for (c in cv) {
+ addInternal(c, tool)
+ }
+ }
+
+ fun add(evt: MotionEvent) {
+ val tool = evt.getToolType(0)
+ for (i in 0 until evt.historySize) {
+ evt.getHistoricalPointerCoords(0, i, tmpSpot)
+ addInternal(tmpSpot, tool)
+ }
+ evt.getPointerCoords(0, tmpSpot)
+ addInternal(tmpSpot, tool)
+ }
+
+ fun finish() {
+ while (spots.size > 0) {
+ filterInto(tmpSpot, lastTool)
+ spots.removeLast()
+ mPlotter.plot(tmpSpot)
+ }
+
+ spots.clear()
+ }
+
+ companion object {
+ var PRECISE_STYLUS_INPUT = true
+ }
+}
+
+
diff --git a/packages/EasterEgg/src/com/android/egg/paint/ToolbarView.kt b/packages/EasterEgg/src/com/android/egg/paint/ToolbarView.kt
new file mode 100644
index 0000000..86b11e7
--- /dev/null
+++ b/packages/EasterEgg/src/com/android/egg/paint/ToolbarView.kt
@@ -0,0 +1,77 @@
+/*
+ * 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.egg.paint
+
+import android.content.Context
+import android.graphics.Canvas
+import android.graphics.Color
+import android.graphics.Paint
+import android.graphics.Rect
+import android.graphics.drawable.Drawable
+import android.text.TextPaint
+import android.transition.ChangeBounds
+import android.transition.Transition
+import android.transition.TransitionListenerAdapter
+import android.transition.TransitionManager
+import android.util.AttributeSet
+import android.view.*
+import android.view.animation.OvershootInterpolator
+import android.widget.FrameLayout
+
+class ToolbarView : FrameLayout {
+ var inTransition = false
+ var transitionListener: Transition.TransitionListener = object : TransitionListenerAdapter() {
+ override fun onTransitionStart(transition: Transition?) {
+ inTransition = true
+ }
+ override fun onTransitionEnd(transition: Transition?) {
+ inTransition = false
+ }
+ }
+
+ constructor(context: Context) : super(context) {
+ init(null, 0)
+ }
+
+ constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
+ init(attrs, 0)
+ }
+
+ constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(context, attrs, defStyle) {
+ init(attrs, defStyle)
+ }
+
+ override fun onApplyWindowInsets(insets: WindowInsets?): WindowInsets {
+ var lp = layoutParams as FrameLayout.LayoutParams?
+ if (lp != null && insets != null) {
+ if (insets.hasStableInsets()) {
+ lp.topMargin = insets.stableInsetTop
+ lp.bottomMargin = insets.stableInsetBottom
+ } else {
+ lp.topMargin = insets.systemWindowInsetTop
+ lp.bottomMargin = insets.systemWindowInsetBottom
+ }
+ layoutParams = lp
+ }
+
+ return super.onApplyWindowInsets(insets)
+ }
+
+ private fun init(attrs: AttributeSet?, defStyle: Int) {
+ }
+
+}
diff --git a/packages/ExtServices/res/values/strings.xml b/packages/ExtServices/res/values/strings.xml
index 72647ab..617e49a 100644
--- a/packages/ExtServices/res/values/strings.xml
+++ b/packages/ExtServices/res/values/strings.xml
@@ -18,7 +18,6 @@
<string name="app_name">Android Services Library</string>
<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">
diff --git a/packages/ExtServices/src/android/ext/services/notification/Assistant.java b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
index f878822..a539b1f 100644
--- a/packages/ExtServices/src/android/ext/services/notification/Assistant.java
+++ b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
@@ -19,7 +19,9 @@
import static android.app.NotificationManager.IMPORTANCE_MIN;
import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE;
+import android.annotation.NonNull;
import android.app.INotificationManager;
+import android.app.Notification;
import android.content.ContentResolver;
import android.content.Context;
import android.database.ContentObserver;
@@ -80,6 +82,7 @@
private float mDismissToViewRatioLimit;
private int mStreakLimit;
+ private SmartActionsHelper mSmartActionsHelper;
// key : impressions tracker
// TODO: prune deleted channels and apps
@@ -99,6 +102,7 @@
// Contexts are correctly hooked up by the creation step, which is required for the observer
// to be hooked up/initialized.
new SettingsObserver(mHandler);
+ mSmartActionsHelper = new SmartActionsHelper();
}
private void loadFile() {
@@ -187,7 +191,26 @@
@Override
public Adjustment onNotificationEnqueued(StatusBarNotification sbn) {
if (DEBUG) Log.i(TAG, "ENQUEUED " + sbn.getKey());
- return null;
+ ArrayList<Notification.Action> actions =
+ mSmartActionsHelper.suggestActions(this, sbn);
+ if (actions.isEmpty()) {
+ return null;
+ }
+ return createEnqueuedNotificationAdjustment(sbn, actions);
+ }
+
+ /** A convenience helper for creating an adjustment for an SBN. */
+ private Adjustment createEnqueuedNotificationAdjustment(
+ @NonNull StatusBarNotification statusBarNotification,
+ @NonNull ArrayList<Notification.Action> smartActions) {
+ Bundle signals = new Bundle();
+ signals.putParcelableArrayList(Adjustment.KEY_SMART_ACTIONS, smartActions);
+ return new Adjustment(
+ statusBarNotification.getPackageName(),
+ statusBarNotification.getKey(),
+ signals,
+ "smart action" /* explanation */,
+ statusBarNotification.getUserId());
}
@Override
@@ -292,8 +315,7 @@
if (DEBUG) Log.d(TAG, "User probably doesn't want " + key);
Bundle signals = new Bundle();
signals.putInt(Adjustment.KEY_USER_SENTIMENT, USER_SENTIMENT_NEGATIVE);
- return new Adjustment(packageName, key, signals,
- getContext().getString(R.string.prompt_block_reason), user);
+ return new Adjustment(packageName, key, signals, "", user);
}
// for testing
@@ -379,4 +401,4 @@
}
}
}
-}
\ No newline at end of file
+}
diff --git a/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java b/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java
new file mode 100644
index 0000000..ed5cbab
--- /dev/null
+++ b/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java
@@ -0,0 +1,227 @@
+/**
+ * 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.notification;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.Notification;
+import android.app.RemoteAction;
+import android.app.RemoteInput;
+import android.content.Context;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.os.Process;
+import android.service.notification.StatusBarNotification;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.view.textclassifier.TextClassification;
+import android.view.textclassifier.TextClassificationManager;
+import android.view.textclassifier.TextClassifier;
+import android.view.textclassifier.TextLinks;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.ArrayList;
+import java.util.Collections;
+
+public class SmartActionsHelper {
+ private static final ArrayList<Notification.Action> EMPTY_LIST = new ArrayList<>();
+
+ // If a notification has any of these flags set, it's inelgibile for actions being added.
+ private static final int FLAG_MASK_INELGIBILE_FOR_ACTIONS =
+ Notification.FLAG_ONGOING_EVENT
+ | Notification.FLAG_FOREGROUND_SERVICE
+ | Notification.FLAG_GROUP_SUMMARY
+ | Notification.FLAG_NO_CLEAR;
+ private static final int MAX_ACTION_EXTRACTION_TEXT_LENGTH = 400;
+ private static final int MAX_ACTIONS_PER_LINK = 1;
+ private static final int MAX_SMART_ACTIONS = Notification.MAX_ACTION_BUTTONS;
+
+ SmartActionsHelper() {}
+
+ /**
+ * Adds action adjustments based on the notification contents.
+ *
+ * TODO: Once we have a API in {@link TextClassificationManager} to predict smart actions
+ * from notification text / message, we can replace most of the code here by consuming that API.
+ */
+ @NonNull
+ ArrayList<Notification.Action> suggestActions(
+ @Nullable Context context, @NonNull StatusBarNotification sbn) {
+ if (!isEligibleForActionAdjustment(sbn)) {
+ return EMPTY_LIST;
+ }
+ if (context == null) {
+ return EMPTY_LIST;
+ }
+ TextClassificationManager tcm = context.getSystemService(TextClassificationManager.class);
+ if (tcm == null) {
+ return EMPTY_LIST;
+ }
+ Notification.Action[] actions = sbn.getNotification().actions;
+ int numOfExistingActions = actions == null ? 0: actions.length;
+ int maxSmartActions = MAX_SMART_ACTIONS - numOfExistingActions;
+ return suggestActionsFromText(
+ tcm,
+ getMostSalientActionText(sbn.getNotification()), maxSmartActions);
+ }
+
+ /**
+ * Returns whether a notification is eligible for action adjustments.
+ *
+ * <p>We exclude system notifications, those that get refreshed frequently, or ones that relate
+ * to fundamental phone functionality where any error would result in a very negative user
+ * experience.
+ */
+ private boolean isEligibleForActionAdjustment(@NonNull StatusBarNotification sbn) {
+ Notification notification = sbn.getNotification();
+ String pkg = sbn.getPackageName();
+ if (!Process.myUserHandle().equals(sbn.getUser())) {
+ return false;
+ }
+ if (notification.actions != null
+ && notification.actions.length >= Notification.MAX_ACTION_BUTTONS) {
+ return false;
+ }
+ if (0 != (notification.flags & FLAG_MASK_INELGIBILE_FOR_ACTIONS)) {
+ return false;
+ }
+ if (TextUtils.isEmpty(pkg) || pkg.equals("android")) {
+ return false;
+ }
+ // For now, we are only interested in messages.
+ return Notification.CATEGORY_MESSAGE.equals(notification.category)
+ || Notification.MessagingStyle.class.equals(notification.getNotificationStyle())
+ || hasInlineReply(notification);
+ }
+
+ private boolean hasInlineReply(Notification notification) {
+ Notification.Action[] actions = notification.actions;
+ if (actions == null) {
+ return false;
+ }
+ for (Notification.Action action : actions) {
+ RemoteInput[] remoteInputs = action.getRemoteInputs();
+ if (remoteInputs == null) {
+ continue;
+ }
+ for (RemoteInput remoteInput : remoteInputs) {
+ if (remoteInput.getAllowFreeFormInput()) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /** Returns the text most salient for action extraction in a notification. */
+ @Nullable
+ private CharSequence getMostSalientActionText(@NonNull Notification notification) {
+ /* If it's messaging style, use the most recent message. */
+ Parcelable[] messages = notification.extras.getParcelableArray(Notification.EXTRA_MESSAGES);
+ if (messages != null && messages.length != 0) {
+ Bundle lastMessage = (Bundle) messages[messages.length - 1];
+ CharSequence lastMessageText =
+ lastMessage.getCharSequence(Notification.MessagingStyle.Message.KEY_TEXT);
+ if (!TextUtils.isEmpty(lastMessageText)) {
+ return lastMessageText;
+ }
+ }
+
+ // Fall back to using the normal text.
+ return notification.extras.getCharSequence(Notification.EXTRA_TEXT);
+ }
+
+ /** Returns a list of actions to act on entities in a given piece of text. */
+ @NonNull
+ private ArrayList<Notification.Action> suggestActionsFromText(
+ @NonNull TextClassificationManager tcm, @Nullable CharSequence text,
+ int maxSmartActions) {
+ if (TextUtils.isEmpty(text)) {
+ return EMPTY_LIST;
+ }
+ TextClassifier textClassifier = tcm.getTextClassifier();
+
+ // We want to process only text visible to the user to avoid confusing suggestions, so we
+ // truncate the text to a reasonable length. This is particularly important for e.g.
+ // email apps that sometimes include the text for the entire thread.
+ text = text.subSequence(0, Math.min(text.length(), MAX_ACTION_EXTRACTION_TEXT_LENGTH));
+
+ // Extract all entities.
+ TextLinks.Request textLinksRequest = new TextLinks.Request.Builder(text)
+ .setEntityConfig(
+ TextClassifier.EntityConfig.createWithHints(
+ Collections.singletonList(
+ TextClassifier.HINT_TEXT_IS_NOT_EDITABLE)))
+ .build();
+ TextLinks links = textClassifier.generateLinks(textLinksRequest);
+ ArrayMap<String, Integer> entityTypeFrequency = getEntityTypeFrequency(links);
+
+ ArrayList<Notification.Action> actions = new ArrayList<>();
+ for (TextLinks.TextLink link : links.getLinks()) {
+ // Ignore any entity type for which we have too many entities. This is to handle the
+ // case where a notification contains e.g. a list of phone numbers. In such cases, the
+ // user likely wants to act on the whole list rather than an individual entity.
+ if (link.getEntityCount() == 0
+ || entityTypeFrequency.get(link.getEntity(0)) != 1) {
+ continue;
+ }
+
+ // Generate the actions, and add the most prominent ones to the action bar.
+ TextClassification classification =
+ textClassifier.classifyText(
+ new TextClassification.Request.Builder(
+ text, link.getStart(), link.getEnd()).build());
+ int numOfActions = Math.min(
+ MAX_ACTIONS_PER_LINK, classification.getActions().size());
+ for (int i = 0; i < numOfActions; ++i) {
+ RemoteAction action = classification.getActions().get(i);
+ actions.add(
+ new Notification.Action.Builder(
+ action.getIcon(),
+ action.getTitle(),
+ action.getActionIntent())
+ .build());
+ // We have enough smart actions.
+ if (actions.size() >= maxSmartActions) {
+ return actions;
+ }
+ }
+ }
+ return actions;
+ }
+
+ /**
+ * Given the links extracted from a piece of text, returns the frequency of each entity
+ * type.
+ */
+ @NonNull
+ private ArrayMap<String, Integer> getEntityTypeFrequency(@NonNull TextLinks links) {
+ ArrayMap<String, Integer> entityTypeCount = new ArrayMap<>();
+ for (TextLinks.TextLink link : links.getLinks()) {
+ if (link.getEntityCount() == 0) {
+ continue;
+ }
+ String entityType = link.getEntity(0);
+ if (entityTypeCount.containsKey(entityType)) {
+ entityTypeCount.put(entityType, entityTypeCount.get(entityType) + 1);
+ } else {
+ entityTypeCount.put(entityType, 1);
+ }
+ }
+ return entityTypeCount;
+ }
+}
diff --git a/packages/InputDevices/res/values-in/strings.xml b/packages/InputDevices/res/values-in/strings.xml
index 0c3bd7e..16b2010 100644
--- a/packages/InputDevices/res/values-in/strings.xml
+++ b/packages/InputDevices/res/values-in/strings.xml
@@ -31,7 +31,7 @@
<string name="keyboard_layout_icelandic" msgid="5836645650912489642">"Islandia"</string>
<string name="keyboard_layout_brazilian" msgid="5117896443147781939">"Brasil"</string>
<string name="keyboard_layout_portuguese" msgid="2888198587329660305">"Portugis"</string>
- <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slowakia"</string>
+ <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slovakia"</string>
<string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Slovenia"</string>
<string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turki"</string>
<string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ukraina"</string>
diff --git a/packages/PrintSpooler/res/drawable-hdpi/ic_expand_less.png b/packages/PrintSpooler/res/drawable-hdpi/ic_expand_less.png
deleted file mode 100644
index b6a5eb5..0000000
--- a/packages/PrintSpooler/res/drawable-hdpi/ic_expand_less.png
+++ /dev/null
Binary files differ
diff --git a/packages/PrintSpooler/res/drawable-hdpi/ic_expand_more.png b/packages/PrintSpooler/res/drawable-hdpi/ic_expand_more.png
deleted file mode 100644
index 4e36bd2..0000000
--- a/packages/PrintSpooler/res/drawable-hdpi/ic_expand_more.png
+++ /dev/null
Binary files differ
diff --git a/packages/PrintSpooler/res/drawable-hdpi/ic_grayedout_printer.png b/packages/PrintSpooler/res/drawable-hdpi/ic_grayedout_printer.png
deleted file mode 100644
index 5e54970..0000000
--- a/packages/PrintSpooler/res/drawable-hdpi/ic_grayedout_printer.png
+++ /dev/null
Binary files differ
diff --git a/packages/PrintSpooler/res/drawable-hdpi/ic_restart.png b/packages/PrintSpooler/res/drawable-hdpi/ic_restart.png
deleted file mode 100644
index bb9d855..0000000
--- a/packages/PrintSpooler/res/drawable-hdpi/ic_restart.png
+++ /dev/null
Binary files differ
diff --git a/packages/PrintSpooler/res/drawable-hdpi/stat_notify_cancelling.png b/packages/PrintSpooler/res/drawable-hdpi/stat_notify_cancelling.png
deleted file mode 100644
index 2757db0..0000000
--- a/packages/PrintSpooler/res/drawable-hdpi/stat_notify_cancelling.png
+++ /dev/null
Binary files differ
diff --git a/packages/PrintSpooler/res/drawable-mdpi/ic_expand_less.png b/packages/PrintSpooler/res/drawable-mdpi/ic_expand_less.png
deleted file mode 100644
index 428a946..0000000
--- a/packages/PrintSpooler/res/drawable-mdpi/ic_expand_less.png
+++ /dev/null
Binary files differ
diff --git a/packages/PrintSpooler/res/drawable-mdpi/ic_expand_less_24dp.png b/packages/PrintSpooler/res/drawable-mdpi/ic_expand_less_24dp.png
deleted file mode 100644
index 3220eea..0000000
--- a/packages/PrintSpooler/res/drawable-mdpi/ic_expand_less_24dp.png
+++ /dev/null
Binary files differ
diff --git a/packages/PrintSpooler/res/drawable-mdpi/ic_expand_more.png b/packages/PrintSpooler/res/drawable-mdpi/ic_expand_more.png
deleted file mode 100644
index fbbd094..0000000
--- a/packages/PrintSpooler/res/drawable-mdpi/ic_expand_more.png
+++ /dev/null
Binary files differ
diff --git a/packages/PrintSpooler/res/drawable-mdpi/ic_expand_more_24dp.png b/packages/PrintSpooler/res/drawable-mdpi/ic_expand_more_24dp.png
deleted file mode 100644
index 5530f52..0000000
--- a/packages/PrintSpooler/res/drawable-mdpi/ic_expand_more_24dp.png
+++ /dev/null
Binary files differ
diff --git a/packages/PrintSpooler/res/drawable-mdpi/ic_grayedout_printer.png b/packages/PrintSpooler/res/drawable-mdpi/ic_grayedout_printer.png
deleted file mode 100644
index 5e54970..0000000
--- a/packages/PrintSpooler/res/drawable-mdpi/ic_grayedout_printer.png
+++ /dev/null
Binary files differ
diff --git a/packages/PrintSpooler/res/drawable-mdpi/ic_restart.png b/packages/PrintSpooler/res/drawable-mdpi/ic_restart.png
deleted file mode 100644
index bd611e8..0000000
--- a/packages/PrintSpooler/res/drawable-mdpi/ic_restart.png
+++ /dev/null
Binary files differ
diff --git a/packages/PrintSpooler/res/drawable-mdpi/stat_notify_cancelling.png b/packages/PrintSpooler/res/drawable-mdpi/stat_notify_cancelling.png
deleted file mode 100644
index c1b380a..0000000
--- a/packages/PrintSpooler/res/drawable-mdpi/stat_notify_cancelling.png
+++ /dev/null
Binary files differ
diff --git a/packages/PrintSpooler/res/drawable-xhdpi/ic_expand_less.png b/packages/PrintSpooler/res/drawable-xhdpi/ic_expand_less.png
deleted file mode 100644
index 6161c20..0000000
--- a/packages/PrintSpooler/res/drawable-xhdpi/ic_expand_less.png
+++ /dev/null
Binary files differ
diff --git a/packages/PrintSpooler/res/drawable-xhdpi/ic_expand_more.png b/packages/PrintSpooler/res/drawable-xhdpi/ic_expand_more.png
deleted file mode 100644
index 3a89805..0000000
--- a/packages/PrintSpooler/res/drawable-xhdpi/ic_expand_more.png
+++ /dev/null
Binary files differ
diff --git a/packages/PrintSpooler/res/drawable-xhdpi/ic_grayedout_printer.png b/packages/PrintSpooler/res/drawable-xhdpi/ic_grayedout_printer.png
deleted file mode 100644
index 5e54970..0000000
--- a/packages/PrintSpooler/res/drawable-xhdpi/ic_grayedout_printer.png
+++ /dev/null
Binary files differ
diff --git a/packages/PrintSpooler/res/drawable-xhdpi/ic_restart.png b/packages/PrintSpooler/res/drawable-xhdpi/ic_restart.png
deleted file mode 100644
index a7fdc0d..0000000
--- a/packages/PrintSpooler/res/drawable-xhdpi/ic_restart.png
+++ /dev/null
Binary files differ
diff --git a/packages/PrintSpooler/res/drawable-xhdpi/stat_notify_cancelling.png b/packages/PrintSpooler/res/drawable-xhdpi/stat_notify_cancelling.png
deleted file mode 100644
index fedc00e..0000000
--- a/packages/PrintSpooler/res/drawable-xhdpi/stat_notify_cancelling.png
+++ /dev/null
Binary files differ
diff --git a/packages/PrintSpooler/res/drawable-xxhdpi/ic_expand_less.png b/packages/PrintSpooler/res/drawable-xxhdpi/ic_expand_less.png
deleted file mode 100644
index 52a52d9..0000000
--- a/packages/PrintSpooler/res/drawable-xxhdpi/ic_expand_less.png
+++ /dev/null
Binary files differ
diff --git a/packages/PrintSpooler/res/drawable-xxhdpi/ic_expand_more.png b/packages/PrintSpooler/res/drawable-xxhdpi/ic_expand_more.png
deleted file mode 100644
index 15e6abd..0000000
--- a/packages/PrintSpooler/res/drawable-xxhdpi/ic_expand_more.png
+++ /dev/null
Binary files differ
diff --git a/packages/PrintSpooler/res/drawable-xxxhdpi/ic_expand_less.png b/packages/PrintSpooler/res/drawable-xxxhdpi/ic_expand_less.png
deleted file mode 100644
index 46811a1..0000000
--- a/packages/PrintSpooler/res/drawable-xxxhdpi/ic_expand_less.png
+++ /dev/null
Binary files differ
diff --git a/packages/PrintSpooler/res/drawable-xxxhdpi/ic_expand_more.png b/packages/PrintSpooler/res/drawable-xxxhdpi/ic_expand_more.png
deleted file mode 100644
index 141f28b..0000000
--- a/packages/PrintSpooler/res/drawable-xxxhdpi/ic_expand_more.png
+++ /dev/null
Binary files differ
diff --git a/packages/PrintSpooler/res/drawable/ic_clear.xml b/packages/PrintSpooler/res/drawable/ic_clear.xml
new file mode 100644
index 0000000..076e8ef
--- /dev/null
+++ b/packages/PrintSpooler/res/drawable/ic_clear.xml
@@ -0,0 +1,26 @@
+<?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.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z"
+ android:fillColor="?android:colorForeground"/>
+</vector>
diff --git a/packages/PrintSpooler/res/drawable/ic_expand_less.xml b/packages/PrintSpooler/res/drawable/ic_expand_less.xml
index 6f1ece1..c3e87dc 100644
--- a/packages/PrintSpooler/res/drawable/ic_expand_less.xml
+++ b/packages/PrintSpooler/res/drawable/ic_expand_less.xml
@@ -1,43 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 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.
- You may obtain a copy of the License at
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
+ http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
-<selector xmlns:android="http://schemas.android.com/apk/res/android"
- android:autoMirrored="true">
-
- <item
- android:state_checked="true">
- <bitmap
- android:src="@drawable/ic_expand_less"
- android:tint="?android:attr/colorControlActivated">
- </bitmap>
- </item>
-
- <item
- android:state_pressed="true">
- <bitmap
- android:src="@drawable/ic_expand_less"
- android:tint="?android:attr/colorControlActivated">
- </bitmap>
- </item>
-
- <item>
- <bitmap
- android:src="@drawable/ic_expand_less"
- android:tint="?android:attr/colorControlNormal">
- </bitmap>
- </item>
-
-</selector>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:pathData="M7.41,15.41L12,10.83l4.59,4.58L18,14l-6,-6 -6,6z"
+ android:fillColor="?android:colorForeground" />
+</vector>
\ No newline at end of file
diff --git a/packages/PrintSpooler/res/drawable/ic_expand_more.xml b/packages/PrintSpooler/res/drawable/ic_expand_more.xml
index 8d71452..3895144 100644
--- a/packages/PrintSpooler/res/drawable/ic_expand_more.xml
+++ b/packages/PrintSpooler/res/drawable/ic_expand_more.xml
@@ -1,43 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 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.
- You may obtain a copy of the License at
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
+ http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
-<selector xmlns:android="http://schemas.android.com/apk/res/android"
- android:autoMirrored="true">
-
- <item
- android:state_checked="true">
- <bitmap
- android:src="@drawable/ic_expand_more"
- android:tint="?android:attr/colorControlActivated">
- </bitmap>
- </item>
-
- <item
- android:state_pressed="true">
- <bitmap
- android:src="@drawable/ic_expand_more"
- android:tint="?android:attr/colorControlActivated">
- </bitmap>
- </item>
-
- <item>
- <bitmap
- android:src="@drawable/ic_expand_more"
- android:tint="?android:attr/colorControlNormal">
- </bitmap>
- </item>
-
-</selector>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:pathData="M7.41,8.59L12,13.17l4.59,-4.58L18,10l-6,6l-6,-6L7.41,8.59z"
+ android:fillColor="?android:colorForeground" />
+</vector>
\ No newline at end of file
diff --git a/packages/PrintSpooler/res/layout/preview_page.xml b/packages/PrintSpooler/res/layout/preview_page.xml
index aafdd8f..8db347e 100644
--- a/packages/PrintSpooler/res/layout/preview_page.xml
+++ b/packages/PrintSpooler/res/layout/preview_page.xml
@@ -44,7 +44,7 @@
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:textAppearance="?android:attr/textAppearanceSmall"
- android:textColor="?android:attr/textColorPrimary">
+ android:textColor="@android:color/white">
</TextView>
<ImageView
diff --git a/packages/PrintSpooler/res/layout/preview_page_error.xml b/packages/PrintSpooler/res/layout/preview_page_error.xml
index 4e9fb77..99ab99d 100644
--- a/packages/PrintSpooler/res/layout/preview_page_error.xml
+++ b/packages/PrintSpooler/res/layout/preview_page_error.xml
@@ -21,11 +21,14 @@
android:gravity="center">
<ImageView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginBottom="12dip"
- android:src="@drawable/print_warning"
- android:contentDescription="@null" />
+ android:layout_width="120dp"
+ android:layout_height="110dp"
+ android:layout_marginBottom="12dip"
+ android:src="@*android:drawable/ic_print_error"
+ android:scaleType="fitEnd"
+ android:alpha="0.1"
+ android:tint="@android:color/black"
+ android:importantForAccessibility="no" />
<TextView
android:layout_width="wrap_content"
diff --git a/packages/PrintSpooler/res/layout/preview_page_loading.xml b/packages/PrintSpooler/res/layout/preview_page_loading.xml
index 1af3a17..918edd9 100644
--- a/packages/PrintSpooler/res/layout/preview_page_loading.xml
+++ b/packages/PrintSpooler/res/layout/preview_page_loading.xml
@@ -19,14 +19,13 @@
android:layout_height="fill_parent">
<ImageView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_margin="36dip"
+ android:layout_width="120dp"
+ android:layout_height="110dp"
android:layout_gravity="center"
- android:src="@drawable/ic_grayedout_printer"
- android:contentDescription="@null"
- android:scaleType="centerInside"
- android:adjustViewBounds="true">
- </ImageView>
+ android:src="@*android:drawable/ic_print"
+ android:scaleType="fitCenter"
+ android:alpha="0.1"
+ android:tint="@android:color/black"
+ android:importantForAccessibility="no" />
</FrameLayout>
diff --git a/packages/PrintSpooler/res/layout/preview_page_selected.xml b/packages/PrintSpooler/res/layout/preview_page_selected.xml
index 77f4727..6727142 100644
--- a/packages/PrintSpooler/res/layout/preview_page_selected.xml
+++ b/packages/PrintSpooler/res/layout/preview_page_selected.xml
@@ -42,7 +42,7 @@
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:textAppearance="?android:attr/textAppearanceSmall"
- android:textColor="?android:attr/textColorPrimary">
+ android:textColor="@android:color/white">
</TextView>
<ImageView
diff --git a/packages/PrintSpooler/res/layout/print_activity.xml b/packages/PrintSpooler/res/layout/print_activity.xml
index 774f320..212f398 100644
--- a/packages/PrintSpooler/res/layout/print_activity.xml
+++ b/packages/PrintSpooler/res/layout/print_activity.xml
@@ -29,7 +29,7 @@
android:paddingStart="8dip"
android:layout_marginEnd="16dp"
android:elevation="@dimen/preview_controls_elevation"
- android:background="?android:attr/colorPrimary">
+ style="?android:actionBarStyle">
<com.android.printspooler.widget.ClickInterceptSpinner
android:id="@+id/destination_spinner"
@@ -55,7 +55,7 @@
android:paddingBottom="8dip"
android:orientation="horizontal"
android:elevation="@dimen/preview_controls_elevation"
- android:background="?android:attr/colorPrimary">
+ style="?android:actionBarStyle">
<TextView
android:layout_width="wrap_content"
@@ -121,7 +121,6 @@
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:animateLayoutChanges="true"
- android:background="@color/print_preview_background_color"
android:gravity="center">
<!-- Error message added here -->
diff --git a/packages/PrintSpooler/res/layout/print_activity_controls.xml b/packages/PrintSpooler/res/layout/print_activity_controls.xml
index 69d4f91..3aafc99 100644
--- a/packages/PrintSpooler/res/layout/print_activity_controls.xml
+++ b/packages/PrintSpooler/res/layout/print_activity_controls.xml
@@ -22,7 +22,7 @@
android:layout_height="wrap_content"
android:orientation="vertical"
android:elevation="@dimen/preview_controls_elevation"
- android:background="?android:attr/colorPrimary">
+ style="?android:actionBarStyle">
<LinearLayout
android:id="@+id/draggable_content"
diff --git a/packages/PrintSpooler/res/layout/print_error_fragment.xml b/packages/PrintSpooler/res/layout/print_error_fragment.xml
index 3ea2abd..9d9dd01 100644
--- a/packages/PrintSpooler/res/layout/print_error_fragment.xml
+++ b/packages/PrintSpooler/res/layout/print_error_fragment.xml
@@ -16,35 +16,39 @@
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_marginStart="16dip"
+ android:layout_marginEnd="16dip"
android:gravity="center"
android:orientation="vertical">
<ImageView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginBottom="12dip"
- android:src="@drawable/ic_grayedout_printer"
- android:contentDescription="@null">
- </ImageView>
+ android:layout_width="120dp"
+ android:layout_height="110dp"
+ android:layout_marginBottom="12dp"
+ android:src="@*android:drawable/ic_print_error"
+ android:scaleType="fitEnd"
+ android:alpha="0.1"
+ android:tint="?android:colorForeground"
+ android:importantForAccessibility="no" />
<TextView
android:id="@+id/message"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginStart="16dip"
- android:layout_marginEnd="16dip"
- android:gravity="center_horizontal"
- android:text="@string/print_error_default_message"
- android:textAppearance="?android:attr/textAppearanceLargeInverse">
- </TextView>
+ android:layout_marginBottom="16dp"
+ android:gravity="center"
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ android:textColor="?android:attr/textColorSecondary"
+ android:text="@string/print_error_default_message" />
<Button
android:id="@+id/action_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:text="@string/print_error_retry">
- </Button>
+ style="?android:attr/borderlessButtonStyle"
+ android:textColor="?android:attr/textColorSecondary"
+ android:text="@string/print_add_printer" />
</LinearLayout>
diff --git a/packages/PrintSpooler/res/layout/print_progress_fragment.xml b/packages/PrintSpooler/res/layout/print_progress_fragment.xml
index 3b010f8..89071605 100644
--- a/packages/PrintSpooler/res/layout/print_progress_fragment.xml
+++ b/packages/PrintSpooler/res/layout/print_progress_fragment.xml
@@ -15,34 +15,39 @@
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_marginStart="16dp"
+ android:layout_marginEnd="16dp"
android:gravity="center"
android:orientation="vertical">
<ImageView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginBottom="12dip"
- android:src="@drawable/ic_grayedout_printer"
- android:contentDescription="@null">
- </ImageView>
-
- <ProgressBar
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:indeterminate="true"
- style="?android:attr/progressBarStyleHorizontal">
- </ProgressBar>
+ android:layout_width="120dp"
+ android:layout_height="110dp"
+ android:layout_marginBottom="12dp"
+ android:src="@*android:drawable/ic_print"
+ android:scaleType="fitEnd"
+ android:alpha="0.1"
+ android:tint="?android:colorForeground"
+ android:importantForAccessibility="no" />
<TextView
android:id="@+id/message"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:textAppearance="?android:attr/textAppearanceLargeInverse"
- android:text="@string/print_preparing_preview">
- </TextView>
+ android:layout_marginBottom="16dp"
+ android:gravity="center"
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ android:textColor="?android:attr/textColorSecondary"
+ android:text="@string/print_preparing_preview" />
+
+ <ProgressBar
+ android:layout_width="300dp"
+ android:layout_height="wrap_content"
+ android:indeterminate="true"
+ android:importantForAccessibility="no"
+ style="?android:attr/progressBarStyleHorizontal" />
</LinearLayout>
diff --git a/packages/PrintSpooler/res/layout/select_printer_activity.xml b/packages/PrintSpooler/res/layout/select_printer_activity.xml
index 91beff6..681924b 100644
--- a/packages/PrintSpooler/res/layout/select_printer_activity.xml
+++ b/packages/PrintSpooler/res/layout/select_printer_activity.xml
@@ -31,45 +31,48 @@
android:visibility="gone">
<LinearLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_marginStart="16dp"
+ android:layout_marginEnd="16dp"
android:gravity="center"
android:orientation="vertical">
<ImageView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginBottom="12dip"
- android:src="@*android:drawable/ic_grayedout_printer"
- android:importantForAccessibility="no">
- </ImageView>
+ android:layout_width="120dp"
+ android:layout_height="110dp"
+ android:layout_marginBottom="12dp"
+ android:src="@*android:drawable/ic_print"
+ android:scaleType="fitEnd"
+ android:alpha="0.1"
+ android:tint="?android:colorForeground"
+ android:importantForAccessibility="no" />
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:layout_marginBottom="16dp"
+ android:gravity="center"
android:textAppearance="?android:attr/textAppearanceLarge"
android:textColor="?android:attr/textColorSecondary"
- android:text="@string/print_searching_for_printers">
- </TextView>
+ android:text="@string/print_searching_for_printers" />
<ProgressBar
android:id="@+id/progress_bar"
- android:layout_width="fill_parent"
+ android:layout_width="300dp"
android:layout_height="wrap_content"
android:indeterminate="true"
- style="?android:attr/progressBarStyleHorizontal">
- </ProgressBar>
+ android:importantForAccessibility="no"
+ style="?android:attr/progressBarStyleHorizontal" />
<Button
- android:id="@+id/button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- style="?android:attr/buttonBarButtonStyle"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:text="@string/print_add_printer"
- android:textAllCaps="true" />
+ android:id="@+id/button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ style="?android:attr/borderlessButtonStyle"
+ android:textColor="?android:attr/textColorSecondary"
+ android:text="@string/print_add_printer" />
</LinearLayout>
diff --git a/packages/PrintSpooler/res/values-hy/strings.xml b/packages/PrintSpooler/res/values-hy/strings.xml
index 56045be..0009397 100644
--- a/packages/PrintSpooler/res/values-hy/strings.xml
+++ b/packages/PrintSpooler/res/values-hy/strings.xml
@@ -99,8 +99,8 @@
<item msgid="79513688117503758">"Կարճեզր"</item>
</string-array>
<string-array name="orientation_labels">
- <item msgid="4061931020926489228">"Դիմանկար"</item>
- <item msgid="3199660090246166812">"Լանդշաֆտ"</item>
+ <item msgid="4061931020926489228">"Ուղղաձիգ"</item>
+ <item msgid="3199660090246166812">"Հորիզոնական"</item>
</string-array>
<string name="print_write_error_message" msgid="5787642615179572543">"Հնարավոր չէ գրել ֆայլում"</string>
<string name="print_error_default_message" msgid="8602678405502922346">"Չհաջողվեց: Նորից փորձեք:"</string>
diff --git a/packages/PrintSpooler/res/values-km/strings.xml b/packages/PrintSpooler/res/values-km/strings.xml
index ed8219e..c878ebe 100644
--- a/packages/PrintSpooler/res/values-km/strings.xml
+++ b/packages/PrintSpooler/res/values-km/strings.xml
@@ -27,7 +27,7 @@
<string name="label_duplex" msgid="5370037254347072243">"សងខាង"</string>
<string name="label_orientation" msgid="2853142581990496477">"ទិស"</string>
<string name="label_pages" msgid="7768589729282182230">"ទំព័រ"</string>
- <string name="destination_default_text" msgid="5422708056807065710">"ជ្រើសម៉ាស៊ីនបោះពុម្ព"</string>
+ <string name="destination_default_text" msgid="5422708056807065710">"ជ្រើសរើសម៉ាស៊ីនបោះពុម្ព"</string>
<string name="template_all_pages" msgid="3322235982020148762">"<xliff:g id="PAGE_COUNT">%1$s</xliff:g> ទាំងអស់"</string>
<string name="template_page_range" msgid="428638530038286328">"ជួរនៃ <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="pages_range_example" msgid="8558694453556945172">"ឧ. 1—5,8,11—13"</string>
@@ -103,7 +103,7 @@
<item msgid="3199660090246166812">"ផ្ដេក"</item>
</string-array>
<string name="print_write_error_message" msgid="5787642615179572543">"មិនអាចសរសេរទៅកាន់ឯកសារ"</string>
- <string name="print_error_default_message" msgid="8602678405502922346">"សូមទោស វាមិនដំណើរការទេ។ ព្យាយាមម្ដងទៀត។"</string>
+ <string name="print_error_default_message" msgid="8602678405502922346">"សូមអភ័យទោស វាមិនដំណើរការទេ។ ព្យាយាមម្ដងទៀត។"</string>
<string name="print_error_retry" msgid="1426421728784259538">"ព្យាយាមម្ដងទៀត"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"ឥឡូវនេះ ម៉ាស៊ីនបោះពុម្ពនេះមិនអាចប្រើបាន។"</string>
<string name="print_cannot_load_page" msgid="6179560924492912009">"មិនអាចបង្ហាញការមើលជាមុនបានទេ"</string>
diff --git a/packages/PrintSpooler/res/values-sw/strings.xml b/packages/PrintSpooler/res/values-sw/strings.xml
index 8aaf6f4..12f5b60 100644
--- a/packages/PrintSpooler/res/values-sw/strings.xml
+++ b/packages/PrintSpooler/res/values-sw/strings.xml
@@ -103,7 +103,7 @@
<item msgid="3199660090246166812">"Mlalo"</item>
</string-array>
<string name="print_write_error_message" msgid="5787642615179572543">"Haikuweza kuandika kwenye faili"</string>
- <string name="print_error_default_message" msgid="8602678405502922346">"Samahani, hiyo haikufanya kazi. Jaribu tena."</string>
+ <string name="print_error_default_message" msgid="8602678405502922346">"Samahani, imeshindwa kufanya kazi. Jaribu tena."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Jaribu tena"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Printa hii haipatikani kwa sasa."</string>
<string name="print_cannot_load_page" msgid="6179560924492912009">"Haiwezi kupakia onyesho la kuchungulia"</string>
diff --git a/packages/PrintSpooler/res/values/colors.xml b/packages/PrintSpooler/res/values/colors.xml
index 68bc6f2..cb9e886 100644
--- a/packages/PrintSpooler/res/values/colors.xml
+++ b/packages/PrintSpooler/res/values/colors.xml
@@ -18,8 +18,6 @@
<color name="print_preview_scrim_color">#99000000</color>
- <color name="print_preview_background_color">#F2F1F2</color>
-
<color name="unselected_page_background_color">#C0C0C0</color>
<color name="material_grey_500">#ffa3a3a3</color>
diff --git a/packages/PrintSpooler/res/values/themes.xml b/packages/PrintSpooler/res/values/themes.xml
index a968ffa..844e9c9 100644
--- a/packages/PrintSpooler/res/values/themes.xml
+++ b/packages/PrintSpooler/res/values/themes.xml
@@ -21,17 +21,14 @@
</style>
<style name="Theme.SelectPrinterActivity"
- parent="android:style/Theme.DeviceDefault.Light.DarkActionBar">
+ parent="android:style/Theme.DeviceDefault.Light">
<item name="android:textAppearanceListItemSecondary">@style/ListItemSecondary</item>
</style>
- <style name="Theme.PrintActivity" parent="@android:style/Theme.DeviceDefault">
+ <style name="Theme.PrintActivity" parent="@android:style/Theme.DeviceDefault.Light">
<item name="android:windowIsTranslucent">true</item>
- <item name="android:windowBackground">@android:color/transparent</item>
- <item name="android:windowContentOverlay">@null</item>
<item name="android:windowActionBar">false</item>
<item name="android:windowNoTitle">true</item>
- <item name="android:backgroundDimEnabled">false</item>
</style>
</resources>
diff --git a/packages/PrintSpooler/src/com/android/printspooler/model/NotificationController.java b/packages/PrintSpooler/src/com/android/printspooler/model/NotificationController.java
index 9d737e0..abdfad5 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/model/NotificationController.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/model/NotificationController.java
@@ -166,7 +166,7 @@
*/
private Action createCancelAction(PrintJobInfo printJob) {
return new Action.Builder(
- Icon.createWithResource(mContext, R.drawable.stat_notify_cancelling),
+ Icon.createWithResource(mContext, R.drawable.ic_clear),
mContext.getString(R.string.cancel), createCancelIntent(printJob)).build();
}
@@ -225,7 +225,7 @@
private void createFailedNotification(PrintJobInfo printJob) {
Action.Builder restartActionBuilder = new Action.Builder(
- Icon.createWithResource(mContext, R.drawable.ic_restart),
+ Icon.createWithResource(mContext, com.android.internal.R.drawable.ic_restart),
mContext.getString(R.string.restart), createRestartIntent(printJob.getId()));
createNotification(printJob, createCancelAction(printJob), restartActionBuilder.build());
@@ -317,7 +317,7 @@
if (!printJob.isCancelling()) {
return com.android.internal.R.drawable.ic_print;
} else {
- return R.drawable.stat_notify_cancelling;
+ return R.drawable.ic_clear;
}
}
}
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
index 06fbf9f..59f272f 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
@@ -297,7 +297,7 @@
+ " cannot be null");
}
- mCallingPackageName = extras.getString(DocumentsContract.EXTRA_PACKAGE_NAME);
+ mCallingPackageName = extras.getString(Intent.EXTRA_PACKAGE_NAME);
if (savedInstanceState == null) {
MetricsLogger.action(this, MetricsEvent.PRINT_PREVIEW, mCallingPackageName);
@@ -715,7 +715,7 @@
Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
intent.setType("application/pdf");
intent.putExtra(Intent.EXTRA_TITLE, info.getName());
- intent.putExtra(DocumentsContract.EXTRA_PACKAGE_NAME, mCallingPackageName);
+ intent.putExtra(Intent.EXTRA_PACKAGE_NAME, mCallingPackageName);
try {
startActivityForResult(intent, ACTIVITY_REQUEST_CREATE_FILE);
diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml
index d8137b4..e3b4bc9 100644
--- a/packages/SettingsLib/res/values-as/strings.xml
+++ b/packages/SettingsLib/res/values-as/strings.xml
@@ -224,8 +224,7 @@
<string name="bluetooth_select_a2dp_codec_channel_mode" msgid="884855779449390540">"ব্লুটুথ অডিঅ\' চ্চেনেল ম\'ড"</string>
<string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="7234956835280563341">"ব্লুটুথ অডিঅ\' ক\'ডেকৰ বাছনি\nআৰম্ভ কৰক: চ্চেনেল ম\'ড"</string>
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"ব্লুটুথ অডিঅ’ LDAC ক’ডেক: পৰিৱেশনৰ মান"</string>
- <!-- no translation found for bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title (6893955536658137179) -->
- <skip />
+ <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="6893955536658137179">"ব্লুটুথ অডিঅ\' LDAC\nক\'ডেক বাছনি আৰম্ভ কৰক: প্লেবেকৰ গুণগত মান"</string>
<string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"ষ্ট্ৰীম কৰি থকা হৈছে: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
<string name="select_private_dns_configuration_title" msgid="3700456559305263922">"ব্যক্তিগত DNS"</string>
<string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"ব্যক্তিগত DNS ম\'ড বাছনি কৰক"</string>
@@ -366,18 +365,12 @@
<string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"ৰং শুধৰণী"</string>
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"এই সুবিধাটো পৰীক্ষামূলক, সেয়ে ই কাৰ্যক্ষমতাৰ ওপৰত প্ৰভাৱ পেলাব পাৰে।"</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g>ৰ দ্বাৰা অগ্ৰাহ্য কৰা হৈছে"</string>
- <!-- no translation found for power_remaining_settings_home_page (4845022416859002011) -->
- <skip />
- <!-- no translation found for power_remaining_duration_only (6123167166221295462) -->
- <skip />
- <!-- no translation found for power_discharging_duration (8848256785736335185) -->
- <skip />
- <!-- no translation found for power_remaining_duration_only_enhanced (4189311599812296592) -->
- <skip />
- <!-- no translation found for power_discharging_duration_enhanced (1992003260664804080) -->
- <skip />
- <!-- no translation found for power_remaining_duration_only_short (3463575350656389957) -->
- <skip />
+ <string name="power_remaining_settings_home_page" msgid="4845022416859002011">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
+ <string name="power_remaining_duration_only" msgid="6123167166221295462">"প্রায় <xliff:g id="TIME_REMAINING">%1$s</xliff:g> বাকী আছে"</string>
+ <string name="power_discharging_duration" msgid="8848256785736335185">"প্রায় <xliff:g id="TIME_REMAINING">%1$s</xliff:g> বাকী আছে (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_duration_only_enhanced" msgid="4189311599812296592">"আপোনাৰ ব্যৱহাৰৰ ওপৰত ভিত্তি কৰি প্ৰায় <xliff:g id="TIME_REMAINING">%1$s</xliff:g> বাকী আছে"</string>
+ <string name="power_discharging_duration_enhanced" msgid="1992003260664804080">"আপোনাৰ ব্যৱহাৰৰ ওপৰত ভিত্তি কৰি প্রায় <xliff:g id="TIME_REMAINING">%1$s</xliff:g> বাকী আছে (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_duration_only_short" msgid="3463575350656389957">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> বাকী আছে"</string>
<string name="power_discharge_by_enhanced" msgid="2095821536747992464">"আপোনাৰ ব্যৱহাৰৰ ওপৰত ভিত্তি কৰি বেটাৰি আনুমানিকভাৱে <xliff:g id="TIME">%1$s</xliff:g> লৈকে চলিব (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"আপোনাৰ ব্যৱহাৰৰ ওপৰত ভিত্তি কৰি বেটাৰি আনুমানিকভাৱে <xliff:g id="TIME">%1$s</xliff:g> লৈকে চলিব"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"বেটাৰি আনুমানিকভাৱে <xliff:g id="TIME">%1$s</xliff:g> লৈকে চলিব (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml
index 98a95c2..66f5c07 100644
--- a/packages/SettingsLib/res/values-bn/strings.xml
+++ b/packages/SettingsLib/res/values-bn/strings.xml
@@ -278,7 +278,7 @@
<string name="media_category" msgid="4388305075496848353">"মিডিয়া"</string>
<string name="debug_monitoring_category" msgid="7640508148375798343">"পর্যবেক্ষণে রাখা"</string>
<string name="strict_mode" msgid="1938795874357830695">"কঠোর মোড সক্ষম"</string>
- <string name="strict_mode_summary" msgid="142834318897332338">"মুখ্য থ্রেডে অ্যাপ্লিকেশানগুলির দীর্ঘ কার্যকলাপের সময় স্ক্রিন ফ্ল্যাশ করে"</string>
+ <string name="strict_mode_summary" msgid="142834318897332338">"মুখ্য থ্রেডে অ্যাপ্লিকেশানগুলির দীর্ঘ অ্যাক্টিভিটির সময় স্ক্রিন ফ্ল্যাশ করে"</string>
<string name="pointer_location" msgid="6084434787496938001">"পয়েন্টারের লোকেশন"</string>
<string name="pointer_location_summary" msgid="840819275172753713">"স্ক্রিন ওভারলে বর্তমান স্পর্শ ডেটা দেখাচ্ছে"</string>
<string name="show_touches" msgid="2642976305235070316">"আলতো চাপ দেখান"</string>
@@ -302,7 +302,7 @@
<string name="force_rtl_layout_all_locales_summary" msgid="9192797796616132534">"সমস্ত স্থানের জন্য RTL এ স্ক্রিন লেআউট দিকনির্দেশে জোর দেয়"</string>
<string name="force_msaa" msgid="7920323238677284387">"4x MSAA এ জোর দিন"</string>
<string name="force_msaa_summary" msgid="9123553203895817537">"OpenGL ES 2.0 অ্যাপ্লিকেশানগুলির মধ্যে 4x MSAA সক্রিয় করুন"</string>
- <string name="show_non_rect_clip" msgid="505954950474595172">"অ-আয়তক্ষেত্রাকার ক্লিপ কার্যকলাপ ডিবাগ করুন"</string>
+ <string name="show_non_rect_clip" msgid="505954950474595172">"অ-আয়তক্ষেত্রাকার ক্লিপ অ্যাক্টিভিটি ডিবাগ করুন"</string>
<string name="track_frame_time" msgid="6094365083096851167">"প্রোফাইল HWUI রেন্ডারিং"</string>
<string name="enable_gpu_debug_layers" msgid="3848838293793255097">"GPU ডিবাগ স্তর সক্ষম করুন"</string>
<string name="enable_gpu_debug_layers_summary" msgid="8009136940671194940">"ডিবাগ অ্যাপের জন্য GPU ডিবাগ স্তর লোড হতে দিন"</string>
@@ -311,8 +311,8 @@
<string name="animator_duration_scale_title" msgid="3406722410819934083">"অ্যানিমেটর সময়কাল স্কেল"</string>
<string name="overlay_display_devices_title" msgid="5364176287998398539">"গৌণ প্রদর্শনগুলি নকল করুন"</string>
<string name="debug_applications_category" msgid="4206913653849771549">"অ্যাপ"</string>
- <string name="immediately_destroy_activities" msgid="1579659389568133959">"কার্যকলাপ রাখবেন না"</string>
- <string name="immediately_destroy_activities_summary" msgid="3592221124808773368">"ব্যবহারকারী এটি ছেড়ে যাওয়ার পরে যত তাড়াতাড়ি সম্ভব প্রতিটি কার্যকলাপ ধ্বংস করুন"</string>
+ <string name="immediately_destroy_activities" msgid="1579659389568133959">"অ্যাক্টিভিটি রাখবেন না"</string>
+ <string name="immediately_destroy_activities_summary" msgid="3592221124808773368">"ব্যবহারকারী এটি ছেড়ে যাওয়ার পরে যত তাড়াতাড়ি সম্ভব প্রতিটি অ্যাক্টিভিটি ধ্বংস করুন"</string>
<string name="app_process_limit_title" msgid="4280600650253107163">"পশ্চাদপট প্রক্রিয়ার সীমা"</string>
<string name="show_all_anrs" msgid="4924885492787069007">"ব্যাকগ্রাউন্ডের ANR দেখুন"</string>
<string name="show_all_anrs_summary" msgid="6636514318275139826">"ব্যাকগ্রাউন্ডের অ্যাপগুলির জন্য \'অ্যাপ থেকে সাড়া পাওয়া যাচ্ছে না\' মেসেজ দেখান"</string>
diff --git a/packages/SettingsLib/res/values-bs/arrays.xml b/packages/SettingsLib/res/values-bs/arrays.xml
index 8577779..9b3c38c 100644
--- a/packages/SettingsLib/res/values-bs/arrays.xml
+++ b/packages/SettingsLib/res/values-bs/arrays.xml
@@ -27,7 +27,7 @@
<item msgid="515055375277271756">"Autentifikacija…"</item>
<item msgid="1943354004029184381">"Dobivanje IP adrese…"</item>
<item msgid="4221763391123233270">"Povezano"</item>
- <item msgid="624838831631122137">"Suspendiran"</item>
+ <item msgid="624838831631122137">"Suspendirano"</item>
<item msgid="7979680559596111948">"Prekidanje veze…"</item>
<item msgid="1634960474403853625">"Isključen"</item>
<item msgid="746097431216080650">"Neuspješno"</item>
@@ -38,11 +38,11 @@
<item msgid="7714855332363650812"></item>
<item msgid="8878186979715711006">"Skeniranje…"</item>
<item msgid="355508996603873860">"Povezivanje na mrežu <xliff:g id="NETWORK_NAME">%1$s</xliff:g>..."</item>
- <item msgid="554971459996405634">"Autentifikacija sa mrežom <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="554971459996405634">"Autentifikacija s mrežom <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
<item msgid="7928343808033020343">"Dobivanje IP adrese iz mreže <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
- <item msgid="8937994881315223448">"Povezan na mrežu <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</item>
- <item msgid="1330262655415760617">"Suspendiran"</item>
- <item msgid="7698638434317271902">"Prekidanje veze sa mrežom <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="8937994881315223448">"Povezano na mrežu <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</item>
+ <item msgid="1330262655415760617">"Suspendirano"</item>
+ <item msgid="7698638434317271902">"Prekidanje veze s mrežom <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
<item msgid="197508606402264311">"Isključen"</item>
<item msgid="8578370891960825148">"Neuspješno"</item>
<item msgid="5660739516542454527">"Blokirano"</item>
@@ -59,7 +59,7 @@
<item msgid="45075631231212732">"Uvijek koristi HDCP provjeru"</item>
</string-array>
<string-array name="bluetooth_avrcp_versions">
- <item msgid="5347678900838034763">"AVRCP 1.4 (Zadano)"</item>
+ <item msgid="5347678900838034763">"AVRCP 1.4 (zadano)"</item>
<item msgid="2809759619990248160">"AVRCP 1.3"</item>
<item msgid="6199178154704729352">"AVRCP 1.5"</item>
<item msgid="5172170854953034852">"AVRCP 1.6"</item>
@@ -71,58 +71,58 @@
<item msgid="3422726142222090896">"avrcp16"</item>
</string-array>
<string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="7065842274271279580">"Koristi odabir sistema (Zadano)"</item>
+ <item msgid="7065842274271279580">"Koristi odabir sistema (zadano)"</item>
<item msgid="7539690996561263909">"SBC"</item>
<item msgid="686685526567131661">"AAC"</item>
<item msgid="5254942598247222737">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> audio"</item>
<item msgid="2091430979086738145">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> audio"</item>
<item msgid="6751080638867012696">"LDAC"</item>
- <item msgid="723675059572222462">"Omogućite opcionalne kodeke"</item>
- <item msgid="3304843301758635896">"Onemogućite opcionalne kodeke"</item>
+ <item msgid="723675059572222462">"Omogući opcionalne kodeke"</item>
+ <item msgid="3304843301758635896">"Onemogući opcionalne kodeke"</item>
</string-array>
<string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="5062108632402595000">"Koristi odabir sistema (Zadano)"</item>
+ <item msgid="5062108632402595000">"Koristi odabir sistema (zadano)"</item>
<item msgid="6898329690939802290">"SBC"</item>
<item msgid="6839647709301342559">"AAC"</item>
<item msgid="7848030269621918608">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> audio"</item>
<item msgid="298198075927343893">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g> audio"</item>
<item msgid="7950781694447359344">"LDAC"</item>
- <item msgid="2209680154067241740">"Omogućite opcionalne kodeke"</item>
- <item msgid="741805482892725657">"Onemogućite opcionalne kodeke"</item>
+ <item msgid="2209680154067241740">"Omogući opcionalne kodeke"</item>
+ <item msgid="741805482892725657">"Onemogući opcionalne kodeke"</item>
</string-array>
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
- <item msgid="3093023430402746802">"Koristi odabir sistema (Zadano)"</item>
+ <item msgid="3093023430402746802">"Koristi odabir sistema (zadano)"</item>
<item msgid="8895532488906185219">"44,1 kHz"</item>
<item msgid="2909915718994807056">"48,0 kHz"</item>
<item msgid="3347287377354164611">"88,2 kHz"</item>
<item msgid="1234212100239985373">"96,0 kHz"</item>
</string-array>
<string-array name="bluetooth_a2dp_codec_sample_rate_summaries">
- <item msgid="3214516120190965356">"Koristi odabir sistema (Zadano)"</item>
+ <item msgid="3214516120190965356">"Koristi odabir sistema (zadano)"</item>
<item msgid="4482862757811638365">"44,1 kHz"</item>
<item msgid="354495328188724404">"48,0 kHz"</item>
<item msgid="7329816882213695083">"88,2 kHz"</item>
<item msgid="6967397666254430476">"96,0 kHz"</item>
</string-array>
<string-array name="bluetooth_a2dp_codec_bits_per_sample_titles">
- <item msgid="2684127272582591429">"Koristi odabir sistema (Zadano)"</item>
+ <item msgid="2684127272582591429">"Koristi odabir sistema (zadano)"</item>
<item msgid="5618929009984956469">"16 bitova/uzorak"</item>
<item msgid="3412640499234627248">"24 bitova/uzorak"</item>
<item msgid="121583001492929387">"32 bitova/uzorak"</item>
</string-array>
<string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries">
- <item msgid="1081159789834584363">"Koristi odabir sistema (Zadano)"</item>
+ <item msgid="1081159789834584363">"Koristi odabir sistema (zadano)"</item>
<item msgid="4726688794884191540">"16 bitova/uzorak"</item>
<item msgid="305344756485516870">"24 bitova/uzorak"</item>
<item msgid="244568657919675099">"32 bitova/uzorak"</item>
</string-array>
<string-array name="bluetooth_a2dp_codec_channel_mode_titles">
- <item msgid="5226878858503393706">"Koristi odabir sistema (Zadano)"</item>
+ <item msgid="5226878858503393706">"Koristi odabir sistema (zadano)"</item>
<item msgid="4106832974775067314">"Mono"</item>
<item msgid="5571632958424639155">"Stereo"</item>
</string-array>
<string-array name="bluetooth_a2dp_codec_channel_mode_summaries">
- <item msgid="4118561796005528173">"Koristi odabir sistema (Zadano)"</item>
+ <item msgid="4118561796005528173">"Koristi odabir sistema (zadano)"</item>
<item msgid="8900559293912978337">"Mono"</item>
<item msgid="8883739882299884241">"Stereo"</item>
</string-array>
@@ -130,13 +130,13 @@
<item msgid="7158319962230727476">"Optimizirano za kvalitet zvuka (990 kbps/909 kbps)"</item>
<item msgid="2921767058740704969">"Uravnotežen kvalitet zvuka i veze (660kbps/606kbps)"</item>
<item msgid="8860982705384396512">"Optimizirano za kvalitet veze (330 kbps/303 kbps)"</item>
- <item msgid="4414060457677684127">"Maksimalan napor (Prilagodljiva brzina prijenosa)"</item>
+ <item msgid="4414060457677684127">"Maksimalan napor (prilagodljiva brzina prijenosa)"</item>
</string-array>
<string-array name="bluetooth_a2dp_codec_ldac_playback_quality_summaries">
<item msgid="6398189564246596868">"Optimizirano za kvalitet zvuka"</item>
<item msgid="4327143584633311908">"Uravnotežen kvalitet zvuka i veze"</item>
<item msgid="4681409244565426925">"Optimizirano za kvalitet veze"</item>
- <item msgid="364670732877872677">"Maksimalan napor (Prilagodljiva brzina prijenosa)"</item>
+ <item msgid="364670732877872677">"Maksimalan napor (prilagodljiva brzina prijenosa)"</item>
</string-array>
<string-array name="bluetooth_audio_active_device_summaries">
<item msgid="4862957058729193940"></item>
@@ -169,7 +169,7 @@
<string-array name="select_logpersist_titles">
<item msgid="1744840221860799971">"Isključeno"</item>
<item msgid="3054662377365844197">"Sve"</item>
- <item msgid="688870735111627832">"Svi osim radija"</item>
+ <item msgid="688870735111627832">"Sve osim radija"</item>
<item msgid="2850427388488887328">"samo kernel"</item>
</string-array>
<string-array name="select_logpersist_summaries">
@@ -208,13 +208,13 @@
<string-array name="overlay_display_devices_entries">
<item msgid="1606809880904982133">"Nema"</item>
<item msgid="9033194758688161545">"480p"</item>
- <item msgid="1025306206556583600">"480p (osiguran)"</item>
+ <item msgid="1025306206556583600">"480p (sigurno)"</item>
<item msgid="1853913333042744661">"720p"</item>
- <item msgid="3414540279805870511">"720p (osiguran)"</item>
+ <item msgid="3414540279805870511">"720p (sigurno)"</item>
<item msgid="9039818062847141551">"1080p"</item>
- <item msgid="4939496949750174834">"1080p (osiguran)"</item>
+ <item msgid="4939496949750174834">"1080p (sigurno)"</item>
<item msgid="1833612718524903568">"4K"</item>
- <item msgid="238303513127879234">"4K (osiguran)"</item>
+ <item msgid="238303513127879234">"4K (sigurno)"</item>
<item msgid="3547211260846843098">"4K (povećava rezoluciju)"</item>
<item msgid="5411365648951414254">"4K (povećava rezoluciju, osiguran)"</item>
<item msgid="1311305077526792901">"720p, 1080p (dupli ekran)"</item>
diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml
index b8195b1..e049f6f 100644
--- a/packages/SettingsLib/res/values-bs/strings.xml
+++ b/packages/SettingsLib/res/values-bs/strings.xml
@@ -43,7 +43,7 @@
<string name="wifi_status_no_internet" msgid="5784710974669608361">"Nema internetske veze"</string>
<string name="wifi_status_sign_in_required" msgid="123517180404752756">"Potrebna je prijava"</string>
<string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"Pristupna tačka je privremeno puna"</string>
- <string name="connected_via_carrier" msgid="7583780074526041912">"Povezana koristeći %1$s"</string>
+ <string name="connected_via_carrier" msgid="7583780074526041912">"Povezano koristeći %1$s"</string>
<string name="available_via_carrier" msgid="1469036129740799053">"Dostupna koristeći %1$s"</string>
<string name="speed_label_very_slow" msgid="1867055264243608530">"Veoma sporo"</string>
<string name="speed_label_slow" msgid="813109590815810235">"Sporo"</string>
@@ -84,11 +84,11 @@
<string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Povezano na slušni aparat"</string>
<string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Povezano sa zvukom medija"</string>
<string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Povezano na zvuk telefona"</string>
- <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Povezan na server za prijenos podataka"</string>
+ <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Povezano sa serverom za prijenos podataka"</string>
<string name="bluetooth_map_profile_summary_connected" msgid="8191407438851351713">"Povezano na mapu"</string>
<string name="bluetooth_sap_profile_summary_connected" msgid="8561765057453083838">"Povezan na SAP"</string>
- <string name="bluetooth_opp_profile_summary_not_connected" msgid="1267091356089086285">"Nije povezan na server za prijenos podataka"</string>
- <string name="bluetooth_hid_profile_summary_connected" msgid="3381760054215168689">"Spojen na ulazni uređaj"</string>
+ <string name="bluetooth_opp_profile_summary_not_connected" msgid="1267091356089086285">"Nije povezano sa serverom za prijenos podataka"</string>
+ <string name="bluetooth_hid_profile_summary_connected" msgid="3381760054215168689">"Povezano s ulaznim uređajem"</string>
<string name="bluetooth_pan_user_profile_summary_connected" msgid="6436258151814414028">"Povezano na uređaj za pristup internetu"</string>
<string name="bluetooth_pan_nap_profile_summary_connected" msgid="1322694224800769308">"Dijeljenje lokalne internetske veze s uređajem"</string>
<string name="bluetooth_pan_profile_summary_use_for" msgid="5736111170225304239">"Koristi za pristup internetu"</string>
@@ -102,10 +102,10 @@
<string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Upari"</string>
<string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"UPARI"</string>
<string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Otkaži"</string>
- <string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"Uparivanje odobrava pristup kontaktima i istoriji poziva kada je uspostavljeno."</string>
- <string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"Nije se moguće upariti s uređajem <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
- <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"Nije se moguće upariti s uređajem <xliff:g id="DEVICE_NAME">%1$s</xliff:g> zbog pogrešnog PIN-a ili pristupnog koda."</string>
- <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"Ne može komunicirati sa uređajem <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"Uparivanje odobrava pristup kontaktima i historiji poziva kada je uspostavljeno."</string>
+ <string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"Nije moguće upariti s uređajem <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"Nije moguće upariti s uređajem <xliff:g id="DEVICE_NAME">%1$s</xliff:g> zbog pogrešnog PIN-a ili pristupnog koda."</string>
+ <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"Nije moguće komunicirati s uređajem <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
<string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"Uređaj <xliff:g id="DEVICE_NAME">%1$s</xliff:g> je odbio uparivanje."</string>
<string name="bluetooth_talkback_computer" msgid="4875089335641234463">"Računar"</string>
<string name="bluetooth_talkback_headset" msgid="5140152177885220949">"Slušalice s mikrofonom"</string>
@@ -118,12 +118,12 @@
<string name="bluetooth_hearingaid_right_pairing_message" msgid="1550373802309160891">"Uparivanje desnog slušnog aparata…"</string>
<string name="bluetooth_hearingaid_left_battery_level" msgid="8797811465352097562">"Lijevi - <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> baterije"</string>
<string name="bluetooth_hearingaid_right_battery_level" msgid="7309476148173459677">"Desni - <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> baterije"</string>
- <string name="accessibility_wifi_off" msgid="1166761729660614716">"WiFi isključen."</string>
+ <string name="accessibility_wifi_off" msgid="1166761729660614716">"WiFi je isključen."</string>
<string name="accessibility_no_wifi" msgid="8834610636137374508">"WiFi nije povezan."</string>
- <string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"WiFi jedna crtica."</string>
- <string name="accessibility_wifi_two_bars" msgid="3569851234710034416">"WiFi dvije crtice."</string>
- <string name="accessibility_wifi_three_bars" msgid="8134185644861380311">"WiFi tri crtice."</string>
- <string name="accessibility_wifi_signal_full" msgid="7061045677694702">"WiFi puni signal."</string>
+ <string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"WiFi signal ima jednu crtu."</string>
+ <string name="accessibility_wifi_two_bars" msgid="3569851234710034416">"WiFi signal ima dvije crte."</string>
+ <string name="accessibility_wifi_three_bars" msgid="8134185644861380311">"WiFi signal ima tri crte."</string>
+ <string name="accessibility_wifi_signal_full" msgid="7061045677694702">"WiFi signal je pun."</string>
<string name="accessibility_wifi_security_type_none" msgid="1223747559986205423">"Otvorena mreža"</string>
<string name="accessibility_wifi_security_type_secured" msgid="862921720418885331">"Sigurna mreža"</string>
<string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string>
@@ -132,7 +132,7 @@
<string name="tether_settings_title_usb" msgid="6688416425801386511">"Povezivanje mobitela USB-om"</string>
<string name="tether_settings_title_wifi" msgid="3277144155960302049">"Prijenosna pristupna tačka"</string>
<string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Dijeljenje Bluetooth veze"</string>
- <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"Dijeljenje veze"</string>
+ <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"Povezivanje putem mobitela"</string>
<string name="tether_settings_title_all" msgid="8356136101061143841">"Povezivanje putem mobitela i prijenosna pristupna tačka"</string>
<string name="managed_user_title" msgid="8109605045406748842">"Sve radne aplikacije"</string>
<string name="user_guest" msgid="8475274842845401871">"Gost"</string>
@@ -145,7 +145,7 @@
<string name="tts_default_rate_title" msgid="6030550998379310088">"Brzina govora"</string>
<string name="tts_default_rate_summary" msgid="4061815292287182801">"Brzina kojom se izgovara tekst"</string>
<string name="tts_default_pitch_title" msgid="6135942113172488671">"Visina"</string>
- <string name="tts_default_pitch_summary" msgid="1944885882882650009">"Utječe na ton sintetiziranog govora"</string>
+ <string name="tts_default_pitch_summary" msgid="1944885882882650009">"Utiče na ton sintetiziranog govora"</string>
<string name="tts_default_lang_title" msgid="8018087612299820556">"Jezik"</string>
<string name="tts_lang_use_system" msgid="2679252467416513208">"Korištenje sistemskog jezika"</string>
<string name="tts_lang_not_selected" msgid="7395787019276734765">"Jezik nije izabran"</string>
@@ -187,7 +187,7 @@
<string name="development_settings_summary" msgid="1815795401632854041">"Postavi opcije za razvoj aplikacija"</string>
<string name="development_settings_not_available" msgid="4308569041701535607">"Opcije za programere nisu dostupne za ovog korisnika"</string>
<string name="vpn_settings_not_available" msgid="956841430176985598">"VPN postavke nisu dostupne za ovog korisnika"</string>
- <string name="tethering_settings_not_available" msgid="6765770438438291012">"Postavke za privezivanje nisu dostupne za ovog korisnika"</string>
+ <string name="tethering_settings_not_available" msgid="6765770438438291012">"Postavke za povezivanje putem mobitela nisu dostupne za ovog korisnika"</string>
<string name="apn_settings_not_available" msgid="7873729032165324000">"Postavke za ime pristupne tačke nisu dostupne za ovog korisnika"</string>
<string name="enable_adb" msgid="7982306934419797485">"Otklanjanje grešaka putem uređaja spojenog na USB"</string>
<string name="enable_adb_summary" msgid="4881186971746056635">"Način rada za uklanjanje grešaka kada je povezan USB"</string>
@@ -201,7 +201,7 @@
<string name="oem_unlock_enable" msgid="6040763321967327691">"OEM otključavanje"</string>
<string name="oem_unlock_enable_summary" msgid="4720281828891618376">"Dozvoli otključavanje bootloadera"</string>
<string name="confirm_enable_oem_unlock_title" msgid="4802157344812385674">"Želite li dozvoliti OEM otključavanje?"</string>
- <string name="confirm_enable_oem_unlock_text" msgid="5517144575601647022">"UPOZORENJE: Funkcije zaštite ovog uređaja neće funkcionisati dok je ova postavka uključena."</string>
+ <string name="confirm_enable_oem_unlock_text" msgid="5517144575601647022">"UPOZORENJE: Funkcije zaštite ovog uređaja neće funkcionirati dok je ova postavka uključena."</string>
<string name="mock_location_app" msgid="7966220972812881854">"Odaberite aplikaciju za lažne lokacije"</string>
<string name="mock_location_app_not_set" msgid="809543285495344223">"Aplikacija za lažnu lokaciju nije postavljena"</string>
<string name="mock_location_app_set" msgid="8966420655295102685">"Aplikacija za lažne lokacije: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
@@ -210,7 +210,7 @@
<string name="wifi_verbose_logging" msgid="4203729756047242344">"Omogući detaljniju evidenciju za WiFi"</string>
<string name="wifi_connected_mac_randomization" msgid="3168165236877957767">"Nasumični odabir MAC adrese pri povezivanju"</string>
<string name="mobile_data_always_on" msgid="8774857027458200434">"Mobilna mreža za prijenos podataka je uvijek aktivna"</string>
- <string name="tethering_hardware_offload" msgid="7470077827090325814">"Hardversko ubrzavanje dijeljenja veze"</string>
+ <string name="tethering_hardware_offload" msgid="7470077827090325814">"Hardversko ubrzavanje za povezivanje putem mobitela"</string>
<string name="bluetooth_show_devices_without_names" msgid="4708446092962060176">"Prikaži Bluetooth uređaje bez naziva"</string>
<string name="bluetooth_disable_absolute_volume" msgid="2660673801947898809">"Onemogući apsolutnu jačinu zvuka"</string>
<string name="bluetooth_select_avrcp_version_string" msgid="3750059931120293633">"Bluetooth AVRCP verzija"</string>
@@ -220,7 +220,7 @@
<string name="bluetooth_select_a2dp_codec_sample_rate" msgid="4788245703824623062">"Brzina uzorkovanja za Bluetooth audio"</string>
<string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="8010380028880963535">"Aktivirajte Bluetooth Audio Codec\nOdabir: Brzina uzorkovanja"</string>
<string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="2099645202720164141">"Bluetooth audio bitovi po uzorku"</string>
- <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="8063859754619484760">"Aktivirajte Bluetooth Audio Codec\nOdabir: Bitovi po semplu"</string>
+ <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="8063859754619484760">"Aktivirajte Bluetooth Audio Codec\nOdabir: Bitovi po uzorku"</string>
<string name="bluetooth_select_a2dp_codec_channel_mode" msgid="884855779449390540">"Način Bluetooth audio kanala"</string>
<string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="7234956835280563341">"Aktivirajte Bluetooth Audio Codec\nOdabir: Način rada po kanalima"</string>
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"Bluetooth Audio LDAC kodek: Kvalitet reprodukcije"</string>
@@ -249,7 +249,7 @@
<string name="allow_mock_location" msgid="2787962564578664888">"Dozvoli lažne lokacije"</string>
<string name="allow_mock_location_summary" msgid="317615105156345626">"Dozvoli lažne lokacije"</string>
<string name="debug_view_attributes" msgid="6485448367803310384">"Omogući pregled atributa prikaza"</string>
- <string name="mobile_data_always_on_summary" msgid="8149773901431697910">"Uvijek drži prijenos podataka na mobilnoj mreži aktivnim, čak i kada je WiFi je aktivan (za brzo prebacivanje između mreža)."</string>
+ <string name="mobile_data_always_on_summary" msgid="8149773901431697910">"Uvijek drži prijenos podataka na mobilnoj mreži aktivnim, čak i kada je WiFi aktivan (za brzo prebacivanje između mreža)."</string>
<string name="tethering_hardware_offload_summary" msgid="7726082075333346982">"Koristi hardversko ubrzavanje dijeljenja veze, ako je dostupno"</string>
<string name="adb_warning_title" msgid="6234463310896563253">"Omogućiti otklanjanje grešaka putem uređaja spojenog na USB?"</string>
<string name="adb_warning_message" msgid="7316799925425402244">"Otklanjanje grešaka putem uređaja spojenog na USB je namijenjeno samo u svrhe razvoja aplikacija. Koristite ga za kopiranje podataka između računara i uređaja, instaliranje aplikacija na uređaj bez obavještenja te čitanje podataka iz zapisnika."</string>
@@ -277,7 +277,7 @@
<string name="debug_hw_drawing_category" msgid="6220174216912308658">"Prikaz s hardverskom akceleracijom"</string>
<string name="media_category" msgid="4388305075496848353">"Mediji"</string>
<string name="debug_monitoring_category" msgid="7640508148375798343">"Praćenje"</string>
- <string name="strict_mode" msgid="1938795874357830695">"Omogućen strogi režim"</string>
+ <string name="strict_mode" msgid="1938795874357830695">"Omogućen strogi način rada"</string>
<string name="strict_mode_summary" msgid="142834318897332338">"Prikaži ekran uz treptanje kada aplikacije vrše duge operacije u glavnoj niti"</string>
<string name="pointer_location" msgid="6084434787496938001">"Lokacija pokazivača"</string>
<string name="pointer_location_summary" msgid="840819275172753713">"Trenutni podaci o dodirivanju prikazuju se u nadsloju preko ekrana"</string>
@@ -298,8 +298,8 @@
<string name="usb_audio_disable_routing_summary" msgid="980282760277312264">"Onemogući autom. usmjerav. na USB audio periferije"</string>
<string name="debug_layout" msgid="5981361776594526155">"Prikaži granice rasporeda"</string>
<string name="debug_layout_summary" msgid="2001775315258637682">"Prikaži granice isječka, margine itd."</string>
- <string name="force_rtl_layout_all_locales" msgid="2259906643093138978">"Prisilno postavi raspored s desna u lijevo"</string>
- <string name="force_rtl_layout_all_locales_summary" msgid="9192797796616132534">"Prisilno postavi raspored ekrana s desna u lijevo za sve regije"</string>
+ <string name="force_rtl_layout_all_locales" msgid="2259906643093138978">"Prisilno postavi raspored s desna ulijevo"</string>
+ <string name="force_rtl_layout_all_locales_summary" msgid="9192797796616132534">"Prisilno postavi raspored ekrana s desna ulijevo za sve regije"</string>
<string name="force_msaa" msgid="7920323238677284387">"Prinudno primijeni 4x MSAA"</string>
<string name="force_msaa_summary" msgid="9123553203895817537">"Omogući 4x MSAA u OpenGL ES 2.0 aplikacijama"</string>
<string name="show_non_rect_clip" msgid="505954950474595172">"Ispravi greške na nepravougaonim operacijama isjecanja"</string>
@@ -321,19 +321,19 @@
<string name="force_allow_on_external" msgid="3215759785081916381">"Nametni aplikacije na vanjskoj pohrani"</string>
<string name="force_allow_on_external_summary" msgid="3640752408258034689">"Omogućava upisivanje svih aplikacija u vanjsku pohranu, bez obzira na prikazane vrijednosti"</string>
<string name="force_resizable_activities" msgid="8615764378147824985">"Nametni aktivnostima mijenjanje veličina"</string>
- <string name="force_resizable_activities_summary" msgid="6667493494706124459">"Omogući mijenjanje veličine svih aktivnosti za prikaz sa više prozora, bez obzira na prikazane vrijednosti."</string>
+ <string name="force_resizable_activities_summary" msgid="6667493494706124459">"Omogući mijenjanje veličine svih aktivnosti za prikaz s više prozora, bez obzira na prikazane vrijednosti."</string>
<string name="enable_freeform_support" msgid="1461893351278940416">"Omogući prozore nepravilnih oblika"</string>
<string name="enable_freeform_support_summary" msgid="8247310463288834487">"Omogući podršku za eksperimentalne prozore nepravilnih oblika."</string>
<string name="local_backup_password_title" msgid="3860471654439418822">"Lozinka za sigurnosnu kopiju radne površine"</string>
<string name="local_backup_password_summary_none" msgid="6951095485537767956">"Potpune sigurnosne kopije za računare trenutno nisu zaštićene"</string>
- <string name="local_backup_password_summary_change" msgid="5376206246809190364">"Dodirnite da promijenite ili uklonite lozinku za potpune rezervne kopije sa radne površine"</string>
- <string name="local_backup_password_toast_success" msgid="582016086228434290">"Nova lozinka za sigurnosnu kopiju postavljena"</string>
+ <string name="local_backup_password_summary_change" msgid="5376206246809190364">"Dodirnite da promijenite ili uklonite lozinku za potpune rezervne kopije s radne površine"</string>
+ <string name="local_backup_password_toast_success" msgid="582016086228434290">"Nova lozinka za sigurnosnu kopiju je postavljena"</string>
<string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"Nova lozinka i potvrda se ne podudaraju"</string>
<string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"Nije uspjelo postavljanje lozinke za sigurnosnu kopiju"</string>
<string-array name="color_mode_names">
- <item msgid="2425514299220523812">"Živopisan (zadano)"</item>
+ <item msgid="2425514299220523812">"Živopisno (zadano)"</item>
<item msgid="8446070607501413455">"Prirodan"</item>
- <item msgid="6553408765810699025">"Standardni"</item>
+ <item msgid="6553408765810699025">"Standardno"</item>
</string-array>
<string-array name="color_mode_descriptions">
<item msgid="4979629397075120893">"Unaprijeđene boje"</item>
@@ -353,9 +353,9 @@
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Pretvaranje…"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Fajl je već šifriran"</string>
<string name="title_convert_fbe" msgid="1263622876196444453">"Pretvaranje u šifrirane fajlove"</string>
- <string name="convert_to_fbe_warning" msgid="6139067817148865527">"Pretvorite particiju sa podacima u particiju šifriranu sistemom fajlova.\n !! Upozorenje!! Ovo će izbrisati sve vaše podatke.\n Ova funkcija je u alfa fazi razvoja i možda neće ispravno raditi.\n Pritisnite \"Obriši i pretvori…\" da nastavite."</string>
+ <string name="convert_to_fbe_warning" msgid="6139067817148865527">"Pretvorite particiju s podacima u particiju šifriranu sistemom fajlova.\n !! Upozorenje!! Ovo će izbrisati sve vaše podatke.\n Ova funkcija je u alfa fazi razvoja i možda neće ispravno raditi.\n Pritisnite \"Obriši i pretvori…\" da nastavite."</string>
<string name="button_convert_fbe" msgid="5152671181309826405">"Obriši i pretvori…"</string>
- <string name="picture_color_mode" msgid="4560755008730283695">"Režim boja Slika"</string>
+ <string name="picture_color_mode" msgid="4560755008730283695">"Način rada boja slika"</string>
<string name="picture_color_mode_desc" msgid="1141891467675548590">"Koristi sRGB"</string>
<string name="daltonizer_mode_disabled" msgid="7482661936053801862">"Onemogućeno"</string>
<string name="daltonizer_mode_monochromacy" msgid="8485709880666106721">"Crno-bijelo"</string>
@@ -422,8 +422,8 @@
<string name="retail_demo_reset_title" msgid="696589204029930100">"Potrebna je lozinka"</string>
<string name="active_input_method_subtypes" msgid="3596398805424733238">"Aktivne metode unosa"</string>
<string name="use_system_language_to_select_input_method_subtypes" msgid="5747329075020379587">"Koristi jezik sistema"</string>
- <string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Nije uspjelo otvaranje postavki za <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>"</string>
- <string name="ime_security_warning" msgid="4135828934735934248">"Ovaj način unosa može prikupiti sav tekst koji otkucate, uključujući lične podatke kao što su lozinke i brojevi kreditnih kartica. Način omogućava aplikacija <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. Da li želite koristiti ovaj način unosa?"</string>
+ <string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Otvaranje postavki za <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g> nije uspjelo"</string>
+ <string name="ime_security_warning" msgid="4135828934735934248">"Ovaj način unosa može prikupiti sav tekst koji upišete, uključujući lične podatke kao što su lozinke i brojevi kreditnih kartica. Način omogućava aplikacija <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. Da li želite koristiti ovaj način unosa?"</string>
<string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Napomena: Nakon ponovnog pokretanja, ova aplikacija se neće moći pokrenuti dok ne otključate telefon"</string>
<string name="ims_reg_title" msgid="7609782759207241443">"Stanje IMS registracije"</string>
<string name="ims_reg_status_registered" msgid="933003316932739188">"Registrirano"</string>
diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index 2488f55..50d9c17 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -398,8 +398,8 @@
<string name="enabled_by_admin" msgid="5302986023578399263">"Activada per l\'administrador"</string>
<string name="disabled_by_admin" msgid="8505398946020816620">"Desactivada per l\'administrador"</string>
<string name="disabled" msgid="9206776641295849915">"Desactivat"</string>
- <string name="external_source_trusted" msgid="2707996266575928037">"Permeses"</string>
- <string name="external_source_untrusted" msgid="2677442511837596726">"No permeses"</string>
+ <string name="external_source_trusted" msgid="2707996266575928037">"Amb permís"</string>
+ <string name="external_source_untrusted" msgid="2677442511837596726">"Sense permís"</string>
<string name="install_other_apps" msgid="6986686991775883017">"Instal·lar aplicacions desconegudes"</string>
<string name="home" msgid="3256884684164448244">"Pàgina d\'inici de configuració"</string>
<string-array name="battery_labels">
diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml
index e9b07a4..f04767b 100644
--- a/packages/SettingsLib/res/values-es/strings.xml
+++ b/packages/SettingsLib/res/values-es/strings.xml
@@ -217,8 +217,8 @@
<string name="bluetooth_select_avrcp_version_dialog_title" msgid="7277329668298705702">"Selecciona la versión AVRCP del Bluetooth"</string>
<string name="bluetooth_select_a2dp_codec_type" msgid="90597356942154882">"Códec de audio de Bluetooth"</string>
<string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="8436224899475822557">"Activar el códec de audio por Bluetooth\nSelección"</string>
- <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="4788245703824623062">"Porcentaje de muestreo de audio por Bluetooth"</string>
- <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="8010380028880963535">"Activar el códec de audio por Bluetooth\nSelección: porcentaje de muestreo"</string>
+ <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="4788245703824623062">"Frecuencia de muestreo de audio por Bluetooth"</string>
+ <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="8010380028880963535">"Activar el códec de audio por Bluetooth\nSelección: frecuencia de muestreo"</string>
<string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="2099645202720164141">"Bits por muestra del audio Bluetooth"</string>
<string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="8063859754619484760">"Activar el códec de audio por Bluetooth\nSelección: bits por muestra"</string>
<string name="bluetooth_select_a2dp_codec_channel_mode" msgid="884855779449390540">"Modo de canal de audio por Bluetooth"</string>
@@ -238,7 +238,7 @@
<string name="wifi_connected_mac_randomization_summary" msgid="1743059848752201485">"Ordenar las direcciones MAC de forma aleatoria al conectarse a redes Wi‑Fi"</string>
<string name="wifi_metered_label" msgid="4514924227256839725">"Medida"</string>
<string name="wifi_unmetered_label" msgid="6124098729457992931">"No medida"</string>
- <string name="select_logd_size_title" msgid="7433137108348553508">"Tamaños de búfer de registrador"</string>
+ <string name="select_logd_size_title" msgid="7433137108348553508">"Tamaños del búfer para registrar"</string>
<string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Elige el tamaño del Logger por búfer"</string>
<string name="dev_logpersist_clear_warning_title" msgid="684806692440237967">"¿Borrar almacenamiento continuo del registrador?"</string>
<string name="dev_logpersist_clear_warning_message" msgid="2256582531342994562">"Cuando ya no supervisamos la actividad con el registrador de forma continua, estamos obligados a borrar los datos del registrador almacenados en el dispositivo."</string>
diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml
index 7dfd429..447ce3c 100644
--- a/packages/SettingsLib/res/values-fa/strings.xml
+++ b/packages/SettingsLib/res/values-fa/strings.xml
@@ -159,7 +159,7 @@
<string name="tts_default_sample_string" msgid="4040835213373086322">"این نمونهای از ترکیب گفتار است"</string>
<string name="tts_status_title" msgid="7268566550242584413">"وضعیت زبان پیشفرض"</string>
<string name="tts_status_ok" msgid="1309762510278029765">"<xliff:g id="LOCALE">%1$s</xliff:g> کاملاً پشتیبانی میشود"</string>
- <string name="tts_status_requires_network" msgid="6042500821503226892">"<xliff:g id="LOCALE">%1$s</xliff:g> به اتصال شبکه نیاز دارد"</string>
+ <string name="tts_status_requires_network" msgid="6042500821503226892">"<xliff:g id="LOCALE">%1$s</xliff:g> باید اتصال شبکه داشته باشد"</string>
<string name="tts_status_not_supported" msgid="4491154212762472495">"<xliff:g id="LOCALE">%1$s</xliff:g> پشتیبانی نمیشود"</string>
<string name="tts_status_checking" msgid="5339150797940483592">"در حال بررسی…"</string>
<string name="tts_engine_settings_title" msgid="3499112142425680334">"تنظیمات برای <xliff:g id="TTS_ENGINE_NAME">%s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-gl/arrays.xml b/packages/SettingsLib/res/values-gl/arrays.xml
index a25d136..f8a69c2 100644
--- a/packages/SettingsLib/res/values-gl/arrays.xml
+++ b/packages/SettingsLib/res/values-gl/arrays.xml
@@ -207,17 +207,17 @@
</string-array>
<string-array name="overlay_display_devices_entries">
<item msgid="1606809880904982133">"Ningunha"</item>
- <item msgid="9033194758688161545">"480 p"</item>
- <item msgid="1025306206556583600">"480 p (seguro)"</item>
- <item msgid="1853913333042744661">"720 p"</item>
- <item msgid="3414540279805870511">"720 p (seguro)"</item>
- <item msgid="9039818062847141551">"1080 p"</item>
- <item msgid="4939496949750174834">"1080 p (seguro)"</item>
+ <item msgid="9033194758688161545">"480p"</item>
+ <item msgid="1025306206556583600">"480p (seguro)"</item>
+ <item msgid="1853913333042744661">"720p"</item>
+ <item msgid="3414540279805870511">"720p (seguro)"</item>
+ <item msgid="9039818062847141551">"1080p"</item>
+ <item msgid="4939496949750174834">"1080p (seguro)"</item>
<item msgid="1833612718524903568">"4K"</item>
<item msgid="238303513127879234">"4K (seguro)"</item>
<item msgid="3547211260846843098">"4K (mellorado)"</item>
<item msgid="5411365648951414254">"4K (mellorado e seguro)"</item>
- <item msgid="1311305077526792901">"720 p, 1080 p (pantalla dual)"</item>
+ <item msgid="1311305077526792901">"720p, 1080p (pantalla dual)"</item>
</string-array>
<string-array name="enable_opengl_traces_entries">
<item msgid="3191973083884253830">"Ningún"</item>
diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml
index 4f26f6a..3e55dc3 100644
--- a/packages/SettingsLib/res/values-gl/strings.xml
+++ b/packages/SettingsLib/res/values-gl/strings.xml
@@ -110,7 +110,7 @@
<string name="bluetooth_talkback_computer" msgid="4875089335641234463">"Ordenador"</string>
<string name="bluetooth_talkback_headset" msgid="5140152177885220949">"Auriculares con micrófono"</string>
<string name="bluetooth_talkback_phone" msgid="4260255181240622896">"Teléfono"</string>
- <string name="bluetooth_talkback_imaging" msgid="551146170554589119">"Imaxes"</string>
+ <string name="bluetooth_talkback_imaging" msgid="551146170554589119">"Dispositivo de imaxe"</string>
<string name="bluetooth_talkback_headphone" msgid="26580326066627664">"Auriculares"</string>
<string name="bluetooth_talkback_input_peripheral" msgid="5165842622743212268">"Periférico de entrada"</string>
<string name="bluetooth_talkback_bluetooth" msgid="5615463912185280812">"Bluetooth"</string>
@@ -213,8 +213,8 @@
<string name="tethering_hardware_offload" msgid="7470077827090325814">"Aceleración de hardware para conexión compartida"</string>
<string name="bluetooth_show_devices_without_names" msgid="4708446092962060176">"Mostrar dispositivos Bluetooth sen nomes"</string>
<string name="bluetooth_disable_absolute_volume" msgid="2660673801947898809">"Desactivar volume absoluto"</string>
- <string name="bluetooth_select_avrcp_version_string" msgid="3750059931120293633">"Versión AVRCP de Bluetooth"</string>
- <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7277329668298705702">"Selecciona a versión AVRCP de Bluetooth"</string>
+ <string name="bluetooth_select_avrcp_version_string" msgid="3750059931120293633">"Versión de Bluetooth AVRCP"</string>
+ <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7277329668298705702">"Selecciona a versión de Bluetooth AVRCP"</string>
<string name="bluetooth_select_a2dp_codec_type" msgid="90597356942154882">"Códec de audio por Bluetooth"</string>
<string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="8436224899475822557">"Activar códec de audio por Bluetooth\nSelección"</string>
<string name="bluetooth_select_a2dp_codec_sample_rate" msgid="4788245703824623062">"Taxa de mostraxe de audio por Bluetooth"</string>
@@ -353,8 +353,8 @@
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Converter..."</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Xa se encriptou o ficheiro"</string>
<string name="title_convert_fbe" msgid="1263622876196444453">"Convertendo no encriptado baseado en ficheiros"</string>
- <string name="convert_to_fbe_warning" msgid="6139067817148865527">"Converte a partición de datos nunha encriptación baseada en ficheiros.\n Advertencia: Esta acción borrará todos os datos.\n Esta función é alfa e quizais non funcione correctamente.\n Para continuar, toca Limpar e converter..."</string>
- <string name="button_convert_fbe" msgid="5152671181309826405">"Limpar e converter..."</string>
+ <string name="convert_to_fbe_warning" msgid="6139067817148865527">"Converte a partición de datos nunha encriptación baseada en ficheiros.\n Advertencia: Esta acción borrará todos os datos.\n Esta función é alfa e quizais non funcione correctamente.\n Para continuar, toca Borrar e converter..."</string>
+ <string name="button_convert_fbe" msgid="5152671181309826405">"Borrar e converter..."</string>
<string name="picture_color_mode" msgid="4560755008730283695">"Modo de cor da imaxe"</string>
<string name="picture_color_mode_desc" msgid="1141891467675548590">"Utiliza sRGB"</string>
<string name="daltonizer_mode_disabled" msgid="7482661936053801862">"Desactivado"</string>
diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml
index 2c43d33..b2a6bd0 100644
--- a/packages/SettingsLib/res/values-ja/strings.xml
+++ b/packages/SettingsLib/res/values-ja/strings.xml
@@ -141,7 +141,7 @@
<string name="launch_defaults_some" msgid="313159469856372621">"一部デフォルトで設定"</string>
<string name="launch_defaults_none" msgid="4241129108140034876">"デフォルトの設定なし"</string>
<string name="tts_settings" msgid="8186971894801348327">"テキスト読み上げの設定"</string>
- <string name="tts_settings_title" msgid="1237820681016639683">"テキスト読み上げの出力"</string>
+ <string name="tts_settings_title" msgid="1237820681016639683">"テキスト読み上げの設定"</string>
<string name="tts_default_rate_title" msgid="6030550998379310088">"音声の速度"</string>
<string name="tts_default_rate_summary" msgid="4061815292287182801">"テキストの読み上げ速度"</string>
<string name="tts_default_pitch_title" msgid="6135942113172488671">"音の高さ"</string>
diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml
index 7f9f4a6..a3293e5 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -307,7 +307,7 @@
<string name="enable_gpu_debug_layers" msgid="3848838293793255097">"Slå på GPU-feilsøkingslag"</string>
<string name="enable_gpu_debug_layers_summary" msgid="8009136940671194940">"Tillat GPU-feilsøkingslag for feilsøkingsapper"</string>
<string name="window_animation_scale_title" msgid="6162587588166114700">"Animasjonsskala for vindu"</string>
- <string name="transition_animation_scale_title" msgid="387527540523595875">"Overgangsanimasjonsskala"</string>
+ <string name="transition_animation_scale_title" msgid="387527540523595875">"Animasjonsskala for overgang"</string>
<string name="animator_duration_scale_title" msgid="3406722410819934083">"Varighetsskala for animasjon"</string>
<string name="overlay_display_devices_title" msgid="5364176287998398539">"Simulering av sekundærskjermer"</string>
<string name="debug_applications_category" msgid="4206913653849771549">"Apper"</string>
diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml
index 3e2afed..ca6fe88 100644
--- a/packages/SettingsLib/res/values-ne/strings.xml
+++ b/packages/SettingsLib/res/values-ne/strings.xml
@@ -320,7 +320,7 @@
<string name="show_notification_channel_warnings_summary" msgid="5536803251863694895">"अनुप्रयोगले कुनै मान्य च्यानल बिना सूचना पोस्ट गर्दा स्क्रिनमा चेतावनी देखाउँछ"</string>
<string name="force_allow_on_external" msgid="3215759785081916381">"बाह्यमा बल प्रयोगको अनुमति प्राप्त अनुप्रयोगहरू"</string>
<string name="force_allow_on_external_summary" msgid="3640752408258034689">"म्यानिफेेस्टका मानहरूको ख्याल नगरी कुनै पनि अनुप्रयोगलाई बाह्य भण्डारणमा लेख्न सकिने खाले बनाउँछ"</string>
- <string name="force_resizable_activities" msgid="8615764378147824985">"गतिविधिहरू रिसाइज गर्नको लागि बाध्य गर्नुहोस्"</string>
+ <string name="force_resizable_activities" msgid="8615764378147824985">"आकार बदल्न योग्य हुने बनाउन गतिविधिहरूलाई बाध्यात्मक बनाउनुहोस्।"</string>
<string name="force_resizable_activities_summary" msgid="6667493494706124459">"म्यानिफेेस्ट मानहरूको ख्याल नगरी, बहु-विन्डोको लागि सबै रिसाइज गर्न सकिने गतिविधिहरू बनाउनुहोस्।"</string>
<string name="enable_freeform_support" msgid="1461893351278940416">"फ्रिफर्म विन्डोहरू सक्रिय गर्नुहोस्"</string>
<string name="enable_freeform_support_summary" msgid="8247310463288834487">"प्रयोगात्मक फ्रिफर्म विन्डोहरूका लागि समर्थन सक्रिय गर्नुहोस्।"</string>
diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml
index c84875e..987d075 100644
--- a/packages/SettingsLib/res/values-or/strings.xml
+++ b/packages/SettingsLib/res/values-or/strings.xml
@@ -224,8 +224,7 @@
<string name="bluetooth_select_a2dp_codec_channel_mode" msgid="884855779449390540">"ବ୍ଲୁ-ଟୂଥ୍ ଅଡିଓ ଚ୍ୟାନେଲ୍ ମୋଡ୍"</string>
<string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="7234956835280563341">"ବ୍ଲୁ-ଟୂଥ୍ ଅଡିଓ କୋଡେକ୍\nସିଲେକ୍ସନ୍କୁ ଗତିଶୀଳ କରନ୍ତୁ: ଚ୍ୟାନେଲ୍ ମୋଡ୍"</string>
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3619694372407843405">"ବ୍ଲୁ-ଟୂଥ୍ ଅଡିଓ LDAC କୋଡେକ୍: ପ୍ଲେବ୍ୟାକ୍ ଗୁଣବତ୍ତା"</string>
- <!-- no translation found for bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title (6893955536658137179) -->
- <skip />
+ <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="6893955536658137179">"ବ୍ଲୁ-ଟୁଥ୍ ଅଡିଓ LDAC\nକୋଡେକ୍ ଚୟନକୁ ଗତିଶୀଳ କରନ୍ତୁ: ପ୍ଲେବ୍ୟାକ୍ କ୍ୱାଲିଟୀ"</string>
<string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"ଷ୍ଟ୍ରିମ୍ କରୁଛି: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
<string name="select_private_dns_configuration_title" msgid="3700456559305263922">"ବ୍ୟକ୍ତିଗତ DNS"</string>
<string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"ବ୍ୟକ୍ତିଗତ DNS ମୋଡ୍ ବାଛନ୍ତୁ"</string>
diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml
index 7ec97b5..e472b15 100644
--- a/packages/SettingsLib/res/values-pl/strings.xml
+++ b/packages/SettingsLib/res/values-pl/strings.xml
@@ -194,7 +194,7 @@
<string name="clear_adb_keys" msgid="4038889221503122743">"Odwołaj dostęp do debugowania USB"</string>
<string name="bugreport_in_power" msgid="7923901846375587241">"Skrót do zgłoszenia błędu"</string>
<string name="bugreport_in_power_summary" msgid="1778455732762984579">"Pokaż w menu zasilania przycisk zgłaszania błędu"</string>
- <string name="keep_screen_on" msgid="1146389631208760344">"Pozostaw ekran włączony"</string>
+ <string name="keep_screen_on" msgid="1146389631208760344">"Pozostaw włączony ekran"</string>
<string name="keep_screen_on_summary" msgid="2173114350754293009">"Ekran nie będzie gaszony podczas ładowania telefonu"</string>
<string name="bt_hci_snoop_log" msgid="3340699311158865670">"Włącz dziennik snoop Bluetooth HCI"</string>
<string name="bt_hci_snoop_log_summary" msgid="366083475849911315">"Przechwyć wszystkie pakiety Bluetooth HCI do pliku (przełącz Bluetooth po zmianie tego ustawienia)"</string>
diff --git a/packages/SettingsLib/res/values-pt-rBR/arrays.xml b/packages/SettingsLib/res/values-pt-rBR/arrays.xml
index dfdc09c..563698a 100644
--- a/packages/SettingsLib/res/values-pt-rBR/arrays.xml
+++ b/packages/SettingsLib/res/values-pt-rBR/arrays.xml
@@ -160,11 +160,11 @@
</string-array>
<string-array name="select_logd_size_summaries">
<item msgid="6921048829791179331">"Desativado"</item>
- <item msgid="2969458029344750262">"64 K/buffer de log"</item>
- <item msgid="1342285115665698168">"256 K/buffer de log"</item>
- <item msgid="1314234299552254621">"1 M/buffer de log"</item>
- <item msgid="3606047780792894151">"4 M/buffer de log"</item>
- <item msgid="5431354956856655120">"16 M/buffer de log"</item>
+ <item msgid="2969458029344750262">"64 K/buffer de registro"</item>
+ <item msgid="1342285115665698168">"256 K/buffer de registro"</item>
+ <item msgid="1314234299552254621">"1 M/buffer de registro"</item>
+ <item msgid="3606047780792894151">"4 M/buffer de registro"</item>
+ <item msgid="5431354956856655120">"16 M/buffer de registro"</item>
</string-array>
<string-array name="select_logpersist_titles">
<item msgid="1744840221860799971">"Desativado"</item>
diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml
index d1726d3..98b877be 100644
--- a/packages/SettingsLib/res/values-pt-rBR/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml
@@ -195,8 +195,8 @@
<string name="bugreport_in_power" msgid="7923901846375587241">"Atalho para relatório do bug"</string>
<string name="bugreport_in_power_summary" msgid="1778455732762984579">"Mostrar um botão para gerar relatórios de bugs no menu do botão liga/desliga"</string>
<string name="keep_screen_on" msgid="1146389631208760344">"Permanecer ativo"</string>
- <string name="keep_screen_on_summary" msgid="2173114350754293009">"A tela nunca entrará em inatividade enquanto estiver carregando."</string>
- <string name="bt_hci_snoop_log" msgid="3340699311158865670">"Ativar log de rastreamento Bluetooth HCI"</string>
+ <string name="keep_screen_on_summary" msgid="2173114350754293009">"A tela nunca entrará em suspensão enquanto estiver carregando."</string>
+ <string name="bt_hci_snoop_log" msgid="3340699311158865670">"Ativar registro de rastreamento Bluetooth HCI"</string>
<string name="bt_hci_snoop_log_summary" msgid="366083475849911315">"Capturar todos os pacotes Bluetooth HCI em um arquivo (ative o Bluetooth depois de alterar esta configuração)"</string>
<string name="oem_unlock_enable" msgid="6040763321967327691">"Desbloqueio de OEM"</string>
<string name="oem_unlock_enable_summary" msgid="4720281828891618376">"Permitir que o bootloader seja desbloqueado"</string>
@@ -239,7 +239,7 @@
<string name="wifi_metered_label" msgid="4514924227256839725">"Limitada"</string>
<string name="wifi_unmetered_label" msgid="6124098729457992931">"Ilimitada"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Tamanhos de buffer de logger"</string>
- <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Sel. tam. de logger/buffer de log"</string>
+ <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Sel. tam. de logger/buffer de registro"</string>
<string name="dev_logpersist_clear_warning_title" msgid="684806692440237967">"Limpar armazenamento de logger constante?"</string>
<string name="dev_logpersist_clear_warning_message" msgid="2256582531342994562">"Quando não estivermos mais monitorando com o logger constante, devemos limpar o residente de dados de logger do seu dispositivo."</string>
<string name="select_logpersist_title" msgid="7530031344550073166">"Armazenar dados de logger constantemente no dispositivo"</string>
diff --git a/packages/SettingsLib/res/values-pt/arrays.xml b/packages/SettingsLib/res/values-pt/arrays.xml
index dfdc09c..563698a 100644
--- a/packages/SettingsLib/res/values-pt/arrays.xml
+++ b/packages/SettingsLib/res/values-pt/arrays.xml
@@ -160,11 +160,11 @@
</string-array>
<string-array name="select_logd_size_summaries">
<item msgid="6921048829791179331">"Desativado"</item>
- <item msgid="2969458029344750262">"64 K/buffer de log"</item>
- <item msgid="1342285115665698168">"256 K/buffer de log"</item>
- <item msgid="1314234299552254621">"1 M/buffer de log"</item>
- <item msgid="3606047780792894151">"4 M/buffer de log"</item>
- <item msgid="5431354956856655120">"16 M/buffer de log"</item>
+ <item msgid="2969458029344750262">"64 K/buffer de registro"</item>
+ <item msgid="1342285115665698168">"256 K/buffer de registro"</item>
+ <item msgid="1314234299552254621">"1 M/buffer de registro"</item>
+ <item msgid="3606047780792894151">"4 M/buffer de registro"</item>
+ <item msgid="5431354956856655120">"16 M/buffer de registro"</item>
</string-array>
<string-array name="select_logpersist_titles">
<item msgid="1744840221860799971">"Desativado"</item>
diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml
index d1726d3..98b877be 100644
--- a/packages/SettingsLib/res/values-pt/strings.xml
+++ b/packages/SettingsLib/res/values-pt/strings.xml
@@ -195,8 +195,8 @@
<string name="bugreport_in_power" msgid="7923901846375587241">"Atalho para relatório do bug"</string>
<string name="bugreport_in_power_summary" msgid="1778455732762984579">"Mostrar um botão para gerar relatórios de bugs no menu do botão liga/desliga"</string>
<string name="keep_screen_on" msgid="1146389631208760344">"Permanecer ativo"</string>
- <string name="keep_screen_on_summary" msgid="2173114350754293009">"A tela nunca entrará em inatividade enquanto estiver carregando."</string>
- <string name="bt_hci_snoop_log" msgid="3340699311158865670">"Ativar log de rastreamento Bluetooth HCI"</string>
+ <string name="keep_screen_on_summary" msgid="2173114350754293009">"A tela nunca entrará em suspensão enquanto estiver carregando."</string>
+ <string name="bt_hci_snoop_log" msgid="3340699311158865670">"Ativar registro de rastreamento Bluetooth HCI"</string>
<string name="bt_hci_snoop_log_summary" msgid="366083475849911315">"Capturar todos os pacotes Bluetooth HCI em um arquivo (ative o Bluetooth depois de alterar esta configuração)"</string>
<string name="oem_unlock_enable" msgid="6040763321967327691">"Desbloqueio de OEM"</string>
<string name="oem_unlock_enable_summary" msgid="4720281828891618376">"Permitir que o bootloader seja desbloqueado"</string>
@@ -239,7 +239,7 @@
<string name="wifi_metered_label" msgid="4514924227256839725">"Limitada"</string>
<string name="wifi_unmetered_label" msgid="6124098729457992931">"Ilimitada"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Tamanhos de buffer de logger"</string>
- <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Sel. tam. de logger/buffer de log"</string>
+ <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Sel. tam. de logger/buffer de registro"</string>
<string name="dev_logpersist_clear_warning_title" msgid="684806692440237967">"Limpar armazenamento de logger constante?"</string>
<string name="dev_logpersist_clear_warning_message" msgid="2256582531342994562">"Quando não estivermos mais monitorando com o logger constante, devemos limpar o residente de dados de logger do seu dispositivo."</string>
<string name="select_logpersist_title" msgid="7530031344550073166">"Armazenar dados de logger constantemente no dispositivo"</string>
diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml
index 77de8db..a421c72 100644
--- a/packages/SettingsLib/res/values-ru/strings.xml
+++ b/packages/SettingsLib/res/values-ru/strings.xml
@@ -56,7 +56,7 @@
<string name="bluetooth_disconnecting" msgid="8913264760027764974">"Отключение..."</string>
<string name="bluetooth_connecting" msgid="8555009514614320497">"Подключение..."</string>
<string name="bluetooth_connected" msgid="5427152882755735944">"Подключено<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
- <string name="bluetooth_pairing" msgid="1426882272690346242">"Сопряжение..."</string>
+ <string name="bluetooth_pairing" msgid="1426882272690346242">"Устанавливается соединение..."</string>
<string name="bluetooth_connected_no_headset" msgid="616068069034994802">"Подключено (кроме звонков)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_a2dp" msgid="3736431800395923868">"Подключено (кроме аудио)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_map" msgid="3200033913678466453">"Подключено (кроме сообщений)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
@@ -102,11 +102,11 @@
<string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Добавить"</string>
<string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"ДОБАВИТЬ"</string>
<string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Отмена"</string>
- <string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"Сопряжение обеспечивает доступ к вашим контактам и журналу звонков при подключении."</string>
- <string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"Не удалось установить сопряжение с устройством \"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>\"."</string>
- <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"Не удалось установить сопряжение с устройством \"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>\", так как введен неверный PIN-код или пароль."</string>
+ <string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"Установление соединения обеспечивает доступ к вашим контактам и журналу звонков при подключении."</string>
+ <string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"Не удалось подключиться к устройству \"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>\"."</string>
+ <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"Не удалось подключиться к устройству \"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>\", так как введен неверный PIN-код или пароль."</string>
<string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"Не удается установить соединение с устройством \"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>\"."</string>
- <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> не разрешает сопряжение."</string>
+ <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> не разрешает подключение."</string>
<string name="bluetooth_talkback_computer" msgid="4875089335641234463">"Компьютер"</string>
<string name="bluetooth_talkback_headset" msgid="5140152177885220949">"Гарнитура"</string>
<string name="bluetooth_talkback_phone" msgid="4260255181240622896">"Телефон"</string>
diff --git a/packages/SettingsLib/res/values-sw/arrays.xml b/packages/SettingsLib/res/values-sw/arrays.xml
index f13c2ea..fdf9fd1 100644
--- a/packages/SettingsLib/res/values-sw/arrays.xml
+++ b/packages/SettingsLib/res/values-sw/arrays.xml
@@ -59,7 +59,7 @@
<item msgid="45075631231212732">"Kila wakati tumia ukakuaji wa HDCP"</item>
</string-array>
<string-array name="bluetooth_avrcp_versions">
- <item msgid="5347678900838034763">"AVRCP 1.4 (Chaguo msingi)"</item>
+ <item msgid="5347678900838034763">"AVRCP 1.4 (Chaguomsingi)"</item>
<item msgid="2809759619990248160">"AVRCP 1.3"</item>
<item msgid="6199178154704729352">"AVRCP 1.5"</item>
<item msgid="5172170854953034852">"AVRCP 1.6"</item>
@@ -71,7 +71,7 @@
<item msgid="3422726142222090896">"avrcp16"</item>
</string-array>
<string-array name="bluetooth_a2dp_codec_titles">
- <item msgid="7065842274271279580">"Tumia Uteuzi wa Mfumo (Chaguo msingi)"</item>
+ <item msgid="7065842274271279580">"Tumia Uteuzi wa Mfumo (Chaguomsingi)"</item>
<item msgid="7539690996561263909">"SBC"</item>
<item msgid="686685526567131661">"AAC"</item>
<item msgid="5254942598247222737">"Sauti ya <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
@@ -81,7 +81,7 @@
<item msgid="3304843301758635896">"Zima Kodeki Zisizo za Lazima"</item>
</string-array>
<string-array name="bluetooth_a2dp_codec_summaries">
- <item msgid="5062108632402595000">"Tumia Uteuzi wa Mfumo (Chaguo msingi)"</item>
+ <item msgid="5062108632402595000">"Tumia Uteuzi wa Mfumo (Chaguomsingi)"</item>
<item msgid="6898329690939802290">"SBC"</item>
<item msgid="6839647709301342559">"AAC"</item>
<item msgid="7848030269621918608">"Sauti ya <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
@@ -91,38 +91,38 @@
<item msgid="741805482892725657">"Zima Kodeki Zisizo za Lazima"</item>
</string-array>
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
- <item msgid="3093023430402746802">"Tumia Uteuzi wa Mfumo (Chaguo msingi)"</item>
+ <item msgid="3093023430402746802">"Tumia Uteuzi wa Mfumo (Chaguomsingi)"</item>
<item msgid="8895532488906185219">"kHz 44.1"</item>
<item msgid="2909915718994807056">"kHz 48.0"</item>
<item msgid="3347287377354164611">"kHz 88.2"</item>
<item msgid="1234212100239985373">"kHz 96.0"</item>
</string-array>
<string-array name="bluetooth_a2dp_codec_sample_rate_summaries">
- <item msgid="3214516120190965356">"Tumia Uteuzi wa Mfumo (Chaguo msingi)"</item>
+ <item msgid="3214516120190965356">"Tumia Uteuzi wa Mfumo (Chaguomsingi)"</item>
<item msgid="4482862757811638365">"kHz 44.1"</item>
<item msgid="354495328188724404">"kHz 48.0"</item>
<item msgid="7329816882213695083">"kHz 88.2"</item>
<item msgid="6967397666254430476">"kHz 96.0"</item>
</string-array>
<string-array name="bluetooth_a2dp_codec_bits_per_sample_titles">
- <item msgid="2684127272582591429">"Tumia Uteuzi wa Mfumo (Chaguo msingi)"</item>
+ <item msgid="2684127272582591429">"Tumia Uteuzi wa Mfumo (Chaguomsingi)"</item>
<item msgid="5618929009984956469">"Biti 16 kwa kila sampuli"</item>
<item msgid="3412640499234627248">"Biti 24 kwa kila sampuli"</item>
<item msgid="121583001492929387">"Biti 32 kwa kila sampuli"</item>
</string-array>
<string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries">
- <item msgid="1081159789834584363">"Tumia Uteuzi wa Mfumo (Chaguo msingi)"</item>
+ <item msgid="1081159789834584363">"Tumia Uteuzi wa Mfumo (Chaguomsingi)"</item>
<item msgid="4726688794884191540">"Biti 16 kwa kila sampuli"</item>
<item msgid="305344756485516870">"Biti 24 kwa kila sampuli"</item>
<item msgid="244568657919675099">"Biti 32 kwa kila sampuli"</item>
</string-array>
<string-array name="bluetooth_a2dp_codec_channel_mode_titles">
- <item msgid="5226878858503393706">"Tumia Uteuzi wa Mfumo (Chaguo msingi)"</item>
+ <item msgid="5226878858503393706">"Tumia Uteuzi wa Mfumo (Chaguomsingi)"</item>
<item msgid="4106832974775067314">"Mono"</item>
<item msgid="5571632958424639155">"Stereo"</item>
</string-array>
<string-array name="bluetooth_a2dp_codec_channel_mode_summaries">
- <item msgid="4118561796005528173">"Tumia Uteuzi wa Mfumo (Chaguo msingi)"</item>
+ <item msgid="4118561796005528173">"Tumia Uteuzi wa Mfumo (Chaguomsingi)"</item>
<item msgid="8900559293912978337">"Mono"</item>
<item msgid="8883739882299884241">"Stereo"</item>
</string-array>
diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml
index 53f5d0c..2d752ea 100644
--- a/packages/SettingsLib/res/values-sw/strings.xml
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -138,7 +138,7 @@
<string name="user_guest" msgid="8475274842845401871">"Aliyealikwa"</string>
<string name="unknown" msgid="1592123443519355854">"Haijulikani"</string>
<string name="running_process_item_user_label" msgid="3129887865552025943">"Mtumiaji: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
- <string name="launch_defaults_some" msgid="313159469856372621">"Baadhi ya chaguo msingi zimewekwa"</string>
+ <string name="launch_defaults_some" msgid="313159469856372621">"Baadhi ya chaguomsingi zimewekwa"</string>
<string name="launch_defaults_none" msgid="4241129108140034876">"Hakuna chaguo-misingi zilizowekwa"</string>
<string name="tts_settings" msgid="8186971894801348327">"Mipangilio ya kusoma maandishi kwa sauti"</string>
<string name="tts_settings_title" msgid="1237820681016639683">"Kusoma maandishi kwa sauti"</string>
@@ -157,7 +157,7 @@
<string name="tts_engine_security_warning" msgid="8786238102020223650">"Hotuba hii inawezesha injini huenda ikaweza kukusanya maandishi ambayo yatazungumziwa, ikijumlisha data ya kibinafsi ya nenosiri na namba ya kaddi ya mkopo. Inatoka kwa injini ya <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g> Wezesha matumizi ya hotuba hii iliyowezeshwa ya injini?"</string>
<string name="tts_engine_network_required" msgid="1190837151485314743">"Lugha hii inahitaji muunganisho wa mtandao unaofanya kazi ili kipengele cha kusoma maandishi kwa sauti kifanye kazi."</string>
<string name="tts_default_sample_string" msgid="4040835213373086322">"Huu ni mfano wa usanisi usemaji"</string>
- <string name="tts_status_title" msgid="7268566550242584413">"Hali ya lugha chaguo msingi"</string>
+ <string name="tts_status_title" msgid="7268566550242584413">"Hali ya lugha chaguomsingi"</string>
<string name="tts_status_ok" msgid="1309762510278029765">"<xliff:g id="LOCALE">%1$s</xliff:g> inaweza kutumiwa kikamilifu"</string>
<string name="tts_status_requires_network" msgid="6042500821503226892">"<xliff:g id="LOCALE">%1$s</xliff:g> inahitaji muunganisho wa mtandao"</string>
<string name="tts_status_not_supported" msgid="4491154212762472495">"<xliff:g id="LOCALE">%1$s</xliff:g> haiwezi kutumiwa"</string>
@@ -167,7 +167,7 @@
<string name="tts_engine_preference_section_title" msgid="448294500990971413">"Injini inayofaa"</string>
<string name="tts_general_section_title" msgid="4402572014604490502">"Kwa ujumla"</string>
<string name="tts_reset_speech_pitch_title" msgid="5789394019544785915">"Weka upya mipangilio ya ubora wa matamshi"</string>
- <string name="tts_reset_speech_pitch_summary" msgid="8700539616245004418">"Rejesha mipangilio ya ubora wa matamshi kuwa ya chaguo msingi."</string>
+ <string name="tts_reset_speech_pitch_summary" msgid="8700539616245004418">"Rejesha mipangilio ya ubora wa matamshi kuwa ya chaguomsingi."</string>
<string-array name="tts_rate_entries">
<item msgid="6695494874362656215">"Polepole sana"</item>
<item msgid="4795095314303559268">"Polepole"</item>
@@ -331,7 +331,7 @@
<string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"Nenosiri jipya na uthibitisho havioani"</string>
<string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"Imeshindwa kuweka nenosiri la hifadhi rudufu"</string>
<string-array name="color_mode_names">
- <item msgid="2425514299220523812">"Maridadi (chaguo msingi)"</item>
+ <item msgid="2425514299220523812">"Maridadi (chaguomsingi)"</item>
<item msgid="8446070607501413455">"Asili"</item>
<item msgid="6553408765810699025">"Muundo-msingi"</item>
</string-array>
@@ -410,7 +410,7 @@
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> zilizopita"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"Zimesalia <xliff:g id="ID_1">%1$s</xliff:g>"</string>
<string name="screen_zoom_summary_small" msgid="5867245310241621570">"Ndogo"</string>
- <string name="screen_zoom_summary_default" msgid="2247006805614056507">"Chaguo msingi"</string>
+ <string name="screen_zoom_summary_default" msgid="2247006805614056507">"Chaguomsingi"</string>
<string name="screen_zoom_summary_large" msgid="4835294730065424084">"Kubwa"</string>
<string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Kubwa kiasi"</string>
<string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Kubwa zaidi"</string>
diff --git a/packages/SettingsLib/res/values-ta/arrays.xml b/packages/SettingsLib/res/values-ta/arrays.xml
index 7b69c78..54adf82 100644
--- a/packages/SettingsLib/res/values-ta/arrays.xml
+++ b/packages/SettingsLib/res/values-ta/arrays.xml
@@ -22,7 +22,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string-array name="wifi_status">
<item msgid="1922181315419294640"></item>
- <item msgid="8934131797783724664">"ஸ்கேன் செய்கிறது…"</item>
+ <item msgid="8934131797783724664">"தேடுகிறது..."</item>
<item msgid="8513729475867537913">"இணைக்கிறது..."</item>
<item msgid="515055375277271756">"அங்கீகரிக்கிறது..."</item>
<item msgid="1943354004029184381">"IP முகவரியைப் பெறுகிறது…"</item>
@@ -36,7 +36,7 @@
</string-array>
<string-array name="wifi_status_with_ssid">
<item msgid="7714855332363650812"></item>
- <item msgid="8878186979715711006">"ஸ்கேன் செய்கிறது…"</item>
+ <item msgid="8878186979715711006">"தேடுகிறது..."</item>
<item msgid="355508996603873860">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> இல் இணைக்கிறது…"</item>
<item msgid="554971459996405634">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> உடன் அங்கீகரிக்கிறது…"</item>
<item msgid="7928343808033020343">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> இலிருந்து IP முகவரியைப் பெறுகிறது…"</item>
@@ -180,30 +180,30 @@
</string-array>
<string-array name="window_animation_scale_entries">
<item msgid="8134156599370824081">"அனிமேஷனை முடக்கு"</item>
- <item msgid="6624864048416710414">"அனிமேஷன் அளவு .5x"</item>
- <item msgid="2219332261255416635">"அனிமேஷன் அளவு 1x"</item>
- <item msgid="3544428804137048509">"அனிமேஷன் அளவு 1.5x"</item>
- <item msgid="3110710404225974514">"அனிமேஷன் அளவு 2x"</item>
- <item msgid="4402738611528318731">"அனிமேஷன் அளவு 5x"</item>
- <item msgid="6189539267968330656">"அனிமேஷன் அளவு 10x"</item>
+ <item msgid="6624864048416710414">"அனிமேஷன் வேகம் .5x"</item>
+ <item msgid="2219332261255416635">"அனிமேஷன் வேகம் 1x"</item>
+ <item msgid="3544428804137048509">"அனிமேஷன் வேகம் 1.5x"</item>
+ <item msgid="3110710404225974514">"அனிமேஷன் வேகம் 2x"</item>
+ <item msgid="4402738611528318731">"அனிமேஷன் வேகம் 5x"</item>
+ <item msgid="6189539267968330656">"அனிமேஷன் வேகம் 10x"</item>
</string-array>
<string-array name="transition_animation_scale_entries">
<item msgid="8464255836173039442">"அனிமேஷனை முடக்கு"</item>
- <item msgid="3375781541913316411">"அனிமேஷன் அளவு .5x"</item>
- <item msgid="1991041427801869945">"அனிமேஷன் அளவு 1x"</item>
- <item msgid="4012689927622382874">"அனிமேஷன் அளவு 1.5x"</item>
- <item msgid="3289156759925947169">"அனிமேஷன் அளவு 2x"</item>
- <item msgid="7705857441213621835">"அனிமேஷன் அளவு 5x"</item>
- <item msgid="6660750935954853365">"அனிமேஷன் அளவு 10x"</item>
+ <item msgid="3375781541913316411">"அனிமேஷன் வேகம் .5x"</item>
+ <item msgid="1991041427801869945">"அனிமேஷன் வேகம் 1x"</item>
+ <item msgid="4012689927622382874">"அனிமேஷன் வேகம் 1.5x"</item>
+ <item msgid="3289156759925947169">"அனிமேஷன் வேகம் 2x"</item>
+ <item msgid="7705857441213621835">"அனிமேஷன் வேகம் 5x"</item>
+ <item msgid="6660750935954853365">"அனிமேஷன் வேகம் 10x"</item>
</string-array>
<string-array name="animator_duration_scale_entries">
<item msgid="6039901060648228241">"அனிமேஷனை முடக்கு"</item>
- <item msgid="1138649021950863198">"அனிமேஷன் அளவு .5x"</item>
- <item msgid="4394388961370833040">"அனிமேஷன் அளவு 1x"</item>
- <item msgid="8125427921655194973">"அனிமேஷன் அளவு 1.5x"</item>
- <item msgid="3334024790739189573">"அனிமேஷன் அளவு 2x"</item>
- <item msgid="3170120558236848008">"அனிமேஷன் அளவு 5x"</item>
- <item msgid="1069584980746680398">"அனிமேஷன் அளவு 10x"</item>
+ <item msgid="1138649021950863198">"அனிமேஷன் வேகம் .5x"</item>
+ <item msgid="4394388961370833040">"அனிமேஷன் வேகம் 1x"</item>
+ <item msgid="8125427921655194973">"அனிமேஷன் வேகம் 1.5x"</item>
+ <item msgid="3334024790739189573">"அனிமேஷன் வேகம் 2x"</item>
+ <item msgid="3170120558236848008">"அனிமேஷன் வேகம் 5x"</item>
+ <item msgid="1069584980746680398">"அனிமேஷன் வேகம் 10x"</item>
</string-array>
<string-array name="overlay_display_devices_entries">
<item msgid="1606809880904982133">"ஏதுமில்லை"</item>
diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml
index 828e5427..5f63fee 100644
--- a/packages/SettingsLib/res/values-ta/strings.xml
+++ b/packages/SettingsLib/res/values-ta/strings.xml
@@ -306,8 +306,8 @@
<string name="track_frame_time" msgid="6094365083096851167">"சுயவிவர HWUI ரெண்டரிங்"</string>
<string name="enable_gpu_debug_layers" msgid="3848838293793255097">"GPU பிழைத்திருத்த லேயர்களை இயக்கு"</string>
<string name="enable_gpu_debug_layers_summary" msgid="8009136940671194940">"பிழைத்திருத்த ஆப்ஸிற்கு, GPU பிழைத்திருத்த லேயர்களை ஏற்றுவதற்கு அனுமதி"</string>
- <string name="window_animation_scale_title" msgid="6162587588166114700">"சாளர அனிமேஷன் அளவு"</string>
- <string name="transition_animation_scale_title" msgid="387527540523595875">"அனிமேஷன் மாற்றத்தின் அளவு"</string>
+ <string name="window_animation_scale_title" msgid="6162587588166114700">"சாளர அனிமேஷன் வேகம்"</string>
+ <string name="transition_animation_scale_title" msgid="387527540523595875">"அனிமேஷன் மாற்றத்தின் வேகம்"</string>
<string name="animator_duration_scale_title" msgid="3406722410819934083">"அனிமேட்டர் கால அளவு"</string>
<string name="overlay_display_devices_title" msgid="5364176287998398539">"இரண்டாம்நிலைக் காட்சிகளை உருவகப்படுத்து"</string>
<string name="debug_applications_category" msgid="4206913653849771549">"ஆப்ஸ்"</string>
@@ -358,7 +358,7 @@
<string name="picture_color_mode" msgid="4560755008730283695">"படத்தின் வண்ணப் பயன்முறை"</string>
<string name="picture_color_mode_desc" msgid="1141891467675548590">"sRGBஐப் பயன்படுத்தும்"</string>
<string name="daltonizer_mode_disabled" msgid="7482661936053801862">"முடக்கப்பட்டது"</string>
- <string name="daltonizer_mode_monochromacy" msgid="8485709880666106721">"மோனோகுரோமசி"</string>
+ <string name="daltonizer_mode_monochromacy" msgid="8485709880666106721">"ஒற்றை நிறத் தன்மை"</string>
<string name="daltonizer_mode_deuteranomaly" msgid="5475532989673586329">"நிறம் அடையாளங்காண முடியாமை (சிவப்பு-பச்சை)"</string>
<string name="daltonizer_mode_protanomaly" msgid="8424148009038666065">"நிறம் அடையாளங்காண முடியாமை (சிவப்பு-பச்சை)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="481725854987912389">"நிறம் அடையாளங்காண முடியாமை (நீலம்-மஞ்சள்)"</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/CustomDialogPreferenceCompat.java b/packages/SettingsLib/src/com/android/settingslib/CustomDialogPreferenceCompat.java
new file mode 100644
index 0000000..6ac9d4e
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/CustomDialogPreferenceCompat.java
@@ -0,0 +1,129 @@
+/*
+ * 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.settingslib;
+
+import android.app.Dialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.util.AttributeSet;
+import android.view.View;
+
+import androidx.appcompat.app.AlertDialog;
+import androidx.preference.DialogPreference;
+import androidx.preference.PreferenceDialogFragmentCompat;
+
+public class CustomDialogPreferenceCompat extends DialogPreference {
+
+ private CustomPreferenceDialogFragment mFragment;
+ private DialogInterface.OnShowListener mOnShowListener;
+
+ public CustomDialogPreferenceCompat(Context context, AttributeSet attrs, int defStyleAttr,
+ int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ public CustomDialogPreferenceCompat(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ public CustomDialogPreferenceCompat(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public CustomDialogPreferenceCompat(Context context) {
+ super(context);
+ }
+
+ public boolean isDialogOpen() {
+ return getDialog() != null && getDialog().isShowing();
+ }
+
+ public Dialog getDialog() {
+ return mFragment != null ? mFragment.getDialog() : null;
+ }
+
+ public void setOnShowListener(DialogInterface.OnShowListener listner) {
+ mOnShowListener = listner;
+ }
+
+ protected void onPrepareDialogBuilder(AlertDialog.Builder builder,
+ DialogInterface.OnClickListener listener) {
+ }
+
+ protected void onDialogClosed(boolean positiveResult) {
+ }
+
+ protected void onClick(DialogInterface dialog, int which) {
+ }
+
+ protected void onBindDialogView(View view) {
+ }
+
+ private void setFragment(CustomPreferenceDialogFragment fragment) {
+ mFragment = fragment;
+ }
+
+ private DialogInterface.OnShowListener getOnShowListener() {
+ return mOnShowListener;
+ }
+
+ public static class CustomPreferenceDialogFragment extends PreferenceDialogFragmentCompat {
+
+ public static CustomPreferenceDialogFragment newInstance(String key) {
+ final CustomPreferenceDialogFragment fragment = new CustomPreferenceDialogFragment();
+ final Bundle b = new Bundle(1);
+ b.putString(ARG_KEY, key);
+ fragment.setArguments(b);
+ return fragment;
+ }
+
+ private CustomDialogPreferenceCompat getCustomizablePreference() {
+ return (CustomDialogPreferenceCompat) getPreference();
+ }
+
+ @Override
+ protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
+ super.onPrepareDialogBuilder(builder);
+ getCustomizablePreference().setFragment(this);
+ getCustomizablePreference().onPrepareDialogBuilder(builder, this);
+ }
+
+ @Override
+ public void onDialogClosed(boolean positiveResult) {
+ getCustomizablePreference().onDialogClosed(positiveResult);
+ }
+
+ @Override
+ protected void onBindDialogView(View view) {
+ super.onBindDialogView(view);
+ getCustomizablePreference().onBindDialogView(view);
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ final Dialog dialog = super.onCreateDialog(savedInstanceState);
+ dialog.setOnShowListener(getCustomizablePreference().getOnShowListener());
+ return dialog;
+ }
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ super.onClick(dialog, which);
+ getCustomizablePreference().onClick(dialog, which);
+ }
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/CustomEditTextPreferenceCompat.java b/packages/SettingsLib/src/com/android/settingslib/CustomEditTextPreferenceCompat.java
new file mode 100644
index 0000000..6ddc89a
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/CustomEditTextPreferenceCompat.java
@@ -0,0 +1,136 @@
+/*
+ * 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.settingslib;
+
+import static android.text.InputType.TYPE_CLASS_TEXT;
+import static android.text.InputType.TYPE_TEXT_FLAG_CAP_SENTENCES;
+
+import android.app.Dialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.EditText;
+
+import androidx.annotation.CallSuper;
+import androidx.appcompat.app.AlertDialog;
+import androidx.preference.EditTextPreference;
+import androidx.preference.EditTextPreferenceDialogFragmentCompat;
+
+public class CustomEditTextPreferenceCompat extends EditTextPreference {
+
+ private CustomPreferenceDialogFragment mFragment;
+
+ public CustomEditTextPreferenceCompat(Context context, AttributeSet attrs, int defStyleAttr,
+ int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ public CustomEditTextPreferenceCompat(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ public CustomEditTextPreferenceCompat(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public CustomEditTextPreferenceCompat(Context context) {
+ super(context);
+ }
+
+ public EditText getEditText() {
+ if (mFragment != null) {
+ final Dialog dialog = mFragment.getDialog();
+ if (dialog != null) {
+ return (EditText) dialog.findViewById(android.R.id.edit);
+ }
+ }
+ return null;
+ }
+
+ public boolean isDialogOpen() {
+ return getDialog() != null && getDialog().isShowing();
+ }
+
+ public Dialog getDialog() {
+ return mFragment != null ? mFragment.getDialog() : null;
+ }
+
+ protected void onPrepareDialogBuilder(AlertDialog.Builder builder,
+ DialogInterface.OnClickListener listener) {
+ }
+
+ protected void onDialogClosed(boolean positiveResult) {
+ }
+
+ protected void onClick(DialogInterface dialog, int which) {
+ }
+
+ @CallSuper
+ protected void onBindDialogView(View view) {
+ final EditText editText = view.findViewById(android.R.id.edit);
+ if (editText != null) {
+ editText.setInputType(TYPE_CLASS_TEXT | TYPE_TEXT_FLAG_CAP_SENTENCES);
+ editText.requestFocus();
+ }
+ }
+
+ private void setFragment(CustomPreferenceDialogFragment fragment) {
+ mFragment = fragment;
+ }
+
+ public static class CustomPreferenceDialogFragment extends
+ EditTextPreferenceDialogFragmentCompat {
+
+ public static CustomPreferenceDialogFragment newInstance(String key) {
+ final CustomPreferenceDialogFragment fragment = new CustomPreferenceDialogFragment();
+ final Bundle b = new Bundle(1);
+ b.putString(ARG_KEY, key);
+ fragment.setArguments(b);
+ return fragment;
+ }
+
+ private CustomEditTextPreferenceCompat getCustomizablePreference() {
+ return (CustomEditTextPreferenceCompat) getPreference();
+ }
+
+ @Override
+ protected void onBindDialogView(View view) {
+ super.onBindDialogView(view);
+ getCustomizablePreference().onBindDialogView(view);
+ }
+
+ @Override
+ protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
+ super.onPrepareDialogBuilder(builder);
+ getCustomizablePreference().setFragment(this);
+ getCustomizablePreference().onPrepareDialogBuilder(builder, this);
+ }
+
+ @Override
+ public void onDialogClosed(boolean positiveResult) {
+ super.onDialogClosed(positiveResult);
+ getCustomizablePreference().onDialogClosed(positiveResult);
+ }
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ super.onClick(dialog, which);
+ getCustomizablePreference().onClick(dialog, which);
+ }
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/ObservableActivity.java b/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/ObservableActivity.java
index f9aa062..2bd0b27 100644
--- a/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/ObservableActivity.java
+++ b/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/ObservableActivity.java
@@ -24,20 +24,22 @@
import android.annotation.Nullable;
import android.app.Activity;
-import androidx.lifecycle.LifecycleOwner;
import android.os.Bundle;
import android.os.PersistableBundle;
import android.view.Menu;
import android.view.MenuItem;
+import androidx.fragment.app.FragmentActivity;
+import androidx.lifecycle.LifecycleOwner;
+
/**
* {@link Activity} that has hooks to observe activity lifecycle events.
*/
-public class ObservableActivity extends Activity implements LifecycleOwner {
+public class ObservableActivity extends FragmentActivity implements LifecycleOwner {
private final Lifecycle mLifecycle = new Lifecycle(this);
- public Lifecycle getLifecycle() {
+ public Lifecycle getSettingsLifecycle() {
return mLifecycle;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/ObservableDialogFragment.java b/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/ObservableDialogFragment.java
index 972e062..869f54f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/ObservableDialogFragment.java
+++ b/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/ObservableDialogFragment.java
@@ -22,14 +22,15 @@
import static androidx.lifecycle.Lifecycle.Event.ON_START;
import static androidx.lifecycle.Lifecycle.Event.ON_STOP;
-import android.app.DialogFragment;
-import androidx.lifecycle.LifecycleOwner;
import android.content.Context;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
+import androidx.fragment.app.DialogFragment;
+import androidx.lifecycle.LifecycleOwner;
+
/**
* {@link DialogFragment} that has hooks to observe fragment lifecycle events.
*/
@@ -37,6 +38,10 @@
protected final Lifecycle mLifecycle = new Lifecycle(this);
+ public Lifecycle getSettingsLifecycle() {
+ return mLifecycle;
+ }
+
@Override
public void onAttach(Context context) {
super.onAttach(context);
@@ -100,9 +105,4 @@
}
return lifecycleHandled;
}
-
- @Override
- public Lifecycle getLifecycle() {
- return mLifecycle;
- }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/ObservableFragment.java b/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/ObservableFragment.java
index 55597cc..6ba930d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/ObservableFragment.java
+++ b/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/ObservableFragment.java
@@ -24,19 +24,20 @@
import static androidx.lifecycle.Lifecycle.Event.ON_STOP;
import android.annotation.CallSuper;
-import android.app.Fragment;
-import androidx.lifecycle.LifecycleOwner;
import android.content.Context;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
+import androidx.fragment.app.Fragment;
+import androidx.lifecycle.LifecycleOwner;
+
public class ObservableFragment extends Fragment implements LifecycleOwner {
private final Lifecycle mLifecycle = new Lifecycle(this);
- public Lifecycle getLifecycle() {
+ public Lifecycle getSettingsLifecycle() {
return mLifecycle;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/ObservablePreferenceFragment.java b/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/ObservablePreferenceFragment.java
index 904681c..bd1e5a5 100644
--- a/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/ObservablePreferenceFragment.java
+++ b/packages/SettingsLib/src/com/android/settingslib/core/lifecycle/ObservablePreferenceFragment.java
@@ -24,24 +24,25 @@
import static androidx.lifecycle.Lifecycle.Event.ON_STOP;
import android.annotation.CallSuper;
-import androidx.lifecycle.LifecycleOwner;
import android.content.Context;
import android.os.Bundle;
-import androidx.preference.PreferenceFragment;
-import androidx.preference.PreferenceScreen;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
+import androidx.lifecycle.LifecycleOwner;
+import androidx.preference.PreferenceFragmentCompat;
+import androidx.preference.PreferenceScreen;
+
/**
- * {@link PreferenceFragment} that has hooks to observe fragment lifecycle events.
+ * {@link PreferenceFragmentCompat} that has hooks to observe fragment lifecycle events.
*/
-public abstract class ObservablePreferenceFragment extends PreferenceFragment
+public abstract class ObservablePreferenceFragment extends PreferenceFragmentCompat
implements LifecycleOwner {
private final Lifecycle mLifecycle = new Lifecycle(this);
- public Lifecycle getLifecycle() {
+ public Lifecycle getSettingsLifecycle() {
return mLifecycle;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java b/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java
index 955f64a..75c2533 100644
--- a/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java
+++ b/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java
@@ -381,7 +381,7 @@
// Create a lookup of local zone IDs.
final List<String> zoneIds = lookupTimeZoneIdsByCountry(locale.getCountry());
- localZoneIds = new HashSet<>(zoneIds);
+ localZoneIds = zoneIds != null ? new HashSet<>(zoneIds) : new HashSet<>();
}
@VisibleForTesting
diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractWifiMacAddressPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractWifiMacAddressPreferenceController.java
index 89d2595..ffbda3a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractWifiMacAddressPreferenceController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractWifiMacAddressPreferenceController.java
@@ -38,6 +38,10 @@
@VisibleForTesting
static final String KEY_WIFI_MAC_ADDRESS = "wifi_mac_address";
+ @VisibleForTesting
+ static final int OFF = 0;
+ @VisibleForTesting
+ static final int ON = 1;
private static final String[] CONNECTIVITY_INTENTS = {
ConnectivityManager.CONNECTIVITY_ACTION,
@@ -80,13 +84,14 @@
protected void updateConnectivity() {
WifiInfo wifiInfo = mWifiManager.getConnectionInfo();
final int macRandomizationMode = Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED, 0);
+ Settings.Global.WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED, OFF);
final String macAddress = wifiInfo == null ? null : wifiInfo.getMacAddress();
- if (TextUtils.isEmpty(macAddress)) {
- mWifiMacAddress.setSummary(R.string.status_unavailable);
- } else if (macRandomizationMode == 1 && WifiInfo.DEFAULT_MAC_ADDRESS.equals(macAddress)) {
+ if (macRandomizationMode == ON && WifiInfo.DEFAULT_MAC_ADDRESS.equals(macAddress)) {
mWifiMacAddress.setSummary(R.string.wifi_status_mac_randomized);
+ } else if (TextUtils.isEmpty(macAddress)
+ || WifiInfo.DEFAULT_MAC_ADDRESS.equals(macAddress)) {
+ mWifiMacAddress.setSummary(R.string.status_unavailable);
} else {
mWifiMacAddress.setSummary(macAddress);
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryManager.java b/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryManager.java
index d960536..2bec1d7 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryManager.java
@@ -125,7 +125,7 @@
}
mCategoryByKeyMap.clear();
mCategories = TileUtils.getCategories(context, mTileByComponentCache,
- false /* categoryDefinedInManifest */, mExtraAction, settingPkg);
+ mExtraAction, settingPkg);
for (DashboardCategory category : mCategories) {
mCategoryByKeyMap.put(category.key, category);
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/Tile.java b/packages/SettingsLib/src/com/android/settingslib/drawer/Tile.java
index f1d43bf..b55d2ef 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/Tile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/Tile.java
@@ -16,6 +16,10 @@
package com.android.settingslib.drawer;
+import static com.android.settingslib.drawer.TileUtils.META_DATA_KEY_PROFILE;
+import static com.android.settingslib.drawer.TileUtils.PROFILE_ALL;
+import static com.android.settingslib.drawer.TileUtils.PROFILE_PRIMARY;
+
import android.content.Intent;
import android.graphics.drawable.Icon;
import android.os.Bundle;
@@ -169,4 +173,11 @@
return new Tile[size];
}
};
+
+ public boolean isPrimaryProfileOnly() {
+ String profile = metaData != null ?
+ metaData.getString(META_DATA_KEY_PROFILE) : PROFILE_ALL;
+ profile = (profile != null ? profile : PROFILE_ALL);
+ return TextUtils.equals(profile, PROFILE_PRIMARY);
+ }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java b/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
index 0f0e4e5..76f24bc 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
@@ -34,7 +34,6 @@
import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
-import android.widget.RemoteViews;
import java.util.ArrayList;
import java.util.Collections;
@@ -96,11 +95,7 @@
/**
* The key used to get the category from metadata of activities of action
* {@link #EXTRA_SETTINGS_ACTION}
- * The value must be one of:
- * <li>com.android.settings.category.wireless</li>
- * <li>com.android.settings.category.device</li>
- * <li>com.android.settings.category.personal</li>
- * <li>com.android.settings.category.system</li>
+ * The value must be from {@link CategoryKey}.
*/
private static final String EXTRA_CATEGORY_KEY = "com.android.settings.category";
@@ -171,50 +166,43 @@
public static final String META_DATA_PREFERENCE_SUMMARY_URI =
"com.android.settings.summary_uri";
- /**
- * Name of the meta-data item that should be set in the AndroidManifest.xml to specify the
- * custom view which should be displayed for the preference. The custom view will be inflated
- * as a remote view.
- *
- * This also can be used with {@link #META_DATA_PREFERENCE_SUMMARY_URI}, by setting the id
- * of the summary TextView to '@android:id/summary'.
- */
- public static final String META_DATA_PREFERENCE_CUSTOM_VIEW =
- "com.android.settings.custom_view";
-
public static final String SETTING_PKG = "com.android.settings";
/**
- * Build a list of DashboardCategory. Each category must be defined in manifest.
- * eg: .Settings$DeviceSettings
- * @deprecated
+ * Value for {@link #META_DATA_KEY_PROFILE}. When the device has a managed profile,
+ * the app will always be run in the primary profile.
+ *
+ * @see #META_DATA_KEY_PROFILE
*/
- @Deprecated
- public static List<DashboardCategory> getCategories(Context context,
- Map<Pair<String, String>, Tile> cache) {
- return getCategories(context, cache, true /*categoryDefinedInManifest*/);
- }
+ public static final String PROFILE_PRIMARY = "primary_profile_only";
+
+ /**
+ * Value for {@link #META_DATA_KEY_PROFILE}. When the device has a managed profile, the user
+ * will be presented with a dialog to choose the profile the app will be run in.
+ *
+ * @see #META_DATA_KEY_PROFILE
+ */
+ public static final String PROFILE_ALL = "all_profiles";
+
+ /**
+ * Name of the meta-data item that should be set in the AndroidManifest.xml
+ * to specify the profile in which the app should be run when the device has a managed profile.
+ * The default value is {@link #PROFILE_ALL} which means the user will be presented with a
+ * dialog to choose the profile. If set to {@link #PROFILE_PRIMARY} the app will always be
+ * run in the primary profile.
+ *
+ * @see #PROFILE_PRIMARY
+ * @see #PROFILE_ALL
+ */
+ public static final String META_DATA_KEY_PROFILE = "com.android.settings.profile";
/**
* Build a list of DashboardCategory.
- * @param categoryDefinedInManifest If true, an dummy activity must exists in manifest to
- * represent this category (eg: .Settings$DeviceSettings)
- */
- public static List<DashboardCategory> getCategories(Context context,
- Map<Pair<String, String>, Tile> cache, boolean categoryDefinedInManifest) {
- return getCategories(context, cache, categoryDefinedInManifest, null, SETTING_PKG);
- }
-
- /**
- * Build a list of DashboardCategory.
- * @param categoryDefinedInManifest If true, an dummy activity must exists in manifest to
- * represent this category (eg: .Settings$DeviceSettings)
* @param extraAction additional intent filter action to be usetileutild to build the dashboard
* categories
*/
public static List<DashboardCategory> getCategories(Context context,
- Map<Pair<String, String>, Tile> cache, boolean categoryDefinedInManifest,
- String extraAction, String settingPkg) {
+ Map<Pair<String, String>, Tile> cache, String extraAction, String settingPkg) {
final long startTime = System.currentTimeMillis();
boolean setup = Global.getInt(context.getContentResolver(), Global.DEVICE_PROVISIONED, 0)
!= 0;
@@ -234,14 +222,12 @@
if (setup) {
getTilesForAction(context, user, EXTRA_SETTINGS_ACTION, cache, null, tiles, false,
settingPkg);
- if (!categoryDefinedInManifest) {
getTilesForAction(context, user, IA_SETTINGS_ACTION, cache, null, tiles, false,
settingPkg);
if (extraAction != null) {
getTilesForAction(context, user, extraAction, cache, null, tiles, false,
settingPkg);
}
- }
}
}
@@ -249,7 +235,9 @@
for (Tile tile : tiles) {
DashboardCategory category = categoryMap.get(tile.category);
if (category == null) {
- category = createCategory(context, tile.category, categoryDefinedInManifest);
+ category = new DashboardCategory();
+ category.key = tile.category;
+
if (category == null) {
Log.w(LOG_TAG, "Couldn't find category " + tile.category);
continue;
@@ -268,40 +256,6 @@
return categories;
}
- /**
- * Create a new DashboardCategory from key.
- *
- * @param context Context to query intent
- * @param categoryKey The category key
- * @param categoryDefinedInManifest If true, an dummy activity must exists in manifest to
- * represent this category (eg: .Settings$DeviceSettings)
- */
- private static DashboardCategory createCategory(Context context, String categoryKey,
- boolean categoryDefinedInManifest) {
- DashboardCategory category = new DashboardCategory();
- category.key = categoryKey;
- if (!categoryDefinedInManifest) {
- return category;
- }
- PackageManager pm = context.getPackageManager();
- List<ResolveInfo> results = pm.queryIntentActivities(new Intent(categoryKey), 0);
- if (results.size() == 0) {
- return null;
- }
- for (ResolveInfo resolved : results) {
- if (!resolved.system) {
- // Do not allow any app to add to settings, only system ones.
- continue;
- }
- category.title = resolved.activityInfo.loadLabel(pm);
- category.priority = SETTING_PKG.equals(
- resolved.activityInfo.applicationInfo.packageName) ? resolved.priority : 0;
- if (DEBUG) Log.d(LOG_TAG, "Adding category " + category.title);
- }
-
- return category;
- }
-
private static void getTilesForAction(Context context,
UserHandle user, String action, Map<Pair<String, String>, Tile> addedCache,
String defaultCategory, ArrayList<Tile> outTiles, boolean requireSettings,
@@ -442,11 +396,6 @@
keyHint = metaData.getString(META_DATA_PREFERENCE_KEYHINT);
}
}
- if (metaData.containsKey(META_DATA_PREFERENCE_CUSTOM_VIEW)) {
- int layoutId = metaData.getInt(META_DATA_PREFERENCE_CUSTOM_VIEW);
- tile.remoteViews = new RemoteViews(applicationInfo.packageName, layoutId);
- updateSummaryAndTitle(context, providerMap, tile);
- }
}
} catch (PackageManager.NameNotFoundException | Resources.NotFoundException e) {
if (DEBUG) Log.d(LOG_TAG, "Couldn't find info", e);
diff --git a/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java b/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
index e5d97c9..3c0f6fe 100644
--- a/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
+++ b/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
@@ -256,11 +256,12 @@
}
}
- public void launchSettings(DreamInfo dreamInfo) {
+ public void launchSettings(Context uiContext, DreamInfo dreamInfo) {
logd("launchSettings(%s)", dreamInfo);
- if (dreamInfo == null || dreamInfo.settingsComponentName == null)
+ if (dreamInfo == null || dreamInfo.settingsComponentName == null) {
return;
- mContext.startActivity(new Intent().setComponent(dreamInfo.settingsComponentName));
+ }
+ uiContext.startActivity(new Intent().setComponent(dreamInfo.settingsComponentName));
}
public void preview(DreamInfo dreamInfo) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/PowerWhitelistBackend.java b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/PowerWhitelistBackend.java
index 8a1c4ef..94efc71 100644
--- a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/PowerWhitelistBackend.java
+++ b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/PowerWhitelistBackend.java
@@ -16,6 +16,7 @@
package com.android.settingslib.fuelgauge;
+import android.app.admin.DevicePolicyManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
@@ -90,6 +91,13 @@
if (TextUtils.equals(pkg, defaultDialer)) {
return true;
}
+
+ final DevicePolicyManager devicePolicyManager = mAppContext.getSystemService(
+ DevicePolicyManager.class);
+ if (devicePolicyManager.packageHasActiveAdmins(pkg)) {
+ return true;
+ }
+
return false;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeEnablerManagerCompat.java b/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeEnablerManagerCompat.java
new file mode 100644
index 0000000..ad1368c
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeEnablerManagerCompat.java
@@ -0,0 +1,265 @@
+/*
+ * 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.settingslib.inputmethod;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.res.Configuration;
+import android.text.TextUtils;
+import android.view.inputmethod.InputMethodInfo;
+import android.view.inputmethod.InputMethodManager;
+import android.view.inputmethod.InputMethodSubtype;
+
+import com.android.settingslib.R;
+
+import java.text.Collator;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+import androidx.preference.Preference;
+import androidx.preference.PreferenceCategory;
+import androidx.preference.PreferenceFragmentCompat;
+import androidx.preference.PreferenceScreen;
+import androidx.preference.TwoStatePreference;
+
+public class InputMethodAndSubtypeEnablerManagerCompat implements
+ Preference.OnPreferenceChangeListener {
+
+ private final PreferenceFragmentCompat mFragment;
+
+ private boolean mHaveHardKeyboard;
+ private final HashMap<String, List<Preference>> mInputMethodAndSubtypePrefsMap =
+ new HashMap<>();
+ private final HashMap<String, TwoStatePreference> mAutoSelectionPrefsMap = new HashMap<>();
+ private InputMethodManager mImm;
+ // TODO: Change mInputMethodInfoList to Map
+ private List<InputMethodInfo> mInputMethodInfoList;
+ private final Collator mCollator = Collator.getInstance();
+
+ public InputMethodAndSubtypeEnablerManagerCompat(PreferenceFragmentCompat fragment) {
+ mFragment = fragment;
+ mImm = fragment.getContext().getSystemService(InputMethodManager.class);
+
+ mInputMethodInfoList = mImm.getInputMethodList();
+ }
+
+ public void init(PreferenceFragmentCompat fragment, String targetImi, PreferenceScreen root) {
+ final Configuration config = fragment.getResources().getConfiguration();
+ mHaveHardKeyboard = (config.keyboard == Configuration.KEYBOARD_QWERTY);
+
+ for (final InputMethodInfo imi : mInputMethodInfoList) {
+ // Add subtype preferences of this IME when it is specified or no IME is specified.
+ if (imi.getId().equals(targetImi) || TextUtils.isEmpty(targetImi)) {
+ addInputMethodSubtypePreferences(fragment, imi, root);
+ }
+ }
+ }
+
+ public void refresh(Context context, PreferenceFragmentCompat fragment) {
+ // Refresh internal states in mInputMethodSettingValues to keep the latest
+ // "InputMethodInfo"s and "InputMethodSubtype"s
+ InputMethodSettingValuesWrapper
+ .getInstance(context).refreshAllInputMethodAndSubtypes();
+ InputMethodAndSubtypeUtilCompat.loadInputMethodSubtypeList(fragment,
+ context.getContentResolver(), mInputMethodInfoList, mInputMethodAndSubtypePrefsMap);
+ updateAutoSelectionPreferences();
+ }
+
+ public void save(Context context, PreferenceFragmentCompat fragment) {
+ InputMethodAndSubtypeUtilCompat.saveInputMethodSubtypeList(fragment,
+ context.getContentResolver(), mInputMethodInfoList, mHaveHardKeyboard);
+ }
+
+ @Override
+ public boolean onPreferenceChange(final Preference pref, final Object newValue) {
+ if (!(newValue instanceof Boolean)) {
+ return true; // Invoke default behavior.
+ }
+ final boolean isChecking = (Boolean) newValue;
+ for (final String imiId : mAutoSelectionPrefsMap.keySet()) {
+ // An auto select subtype preference is changing.
+ if (mAutoSelectionPrefsMap.get(imiId) == pref) {
+ final TwoStatePreference autoSelectionPref = (TwoStatePreference) pref;
+ autoSelectionPref.setChecked(isChecking);
+ // Enable or disable subtypes depending on the auto selection preference.
+ setAutoSelectionSubtypesEnabled(imiId, autoSelectionPref.isChecked());
+ return false;
+ }
+ }
+ // A subtype preference is changing.
+ if (pref instanceof InputMethodSubtypePreference) {
+ final InputMethodSubtypePreference subtypePref = (InputMethodSubtypePreference) pref;
+ subtypePref.setChecked(isChecking);
+ if (!subtypePref.isChecked()) {
+ // It takes care of the case where no subtypes are explicitly enabled then the auto
+ // selection preference is going to be checked.
+ updateAutoSelectionPreferences();
+ }
+ return false;
+ }
+ return true; // Invoke default behavior.
+ }
+
+ private void addInputMethodSubtypePreferences(PreferenceFragmentCompat fragment,
+ InputMethodInfo imi, final PreferenceScreen root) {
+ Context prefContext = fragment.getPreferenceManager().getContext();
+
+ final int subtypeCount = imi.getSubtypeCount();
+ if (subtypeCount <= 1) {
+ return;
+ }
+ final String imiId = imi.getId();
+ final PreferenceCategory keyboardSettingsCategory =
+ new PreferenceCategory(prefContext);
+ root.addPreference(keyboardSettingsCategory);
+ final PackageManager pm = prefContext.getPackageManager();
+ final CharSequence label = imi.loadLabel(pm);
+
+ keyboardSettingsCategory.setTitle(label);
+ keyboardSettingsCategory.setKey(imiId);
+ // TODO: Use toggle Preference if images are ready.
+ final TwoStatePreference autoSelectionPref =
+ new SwitchWithNoTextPreference(prefContext);
+ mAutoSelectionPrefsMap.put(imiId, autoSelectionPref);
+ keyboardSettingsCategory.addPreference(autoSelectionPref);
+ autoSelectionPref.setOnPreferenceChangeListener(this);
+
+ final PreferenceCategory activeInputMethodsCategory =
+ new PreferenceCategory(prefContext);
+ activeInputMethodsCategory.setTitle(R.string.active_input_method_subtypes);
+ root.addPreference(activeInputMethodsCategory);
+
+ CharSequence autoSubtypeLabel = null;
+ final ArrayList<Preference> subtypePreferences = new ArrayList<>();
+ for (int index = 0; index < subtypeCount; ++index) {
+ final InputMethodSubtype subtype = imi.getSubtypeAt(index);
+ if (subtype.overridesImplicitlyEnabledSubtype()) {
+ if (autoSubtypeLabel == null) {
+ autoSubtypeLabel = InputMethodAndSubtypeUtil.getSubtypeLocaleNameAsSentence(
+ subtype, prefContext, imi);
+ }
+ } else {
+ final Preference subtypePref = new InputMethodSubtypePreference(
+ prefContext, subtype, imi);
+ subtypePreferences.add(subtypePref);
+ }
+ }
+ subtypePreferences.sort((lhs, rhs) -> {
+ if (lhs instanceof InputMethodSubtypePreference) {
+ return ((InputMethodSubtypePreference) lhs).compareTo(rhs, mCollator);
+ }
+ return lhs.compareTo(rhs);
+ });
+ for (final Preference pref : subtypePreferences) {
+ activeInputMethodsCategory.addPreference(pref);
+ pref.setOnPreferenceChangeListener(this);
+ InputMethodAndSubtypeUtil.removeUnnecessaryNonPersistentPreference(pref);
+ }
+ mInputMethodAndSubtypePrefsMap.put(imiId, subtypePreferences);
+ if (TextUtils.isEmpty(autoSubtypeLabel)) {
+ autoSelectionPref.setTitle(
+ R.string.use_system_language_to_select_input_method_subtypes);
+ } else {
+ autoSelectionPref.setTitle(autoSubtypeLabel);
+ }
+ }
+
+ private boolean isNoSubtypesExplicitlySelected(final String imiId) {
+ final List<Preference> subtypePrefs = mInputMethodAndSubtypePrefsMap.get(imiId);
+ for (final Preference pref : subtypePrefs) {
+ if (pref instanceof TwoStatePreference && ((TwoStatePreference) pref).isChecked()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private void setAutoSelectionSubtypesEnabled(final String imiId,
+ final boolean autoSelectionEnabled) {
+ final TwoStatePreference autoSelectionPref = mAutoSelectionPrefsMap.get(imiId);
+ if (autoSelectionPref == null) {
+ return;
+ }
+ autoSelectionPref.setChecked(autoSelectionEnabled);
+ final List<Preference> subtypePrefs = mInputMethodAndSubtypePrefsMap.get(imiId);
+ for (final Preference pref : subtypePrefs) {
+ if (pref instanceof TwoStatePreference) {
+ // When autoSelectionEnabled is true, all subtype prefs need to be disabled with
+ // implicitly checked subtypes. In case of false, all subtype prefs need to be
+ // enabled.
+ pref.setEnabled(!autoSelectionEnabled);
+ if (autoSelectionEnabled) {
+ ((TwoStatePreference) pref).setChecked(false);
+ }
+ }
+ }
+ if (autoSelectionEnabled) {
+ InputMethodAndSubtypeUtilCompat.saveInputMethodSubtypeList(
+ mFragment, mFragment.getContext().getContentResolver(),
+ mInputMethodInfoList, mHaveHardKeyboard);
+ updateImplicitlyEnabledSubtypes(imiId);
+ }
+ }
+
+ private void updateImplicitlyEnabledSubtypes(final String targetImiId) {
+ // When targetImiId is null, apply to all subtypes of all IMEs
+ for (final InputMethodInfo imi : mInputMethodInfoList) {
+ final String imiId = imi.getId();
+ final TwoStatePreference autoSelectionPref = mAutoSelectionPrefsMap.get(imiId);
+ // No need to update implicitly enabled subtypes when the user has unchecked the
+ // "subtype auto selection".
+ if (autoSelectionPref == null || !autoSelectionPref.isChecked()) {
+ continue;
+ }
+ if (imiId.equals(targetImiId) || targetImiId == null) {
+ updateImplicitlyEnabledSubtypesOf(imi);
+ }
+ }
+ }
+
+ private void updateImplicitlyEnabledSubtypesOf(final InputMethodInfo imi) {
+ final String imiId = imi.getId();
+ final List<Preference> subtypePrefs = mInputMethodAndSubtypePrefsMap.get(imiId);
+ final List<InputMethodSubtype> implicitlyEnabledSubtypes =
+ mImm.getEnabledInputMethodSubtypeList(imi, true);
+ if (subtypePrefs == null || implicitlyEnabledSubtypes == null) {
+ return;
+ }
+ for (final Preference pref : subtypePrefs) {
+ if (!(pref instanceof TwoStatePreference)) {
+ continue;
+ }
+ final TwoStatePreference subtypePref = (TwoStatePreference) pref;
+ subtypePref.setChecked(false);
+ for (final InputMethodSubtype subtype : implicitlyEnabledSubtypes) {
+ final String implicitlyEnabledSubtypePrefKey = imiId + subtype.hashCode();
+ if (subtypePref.getKey().equals(implicitlyEnabledSubtypePrefKey)) {
+ subtypePref.setChecked(true);
+ break;
+ }
+ }
+ }
+ }
+
+ private void updateAutoSelectionPreferences() {
+ for (final String imiId : mInputMethodAndSubtypePrefsMap.keySet()) {
+ setAutoSelectionSubtypesEnabled(imiId, isNoSubtypesExplicitlySelected(imiId));
+ }
+ updateImplicitlyEnabledSubtypes(null /* targetImiId */ /* check */);
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilCompat.java b/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilCompat.java
new file mode 100644
index 0000000..9ad2adb
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilCompat.java
@@ -0,0 +1,436 @@
+/*
+ * 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.settingslib.inputmethod;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.content.res.Configuration;
+import android.icu.text.ListFormatter;
+import android.provider.Settings;
+import android.provider.Settings.SettingNotFoundException;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.inputmethod.InputMethodInfo;
+import android.view.inputmethod.InputMethodSubtype;
+
+import com.android.internal.app.LocaleHelper;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+import androidx.preference.Preference;
+import androidx.preference.PreferenceFragmentCompat;
+import androidx.preference.PreferenceScreen;
+import androidx.preference.TwoStatePreference;
+
+// TODO: Consolidate this with {@link InputMethodSettingValuesWrapper}.
+public class InputMethodAndSubtypeUtilCompat {
+
+ private static final boolean DEBUG = false;
+ private static final String TAG = "InputMethdAndSubtypeUtlCompat";
+
+ private static final String SUBTYPE_MODE_KEYBOARD = "keyboard";
+ private static final char INPUT_METHOD_SEPARATER = ':';
+ private static final char INPUT_METHOD_SUBTYPE_SEPARATER = ';';
+ private static final int NOT_A_SUBTYPE_ID = -1;
+
+ private static final TextUtils.SimpleStringSplitter sStringInputMethodSplitter
+ = new TextUtils.SimpleStringSplitter(INPUT_METHOD_SEPARATER);
+
+ private static final TextUtils.SimpleStringSplitter sStringInputMethodSubtypeSplitter
+ = new TextUtils.SimpleStringSplitter(INPUT_METHOD_SUBTYPE_SEPARATER);
+
+ // InputMethods and subtypes are saved in the settings as follows:
+ // ime0;subtype0;subtype1:ime1;subtype0:ime2:ime3;subtype0;subtype1
+ public static String buildInputMethodsAndSubtypesString(
+ final HashMap<String, HashSet<String>> imeToSubtypesMap) {
+ final StringBuilder builder = new StringBuilder();
+ for (final String imi : imeToSubtypesMap.keySet()) {
+ if (builder.length() > 0) {
+ builder.append(INPUT_METHOD_SEPARATER);
+ }
+ final HashSet<String> subtypeIdSet = imeToSubtypesMap.get(imi);
+ builder.append(imi);
+ for (final String subtypeId : subtypeIdSet) {
+ builder.append(INPUT_METHOD_SUBTYPE_SEPARATER).append(subtypeId);
+ }
+ }
+ return builder.toString();
+ }
+
+ private static String buildInputMethodsString(final HashSet<String> imiList) {
+ final StringBuilder builder = new StringBuilder();
+ for (final String imi : imiList) {
+ if (builder.length() > 0) {
+ builder.append(INPUT_METHOD_SEPARATER);
+ }
+ builder.append(imi);
+ }
+ return builder.toString();
+ }
+
+ private static int getInputMethodSubtypeSelected(ContentResolver resolver) {
+ try {
+ return Settings.Secure.getInt(resolver,
+ Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE);
+ } catch (SettingNotFoundException e) {
+ return NOT_A_SUBTYPE_ID;
+ }
+ }
+
+ private static boolean isInputMethodSubtypeSelected(ContentResolver resolver) {
+ return getInputMethodSubtypeSelected(resolver) != NOT_A_SUBTYPE_ID;
+ }
+
+ private static void putSelectedInputMethodSubtype(ContentResolver resolver, int hashCode) {
+ Settings.Secure.putInt(resolver, Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE, hashCode);
+ }
+
+ // Needs to modify InputMethodManageService if you want to change the format of saved string.
+ static HashMap<String, HashSet<String>> getEnabledInputMethodsAndSubtypeList(
+ ContentResolver resolver) {
+ final String enabledInputMethodsStr = Settings.Secure.getString(
+ resolver, Settings.Secure.ENABLED_INPUT_METHODS);
+ if (DEBUG) {
+ Log.d(TAG, "--- Load enabled input methods: " + enabledInputMethodsStr);
+ }
+ return parseInputMethodsAndSubtypesString(enabledInputMethodsStr);
+ }
+
+ public static HashMap<String, HashSet<String>> parseInputMethodsAndSubtypesString(
+ final String inputMethodsAndSubtypesString) {
+ final HashMap<String, HashSet<String>> subtypesMap = new HashMap<>();
+ if (TextUtils.isEmpty(inputMethodsAndSubtypesString)) {
+ return subtypesMap;
+ }
+ sStringInputMethodSplitter.setString(inputMethodsAndSubtypesString);
+ while (sStringInputMethodSplitter.hasNext()) {
+ final String nextImsStr = sStringInputMethodSplitter.next();
+ sStringInputMethodSubtypeSplitter.setString(nextImsStr);
+ if (sStringInputMethodSubtypeSplitter.hasNext()) {
+ final HashSet<String> subtypeIdSet = new HashSet<>();
+ // The first element is {@link InputMethodInfoId}.
+ final String imiId = sStringInputMethodSubtypeSplitter.next();
+ while (sStringInputMethodSubtypeSplitter.hasNext()) {
+ subtypeIdSet.add(sStringInputMethodSubtypeSplitter.next());
+ }
+ subtypesMap.put(imiId, subtypeIdSet);
+ }
+ }
+ return subtypesMap;
+ }
+
+ private static HashSet<String> getDisabledSystemIMEs(ContentResolver resolver) {
+ HashSet<String> set = new HashSet<>();
+ String disabledIMEsStr = Settings.Secure.getString(
+ resolver, Settings.Secure.DISABLED_SYSTEM_INPUT_METHODS);
+ if (TextUtils.isEmpty(disabledIMEsStr)) {
+ return set;
+ }
+ sStringInputMethodSplitter.setString(disabledIMEsStr);
+ while(sStringInputMethodSplitter.hasNext()) {
+ set.add(sStringInputMethodSplitter.next());
+ }
+ return set;
+ }
+
+ public static void saveInputMethodSubtypeList(PreferenceFragmentCompat context,
+ ContentResolver resolver, List<InputMethodInfo> inputMethodInfos,
+ boolean hasHardKeyboard) {
+ String currentInputMethodId = Settings.Secure.getString(resolver,
+ Settings.Secure.DEFAULT_INPUT_METHOD);
+ final int selectedInputMethodSubtype = getInputMethodSubtypeSelected(resolver);
+ final HashMap<String, HashSet<String>> enabledIMEsAndSubtypesMap =
+ getEnabledInputMethodsAndSubtypeList(resolver);
+ final HashSet<String> disabledSystemIMEs = getDisabledSystemIMEs(resolver);
+
+ boolean needsToResetSelectedSubtype = false;
+ for (final InputMethodInfo imi : inputMethodInfos) {
+ final String imiId = imi.getId();
+ final Preference pref = context.findPreference(imiId);
+ if (pref == null) {
+ continue;
+ }
+ // In the choose input method screen or in the subtype enabler screen,
+ // <code>pref</code> is an instance of TwoStatePreference.
+ final boolean isImeChecked = (pref instanceof TwoStatePreference) ?
+ ((TwoStatePreference) pref).isChecked()
+ : enabledIMEsAndSubtypesMap.containsKey(imiId);
+ final boolean isCurrentInputMethod = imiId.equals(currentInputMethodId);
+ final boolean systemIme = imi.isSystem();
+ if ((!hasHardKeyboard && InputMethodSettingValuesWrapper.getInstance(
+ context.getActivity()).isAlwaysCheckedIme(imi))
+ || isImeChecked) {
+ if (!enabledIMEsAndSubtypesMap.containsKey(imiId)) {
+ // imiId has just been enabled
+ enabledIMEsAndSubtypesMap.put(imiId, new HashSet<>());
+ }
+ final HashSet<String> subtypesSet = enabledIMEsAndSubtypesMap.get(imiId);
+
+ boolean subtypePrefFound = false;
+ final int subtypeCount = imi.getSubtypeCount();
+ for (int i = 0; i < subtypeCount; ++i) {
+ final InputMethodSubtype subtype = imi.getSubtypeAt(i);
+ final String subtypeHashCodeStr = String.valueOf(subtype.hashCode());
+ final TwoStatePreference subtypePref = (TwoStatePreference) context
+ .findPreference(imiId + subtypeHashCodeStr);
+ // In the Configure input method screen which does not have subtype preferences.
+ if (subtypePref == null) {
+ continue;
+ }
+ if (!subtypePrefFound) {
+ // Once subtype preference is found, subtypeSet needs to be cleared.
+ // Because of system change, hashCode value could have been changed.
+ subtypesSet.clear();
+ // If selected subtype preference is disabled, needs to reset.
+ needsToResetSelectedSubtype = true;
+ subtypePrefFound = true;
+ }
+ // Checking <code>subtypePref.isEnabled()</code> is insufficient to determine
+ // whether the user manually enabled this subtype or not. Implicitly-enabled
+ // subtypes are also checked just as an indicator to users. We also need to
+ // check <code>subtypePref.isEnabled()</code> so that only manually enabled
+ // subtypes can be saved here.
+ if (subtypePref.isEnabled() && subtypePref.isChecked()) {
+ subtypesSet.add(subtypeHashCodeStr);
+ if (isCurrentInputMethod) {
+ if (selectedInputMethodSubtype == subtype.hashCode()) {
+ // Selected subtype is still enabled, there is no need to reset
+ // selected subtype.
+ needsToResetSelectedSubtype = false;
+ }
+ }
+ } else {
+ subtypesSet.remove(subtypeHashCodeStr);
+ }
+ }
+ } else {
+ enabledIMEsAndSubtypesMap.remove(imiId);
+ if (isCurrentInputMethod) {
+ // We are processing the current input method, but found that it's not enabled.
+ // This means that the current input method has been uninstalled.
+ // If currentInputMethod is already uninstalled, InputMethodManagerService will
+ // find the applicable IME from the history and the system locale.
+ if (DEBUG) {
+ Log.d(TAG, "Current IME was uninstalled or disabled.");
+ }
+ currentInputMethodId = null;
+ }
+ }
+ // If it's a disabled system ime, add it to the disabled list so that it
+ // doesn't get enabled automatically on any changes to the package list
+ if (systemIme && hasHardKeyboard) {
+ if (disabledSystemIMEs.contains(imiId)) {
+ if (isImeChecked) {
+ disabledSystemIMEs.remove(imiId);
+ }
+ } else {
+ if (!isImeChecked) {
+ disabledSystemIMEs.add(imiId);
+ }
+ }
+ }
+ }
+
+ final String enabledIMEsAndSubtypesString = buildInputMethodsAndSubtypesString(
+ enabledIMEsAndSubtypesMap);
+ final String disabledSystemIMEsString = buildInputMethodsString(disabledSystemIMEs);
+ if (DEBUG) {
+ Log.d(TAG, "--- Save enabled inputmethod settings. :" + enabledIMEsAndSubtypesString);
+ Log.d(TAG, "--- Save disabled system inputmethod settings. :"
+ + disabledSystemIMEsString);
+ Log.d(TAG, "--- Save default inputmethod settings. :" + currentInputMethodId);
+ Log.d(TAG, "--- Needs to reset the selected subtype :" + needsToResetSelectedSubtype);
+ Log.d(TAG, "--- Subtype is selected :" + isInputMethodSubtypeSelected(resolver));
+ }
+
+ // Redefines SelectedSubtype when all subtypes are unchecked or there is no subtype
+ // selected. And if the selected subtype of the current input method was disabled,
+ // We should reset the selected input method's subtype.
+ if (needsToResetSelectedSubtype || !isInputMethodSubtypeSelected(resolver)) {
+ if (DEBUG) {
+ Log.d(TAG, "--- Reset inputmethod subtype because it's not defined.");
+ }
+ putSelectedInputMethodSubtype(resolver, NOT_A_SUBTYPE_ID);
+ }
+
+ Settings.Secure.putString(resolver,
+ Settings.Secure.ENABLED_INPUT_METHODS, enabledIMEsAndSubtypesString);
+ if (disabledSystemIMEsString.length() > 0) {
+ Settings.Secure.putString(resolver, Settings.Secure.DISABLED_SYSTEM_INPUT_METHODS,
+ disabledSystemIMEsString);
+ }
+ // If the current input method is unset, InputMethodManagerService will find the applicable
+ // IME from the history and the system locale.
+ Settings.Secure.putString(resolver, Settings.Secure.DEFAULT_INPUT_METHOD,
+ currentInputMethodId != null ? currentInputMethodId : "");
+ }
+
+ public static void loadInputMethodSubtypeList(final PreferenceFragmentCompat context,
+ final ContentResolver resolver, final List<InputMethodInfo> inputMethodInfos,
+ final Map<String, List<Preference>> inputMethodPrefsMap) {
+ final HashMap<String, HashSet<String>> enabledSubtypes =
+ getEnabledInputMethodsAndSubtypeList(resolver);
+
+ for (final InputMethodInfo imi : inputMethodInfos) {
+ final String imiId = imi.getId();
+ final Preference pref = context.findPreference(imiId);
+ if (pref instanceof TwoStatePreference) {
+ final TwoStatePreference subtypePref = (TwoStatePreference) pref;
+ final boolean isEnabled = enabledSubtypes.containsKey(imiId);
+ subtypePref.setChecked(isEnabled);
+ if (inputMethodPrefsMap != null) {
+ for (final Preference childPref: inputMethodPrefsMap.get(imiId)) {
+ childPref.setEnabled(isEnabled);
+ }
+ }
+ setSubtypesPreferenceEnabled(context, inputMethodInfos, imiId, isEnabled);
+ }
+ }
+ updateSubtypesPreferenceChecked(context, inputMethodInfos, enabledSubtypes);
+ }
+
+ private static void setSubtypesPreferenceEnabled(final PreferenceFragmentCompat context,
+ final List<InputMethodInfo> inputMethodProperties, final String id,
+ final boolean enabled) {
+ final PreferenceScreen preferenceScreen = context.getPreferenceScreen();
+ for (final InputMethodInfo imi : inputMethodProperties) {
+ if (id.equals(imi.getId())) {
+ final int subtypeCount = imi.getSubtypeCount();
+ for (int i = 0; i < subtypeCount; ++i) {
+ final InputMethodSubtype subtype = imi.getSubtypeAt(i);
+ final TwoStatePreference pref = (TwoStatePreference) preferenceScreen
+ .findPreference(id + subtype.hashCode());
+ if (pref != null) {
+ pref.setEnabled(enabled);
+ }
+ }
+ }
+ }
+ }
+
+ private static void updateSubtypesPreferenceChecked(final PreferenceFragmentCompat context,
+ final List<InputMethodInfo> inputMethodProperties,
+ final HashMap<String, HashSet<String>> enabledSubtypes) {
+ final PreferenceScreen preferenceScreen = context.getPreferenceScreen();
+ for (final InputMethodInfo imi : inputMethodProperties) {
+ final String id = imi.getId();
+ if (!enabledSubtypes.containsKey(id)) {
+ // There is no need to enable/disable subtypes of disabled IMEs.
+ continue;
+ }
+ final HashSet<String> enabledSubtypesSet = enabledSubtypes.get(id);
+ final int subtypeCount = imi.getSubtypeCount();
+ for (int i = 0; i < subtypeCount; ++i) {
+ final InputMethodSubtype subtype = imi.getSubtypeAt(i);
+ final String hashCode = String.valueOf(subtype.hashCode());
+ if (DEBUG) {
+ Log.d(TAG, "--- Set checked state: " + "id" + ", " + hashCode + ", "
+ + enabledSubtypesSet.contains(hashCode));
+ }
+ final TwoStatePreference pref = (TwoStatePreference) preferenceScreen
+ .findPreference(id + hashCode);
+ if (pref != null) {
+ pref.setChecked(enabledSubtypesSet.contains(hashCode));
+ }
+ }
+ }
+ }
+
+ public static void removeUnnecessaryNonPersistentPreference(final Preference pref) {
+ final String key = pref.getKey();
+ if (pref.isPersistent() || key == null) {
+ return;
+ }
+ final SharedPreferences prefs = pref.getSharedPreferences();
+ if (prefs != null && prefs.contains(key)) {
+ prefs.edit().remove(key).apply();
+ }
+ }
+
+ @NonNull
+ public static String getSubtypeLocaleNameAsSentence(@Nullable InputMethodSubtype subtype,
+ @NonNull final Context context, @NonNull final InputMethodInfo inputMethodInfo) {
+ if (subtype == null) {
+ return "";
+ }
+ final Locale locale = getDisplayLocale(context);
+ final CharSequence subtypeName = subtype.getDisplayName(context,
+ inputMethodInfo.getPackageName(), inputMethodInfo.getServiceInfo()
+ .applicationInfo);
+ return LocaleHelper.toSentenceCase(subtypeName.toString(), locale);
+ }
+
+ @NonNull
+ public static String getSubtypeLocaleNameListAsSentence(
+ @NonNull final List<InputMethodSubtype> subtypes, @NonNull final Context context,
+ @NonNull final InputMethodInfo inputMethodInfo) {
+ if (subtypes.isEmpty()) {
+ return "";
+ }
+ final Locale locale = getDisplayLocale(context);
+ final int subtypeCount = subtypes.size();
+ final CharSequence[] subtypeNames = new CharSequence[subtypeCount];
+ for (int i = 0; i < subtypeCount; i++) {
+ subtypeNames[i] = subtypes.get(i).getDisplayName(context,
+ inputMethodInfo.getPackageName(), inputMethodInfo.getServiceInfo()
+ .applicationInfo);
+ }
+ return LocaleHelper.toSentenceCase(
+ ListFormatter.getInstance(locale).format((Object[]) subtypeNames), locale);
+ }
+
+ @NonNull
+ private static Locale getDisplayLocale(@Nullable final Context context) {
+ if (context == null) {
+ return Locale.getDefault();
+ }
+ if (context.getResources() == null) {
+ return Locale.getDefault();
+ }
+ final Configuration configuration = context.getResources().getConfiguration();
+ if (configuration == null) {
+ return Locale.getDefault();
+ }
+ final Locale configurationLocale = configuration.getLocales().get(0);
+ if (configurationLocale == null) {
+ return Locale.getDefault();
+ }
+ return configurationLocale;
+ }
+
+ public static boolean isValidSystemNonAuxAsciiCapableIme(InputMethodInfo imi) {
+ if (imi.isAuxiliaryIme() || !imi.isSystem()) {
+ return false;
+ }
+ final int subtypeCount = imi.getSubtypeCount();
+ for (int i = 0; i < subtypeCount; ++i) {
+ final InputMethodSubtype subtype = imi.getSubtypeAt(i);
+ if (SUBTYPE_MODE_KEYBOARD.equalsIgnoreCase(subtype.getMode())
+ && subtype.isAsciiCapable()) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoaderCompat.java b/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoaderCompat.java
new file mode 100644
index 0000000..d7c14ad
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoaderCompat.java
@@ -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.
+ */
+
+package com.android.settingslib.license;
+
+import android.content.Context;
+import android.util.Log;
+
+import com.android.settingslib.utils.AsyncLoaderCompat;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+import androidx.annotation.VisibleForTesting;
+
+/**
+ * LicenseHtmlLoader is a loader which loads a license html file from default license xml files.
+ */
+public class LicenseHtmlLoaderCompat extends AsyncLoaderCompat<File> {
+ private static final String TAG = "LicenseHtmlLoaderCompat";
+
+ private static final String[] DEFAULT_LICENSE_XML_PATHS = {
+ "/system/etc/NOTICE.xml.gz",
+ "/vendor/etc/NOTICE.xml.gz",
+ "/odm/etc/NOTICE.xml.gz",
+ "/oem/etc/NOTICE.xml.gz"};
+ private static final String NOTICE_HTML_FILE_NAME = "NOTICE.html";
+
+ private Context mContext;
+
+ public LicenseHtmlLoaderCompat(Context context) {
+ super(context);
+ mContext = context;
+ }
+
+ @Override
+ public File loadInBackground() {
+ return generateHtmlFromDefaultXmlFiles();
+ }
+
+ @Override
+ protected void onDiscardResult(File f) {
+ }
+
+ private File generateHtmlFromDefaultXmlFiles() {
+ final List<File> xmlFiles = getVaildXmlFiles();
+ if (xmlFiles.isEmpty()) {
+ Log.e(TAG, "No notice file exists.");
+ return null;
+ }
+
+ File cachedHtmlFile = getCachedHtmlFile();
+ if (!isCachedHtmlFileOutdated(xmlFiles, cachedHtmlFile)
+ || generateHtmlFile(xmlFiles, cachedHtmlFile)) {
+ return cachedHtmlFile;
+ }
+
+ return null;
+ }
+
+ @VisibleForTesting
+ List<File> getVaildXmlFiles() {
+ final List<File> xmlFiles = new ArrayList();
+ for (final String xmlPath : DEFAULT_LICENSE_XML_PATHS) {
+ File file = new File(xmlPath);
+ if (file.exists() && file.length() != 0) {
+ xmlFiles.add(file);
+ }
+ }
+ return xmlFiles;
+ }
+
+ @VisibleForTesting
+ File getCachedHtmlFile() {
+ return new File(mContext.getCacheDir(), NOTICE_HTML_FILE_NAME);
+ }
+
+ @VisibleForTesting
+ boolean isCachedHtmlFileOutdated(List<File> xmlFiles, File cachedHtmlFile) {
+ boolean outdated = true;
+ if (cachedHtmlFile.exists() && cachedHtmlFile.length() != 0) {
+ outdated = false;
+ for (File file : xmlFiles) {
+ if (cachedHtmlFile.lastModified() < file.lastModified()) {
+ outdated = true;
+ break;
+ }
+ }
+ }
+ return outdated;
+ }
+
+ @VisibleForTesting
+ boolean generateHtmlFile(List<File> xmlFiles, File htmlFile) {
+ return LicenseHtmlGeneratorFromXml.generateHtml(xmlFiles, htmlFile);
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/location/InjectedSetting.java b/packages/SettingsLib/src/com/android/settingslib/location/InjectedSetting.java
new file mode 100644
index 0000000..1805f1a
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/location/InjectedSetting.java
@@ -0,0 +1,191 @@
+/*
+ * 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.settingslib.location;
+
+import android.content.Intent;
+import android.os.UserHandle;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.internal.annotations.Immutable;
+
+import java.util.Objects;
+
+/**
+ * Specifies a setting that is being injected into Settings > Location > Location services.
+ *
+ * @see android.location.SettingInjectorService
+ */
+@Immutable
+public class InjectedSetting {
+
+ /**
+ * Package for the subclass of {@link android.location.SettingInjectorService} and for the
+ * settings activity.
+ */
+ public final String packageName;
+
+ /**
+ * Class name for the subclass of {@link android.location.SettingInjectorService} that
+ * specifies dynamic values for the location setting.
+ */
+ public final String className;
+
+ /**
+ * The {@link android.support.v7.preference.Preference#getTitle()} value.
+ */
+ public final String title;
+
+ /**
+ * The {@link android.support.v7.preference.Preference#getIcon()} value.
+ */
+ public final int iconId;
+
+ /**
+ * The user/profile associated with this setting (e.g. managed profile)
+ */
+ public final UserHandle mUserHandle;
+
+ /**
+ * The activity to launch to allow the user to modify the settings value. Assumed to be in the
+ * {@link #packageName} package.
+ */
+ public final String settingsActivity;
+
+ /**
+ * The user restriction associated with this setting.
+ */
+ public final String userRestriction;
+
+ private InjectedSetting(Builder builder) {
+ this.packageName = builder.mPackageName;
+ this.className = builder.mClassName;
+ this.title = builder.mTitle;
+ this.iconId = builder.mIconId;
+ this.mUserHandle = builder.mUserHandle;
+ this.settingsActivity = builder.mSettingsActivity;
+ this.userRestriction = builder.mUserRestriction;
+ }
+
+ @Override
+ public String toString() {
+ return "InjectedSetting{" +
+ "mPackageName='" + packageName + '\'' +
+ ", mClassName='" + className + '\'' +
+ ", label=" + title +
+ ", iconId=" + iconId +
+ ", userId=" + mUserHandle.getIdentifier() +
+ ", settingsActivity='" + settingsActivity + '\'' +
+ ", userRestriction='" + userRestriction +
+ '}';
+ }
+
+ /**
+ * Returns the intent to start the {@link #className} service.
+ */
+ public Intent getServiceIntent() {
+ Intent intent = new Intent();
+ intent.setClassName(packageName, className);
+ return intent;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof InjectedSetting)) return false;
+
+ InjectedSetting that = (InjectedSetting) o;
+
+ return Objects.equals(packageName, that.packageName)
+ && Objects.equals(className, that.className)
+ && Objects.equals(title, that.title)
+ && Objects.equals(iconId, that.iconId)
+ && Objects.equals(mUserHandle, that.mUserHandle)
+ && Objects.equals(settingsActivity, that.settingsActivity)
+ && Objects.equals(userRestriction, that.userRestriction);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = packageName.hashCode();
+ result = 31 * result + className.hashCode();
+ result = 31 * result + title.hashCode();
+ result = 31 * result + iconId;
+ result = 31 * result + (mUserHandle == null ? 0 : mUserHandle.hashCode());
+ result = 31 * result + settingsActivity.hashCode();
+ result = 31 * result + (userRestriction == null ? 0 : userRestriction.hashCode());
+ return result;
+ }
+
+ public static class Builder {
+ private String mPackageName;
+ private String mClassName;
+ private String mTitle;
+ private int mIconId;
+ private UserHandle mUserHandle;
+ private String mSettingsActivity;
+ private String mUserRestriction;
+
+ public Builder setPackageName(String packageName) {
+ mPackageName = packageName;
+ return this;
+ }
+
+ public Builder setClassName(String className) {
+ mClassName = className;
+ return this;
+ }
+
+ public Builder setTitle(String title) {
+ mTitle = title;
+ return this;
+ }
+
+ public Builder setIconId(int iconId) {
+ mIconId = iconId;
+ return this;
+ }
+
+ public Builder setUserHandle(UserHandle userHandle) {
+ mUserHandle = userHandle;
+ return this;
+ }
+
+ public Builder setSettingsActivity(String settingsActivity) {
+ mSettingsActivity = settingsActivity;
+ return this;
+ }
+
+ public Builder setUserRestriction(String userRestriction) {
+ mUserRestriction = userRestriction;
+ return this;
+ }
+
+ public InjectedSetting build() {
+ if (mPackageName == null || mClassName == null || TextUtils.isEmpty(mTitle)
+ || TextUtils.isEmpty(mSettingsActivity)) {
+ if (Log.isLoggable(SettingsInjector.TAG, Log.WARN)) {
+ Log.w(SettingsInjector.TAG, "Illegal setting specification: package="
+ + mPackageName + ", class=" + mClassName
+ + ", title=" + mTitle + ", settingsActivity=" + mSettingsActivity);
+ }
+ return null;
+ }
+ return new InjectedSetting(this);
+ }
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/location/SettingsInjector.java b/packages/SettingsLib/src/com/android/settingslib/location/SettingsInjector.java
new file mode 100644
index 0000000..780fcba
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/location/SettingsInjector.java
@@ -0,0 +1,576 @@
+/*
+ * 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.settingslib.location;
+
+import android.app.ActivityManager;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageItemInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.graphics.drawable.Drawable;
+import android.location.SettingInjectorService;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.util.AttributeSet;
+import android.util.IconDrawableFactory;
+import android.util.Log;
+import android.util.Xml;
+
+import androidx.preference.Preference;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Adds the preferences specified by the {@link InjectedSetting} objects to a preference group.
+ *
+ * Duplicates some code from {@link android.content.pm.RegisteredServicesCache}. We do not use that
+ * class directly because it is not a good match for our use case: we do not need the caching, and
+ * so do not want the additional resource hit at app install/upgrade time; and we would have to
+ * suppress the tie-breaking between multiple services reporting settings with the same name.
+ * Code-sharing would require extracting {@link
+ * android.content.pm.RegisteredServicesCache#parseServiceAttributes(android.content.res.Resources,
+ * String, android.util.AttributeSet)} into an interface, which didn't seem worth it.
+ */
+public class SettingsInjector {
+ static final String TAG = "SettingsInjector";
+
+ /**
+ * If reading the status of a setting takes longer than this, we go ahead and start reading
+ * the next setting.
+ */
+ private static final long INJECTED_STATUS_UPDATE_TIMEOUT_MILLIS = 1000;
+
+ /**
+ * {@link Message#what} value for starting to load status values
+ * in case we aren't already in the process of loading them.
+ */
+ private static final int WHAT_RELOAD = 1;
+
+ /**
+ * {@link Message#what} value sent after receiving a status message.
+ */
+ private static final int WHAT_RECEIVED_STATUS = 2;
+
+ /**
+ * {@link Message#what} value sent after the timeout waiting for a status message.
+ */
+ private static final int WHAT_TIMEOUT = 3;
+
+ private final Context mContext;
+
+ /**
+ * The settings that were injected
+ */
+ protected final Set<Setting> mSettings;
+
+ private final Handler mHandler;
+
+ public SettingsInjector(Context context) {
+ mContext = context;
+ mSettings = new HashSet<Setting>();
+ mHandler = new StatusLoadingHandler();
+ }
+
+ /**
+ * Returns a list for a profile with one {@link InjectedSetting} object for each
+ * {@link android.app.Service} that responds to
+ * {@link SettingInjectorService#ACTION_SERVICE_INTENT} and provides the expected setting
+ * metadata.
+ *
+ * Duplicates some code from {@link android.content.pm.RegisteredServicesCache}.
+ *
+ * TODO: unit test
+ */
+ protected List<InjectedSetting> getSettings(final UserHandle userHandle) {
+ PackageManager pm = mContext.getPackageManager();
+ Intent intent = new Intent(SettingInjectorService.ACTION_SERVICE_INTENT);
+
+ final int profileId = userHandle.getIdentifier();
+ List<ResolveInfo> resolveInfos =
+ pm.queryIntentServicesAsUser(intent, PackageManager.GET_META_DATA, profileId);
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "Found services for profile id " + profileId + ": " + resolveInfos);
+ }
+ List<InjectedSetting> settings = new ArrayList<InjectedSetting>(resolveInfos.size());
+ for (ResolveInfo resolveInfo : resolveInfos) {
+ try {
+ InjectedSetting setting = parseServiceInfo(resolveInfo, userHandle, pm);
+ if (setting == null) {
+ Log.w(TAG, "Unable to load service info " + resolveInfo);
+ } else {
+ settings.add(setting);
+ }
+ } catch (XmlPullParserException e) {
+ Log.w(TAG, "Unable to load service info " + resolveInfo, e);
+ } catch (IOException e) {
+ Log.w(TAG, "Unable to load service info " + resolveInfo, e);
+ }
+ }
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "Loaded settings for profile id " + profileId + ": " + settings);
+ }
+
+ return settings;
+ }
+
+ /**
+ * Adds the InjectedSetting information to a Preference object
+ */
+ private void populatePreference(Preference preference, InjectedSetting setting) {
+ final PackageManager pm = mContext.getPackageManager();
+ Drawable appIcon = null;
+ try {
+ final PackageItemInfo itemInfo = new PackageItemInfo();
+ itemInfo.icon = setting.iconId;
+ itemInfo.packageName = setting.packageName;
+ final ApplicationInfo appInfo = pm.getApplicationInfo(setting.packageName,
+ PackageManager.GET_META_DATA);
+ appIcon = IconDrawableFactory.newInstance(mContext)
+ .getBadgedIcon(itemInfo, appInfo, setting.mUserHandle.getIdentifier());
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(TAG, "Can't get ApplicationInfo for " + setting.packageName, e);
+ }
+ preference.setTitle(setting.title);
+ preference.setSummary(null);
+ preference.setIcon(appIcon);
+ preference.setOnPreferenceClickListener(new ServiceSettingClickedListener(setting));
+ }
+
+ /**
+ * Gets a list of preferences that other apps have injected.
+ *
+ * @param profileId Identifier of the user/profile to obtain the injected settings for or
+ * UserHandle.USER_CURRENT for all profiles associated with current user.
+ */
+ public List<Preference> getInjectedSettings(Context prefContext, final int profileId) {
+ final UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+ final List<UserHandle> profiles = um.getUserProfiles();
+ ArrayList<Preference> prefs = new ArrayList<>();
+ for (UserHandle userHandle : profiles) {
+ if (profileId == UserHandle.USER_CURRENT || profileId == userHandle.getIdentifier()) {
+ Iterable<InjectedSetting> settings = getSettings(userHandle);
+ for (InjectedSetting setting : settings) {
+ Preference preference = createPreference(prefContext, setting);
+ populatePreference(preference, setting);
+ prefs.add(preference);
+ mSettings.add(new Setting(setting, preference));
+ }
+ }
+ }
+
+ reloadStatusMessages();
+
+ return prefs;
+ }
+
+ /**
+ * Creates an injected Preference
+ *
+ * @return the created Preference
+ */
+ protected Preference createPreference(Context prefContext, InjectedSetting setting) {
+ return new Preference(prefContext);
+ }
+
+ /**
+ * Returns the settings parsed from the attributes of the
+ * {@link SettingInjectorService#META_DATA_NAME} tag, or null.
+ *
+ * Duplicates some code from {@link android.content.pm.RegisteredServicesCache}.
+ */
+ private static InjectedSetting parseServiceInfo(ResolveInfo service, UserHandle userHandle,
+ PackageManager pm) throws XmlPullParserException, IOException {
+
+ ServiceInfo si = service.serviceInfo;
+ ApplicationInfo ai = si.applicationInfo;
+
+ if ((ai.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
+ if (Log.isLoggable(TAG, Log.WARN)) {
+ Log.w(TAG, "Ignoring attempt to inject setting from app not in system image: "
+ + service);
+ return null;
+ }
+ }
+
+ XmlResourceParser parser = null;
+ try {
+ parser = si.loadXmlMetaData(pm, SettingInjectorService.META_DATA_NAME);
+ if (parser == null) {
+ throw new XmlPullParserException("No " + SettingInjectorService.META_DATA_NAME
+ + " meta-data for " + service + ": " + si);
+ }
+
+ AttributeSet attrs = Xml.asAttributeSet(parser);
+
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && type != XmlPullParser.START_TAG) {
+ }
+
+ String nodeName = parser.getName();
+ if (!SettingInjectorService.ATTRIBUTES_NAME.equals(nodeName)) {
+ throw new XmlPullParserException("Meta-data does not start with "
+ + SettingInjectorService.ATTRIBUTES_NAME + " tag");
+ }
+
+ Resources res = pm.getResourcesForApplicationAsUser(si.packageName,
+ userHandle.getIdentifier());
+ return parseAttributes(si.packageName, si.name, userHandle, res, attrs);
+ } catch (PackageManager.NameNotFoundException e) {
+ throw new XmlPullParserException(
+ "Unable to load resources for package " + si.packageName);
+ } finally {
+ if (parser != null) {
+ parser.close();
+ }
+ }
+ }
+
+ /**
+ * Returns an immutable representation of the static attributes for the setting, or null.
+ */
+ private static InjectedSetting parseAttributes(String packageName, String className,
+ UserHandle userHandle, Resources res, AttributeSet attrs) {
+
+ TypedArray sa = res.obtainAttributes(attrs, android.R.styleable.SettingInjectorService);
+ try {
+ // Note that to help guard against malicious string injection, we do not allow dynamic
+ // specification of the label (setting title)
+ final String title = sa.getString(android.R.styleable.SettingInjectorService_title);
+ final int iconId =
+ sa.getResourceId(android.R.styleable.SettingInjectorService_icon, 0);
+ final String settingsActivity =
+ sa.getString(android.R.styleable.SettingInjectorService_settingsActivity);
+ final String userRestriction = sa.getString(
+ android.R.styleable.SettingInjectorService_userRestriction);
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "parsed title: " + title + ", iconId: " + iconId
+ + ", settingsActivity: " + settingsActivity);
+ }
+ return new InjectedSetting.Builder()
+ .setPackageName(packageName)
+ .setClassName(className)
+ .setTitle(title)
+ .setIconId(iconId)
+ .setUserHandle(userHandle)
+ .setSettingsActivity(settingsActivity)
+ .setUserRestriction(userRestriction)
+ .build();
+ } finally {
+ sa.recycle();
+ }
+ }
+
+ /**
+ * Checks wheteher there is any preference that other apps have injected.
+ *
+ * @param profileId Identifier of the user/profile to obtain the injected settings for or
+ * UserHandle.USER_CURRENT for all profiles associated with current user.
+ */
+ public boolean hasInjectedSettings(final int profileId) {
+ final UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+ final List<UserHandle> profiles = um.getUserProfiles();
+ final int profileCount = profiles.size();
+ for (int i = 0; i < profileCount; ++i) {
+ final UserHandle userHandle = profiles.get(i);
+ if (profileId == UserHandle.USER_CURRENT || profileId == userHandle.getIdentifier()) {
+ Iterable<InjectedSetting> settings = getSettings(userHandle);
+ for (InjectedSetting setting : settings) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Reloads the status messages for all the preference items.
+ */
+ public void reloadStatusMessages() {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "reloadingStatusMessages: " + mSettings);
+ }
+ mHandler.sendMessage(mHandler.obtainMessage(WHAT_RELOAD));
+ }
+
+ protected class ServiceSettingClickedListener
+ implements Preference.OnPreferenceClickListener {
+ private InjectedSetting mInfo;
+
+ public ServiceSettingClickedListener(InjectedSetting info) {
+ mInfo = info;
+ }
+
+ @Override
+ public boolean onPreferenceClick(Preference preference) {
+ // Activity to start if they click on the preference. Must start in new task to ensure
+ // that "android.settings.LOCATION_SOURCE_SETTINGS" brings user back to
+ // Settings > Location.
+ Intent settingIntent = new Intent();
+ settingIntent.setClassName(mInfo.packageName, mInfo.settingsActivity);
+ // Sometimes the user may navigate back to "Settings" and launch another different
+ // injected setting after one injected setting has been launched.
+ //
+ // FLAG_ACTIVITY_CLEAR_TOP allows multiple Activities to stack on each other. When
+ // "back" button is clicked, the user will navigate through all the injected settings
+ // launched before. Such behavior could be quite confusing sometimes.
+ //
+ // In order to avoid such confusion, we use FLAG_ACTIVITY_CLEAR_TASK, which always clear
+ // up all existing injected settings and make sure that "back" button always brings the
+ // user back to "Settings" directly.
+ settingIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ mContext.startActivityAsUser(settingIntent, mInfo.mUserHandle);
+ return true;
+ }
+ }
+
+ /**
+ * Loads the setting status values one at a time. Each load starts a subclass of {@link
+ * SettingInjectorService}, so to reduce memory pressure we don't want to load too many at
+ * once.
+ */
+ private final class StatusLoadingHandler extends Handler {
+
+ /**
+ * Settings whose status values need to be loaded. A set is used to prevent redundant loads.
+ */
+ private Set<Setting> mSettingsToLoad = new HashSet<Setting>();
+
+ /**
+ * Settings that are being loaded now and haven't timed out. In practice this should have
+ * zero or one elements.
+ */
+ private Set<Setting> mSettingsBeingLoaded = new HashSet<Setting>();
+
+ /**
+ * Settings that are being loaded but have timed out. If only one setting has timed out, we
+ * will go ahead and start loading the next setting so that one slow load won't delay the
+ * load of the other settings.
+ */
+ private Set<Setting> mTimedOutSettings = new HashSet<Setting>();
+
+ private boolean mReloadRequested;
+
+ private StatusLoadingHandler() {
+ super(Looper.getMainLooper());
+ }
+ @Override
+ public void handleMessage(Message msg) {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "handleMessage start: " + msg + ", " + this);
+ }
+
+ // Update state in response to message
+ switch (msg.what) {
+ case WHAT_RELOAD:
+ mReloadRequested = true;
+ break;
+ case WHAT_RECEIVED_STATUS:
+ final Setting receivedSetting = (Setting) msg.obj;
+ receivedSetting.maybeLogElapsedTime();
+ mSettingsBeingLoaded.remove(receivedSetting);
+ mTimedOutSettings.remove(receivedSetting);
+ removeMessages(WHAT_TIMEOUT, receivedSetting);
+ break;
+ case WHAT_TIMEOUT:
+ final Setting timedOutSetting = (Setting) msg.obj;
+ mSettingsBeingLoaded.remove(timedOutSetting);
+ mTimedOutSettings.add(timedOutSetting);
+ if (Log.isLoggable(TAG, Log.WARN)) {
+ Log.w(TAG, "Timed out after " + timedOutSetting.getElapsedTime()
+ + " millis trying to get status for: " + timedOutSetting);
+ }
+ break;
+ default:
+ Log.wtf(TAG, "Unexpected what: " + msg);
+ }
+
+ // Decide whether to load additional settings based on the new state. Start by seeing
+ // if we have headroom to load another setting.
+ if (mSettingsBeingLoaded.size() > 0 || mTimedOutSettings.size() > 1) {
+ // Don't load any more settings until one of the pending settings has completed.
+ // To reduce memory pressure, we want to be loading at most one setting (plus at
+ // most one timed-out setting) at a time. This means we'll be responsible for
+ // bringing in at most two services.
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "too many services already live for " + msg + ", " + this);
+ }
+ return;
+ }
+
+ if (mReloadRequested && mSettingsToLoad.isEmpty() && mSettingsBeingLoaded.isEmpty()
+ && mTimedOutSettings.isEmpty()) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "reloading because idle and reload requesteed " + msg + ", " + this);
+ }
+ // Reload requested, so must reload all settings
+ mSettingsToLoad.addAll(mSettings);
+ mReloadRequested = false;
+ }
+
+ // Remove the next setting to load from the queue, if any
+ Iterator<Setting> iter = mSettingsToLoad.iterator();
+ if (!iter.hasNext()) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "nothing left to do for " + msg + ", " + this);
+ }
+ return;
+ }
+ Setting setting = iter.next();
+ iter.remove();
+
+ // Request the status value
+ setting.startService();
+ mSettingsBeingLoaded.add(setting);
+
+ // Ensure that if receiving the status value takes too long, we start loading the
+ // next value anyway
+ Message timeoutMsg = obtainMessage(WHAT_TIMEOUT, setting);
+ sendMessageDelayed(timeoutMsg, INJECTED_STATUS_UPDATE_TIMEOUT_MILLIS);
+
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "handleMessage end " + msg + ", " + this
+ + ", started loading " + setting);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "StatusLoadingHandler{" +
+ "mSettingsToLoad=" + mSettingsToLoad +
+ ", mSettingsBeingLoaded=" + mSettingsBeingLoaded +
+ ", mTimedOutSettings=" + mTimedOutSettings +
+ ", mReloadRequested=" + mReloadRequested +
+ '}';
+ }
+ }
+
+ /**
+ * Represents an injected setting and the corresponding preference.
+ */
+ protected final class Setting {
+
+ public final InjectedSetting setting;
+ public final Preference preference;
+ public long startMillis;
+
+ public Setting(InjectedSetting setting, Preference preference) {
+ this.setting = setting;
+ this.preference = preference;
+ }
+
+ @Override
+ public String toString() {
+ return "Setting{" +
+ "setting=" + setting +
+ ", preference=" + preference +
+ '}';
+ }
+
+ /**
+ * Returns true if they both have the same {@link #setting} value. Ignores mutable
+ * {@link #preference} and {@link #startMillis} so that it's safe to use in sets.
+ */
+ @Override
+ public boolean equals(Object o) {
+ return this == o || o instanceof Setting && setting.equals(((Setting) o).setting);
+ }
+
+ @Override
+ public int hashCode() {
+ return setting.hashCode();
+ }
+
+ /**
+ * Starts the service to fetch for the current status for the setting, and updates the
+ * preference when the service replies.
+ */
+ public void startService() {
+ final ActivityManager am = (ActivityManager)
+ mContext.getSystemService(Context.ACTIVITY_SERVICE);
+ if (!am.isUserRunning(setting.mUserHandle.getIdentifier())) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "Cannot start service as user "
+ + setting.mUserHandle.getIdentifier() + " is not running");
+ }
+ return;
+ }
+ Handler handler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ Bundle bundle = msg.getData();
+ boolean enabled = bundle.getBoolean(SettingInjectorService.ENABLED_KEY, true);
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, setting + ": received " + msg + ", bundle: " + bundle);
+ }
+ preference.setSummary(null);
+ preference.setEnabled(enabled);
+ mHandler.sendMessage(
+ mHandler.obtainMessage(WHAT_RECEIVED_STATUS, Setting.this));
+ }
+ };
+ Messenger messenger = new Messenger(handler);
+
+ Intent intent = setting.getServiceIntent();
+ intent.putExtra(SettingInjectorService.MESSENGER_KEY, messenger);
+
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, setting + ": sending update intent: " + intent
+ + ", handler: " + handler);
+ startMillis = SystemClock.elapsedRealtime();
+ } else {
+ startMillis = 0;
+ }
+
+ // Start the service, making sure that this is attributed to the user associated with
+ // the setting rather than the system user.
+ mContext.startServiceAsUser(intent, setting.mUserHandle);
+ }
+
+ public long getElapsedTime() {
+ long end = SystemClock.elapsedRealtime();
+ return end - startMillis;
+ }
+
+ public void maybeLogElapsedTime() {
+ if (Log.isLoggable(TAG, Log.DEBUG) && startMillis != 0) {
+ long elapsed = getElapsedTime();
+ Log.d(TAG, this + " update took " + elapsed + " millis");
+ }
+ }
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/ChartDataLoaderCompat.java b/packages/SettingsLib/src/com/android/settingslib/net/ChartDataLoaderCompat.java
new file mode 100644
index 0000000..3adbd4d
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/net/ChartDataLoaderCompat.java
@@ -0,0 +1,146 @@
+/*
+ * 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.settingslib.net;
+
+import static android.net.NetworkStats.SET_DEFAULT;
+import static android.net.NetworkStats.SET_FOREGROUND;
+import static android.net.NetworkStats.TAG_NONE;
+import static android.net.NetworkStatsHistory.FIELD_RX_BYTES;
+import static android.net.NetworkStatsHistory.FIELD_TX_BYTES;
+import static android.text.format.DateUtils.HOUR_IN_MILLIS;
+
+import android.content.Context;
+import android.net.INetworkStatsSession;
+import android.net.NetworkStatsHistory;
+import android.net.NetworkTemplate;
+import android.os.Bundle;
+import android.os.RemoteException;
+
+import com.android.settingslib.AppItem;
+
+import androidx.loader.content.AsyncTaskLoader;
+
+/**
+ * Loader for historical chart data for both network and UID details.
+ */
+public class ChartDataLoaderCompat extends AsyncTaskLoader<ChartData> {
+ private static final String KEY_TEMPLATE = "template";
+ private static final String KEY_APP = "app";
+ private static final String KEY_FIELDS = "fields";
+
+ private final INetworkStatsSession mSession;
+ private final Bundle mArgs;
+
+ public static Bundle buildArgs(NetworkTemplate template, AppItem app) {
+ return buildArgs(template, app, FIELD_RX_BYTES | FIELD_TX_BYTES);
+ }
+
+ public static Bundle buildArgs(NetworkTemplate template, AppItem app, int fields) {
+ final Bundle args = new Bundle();
+ args.putParcelable(KEY_TEMPLATE, template);
+ args.putParcelable(KEY_APP, app);
+ args.putInt(KEY_FIELDS, fields);
+ return args;
+ }
+
+ public ChartDataLoaderCompat(Context context, INetworkStatsSession session, Bundle args) {
+ super(context);
+ mSession = session;
+ mArgs = args;
+ }
+
+ @Override
+ protected void onStartLoading() {
+ super.onStartLoading();
+ forceLoad();
+ }
+
+ @Override
+ public ChartData loadInBackground() {
+ final NetworkTemplate template = mArgs.getParcelable(KEY_TEMPLATE);
+ final AppItem app = mArgs.getParcelable(KEY_APP);
+ final int fields = mArgs.getInt(KEY_FIELDS);
+
+ try {
+ return loadInBackground(template, app, fields);
+ } catch (RemoteException e) {
+ // since we can't do much without history, and we don't want to
+ // leave with half-baked UI, we bail hard.
+ throw new RuntimeException("problem reading network stats", e);
+ }
+ }
+
+ private ChartData loadInBackground(NetworkTemplate template, AppItem app, int fields)
+ throws RemoteException {
+ final ChartData data = new ChartData();
+ data.network = mSession.getHistoryForNetwork(template, fields);
+
+ if (app != null) {
+ // load stats for current uid and template
+ final int size = app.uids.size();
+ for (int i = 0; i < size; i++) {
+ final int uid = app.uids.keyAt(i);
+ data.detailDefault = collectHistoryForUid(
+ template, uid, SET_DEFAULT, data.detailDefault);
+ data.detailForeground = collectHistoryForUid(
+ template, uid, SET_FOREGROUND, data.detailForeground);
+ }
+
+ if (size > 0) {
+ data.detail = new NetworkStatsHistory(data.detailForeground.getBucketDuration());
+ data.detail.recordEntireHistory(data.detailDefault);
+ data.detail.recordEntireHistory(data.detailForeground);
+ } else {
+ data.detailDefault = new NetworkStatsHistory(HOUR_IN_MILLIS);
+ data.detailForeground = new NetworkStatsHistory(HOUR_IN_MILLIS);
+ data.detail = new NetworkStatsHistory(HOUR_IN_MILLIS);
+ }
+ }
+
+ return data;
+ }
+
+ @Override
+ protected void onStopLoading() {
+ super.onStopLoading();
+ cancelLoad();
+ }
+
+ @Override
+ protected void onReset() {
+ super.onReset();
+ cancelLoad();
+ }
+
+ /**
+ * Collect {@link NetworkStatsHistory} for the requested UID, combining with
+ * an existing {@link NetworkStatsHistory} if provided.
+ */
+ private NetworkStatsHistory collectHistoryForUid(
+ NetworkTemplate template, int uid, int set, NetworkStatsHistory existing)
+ throws RemoteException {
+ final NetworkStatsHistory history = mSession.getHistoryForUid(
+ template, uid, set, TAG_NONE, FIELD_RX_BYTES | FIELD_TX_BYTES);
+
+ if (existing != null) {
+ existing.recordEntireHistory(history);
+ return existing;
+ } else {
+ return history;
+ }
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java
index f7aa297..87f5b4f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java
@@ -41,6 +41,7 @@
import android.util.Pair;
import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
import java.time.ZonedDateTime;
import java.util.Date;
@@ -86,7 +87,8 @@
* mContext.getResources().getInteger(R.integer.default_data_warning_level_mb);
}
- private INetworkStatsSession getSession() {
+ @VisibleForTesting
+ INetworkStatsSession getSession() {
if (mSession == null) {
try {
mSession = mStatsService.openSession();
@@ -176,6 +178,30 @@
}
}
+ /**
+ * Get the total usage level recorded in the network history
+ * @param template the network template to retrieve the network history
+ * @return the total usage level recorded in the network history
+ */
+ public long getHistoriclUsageLevel(NetworkTemplate template) {
+ final INetworkStatsSession session = getSession();
+ if (session != null) {
+ try {
+ final NetworkStatsHistory history = session.getHistoryForNetwork(template, FIELDS);
+ final long now = System.currentTimeMillis();
+ final NetworkStatsHistory.Entry entry =
+ history.getValues(0L /* start */, now /* end */, now, null /* recycle */);
+ if (entry != null) {
+ return entry.rxBytes + entry.txBytes;
+ }
+ Log.w(TAG, "Failed to get data usage, no entry data");
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to get data usage, remote call failed");
+ }
+ }
+ return 0L;
+ }
+
private NetworkPolicy findNetworkPolicy(NetworkTemplate template) {
if (mPolicyManager == null || template == null) return null;
final NetworkPolicy[] policies = mPolicyManager.getNetworkPolicies();
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/SummaryForAllUidLoaderCompat.java b/packages/SettingsLib/src/com/android/settingslib/net/SummaryForAllUidLoaderCompat.java
new file mode 100644
index 0000000..c311337
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/net/SummaryForAllUidLoaderCompat.java
@@ -0,0 +1,81 @@
+/*
+ * 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.settingslib.net;
+
+import android.content.Context;
+import android.net.INetworkStatsSession;
+import android.net.NetworkStats;
+import android.net.NetworkTemplate;
+import android.os.Bundle;
+import android.os.RemoteException;
+
+import androidx.loader.content.AsyncTaskLoader;
+
+public class SummaryForAllUidLoaderCompat extends AsyncTaskLoader<NetworkStats> {
+ private static final String KEY_TEMPLATE = "template";
+ private static final String KEY_START = "start";
+ private static final String KEY_END = "end";
+
+ private final INetworkStatsSession mSession;
+ private final Bundle mArgs;
+
+ public static Bundle buildArgs(NetworkTemplate template, long start, long end) {
+ final Bundle args = new Bundle();
+ args.putParcelable(KEY_TEMPLATE, template);
+ args.putLong(KEY_START, start);
+ args.putLong(KEY_END, end);
+ return args;
+ }
+
+ public SummaryForAllUidLoaderCompat(Context context, INetworkStatsSession session,
+ Bundle args) {
+ super(context);
+ mSession = session;
+ mArgs = args;
+ }
+
+ @Override
+ protected void onStartLoading() {
+ super.onStartLoading();
+ forceLoad();
+ }
+
+ @Override
+ public NetworkStats loadInBackground() {
+ final NetworkTemplate template = mArgs.getParcelable(KEY_TEMPLATE);
+ final long start = mArgs.getLong(KEY_START);
+ final long end = mArgs.getLong(KEY_END);
+
+ try {
+ return mSession.getSummaryForAllUid(template, start, end, false);
+ } catch (RemoteException e) {
+ return null;
+ }
+ }
+
+ @Override
+ protected void onStopLoading() {
+ super.onStopLoading();
+ cancelLoad();
+ }
+
+ @Override
+ protected void onReset() {
+ super.onReset();
+ cancelLoad();
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionControllerMixinCompat.java b/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionControllerMixinCompat.java
new file mode 100644
index 0000000..1791217
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionControllerMixinCompat.java
@@ -0,0 +1,143 @@
+/*
+ * 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.settingslib.suggestions;
+
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.Bundle;
+import android.service.settings.suggestions.Suggestion;
+import android.util.Log;
+
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+import java.util.List;
+
+import androidx.annotation.Nullable;
+import androidx.lifecycle.OnLifecycleEvent;
+import androidx.loader.app.LoaderManager;
+import androidx.loader.content.Loader;
+
+/**
+ * Manages IPC communication to SettingsIntelligence for suggestion related services.
+ */
+public class SuggestionControllerMixinCompat implements
+ SuggestionController.ServiceConnectionListener, androidx.lifecycle.LifecycleObserver,
+ LoaderManager.LoaderCallbacks<List<Suggestion>> {
+
+ public interface SuggestionControllerHost {
+ /**
+ * Called when suggestion data fetching is ready.
+ */
+ void onSuggestionReady(List<Suggestion> data);
+
+ /**
+ * Returns {@link LoaderManager} associated with the host. If host is not attached to
+ * activity then return null.
+ */
+ @Nullable
+ LoaderManager getLoaderManager();
+ }
+
+ private static final String TAG = "SuggestionCtrlMixin";
+ private static final boolean DEBUG = false;
+
+ private final Context mContext;
+ private final SuggestionController mSuggestionController;
+ private final SuggestionControllerHost mHost;
+
+ private boolean mSuggestionLoaded;
+
+ public SuggestionControllerMixinCompat(Context context, SuggestionControllerHost host,
+ Lifecycle lifecycle, ComponentName componentName) {
+ mContext = context.getApplicationContext();
+ mHost = host;
+ mSuggestionController = new SuggestionController(mContext, componentName,
+ this /* serviceConnectionListener */);
+ if (lifecycle != null) {
+ lifecycle.addObserver(this);
+ }
+ }
+
+ @OnLifecycleEvent(Lifecycle.Event.ON_START)
+ public void onStart() {
+ if (DEBUG) {
+ Log.d(TAG, "SuggestionController started");
+ }
+ mSuggestionController.start();
+ }
+
+ @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
+ public void onStop() {
+ if (DEBUG) {
+ Log.d(TAG, "SuggestionController stopped.");
+ }
+ mSuggestionController.stop();
+ }
+
+ @Override
+ public void onServiceConnected() {
+ final LoaderManager loaderManager = mHost.getLoaderManager();
+ if (loaderManager != null) {
+ loaderManager.restartLoader(SuggestionLoader.LOADER_ID_SUGGESTIONS,
+ null /* args */, this /* callback */);
+ }
+ }
+
+ @Override
+ public void onServiceDisconnected() {
+ if (DEBUG) {
+ Log.d(TAG, "SuggestionService disconnected");
+ }
+ final LoaderManager loaderManager = mHost.getLoaderManager();
+ if (loaderManager != null) {
+ loaderManager.destroyLoader(SuggestionLoader.LOADER_ID_SUGGESTIONS);
+ }
+ }
+
+ @Override
+ public Loader<List<Suggestion>> onCreateLoader(int id, Bundle args) {
+ if (id == SuggestionLoader.LOADER_ID_SUGGESTIONS) {
+ mSuggestionLoaded = false;
+ return new SuggestionLoaderCompat(mContext, mSuggestionController);
+ }
+ throw new IllegalArgumentException("This loader id is not supported " + id);
+ }
+
+ @Override
+ public void onLoadFinished(Loader<List<Suggestion>> loader, List<Suggestion> data) {
+ mSuggestionLoaded = true;
+ mHost.onSuggestionReady(data);
+ }
+
+ @Override
+ public void onLoaderReset(Loader<List<Suggestion>> loader) {
+ mSuggestionLoaded = false;
+ }
+
+ public boolean isSuggestionLoaded() {
+ return mSuggestionLoaded;
+ }
+
+ public void dismissSuggestion(Suggestion suggestion) {
+ mSuggestionController.dismissSuggestions(suggestion);
+ }
+
+ public void launchSuggestion(Suggestion suggestion) {
+ mSuggestionController.launchSuggestion(suggestion);
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionList.java b/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionList.java
deleted file mode 100644
index a890920..0000000
--- a/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionList.java
+++ /dev/null
@@ -1,85 +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.settingslib.suggestions;
-
-import android.content.Intent;
-import android.util.ArrayMap;
-import android.util.ArraySet;
-
-import com.android.settingslib.drawer.Tile;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-public class SuggestionList {
- // Category -> list of suggestion map
- private final Map<SuggestionCategory, List<Tile>> mSuggestions;
-
- // A flatten list of all suggestions.
- private List<Tile> mSuggestionList;
-
- public SuggestionList() {
- mSuggestions = new ArrayMap<>();
- }
-
- public void addSuggestions(SuggestionCategory category, List<Tile> suggestions) {
- mSuggestions.put(category, suggestions);
- }
-
- public List<Tile> getSuggestions() {
- if (mSuggestionList != null) {
- return mSuggestionList;
- }
- mSuggestionList = new ArrayList<>();
- for (List<Tile> suggestions : mSuggestions.values()) {
- mSuggestionList.addAll(suggestions);
- }
- dedupeSuggestions(mSuggestionList);
- return mSuggestionList;
- }
-
- public boolean isExclusiveSuggestionCategory() {
- if (mSuggestions.size() != 1) {
- // If there is no category, or more than 1 category, it's not exclusive by definition.
- return false;
- }
- for (SuggestionCategory category : mSuggestions.keySet()) {
- if (category.exclusive) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * Filter suggestions list so they are all unique.
- */
- private void dedupeSuggestions(List<Tile> suggestions) {
- final Set<String> intents = new ArraySet<>();
- for (int i = suggestions.size() - 1; i >= 0; i--) {
- final Tile suggestion = suggestions.get(i);
- final String intentUri = suggestion.intent.toUri(Intent.URI_INTENT_SCHEME);
- if (intents.contains(intentUri)) {
- suggestions.remove(i);
- } else {
- intents.add(intentUri);
- }
- }
- }
-}
diff --git a/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionLoaderCompat.java b/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionLoaderCompat.java
new file mode 100644
index 0000000..066de19
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionLoaderCompat.java
@@ -0,0 +1,54 @@
+/*
+ * 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.settingslib.suggestions;
+
+import android.content.Context;
+import android.service.settings.suggestions.Suggestion;
+import android.util.Log;
+
+import com.android.settingslib.utils.AsyncLoaderCompat;
+
+import java.util.List;
+
+public class SuggestionLoaderCompat extends AsyncLoaderCompat<List<Suggestion>> {
+
+ public static final int LOADER_ID_SUGGESTIONS = 42;
+ private static final String TAG = "SuggestionLoader";
+
+ private final SuggestionController mSuggestionController;
+
+ public SuggestionLoaderCompat(Context context, SuggestionController controller) {
+ super(context);
+ mSuggestionController = controller;
+ }
+
+ @Override
+ protected void onDiscardResult(List<Suggestion> result) {
+
+ }
+
+ @Override
+ public List<Suggestion> loadInBackground() {
+ final List<Suggestion> data = mSuggestionController.getSuggestions();
+ if (data == null) {
+ Log.d(TAG, "data is null");
+ } else {
+ Log.d(TAG, "data size " + data.size());
+ }
+ return data;
+ }
+}
\ No newline at end of file
diff --git a/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionParser.java b/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionParser.java
deleted file mode 100644
index 8705c98..0000000
--- a/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionParser.java
+++ /dev/null
@@ -1,498 +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.settingslib.suggestions;
-
-import android.Manifest;
-import android.accounts.Account;
-import android.accounts.AccountManager;
-import android.annotation.RequiresPermission;
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.content.pm.PackageManager;
-import android.content.pm.UserInfo;
-import android.content.res.Resources;
-import android.net.ConnectivityManager;
-import android.net.NetworkInfo;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.provider.Settings;
-import androidx.annotation.VisibleForTesting;
-import android.text.TextUtils;
-import android.text.format.DateUtils;
-import android.util.ArrayMap;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.util.Pair;
-import android.util.Xml;
-import android.view.InflateException;
-
-import com.android.settingslib.drawer.Tile;
-import com.android.settingslib.drawer.TileUtils;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-
-public class SuggestionParser {
-
- private static final String TAG = "SuggestionParser";
-
- // If defined, only returns this suggestion if the feature is supported.
- public static final String META_DATA_REQUIRE_FEATURE = "com.android.settings.require_feature";
-
- // If defined, only display this optional step if an account of that type exists.
- private static final String META_DATA_REQUIRE_ACCOUNT = "com.android.settings.require_account";
-
- // If defined and not true, do not should optional step.
- private static final String META_DATA_IS_SUPPORTED = "com.android.settings.is_supported";
-
- // If defined, only display this optional step if the current user is of that type.
- private static final String META_DATA_REQUIRE_USER_TYPE =
- "com.android.settings.require_user_type";
-
- // If defined, only display this optional step if a connection is available.
- private static final String META_DATA_IS_CONNECTION_REQUIRED =
- "com.android.settings.require_connection";
-
- // The valid values that setup wizard recognizes for differentiating user types.
- private static final String META_DATA_PRIMARY_USER_TYPE_VALUE = "primary";
- private static final String META_DATA_ADMIN_USER_TYPE_VALUE = "admin";
- private static final String META_DATA_GUEST_USER_TYPE_VALUE = "guest";
- private static final String META_DATA_RESTRICTED_USER_TYPE_VALUE = "restricted";
-
- /**
- * Allows suggestions to appear after a certain number of days, and to re-appear if dismissed.
- * For instance:
- * 0,10
- * Will appear immediately, but if the user removes it, it will come back after 10 days.
- *
- * Another example:
- * 10,30
- * Will only show up after 10 days, and then again after 30.
- */
- public static final String META_DATA_DISMISS_CONTROL = "com.android.settings.dismiss";
-
- // Shared prefs keys for storing dismissed state.
- // Index into current dismissed state.
- public static final String SETUP_TIME = "_setup_time";
- private static final String IS_DISMISSED = "_is_dismissed";
-
- // Default dismiss control for smart suggestions.
- private static final String DEFAULT_SMART_DISMISS_CONTROL = "0";
-
- private final Context mContext;
- private final List<SuggestionCategory> mSuggestionList;
- private final ArrayMap<Pair<String, String>, Tile> mAddCache = new ArrayMap<>();
- private final SharedPreferences mSharedPrefs;
- private final String mDefaultDismissControl;
-
- public SuggestionParser(Context context, SharedPreferences sharedPrefs, int orderXml,
- String defaultDismissControl) {
- this(
- context,
- sharedPrefs,
- (List<SuggestionCategory>) new SuggestionOrderInflater(context).parse(orderXml),
- defaultDismissControl);
- }
-
- public SuggestionParser(Context context, SharedPreferences sharedPrefs, int orderXml) {
- this(context, sharedPrefs, orderXml, DEFAULT_SMART_DISMISS_CONTROL);
- }
-
- @VisibleForTesting
- public SuggestionParser(
- Context context,
- SharedPreferences sharedPrefs,
- List<SuggestionCategory> suggestionList,
- String defaultDismissControl) {
- mContext = context;
- mSuggestionList = suggestionList;
- mSharedPrefs = sharedPrefs;
- mDefaultDismissControl = defaultDismissControl;
- }
-
- public SuggestionList getSuggestions(boolean isSmartSuggestionEnabled) {
- final SuggestionList suggestionList = new SuggestionList();
- final int N = mSuggestionList.size();
- for (int i = 0; i < N; i++) {
- final SuggestionCategory category = mSuggestionList.get(i);
- if (category.exclusive && !isExclusiveCategoryExpired(category)) {
- // If suggestions from an exclusive category are present, parsing is stopped
- // and only suggestions from that category are displayed. Note that subsequent
- // exclusive categories are also ignored.
- final List<Tile> exclusiveSuggestions = new ArrayList<>();
-
- // Read suggestion and force isSmartSuggestion to be false so the rule defined
- // from each suggestion itself is used.
- readSuggestions(category, exclusiveSuggestions, false /* isSmartSuggestion */);
- if (!exclusiveSuggestions.isEmpty()) {
- final SuggestionList exclusiveList = new SuggestionList();
- exclusiveList.addSuggestions(category, exclusiveSuggestions);
- return exclusiveList;
- }
- } else {
- // Either the category is not exclusive, or the exclusiveness expired so we should
- // treat it as a normal category.
- final List<Tile> suggestions = new ArrayList<>();
- readSuggestions(category, suggestions, isSmartSuggestionEnabled);
- suggestionList.addSuggestions(category, suggestions);
- }
- }
- return suggestionList;
- }
-
- /**
- * Dismisses a suggestion, returns true if the suggestion has no more dismisses left and should
- * be disabled.
- */
- public boolean dismissSuggestion(Tile suggestion) {
- final String keyBase = suggestion.intent.getComponent().flattenToShortString();
- mSharedPrefs.edit()
- .putBoolean(keyBase + IS_DISMISSED, true)
- .commit();
- return true;
- }
-
- @VisibleForTesting
- public void filterSuggestions(
- List<Tile> suggestions, int countBefore, boolean isSmartSuggestionEnabled) {
- for (int i = countBefore; i < suggestions.size(); i++) {
- if (!isAvailable(suggestions.get(i)) ||
- !isSupported(suggestions.get(i)) ||
- !satisifesRequiredUserType(suggestions.get(i)) ||
- !satisfiesRequiredAccount(suggestions.get(i)) ||
- !satisfiesConnectivity(suggestions.get(i)) ||
- isDismissed(suggestions.get(i), isSmartSuggestionEnabled)) {
- suggestions.remove(i--);
- }
- }
- }
-
- @VisibleForTesting
- void readSuggestions(
- SuggestionCategory category, List<Tile> suggestions, boolean isSmartSuggestionEnabled) {
- int countBefore = suggestions.size();
- Intent intent = new Intent(Intent.ACTION_MAIN);
- intent.addCategory(category.category);
- if (category.pkg != null) {
- intent.setPackage(category.pkg);
- }
- TileUtils.getTilesForIntent(mContext, new UserHandle(UserHandle.myUserId()), intent,
- mAddCache, null, suggestions, true, false, false, true /* shouldUpdateTiles */);
- filterSuggestions(suggestions, countBefore, isSmartSuggestionEnabled);
- if (!category.multiple && suggestions.size() > (countBefore + 1)) {
- // If there are too many, remove them all and only re-add the one with the highest
- // priority.
- Tile item = suggestions.remove(suggestions.size() - 1);
- while (suggestions.size() > countBefore) {
- Tile last = suggestions.remove(suggestions.size() - 1);
- if (last.priority > item.priority) {
- item = last;
- }
- }
- // If category is marked as done, do not add any item.
- if (!isCategoryDone(category.category)) {
- suggestions.add(item);
- }
- }
- }
-
- private boolean isAvailable(Tile suggestion) {
- final String featuresRequired = suggestion.metaData.getString(META_DATA_REQUIRE_FEATURE);
- if (featuresRequired != null) {
- for (String feature : featuresRequired.split(",")) {
- if (TextUtils.isEmpty(feature)) {
- Log.w(TAG, "Found empty substring when parsing required features: "
- + featuresRequired);
- } else if (!mContext.getPackageManager().hasSystemFeature(feature)) {
- Log.i(TAG, suggestion.title + " requires unavailable feature " + feature);
- return false;
- }
- }
- }
- return true;
- }
-
- @RequiresPermission(Manifest.permission.MANAGE_USERS)
- private boolean satisifesRequiredUserType(Tile suggestion) {
- final String requiredUser = suggestion.metaData.getString(META_DATA_REQUIRE_USER_TYPE);
- if (requiredUser != null) {
- final UserManager userManager = mContext.getSystemService(UserManager.class);
- UserInfo userInfo = userManager.getUserInfo(UserHandle.myUserId());
- for (String userType : requiredUser.split("\\|")) {
- final boolean primaryUserCondtionMet = userInfo.isPrimary()
- && META_DATA_PRIMARY_USER_TYPE_VALUE.equals(userType);
- final boolean adminUserConditionMet = userInfo.isAdmin()
- && META_DATA_ADMIN_USER_TYPE_VALUE.equals(userType);
- final boolean guestUserCondtionMet = userInfo.isGuest()
- && META_DATA_GUEST_USER_TYPE_VALUE.equals(userType);
- final boolean restrictedUserCondtionMet = userInfo.isRestricted()
- && META_DATA_RESTRICTED_USER_TYPE_VALUE.equals(userType);
- if (primaryUserCondtionMet || adminUserConditionMet || guestUserCondtionMet
- || restrictedUserCondtionMet) {
- return true;
- }
- }
- Log.i(TAG, suggestion.title + " requires user type " + requiredUser);
- return false;
- }
- return true;
- }
-
- public boolean satisfiesRequiredAccount(Tile suggestion) {
- final String requiredAccountType = suggestion.metaData.getString(META_DATA_REQUIRE_ACCOUNT);
- if (requiredAccountType == null) {
- return true;
- }
- AccountManager accountManager = mContext.getSystemService(AccountManager.class);
- Account[] accounts = accountManager.getAccountsByType(requiredAccountType);
- boolean satisfiesRequiredAccount = accounts.length > 0;
- if (!satisfiesRequiredAccount) {
- Log.i(TAG, suggestion.title + " requires unavailable account type "
- + requiredAccountType);
- }
- return satisfiesRequiredAccount;
- }
-
- public boolean isSupported(Tile suggestion) {
- final int isSupportedResource = suggestion.metaData.getInt(META_DATA_IS_SUPPORTED);
- try {
- if (suggestion.intent == null) {
- return false;
- }
- final Resources res = mContext.getPackageManager().getResourcesForActivity(
- suggestion.intent.getComponent());
- boolean isSupported =
- isSupportedResource != 0 ? res.getBoolean(isSupportedResource) : true;
- if (!isSupported) {
- Log.i(TAG, suggestion.title + " requires unsupported resource "
- + isSupportedResource);
- }
- return isSupported;
- } catch (PackageManager.NameNotFoundException e) {
- Log.w(TAG, "Cannot find resources for " + suggestion.intent.getComponent());
- return false;
- } catch (Resources.NotFoundException e) {
- Log.w(TAG, "Cannot find resources for " + suggestion.intent.getComponent(), e);
- return false;
- }
- }
-
- private boolean satisfiesConnectivity(Tile suggestion) {
- final boolean isConnectionRequired =
- suggestion.metaData.getBoolean(META_DATA_IS_CONNECTION_REQUIRED);
- if (!isConnectionRequired) {
- return true;
- }
- ConnectivityManager cm =
- (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
- NetworkInfo netInfo = cm.getActiveNetworkInfo();
- boolean satisfiesConnectivity = netInfo != null && netInfo.isConnectedOrConnecting();
- if (!satisfiesConnectivity) {
- Log.i(TAG, suggestion.title + " is missing required connection.");
- }
- return satisfiesConnectivity;
- }
-
- public boolean isCategoryDone(String category) {
- String name = Settings.Secure.COMPLETED_CATEGORY_PREFIX + category;
- return Settings.Secure.getInt(mContext.getContentResolver(), name, 0) != 0;
- }
-
- public void markCategoryDone(String category) {
- String name = Settings.Secure.COMPLETED_CATEGORY_PREFIX + category;
- Settings.Secure.putInt(mContext.getContentResolver(), name, 1);
- }
-
- /**
- * Whether or not the category's exclusiveness has expired.
- */
- private boolean isExclusiveCategoryExpired(SuggestionCategory category) {
- final String keySetupTime = category.category + SETUP_TIME;
- final long currentTime = System.currentTimeMillis();
- if (!mSharedPrefs.contains(keySetupTime)) {
- mSharedPrefs.edit()
- .putLong(keySetupTime, currentTime)
- .commit();
- }
- if (category.exclusiveExpireDaysInMillis < 0) {
- // negative means never expires
- return false;
- }
- final long setupTime = mSharedPrefs.getLong(keySetupTime, 0);
- final long elapsedTime = currentTime - setupTime;
- Log.d(TAG, "Day " + elapsedTime / DateUtils.DAY_IN_MILLIS + " for " + category.category);
- return elapsedTime > category.exclusiveExpireDaysInMillis;
- }
-
- @VisibleForTesting
- boolean isDismissed(Tile suggestion, boolean isSmartSuggestionEnabled) {
- String dismissControl = getDismissControl(suggestion, isSmartSuggestionEnabled);
- String keyBase = suggestion.intent.getComponent().flattenToShortString();
- if (!mSharedPrefs.contains(keyBase + SETUP_TIME)) {
- mSharedPrefs.edit()
- .putLong(keyBase + SETUP_TIME, System.currentTimeMillis())
- .commit();
- }
- // Check if it's already manually dismissed
- final boolean isDismissed = mSharedPrefs.getBoolean(keyBase + IS_DISMISSED, false);
- if (isDismissed) {
- return true;
- }
- if (dismissControl == null) {
- return false;
- }
- // Parse when suggestion should first appear. return true to artificially hide suggestion
- // before then.
- int firstAppearDay = parseDismissString(dismissControl);
- long firstAppearDayInMs = getEndTime(mSharedPrefs.getLong(keyBase + SETUP_TIME, 0),
- firstAppearDay);
- if (System.currentTimeMillis() >= firstAppearDayInMs) {
- // Dismiss timeout has passed, undismiss it.
- mSharedPrefs.edit()
- .putBoolean(keyBase + IS_DISMISSED, false)
- .commit();
- return false;
- }
- return true;
- }
-
- private long getEndTime(long startTime, int daysDelay) {
- long days = daysDelay * DateUtils.DAY_IN_MILLIS;
- return startTime + days;
- }
-
- /**
- * Parse the first int from a string formatted as "0,1,2..."
- * The value means suggestion should first appear on Day X.
- */
- private int parseDismissString(String dismissControl) {
- final String[] dismissStrs = dismissControl.split(",");
- return Integer.parseInt(dismissStrs[0]);
- }
-
- private String getDismissControl(Tile suggestion, boolean isSmartSuggestionEnabled) {
- if (isSmartSuggestionEnabled) {
- return mDefaultDismissControl;
- } else {
- return suggestion.metaData.getString(META_DATA_DISMISS_CONTROL);
- }
- }
-
- private static class SuggestionOrderInflater {
- private static final String TAG_LIST = "optional-steps";
- private static final String TAG_ITEM = "step";
-
- private static final String ATTR_CATEGORY = "category";
- private static final String ATTR_PACKAGE = "package";
- private static final String ATTR_MULTIPLE = "multiple";
- private static final String ATTR_EXCLUSIVE = "exclusive";
- private static final String ATTR_EXCLUSIVE_EXPIRE_DAYS = "exclusiveExpireDays";
-
- private final Context mContext;
-
- public SuggestionOrderInflater(Context context) {
- mContext = context;
- }
-
- public Object parse(int resource) {
- XmlPullParser parser = mContext.getResources().getXml(resource);
- final AttributeSet attrs = Xml.asAttributeSet(parser);
- try {
- // Look for the root node.
- int type;
- do {
- type = parser.next();
- } while (type != XmlPullParser.START_TAG && type != XmlPullParser.END_DOCUMENT);
-
- if (type != XmlPullParser.START_TAG) {
- throw new InflateException(parser.getPositionDescription()
- + ": No start tag found!");
- }
-
- // Temp is the root that was found in the xml
- Object xmlRoot = onCreateItem(parser.getName(), attrs);
-
- // Inflate all children under temp
- rParse(parser, xmlRoot, attrs);
- return xmlRoot;
- } catch (XmlPullParserException | IOException e) {
- Log.w(TAG, "Problem parser resource " + resource, e);
- return null;
- }
- }
-
- /**
- * Recursive method used to descend down the xml hierarchy and instantiate
- * items, instantiate their children.
- */
- private void rParse(XmlPullParser parser, Object parent, final AttributeSet attrs)
- throws XmlPullParserException, IOException {
- final int depth = parser.getDepth();
-
- int type;
- while (((type = parser.next()) != XmlPullParser.END_TAG ||
- parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
- if (type != XmlPullParser.START_TAG) {
- continue;
- }
-
- final String name = parser.getName();
-
- Object item = onCreateItem(name, attrs);
- onAddChildItem(parent, item);
- rParse(parser, item, attrs);
- }
- }
-
- protected void onAddChildItem(Object parent, Object child) {
- if (parent instanceof List<?> && child instanceof SuggestionCategory) {
- ((List<SuggestionCategory>) parent).add((SuggestionCategory) child);
- } else {
- throw new IllegalArgumentException("Parent was not a list");
- }
- }
-
- protected Object onCreateItem(String name, AttributeSet attrs) {
- if (name.equals(TAG_LIST)) {
- return new ArrayList<SuggestionCategory>();
- } else if (name.equals(TAG_ITEM)) {
- SuggestionCategory category = new SuggestionCategory();
- category.category = attrs.getAttributeValue(null, ATTR_CATEGORY);
- category.pkg = attrs.getAttributeValue(null, ATTR_PACKAGE);
- String multiple = attrs.getAttributeValue(null, ATTR_MULTIPLE);
- category.multiple = !TextUtils.isEmpty(multiple) && Boolean.parseBoolean(multiple);
- String exclusive = attrs.getAttributeValue(null, ATTR_EXCLUSIVE);
- category.exclusive =
- !TextUtils.isEmpty(exclusive) && Boolean.parseBoolean(exclusive);
- String expireDaysAttr = attrs.getAttributeValue(null,
- ATTR_EXCLUSIVE_EXPIRE_DAYS);
- long expireDays = !TextUtils.isEmpty(expireDaysAttr)
- ? Integer.parseInt(expireDaysAttr)
- : -1;
- category.exclusiveExpireDaysInMillis = DateUtils.DAY_IN_MILLIS * expireDays;
- return category;
- } else {
- throw new IllegalArgumentException("Unknown item " + name);
- }
- }
- }
-}
-
diff --git a/packages/SettingsLib/src/com/android/settingslib/utils/AsyncLoaderCompat.java b/packages/SettingsLib/src/com/android/settingslib/utils/AsyncLoaderCompat.java
new file mode 100644
index 0000000..916d7e3
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/utils/AsyncLoaderCompat.java
@@ -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.
+ */
+
+package com.android.settingslib.utils;
+
+import android.content.Context;
+
+import androidx.loader.content.AsyncTaskLoader;
+
+/**
+ * This class fills in some boilerplate for AsyncTaskLoader to actually load things.
+ *
+ * Subclasses need to implement {@link AsyncLoaderCompat#loadInBackground()} to perform the actual
+ * background task, and {@link AsyncLoaderCompat#onDiscardResult(T)} to clean up previously loaded
+ * results.
+ *
+ * This loader is based on the MailAsyncTaskLoader from the AOSP EmailUnified repo.
+ *
+ * @param <T> the data type to be loaded.
+ */
+public abstract class AsyncLoaderCompat<T> extends AsyncTaskLoader<T> {
+ private T mResult;
+
+ public AsyncLoaderCompat(final Context context) {
+ super(context);
+ }
+
+ @Override
+ protected void onStartLoading() {
+ if (mResult != null) {
+ deliverResult(mResult);
+ }
+
+ if (takeContentChanged() || mResult == null) {
+ forceLoad();
+ }
+ }
+
+ @Override
+ protected void onStopLoading() {
+ cancelLoad();
+ }
+
+ @Override
+ public void deliverResult(final T data) {
+ if (isReset()) {
+ if (data != null) {
+ onDiscardResult(data);
+ }
+ return;
+ }
+
+ final T oldResult = mResult;
+ mResult = data;
+
+ if (isStarted()) {
+ super.deliverResult(data);
+ }
+
+ if (oldResult != null && oldResult != mResult) {
+ onDiscardResult(oldResult);
+ }
+ }
+
+ @Override
+ protected void onReset() {
+ super.onReset();
+
+ onStopLoading();
+
+ if (mResult != null) {
+ onDiscardResult(mResult);
+ }
+ mResult = null;
+ }
+
+ @Override
+ public void onCanceled(final T data) {
+ super.onCanceled(data);
+
+ if (data != null) {
+ onDiscardResult(data);
+ }
+ }
+
+ /**
+ * Called when discarding the load results so subclasses can take care of clean-up or
+ * recycling tasks. This is not called if the same result (by way of pointer equality) is
+ * returned again by a subsequent call to loadInBackground, or if result is null.
+ *
+ * Note that this may be called concurrently with loadInBackground(), and in some circumstances
+ * may be called more than once for a given object.
+ *
+ * @param result The value returned from {@link AsyncLoaderCompat#loadInBackground()} which
+ * is to be discarded.
+ */
+ protected abstract void onDiscardResult(T result);
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/widget/FooterPreferenceMixinCompat.java b/packages/SettingsLib/src/com/android/settingslib/widget/FooterPreferenceMixinCompat.java
new file mode 100644
index 0000000..260ac83
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/widget/FooterPreferenceMixinCompat.java
@@ -0,0 +1,72 @@
+/*
+ * 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.settingslib.widget;
+
+import android.content.Context;
+
+import com.android.settingslib.core.lifecycle.Lifecycle;
+import com.android.settingslib.core.lifecycle.LifecycleObserver;
+import com.android.settingslib.core.lifecycle.events.SetPreferenceScreen;
+
+import androidx.preference.PreferenceFragmentCompat;
+import androidx.preference.PreferenceScreen;
+
+public class FooterPreferenceMixinCompat implements LifecycleObserver, SetPreferenceScreen {
+
+ private final PreferenceFragmentCompat mFragment;
+ private FooterPreference mFooterPreference;
+
+ public FooterPreferenceMixinCompat(PreferenceFragmentCompat fragment, Lifecycle lifecycle) {
+ mFragment = fragment;
+ lifecycle.addObserver(this);
+ }
+
+ @Override
+ public void setPreferenceScreen(PreferenceScreen preferenceScreen) {
+ if (mFooterPreference != null) {
+ preferenceScreen.addPreference(mFooterPreference);
+ }
+ }
+
+ /**
+ * Creates a new {@link FooterPreference}.
+ */
+ public FooterPreference createFooterPreference() {
+ final PreferenceScreen screen = mFragment.getPreferenceScreen();
+ if (mFooterPreference != null && screen != null) {
+ screen.removePreference(mFooterPreference);
+ }
+ mFooterPreference = new FooterPreference(getPrefContext());
+
+ if (screen != null) {
+ screen.addPreference(mFooterPreference);
+ }
+ return mFooterPreference;
+ }
+
+ /**
+ * Returns an UI context with theme properly set for new Preference objects.
+ */
+ private Context getPrefContext() {
+ return mFragment.getPreferenceManager().getContext();
+ }
+
+ public boolean hasFooter() {
+ return mFooterPreference != null;
+ }
+}
+
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index b9c7601..18a44ed 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -756,10 +756,7 @@
}
public CharSequence getSsid() {
- final SpannableString str = new SpannableString(ssid);
- str.setSpan(new TtsSpan.TelephoneBuilder(ssid).build(), 0, ssid.length(),
- Spannable.SPAN_INCLUSIVE_INCLUSIVE);
- return str;
+ return ssid;
}
public String getConfigName() {
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiSavedConfigUtils.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiSavedConfigUtils.java
index 159b2a1..19e3808 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiSavedConfigUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiSavedConfigUtils.java
@@ -55,8 +55,10 @@
try {
List<PasspointConfiguration> savedPasspointConfigs =
wifiManager.getPasspointConfigurations();
- for (PasspointConfiguration config : savedPasspointConfigs) {
- savedConfigs.add(new AccessPoint(context, config));
+ if (savedPasspointConfigs != null) {
+ for (PasspointConfiguration config : savedPasspointConfigs) {
+ savedConfigs.add(new AccessPoint(context, config));
+ }
}
} catch (UnsupportedOperationException e) {
// Passpoint not supported.
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java
index e435a72..0324799 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java
@@ -104,18 +104,13 @@
}
@Test
- public void testSsidIsTelephoneSpan() {
+ public void testSsidIsSpannableString_returnFalse() {
final Bundle bundle = new Bundle();
bundle.putString("key_ssid", TEST_SSID);
final AccessPoint ap = new AccessPoint(InstrumentationRegistry.getTargetContext(), bundle);
final CharSequence ssid = ap.getSsid();
- assertThat(ssid instanceof SpannableString).isTrue();
-
- TtsSpan[] spans = ((SpannableString) ssid).getSpans(0, TEST_SSID.length(), TtsSpan.class);
-
- assertThat(spans.length).isEqualTo(1);
- assertThat(spans[0].getType()).isEqualTo(TtsSpan.TYPE_TELEPHONE);
+ assertThat(ssid instanceof SpannableString).isFalse();
}
@Test
diff --git a/packages/SettingsLib/tests/robotests/res/xml/suggestion_ordering.xml b/packages/SettingsLib/tests/robotests/res/xml/suggestion_ordering.xml
deleted file mode 100644
index f02ac15..0000000
--- a/packages/SettingsLib/tests/robotests/res/xml/suggestion_ordering.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<optional-steps>
- <step category="com.android.settings.suggested.category.DEFERRED_SETUP"
- exclusive="true" />
- <step category="com.android.settings.suggested.category.LOCK_SCREEN" />
- <step category="com.android.settings.suggested.category.TRUST_AGENT" />
- <step category="com.android.settings.suggested.category.EMAIL" />
- <step category="com.android.settings.suggested.category.PARTNER_ACCOUNT"
- multiple="true" />
- <step category="com.android.settings.suggested.category.GESTURE" />
- <step category="com.android.settings.suggested.category.HOTWORD" />
- <step category="com.android.settings.suggested.category.DEFAULT"
- multiple="true" />
- <step category="com.android.settings.suggested.category.SETTINGS_ONLY"
- multiple="true" />
-</optional-steps>
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/CustomEditTextPreferenceComaptTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/CustomEditTextPreferenceComaptTest.java
new file mode 100644
index 0000000..9ba9967
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/CustomEditTextPreferenceComaptTest.java
@@ -0,0 +1,77 @@
+/*
+ * 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.settingslib;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.view.View;
+import android.widget.EditText;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.util.ReflectionHelpers;
+
+@RunWith(SettingsLibRobolectricTestRunner.class)
+public class CustomEditTextPreferenceComaptTest {
+
+ @Mock
+ private View mView;
+
+ private TestPreference mPreference;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mPreference = new TestPreference(RuntimeEnvironment.application);
+ }
+
+ @Test
+ public void bindDialogView_shouldRequestFocus() {
+ final String testText = "";
+ final EditText editText = spy(new EditText(RuntimeEnvironment.application));
+ editText.setText(testText);
+ when(mView.findViewById(android.R.id.edit)).thenReturn(editText);
+
+ mPreference.onBindDialogView(mView);
+
+ verify(editText).requestFocus();
+ }
+
+ @Test
+ public void getEditText_noDialog_shouldNotCrash() {
+ ReflectionHelpers.setField(mPreference, "mFragment",
+ mock(CustomEditTextPreferenceCompat.CustomPreferenceDialogFragment.class));
+
+ mPreference.getEditText();
+
+ // no crash
+ }
+
+ private static class TestPreference extends CustomEditTextPreferenceCompat {
+ public TestPreference(Context context) {
+ super(context);
+ }
+ }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/lifecycle/LifecycleTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/lifecycle/LifecycleTest.java
index 52068e9..28828a0 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/lifecycle/LifecycleTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/lifecycle/LifecycleTest.java
@@ -18,11 +18,12 @@
import static androidx.lifecycle.Lifecycle.Event.ON_START;
import static com.google.common.truth.Truth.assertThat;
-import androidx.lifecycle.LifecycleOwner;
import android.content.Context;
+import android.os.Bundle;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
+import android.widget.LinearLayout;
import com.android.settingslib.SettingsLibRobolectricTestRunner;
import com.android.settingslib.core.lifecycle.events.OnAttach;
@@ -34,13 +35,15 @@
import com.android.settingslib.core.lifecycle.events.OnResume;
import com.android.settingslib.core.lifecycle.events.OnStart;
import com.android.settingslib.core.lifecycle.events.OnStop;
+import com.android.settingslib.testutils.FragmentTestUtils;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.Robolectric;
import org.robolectric.android.controller.ActivityController;
-import org.robolectric.android.controller.FragmentController;
+
+import androidx.lifecycle.LifecycleOwner;
@RunWith(SettingsLibRobolectricTestRunner.class)
public class LifecycleTest {
@@ -64,7 +67,7 @@
public TestFragment() {
mFragObserver = new TestObserver();
- getLifecycle().addObserver(mFragObserver);
+ getSettingsLifecycle().addObserver(mFragObserver);
}
}
@@ -74,9 +77,17 @@
public TestActivity() {
mActObserver = new TestObserver();
- getLifecycle().addObserver(mActObserver);
+ getSettingsLifecycle().addObserver(mActObserver);
}
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ LinearLayout view = new LinearLayout(this);
+ view.setId(1);
+
+ setContentView(view);
+ }
}
public static class TestObserver implements LifecycleObserver, OnAttach, OnStart, OnResume,
@@ -151,11 +162,9 @@
@Test
public void runThroughActivityLifecycles_shouldObserveEverything() {
ActivityController<TestActivity> ac = Robolectric.buildActivity(TestActivity.class);
- TestActivity activity = ac.get();
+ TestActivity activity = ac.setup().get();
- ac.start();
assertThat(activity.mActObserver.mOnStartObserved).isTrue();
- ac.resume();
assertThat(activity.mActObserver.mOnResumeObserved).isTrue();
activity.onCreateOptionsMenu(null);
assertThat(activity.mActObserver.mOnCreateOptionsMenuObserved).isTrue();
@@ -173,50 +182,50 @@
@Test
public void runThroughDialogFragmentLifecycles_shouldObserveEverything() {
- FragmentController<TestDialogFragment> fragmentController =
- Robolectric.buildFragment(TestDialogFragment.class);
- TestDialogFragment fragment = fragmentController.get();
+ final TestDialogFragment fragment = new TestDialogFragment();
+ FragmentTestUtils.startFragment(fragment);
- fragmentController.create().start().resume();
fragment.onCreateOptionsMenu(null, null);
fragment.onPrepareOptionsMenu(null);
fragment.onOptionsItemSelected(null);
- fragmentController.pause().stop().destroy();
+ assertThat(fragment.mFragObserver.mOnCreateOptionsMenuObserved).isTrue();
+ assertThat(fragment.mFragObserver.mOnPrepareOptionsMenuObserved).isTrue();
+ assertThat(fragment.mFragObserver.mOnOptionsItemSelectedObserved).isTrue();
assertThat(fragment.mFragObserver.mOnAttachObserved).isTrue();
assertThat(fragment.mFragObserver.mOnAttachHasContext).isTrue();
assertThat(fragment.mFragObserver.mOnStartObserved).isTrue();
assertThat(fragment.mFragObserver.mOnResumeObserved).isTrue();
+ fragment.onPause();
assertThat(fragment.mFragObserver.mOnPauseObserved).isTrue();
+ fragment.onStop();
assertThat(fragment.mFragObserver.mOnStopObserved).isTrue();
+ fragment.onDestroy();
assertThat(fragment.mFragObserver.mOnDestroyObserved).isTrue();
- assertThat(fragment.mFragObserver.mOnCreateOptionsMenuObserved).isTrue();
- assertThat(fragment.mFragObserver.mOnPrepareOptionsMenuObserved).isTrue();
- assertThat(fragment.mFragObserver.mOnOptionsItemSelectedObserved).isTrue();
}
@Test
public void runThroughFragmentLifecycles_shouldObserveEverything() {
- FragmentController<TestFragment> fragmentController =
- Robolectric.buildFragment(TestFragment.class);
- TestFragment fragment = fragmentController.get();
+ final TestFragment fragment = new TestFragment();
+ FragmentTestUtils.startFragment(fragment);
- fragmentController.create().start().resume();
fragment.onCreateOptionsMenu(null, null);
fragment.onPrepareOptionsMenu(null);
fragment.onOptionsItemSelected(null);
- fragmentController.pause().stop().destroy();
+ assertThat(fragment.mFragObserver.mOnCreateOptionsMenuObserved).isTrue();
+ assertThat(fragment.mFragObserver.mOnPrepareOptionsMenuObserved).isTrue();
+ assertThat(fragment.mFragObserver.mOnOptionsItemSelectedObserved).isTrue();
assertThat(fragment.mFragObserver.mOnAttachObserved).isTrue();
assertThat(fragment.mFragObserver.mOnAttachHasContext).isTrue();
assertThat(fragment.mFragObserver.mOnStartObserved).isTrue();
assertThat(fragment.mFragObserver.mOnResumeObserved).isTrue();
+ fragment.onPause();
assertThat(fragment.mFragObserver.mOnPauseObserved).isTrue();
+ fragment.onStop();
assertThat(fragment.mFragObserver.mOnStopObserved).isTrue();
+ fragment.onDestroy();
assertThat(fragment.mFragObserver.mOnDestroyObserved).isTrue();
- assertThat(fragment.mFragObserver.mOnCreateOptionsMenuObserved).isTrue();
- assertThat(fragment.mFragObserver.mOnPrepareOptionsMenuObserved).isTrue();
- assertThat(fragment.mFragObserver.mOnOptionsItemSelectedObserved).isTrue();
}
@Test
@@ -237,17 +246,16 @@
@Test
public void onOptionItemSelectedShortCircuitsIfAnObserverHandlesTheMenuItem() {
- FragmentController<TestFragment> fragmentController =
- Robolectric.buildFragment(TestFragment.class);
- TestFragment fragment = fragmentController.get();
- OptionItemAccepter accepter = new OptionItemAccepter();
+ final TestFragment fragment = new TestFragment();
+ FragmentTestUtils.startFragment(fragment);
+
+ final OptionItemAccepter accepter = new OptionItemAccepter();
fragment.getLifecycle().addObserver(accepter);
- fragmentController.create().start().resume();
+
fragment.onCreateOptionsMenu(null, null);
fragment.onPrepareOptionsMenu(null);
fragment.onOptionsItemSelected(null);
- fragmentController.pause().stop().destroy();
assertThat(accepter.wasCalled).isFalse();
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/WifiMacAddressPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/WifiMacAddressPreferenceControllerTest.java
index ddbcb87..757df5b 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/WifiMacAddressPreferenceControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/WifiMacAddressPreferenceControllerTest.java
@@ -16,11 +16,10 @@
package com.android.settingslib.deviceinfo;
+import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.spy;
import android.annotation.SuppressLint;
import android.content.Context;
@@ -40,6 +39,7 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
import java.util.Arrays;
import java.util.List;
@@ -48,12 +48,16 @@
@RunWith(SettingsLibRobolectricTestRunner.class)
public class WifiMacAddressPreferenceControllerTest {
@Mock
- private Context mContext;
- @Mock
private Lifecycle mLifecycle;
@Mock
private PreferenceScreen mScreen;
@Mock
+ private WifiManager mWifiManager;
+ @Mock
+ private WifiInfo mWifiInfo;
+
+ private AbstractWifiMacAddressPreferenceController mController;
+ private Context mContext;
private Preference mPreference;
private static final String TEST_MAC_ADDRESS = "00:11:22:33:44:55";
@@ -61,14 +65,20 @@
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
+
+ mContext = spy(RuntimeEnvironment.application);
+ mPreference = new Preference(mContext);
+
doReturn(mPreference).when(mScreen)
.findPreference(AbstractWifiMacAddressPreferenceController.KEY_WIFI_MAC_ADDRESS);
+ doReturn(mWifiManager).when(mContext).getSystemService(WifiManager.class);
+ doReturn(mWifiInfo).when(mWifiManager).getConnectionInfo();
+
+ mController = new ConcreteWifiMacAddressPreferenceController(mContext, mLifecycle);
}
@Test
public void testHasIntentFilters() {
- final AbstractWifiMacAddressPreferenceController wifiMacAddressPreferenceController =
- new ConcreteWifiMacAddressPreferenceController(mContext, mLifecycle);
final List<String> expectedIntents = Arrays.asList(
ConnectivityManager.CONNECTIVITY_ACTION,
WifiManager.LINK_CONFIGURATION_CHANGED_ACTION,
@@ -76,37 +86,110 @@
assertWithMessage("Intent filter should contain expected intents")
- .that(wifiMacAddressPreferenceController.getConnectivityIntents())
+ .that(mController.getConnectivityIntents())
.asList().containsAllIn(expectedIntents);
}
@Test
- public void testWifiMacAddress() {
- final WifiManager wifiManager = mock(WifiManager.class);
- final WifiInfo wifiInfo = mock(WifiInfo.class);
-
- doReturn(null).when(wifiManager).getConnectionInfo();
- doReturn(wifiManager).when(mContext).getSystemService(WifiManager.class);
-
- final AbstractWifiMacAddressPreferenceController wifiMacAddressPreferenceController =
- new ConcreteWifiMacAddressPreferenceController(mContext, mLifecycle);
-
- wifiMacAddressPreferenceController.displayPreference(mScreen);
- verify(mPreference).setSummary(R.string.status_unavailable);
-
- doReturn(wifiInfo).when(wifiManager).getConnectionInfo();
- doReturn(TEST_MAC_ADDRESS).when(wifiInfo).getMacAddress();
- wifiMacAddressPreferenceController.displayPreference(mScreen);
- verify(mPreference).setSummary(TEST_MAC_ADDRESS);
-
+ public void updateConnectivity_nullWifiInfoWithMacRandomizationOff_setMacUnavailable() {
Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED, 1);
- wifiMacAddressPreferenceController.displayPreference(mScreen);
- verify(mPreference, times(2)).setSummary(TEST_MAC_ADDRESS);
+ Settings.Global.WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED,
+ AbstractWifiMacAddressPreferenceController.OFF);
+ doReturn(null).when(mWifiManager).getConnectionInfo();
- doReturn(WifiInfo.DEFAULT_MAC_ADDRESS).when(wifiInfo).getMacAddress();
- wifiMacAddressPreferenceController.displayPreference(mScreen);
- verify(mPreference).setSummary(R.string.wifi_status_mac_randomized);
+ mController.displayPreference(mScreen);
+
+ assertThat(mPreference.getSummary())
+ .isEqualTo(mContext.getString(R.string.status_unavailable));
+ }
+
+ @Test
+ public void updateConnectivity_nullMacWithMacRandomizationOff_setMacUnavailable() {
+ Settings.Global.putInt(mContext.getContentResolver(),
+ Settings.Global.WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED,
+ AbstractWifiMacAddressPreferenceController.OFF);
+ doReturn(null).when(mWifiInfo).getMacAddress();
+
+ mController.displayPreference(mScreen);
+
+ assertThat(mPreference.getSummary())
+ .isEqualTo(mContext.getString(R.string.status_unavailable));
+ }
+
+ @Test
+ public void updateConnectivity_defaultMacWithMacRandomizationOff_setMacUnavailable() {
+ Settings.Global.putInt(mContext.getContentResolver(),
+ Settings.Global.WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED,
+ AbstractWifiMacAddressPreferenceController.OFF);
+ doReturn(WifiInfo.DEFAULT_MAC_ADDRESS).when(mWifiInfo).getMacAddress();
+
+ mController.displayPreference(mScreen);
+
+ assertThat(mPreference.getSummary())
+ .isEqualTo(mContext.getString(R.string.status_unavailable));
+ }
+
+ @Test
+ public void updateConnectivity_validMacWithMacRandomizationOff_setValidMac() {
+ Settings.Global.putInt(mContext.getContentResolver(),
+ Settings.Global.WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED,
+ AbstractWifiMacAddressPreferenceController.OFF);
+ doReturn(TEST_MAC_ADDRESS).when(mWifiInfo).getMacAddress();
+
+ mController.displayPreference(mScreen);
+
+ assertThat(mPreference.getSummary()).isEqualTo(TEST_MAC_ADDRESS);
+ }
+
+ @Test
+ public void updateConnectivity_nullWifiInfoWithMacRandomizationOn_setMacUnavailable() {
+ Settings.Global.putInt(mContext.getContentResolver(),
+ Settings.Global.WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED,
+ AbstractWifiMacAddressPreferenceController.ON);
+ doReturn(null).when(mWifiManager).getConnectionInfo();
+
+ mController.displayPreference(mScreen);
+
+ assertThat(mPreference.getSummary())
+ .isEqualTo(mContext.getString(R.string.status_unavailable));
+ }
+
+ @Test
+ public void updateConnectivity_nullMacWithMacRandomizationOn_setMacUnavailable() {
+ Settings.Global.putInt(mContext.getContentResolver(),
+ Settings.Global.WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED,
+ AbstractWifiMacAddressPreferenceController.ON);
+ doReturn(null).when(mWifiInfo).getMacAddress();
+
+ mController.displayPreference(mScreen);
+
+ assertThat(mPreference.getSummary())
+ .isEqualTo(mContext.getString(R.string.status_unavailable));
+ }
+
+ @Test
+ public void updateConnectivity_defaultMacWithMacRandomizationOn_setMacRandomized() {
+ Settings.Global.putInt(mContext.getContentResolver(),
+ Settings.Global.WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED,
+ AbstractWifiMacAddressPreferenceController.ON);
+ doReturn(WifiInfo.DEFAULT_MAC_ADDRESS).when(mWifiInfo).getMacAddress();
+
+ mController.displayPreference(mScreen);
+
+ assertThat(mPreference.getSummary())
+ .isEqualTo(mContext.getString(R.string.wifi_status_mac_randomized));
+ }
+
+ @Test
+ public void updateConnectivity_validMacWithMacRandomizationOn_setValidMac() {
+ Settings.Global.putInt(mContext.getContentResolver(),
+ Settings.Global.WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED,
+ AbstractWifiMacAddressPreferenceController.ON);
+ doReturn(TEST_MAC_ADDRESS).when(mWifiInfo).getMacAddress();
+
+ mController.displayPreference(mScreen);
+
+ assertThat(mPreference.getSummary()).isEqualTo(TEST_MAC_ADDRESS);
}
private static class ConcreteWifiMacAddressPreferenceController
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileTest.java
new file mode 100644
index 0000000..996a122
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileTest.java
@@ -0,0 +1,48 @@
+package com.android.settingslib.drawer;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static com.android.settingslib.drawer.TileUtils.META_DATA_KEY_PROFILE;
+import static com.android.settingslib.drawer.TileUtils.PROFILE_ALL;
+import static com.android.settingslib.drawer.TileUtils.PROFILE_PRIMARY;
+
+import android.os.Bundle;
+import org.junit.Before;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.junit.Test;
+
+@RunWith(RobolectricTestRunner.class)
+public class TileTest {
+
+ private Tile mTile;
+
+ @Before
+ public void setUp() {
+ mTile = new Tile();
+ mTile.metaData = new Bundle();
+ }
+
+ @Test
+ public void isPrimaryProfileOnly_profilePrimary_shouldReturnTrue() {
+ mTile.metaData.putString(META_DATA_KEY_PROFILE, PROFILE_PRIMARY);
+ assertThat(mTile.isPrimaryProfileOnly()).isTrue();
+ }
+
+ @Test
+ public void isPrimaryProfileOnly_profileAll_shouldReturnFalse() {
+ mTile.metaData.putString(META_DATA_KEY_PROFILE, PROFILE_ALL);
+ assertThat(mTile.isPrimaryProfileOnly()).isFalse();
+ }
+
+ @Test
+ public void isPrimaryProfileOnly_noExplicitValue_shouldReturnFalse() {
+ assertThat(mTile.isPrimaryProfileOnly()).isFalse();
+ }
+
+ @Test
+ public void isPrimaryProfileOnly_nullMetadata_shouldReturnFalse() {
+ mTile.metaData = null;
+ assertThat(mTile.isPrimaryProfileOnly()).isFalse();
+ }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java
index fc1b2238..9df4318 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java
@@ -17,6 +17,7 @@
package com.android.settingslib.drawer;
import static com.google.common.truth.Truth.assertThat;
+
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
@@ -28,7 +29,6 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.robolectric.RuntimeEnvironment.application;
-import static org.robolectric.shadow.api.Shadow.extract;
import android.app.ActivityManager;
import android.content.ContentResolver;
@@ -52,8 +52,7 @@
import android.util.Pair;
import android.widget.RemoteViews;
-import com.android.settingslib.R;
-import com.android.settingslib.suggestions.SuggestionParser;
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
import org.junit.Before;
import org.junit.Test;
@@ -61,17 +60,15 @@
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
import java.util.Map;
-@RunWith(RobolectricTestRunner.class)
+@RunWith(SettingsLibRobolectricTestRunner.class)
@Config(shadows = TileUtilsTest.TileUtilsShadowRemoteViews.class)
public class TileUtilsTest {
@@ -164,35 +161,6 @@
}
@Test
- public void getTilesForIntent_shouldSkipFilteredApps() {
- Intent intent = new Intent();
- Map<Pair<String, String>, Tile> addedCache = new ArrayMap<>();
- List<Tile> outTiles = new ArrayList<>();
- List<ResolveInfo> info = new ArrayList<>();
- ResolveInfo resolveInfo = newInfo(true, null /* category */, null, URI_GET_ICON,
- URI_GET_SUMMARY);
- addMetadataToInfo(resolveInfo, "com.android.settings.require_account", "com.google");
- addMetadataToInfo(resolveInfo, "com.android.settings.require_connection", "true");
- info.add(resolveInfo);
-
- when(mPackageManager.queryIntentActivitiesAsUser(eq(intent), anyInt(), anyInt()))
- .thenReturn(info);
-
- TileUtils.getTilesForIntent(mContext, UserHandle.CURRENT, intent, addedCache,
- null /* defaultCategory */, outTiles, false /* usePriority */,
- false /* checkCategory */, true /* forceTintExternalIcon */);
-
- assertThat(outTiles.size()).isEqualTo(1);
- SuggestionParser parser = new SuggestionParser(
- mContext,
- null,
- Collections.emptyList(),
- "0,10");
- parser.filterSuggestions(outTiles, 0, false);
- assertThat(outTiles.size()).isEqualTo(0);
- }
-
- @Test
public void getCategories_shouldHandleExtraIntentAction() {
final String testCategory = "category1";
final String testAction = "action1";
@@ -210,13 +178,12 @@
.thenReturn(info);
List<DashboardCategory> categoryList = TileUtils.getCategories(
- mContext, cache, false /* categoryDefinedInManifest */, testAction,
- TileUtils.SETTING_PKG);
+ mContext, cache, testAction, TileUtils.SETTING_PKG);
assertThat(categoryList.get(0).getTile(0).category).isEqualTo(testCategory);
}
@Test
- public void getCategories_withPackageName() throws Exception {
+ public void getCategories_withPackageName() {
ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
Map<Pair<String, String>, Tile> cache = new ArrayMap<>();
Global.putInt(mContext.getContentResolver(), Global.DEVICE_PROVISIONED, 1);
@@ -226,9 +193,7 @@
userHandleList.add(new UserHandle(ActivityManager.getCurrentUser()));
when(mUserManager.getUserProfiles()).thenReturn(userHandleList);
- TileUtils.getCategories(
- mContext, cache, false /* categoryDefinedInManifest */, null /* action */,
- TileUtils.SETTING_PKG);
+ TileUtils.getCategories(mContext, cache, null /* action */, TileUtils.SETTING_PKG);
verify(mPackageManager, atLeastOnce()).queryIntentActivitiesAsUser(
intentCaptor.capture(), anyInt(), anyInt());
@@ -237,7 +202,7 @@
}
@Test
- public void getTilesForIntent_shouldReadMetadataTitleAsString() throws RemoteException {
+ public void getTilesForIntent_shouldReadMetadataTitleAsString() {
Intent intent = new Intent();
Map<Pair<String, String>, Tile> addedCache = new ArrayMap<>();
List<Tile> outTiles = new ArrayList<>();
@@ -258,7 +223,7 @@
}
@Test
- public void getTilesForIntent_shouldReadMetadataTitleFromResource() throws RemoteException {
+ public void getTilesForIntent_shouldReadMetadataTitleFromResource() {
Intent intent = new Intent();
Map<Pair<String, String>, Tile> addedCache = new ArrayMap<>();
List<Tile> outTiles = new ArrayList<>();
@@ -373,7 +338,7 @@
}
@Test
- public void getTilesForIntent_shouldProcessUriContentForSystemApp() throws RemoteException {
+ public void getTilesForIntent_shouldProcessUriContentForSystemApp() {
Intent intent = new Intent();
Map<Pair<String, String>, Tile> addedCache = new ArrayMap<>();
List<Tile> outTiles = new ArrayList<>();
@@ -392,108 +357,6 @@
assertThat(outTiles.size()).isEqualTo(1);
}
- @Test
- public void getTilesForIntent_shouldShowRemoteViewIfSpecified() {
- Intent intent = new Intent();
- Map<Pair<String, String>, Tile> addedCache = new ArrayMap<>();
- List<Tile> outTiles = new ArrayList<>();
- List<ResolveInfo> info = new ArrayList<>();
- ResolveInfo resolveInfo = newInfo(true, null /* category */);
- resolveInfo.activityInfo.metaData.putInt("com.android.settings.custom_view",
- R.layout.user_preference);
- info.add(resolveInfo);
-
- when(mPackageManager.queryIntentActivitiesAsUser(eq(intent), anyInt(), anyInt()))
- .thenReturn(info);
-
- TileUtils.getTilesForIntent(mContext, UserHandle.CURRENT, intent, addedCache,
- null /* defaultCategory */, outTiles, false /* usePriority */,
- false /* checkCategory */, true /* forceTintExternalIcon */);
-
- assertThat(outTiles.size()).isEqualTo(1);
- Tile tile = outTiles.get(0);
- assertThat(tile.remoteViews).isNotNull();
- assertThat(tile.remoteViews.getLayoutId()).isEqualTo(R.layout.user_preference);
- }
-
- @Test
- public void getTilesForIntent_summaryUriSpecified_shouldOverrideRemoteViewSummary()
- throws RemoteException {
- Intent intent = new Intent();
- Map<Pair<String, String>, Tile> addedCache = new ArrayMap<>();
- List<Tile> outTiles = new ArrayList<>();
- List<ResolveInfo> info = new ArrayList<>();
- ResolveInfo resolveInfo = newInfo(true, null /* category */, null,
- null, URI_GET_SUMMARY);
- resolveInfo.activityInfo.metaData.putInt("com.android.settings.custom_view",
- R.layout.user_preference);
- info.add(resolveInfo);
-
- when(mPackageManager.queryIntentActivitiesAsUser(eq(intent), anyInt(), anyInt()))
- .thenReturn(info);
-
- // Mock the content provider interaction.
- Bundle bundle = new Bundle();
- bundle.putString(TileUtils.META_DATA_PREFERENCE_SUMMARY, "new summary text");
- when(mIContentProvider.call(anyString(),
- eq(TileUtils.getMethodFromUri(Uri.parse(URI_GET_SUMMARY))), eq(URI_GET_SUMMARY),
- any())).thenReturn(bundle);
- when(mContentResolver.acquireUnstableProvider(anyString()))
- .thenReturn(mIContentProvider);
- when(mContentResolver.acquireUnstableProvider(any(Uri.class)))
- .thenReturn(mIContentProvider);
-
- TileUtils.getTilesForIntent(mContext, UserHandle.CURRENT, intent, addedCache,
- null /* defaultCategory */, outTiles, false /* usePriority */,
- false /* checkCategory */, true /* forceTintExternalIcon */);
-
- assertThat(outTiles.size()).isEqualTo(1);
- Tile tile = outTiles.get(0);
- assertThat(tile.remoteViews).isNotNull();
- assertThat(tile.remoteViews.getLayoutId()).isEqualTo(R.layout.user_preference);
- // Make sure the summary TextView got a new text string.
- TileUtilsShadowRemoteViews shadowRemoteViews = extract(tile.remoteViews);
- assertThat(shadowRemoteViews.overrideViewId).isEqualTo(android.R.id.summary);
- assertThat(shadowRemoteViews.overrideText).isEqualTo("new summary text");
- }
-
- @Test
- public void getTilesForIntent_providerUnavailable_shouldNotOverrideRemoteViewSummary()
- throws RemoteException {
- Intent intent = new Intent();
- Map<Pair<String, String>, Tile> addedCache = new ArrayMap<>();
- List<Tile> outTiles = new ArrayList<>();
- List<ResolveInfo> info = new ArrayList<>();
- ResolveInfo resolveInfo = newInfo(true, null /* category */, null,
- null, URI_GET_SUMMARY);
- resolveInfo.activityInfo.metaData.putInt("com.android.settings.custom_view",
- R.layout.user_preference);
- info.add(resolveInfo);
-
- when(mPackageManager.queryIntentActivitiesAsUser(eq(intent), anyInt(), anyInt()))
- .thenReturn(info);
-
- // Mock the content provider interaction.
- Bundle bundle = new Bundle();
- bundle.putString(TileUtils.META_DATA_PREFERENCE_SUMMARY, "new summary text");
- when(mIContentProvider.call(anyString(),
- eq(TileUtils.getMethodFromUri(Uri.parse(URI_GET_SUMMARY))), eq(URI_GET_SUMMARY),
- any())).thenReturn(bundle);
-
- TileUtils.getTilesForIntent(mContext, UserHandle.CURRENT, intent, addedCache,
- null /* defaultCategory */, outTiles, false /* usePriority */,
- false /* checkCategory */, true /* forceTintExternalIcon */);
-
- assertThat(outTiles.size()).isEqualTo(1);
- Tile tile = outTiles.get(0);
- assertThat(tile.remoteViews).isNotNull();
- assertThat(tile.remoteViews.getLayoutId()).isEqualTo(R.layout.user_preference);
- // Make sure the summary TextView didn't get any text view updates.
- TileUtilsShadowRemoteViews shadowRemoteViews = extract(tile.remoteViews);
- assertThat(shadowRemoteViews.overrideViewId).isNull();
- assertThat(shadowRemoteViews.overrideText).isNull();
- }
-
public static ResolveInfo newInfo(boolean systemApp, String category) {
return newInfo(systemApp, category, null);
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/PowerWhitelistBackendTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/PowerWhitelistBackendTest.java
index a23eebc..23087a9 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/PowerWhitelistBackendTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/PowerWhitelistBackendTest.java
@@ -21,8 +21,10 @@
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
+import android.app.admin.DevicePolicyManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
@@ -51,7 +53,8 @@
@Mock
private IDeviceIdleController mDeviceIdleService;
-
+ @Mock
+ private DevicePolicyManager mDevicePolicyManager;
private PowerWhitelistBackend mPowerWhitelistBackend;
private ShadowPackageManager mPackageManager;
private Context mContext;
@@ -59,7 +62,8 @@
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- mContext = RuntimeEnvironment.application;
+ mContext = spy(RuntimeEnvironment.application);
+ doReturn(mContext).when(mContext).getApplicationContext();
doReturn(new String[] {}).when(mDeviceIdleService).getFullPowerWhitelist();
doReturn(new String[] {}).when(mDeviceIdleService).getSystemPowerWhitelist();
doReturn(new String[] {}).when(mDeviceIdleService).getSystemPowerWhitelistExceptIdle();
@@ -67,6 +71,7 @@
doNothing().when(mDeviceIdleService).removePowerSaveWhitelistApp(anyString());
mPackageManager = Shadow.extract(mContext.getPackageManager());
mPackageManager.setSystemFeature(PackageManager.FEATURE_TELEPHONY, true);
+ doReturn(mDevicePolicyManager).when(mContext).getSystemService(DevicePolicyManager.class);
mPowerWhitelistBackend = new PowerWhitelistBackend(mContext, mDeviceIdleService);
}
@@ -123,6 +128,13 @@
}
@Test
+ public void isWhitelisted_shouldWhitelistActiveDeviceAdminApp() {
+ doReturn(true).when(mDevicePolicyManager).packageHasActiveAdmins(PACKAGE_ONE);
+
+ assertThat(mPowerWhitelistBackend.isWhitelisted(PACKAGE_ONE)).isTrue();
+ }
+
+ @Test
public void testIsSystemWhitelisted() throws Exception {
doReturn(new String[] {PACKAGE_ONE}).when(mDeviceIdleService).getSystemPowerWhitelist();
mPowerWhitelistBackend.refreshList();
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilCompatTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilCompatTest.java
new file mode 100644
index 0000000..ddadac1
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilCompatTest.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 com.android.settingslib.inputmethod;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.pm.ApplicationInfo;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.view.inputmethod.InputMethodInfo;
+import android.view.inputmethod.InputMethodSubtype;
+import android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+
+@RunWith(RobolectricTestRunner.class)
+public class InputMethodAndSubtypeUtilCompatTest {
+
+ private static final HashSet<String> EMPTY_STRING_SET = new HashSet<>();
+
+ private static HashSet<String> asHashSet(String... strings) {
+ HashSet<String> hashSet = new HashSet<>();
+ for (String s : strings) {
+ hashSet.add(s);
+ }
+ return hashSet;
+ }
+
+ @Test
+ public void parseInputMethodsAndSubtypesString_EmptyString() {
+ assertThat(InputMethodAndSubtypeUtilCompat.
+ parseInputMethodsAndSubtypesString("")).isEmpty();
+ assertThat(InputMethodAndSubtypeUtilCompat.
+ parseInputMethodsAndSubtypesString(null)).isEmpty();
+ }
+
+ @Test
+ public void parseInputMethodsAndSubtypesString_SingleImeNoSubtype() {
+ HashMap<String, HashSet<String>> r =
+ InputMethodAndSubtypeUtilCompat.parseInputMethodsAndSubtypesString("ime0");
+ assertThat(r).containsExactly("ime0", EMPTY_STRING_SET);
+ }
+
+ @Test
+ public void parseInputMethodsAndSubtypesString_MultipleImesNoSubtype() {
+ HashMap<String, HashSet<String>> r =
+ InputMethodAndSubtypeUtilCompat.parseInputMethodsAndSubtypesString("ime0:ime1");
+ assertThat(r).containsExactly("ime0", EMPTY_STRING_SET, "ime1", EMPTY_STRING_SET);
+ }
+
+ @Test
+ public void parseInputMethodsAndSubtypesString_SingleImeSingleSubtype() {
+ HashMap<String, HashSet<String>> r =
+ InputMethodAndSubtypeUtilCompat.parseInputMethodsAndSubtypesString("ime0;subtype0");
+ assertThat(r).containsExactly("ime0", asHashSet("subtype0"));
+ }
+
+ @Test
+ public void parseInputMethodsAndSubtypesString_SingleImeDuplicateSameSubtypes() {
+ HashMap<String, HashSet<String>> r =
+ InputMethodAndSubtypeUtilCompat.parseInputMethodsAndSubtypesString(
+ "ime0;subtype0;subtype0");
+ assertThat(r).containsExactly("ime0", asHashSet("subtype0"));
+ }
+
+ @Test
+ public void parseInputMethodsAndSubtypesString_SingleImeMultipleSubtypes() {
+ HashMap<String, HashSet<String>> r =
+ InputMethodAndSubtypeUtilCompat.parseInputMethodsAndSubtypesString(
+ "ime0;subtype0;subtype1");
+ assertThat(r).containsExactly("ime0", asHashSet("subtype0", "subtype1"));
+ }
+
+ @Test
+ public void parseInputMethodsAndSubtypesString_MultiplePairsOfImeSubtype() {
+ assertThat(InputMethodAndSubtypeUtilCompat.parseInputMethodsAndSubtypesString(
+ "ime0;subtype0:ime1;subtype1"))
+ .containsExactly("ime0", asHashSet("subtype0"), "ime1", asHashSet("subtype1"));
+ assertThat(InputMethodAndSubtypeUtilCompat.parseInputMethodsAndSubtypesString(
+ "ime0;subtype0;subtype1:ime1;subtype2"))
+ .containsExactly("ime0", asHashSet("subtype0", "subtype1"),
+ "ime1", asHashSet("subtype2"));
+ assertThat(InputMethodAndSubtypeUtilCompat.parseInputMethodsAndSubtypesString(
+ "ime0;subtype0;subtype1:ime1;subtype1;subtype2"))
+ .containsExactly("ime0", asHashSet("subtype0", "subtype1"),
+ "ime1", asHashSet("subtype1", "subtype2"));
+
+ }
+
+ @Test
+ public void parseInputMethodsAndSubtypesString_MixedImeSubtypePairsAndImeNoSubtype() {
+ HashMap<String, HashSet<String>> r =
+ InputMethodAndSubtypeUtilCompat.parseInputMethodsAndSubtypesString(
+ "ime0;subtype0;subtype1:ime1;subtype1;subtype2:ime2");
+ assertThat(r).containsExactly("ime0", asHashSet("subtype0", "subtype1"),
+ "ime1", asHashSet("subtype1", "subtype2"),
+ "ime2", EMPTY_STRING_SET);
+ }
+
+ @Test
+ public void buildInputMethodsAndSubtypesString_EmptyInput() {
+ HashMap<String, HashSet<String>> map = new HashMap<>();
+ assertThat(map).isEmpty();
+ }
+
+ @Test
+ public void buildInputMethodsAndSubtypesString_SingleIme() {
+ HashMap<String, HashSet<String>> map = new HashMap<>();
+ map.put("ime0", new HashSet<>());
+ String result = InputMethodAndSubtypeUtilCompat.buildInputMethodsAndSubtypesString(map);
+ assertThat(result).isEqualTo("ime0");
+ }
+
+ @Test
+ public void buildInputMethodsAndSubtypesString_SingleImeSingleSubtype() {
+ HashMap<String, HashSet<String>> map = new HashMap<>();
+ map.put("ime0", asHashSet("subtype0"));
+ String result = InputMethodAndSubtypeUtilCompat.buildInputMethodsAndSubtypesString(map);
+ assertThat(result).isEqualTo("ime0;subtype0");
+ }
+
+ @Test
+ public void buildInputMethodsAndSubtypesString_SingleImeMultipleSubtypes() {
+ HashMap<String, HashSet<String>> map = new HashMap<>();
+ map.put("ime0", asHashSet("subtype0", "subtype1"));
+ String result = InputMethodAndSubtypeUtilCompat.buildInputMethodsAndSubtypesString(map);
+
+ // We do not expect what order will be used to concatenate items in
+ // InputMethodAndSubtypeUtil.buildInputMethodsAndSubtypesString() hence accept all possible
+ // permutations here.
+ assertThat(result).matches("ime0;subtype0;subtype1|ime0;subtype1;subtype0");
+ }
+
+ @Test
+ public void buildInputMethodsAndSubtypesString_MultipleImesNoSubtypes() {
+ HashMap<String, HashSet<String>> map = new HashMap<>();
+ map.put("ime0", EMPTY_STRING_SET);
+ map.put("ime1", EMPTY_STRING_SET);
+ String result = InputMethodAndSubtypeUtilCompat.buildInputMethodsAndSubtypesString(map);
+
+ // We do not expect what order will be used to concatenate items in
+ // InputMethodAndSubtypeUtil.buildInputMethodsAndSubtypesString() hence accept all possible
+ // permutations here.
+ assertThat(result).matches("ime0:ime1|ime1:ime0");
+ }
+
+ @Test
+ public void buildInputMethodsAndSubtypesString_MultipleImesWithAndWithoutSubtypes() {
+ HashMap<String, HashSet<String>> map = new HashMap<>();
+ map.put("ime0", asHashSet("subtype0", "subtype1"));
+ map.put("ime1", EMPTY_STRING_SET);
+ String result = InputMethodAndSubtypeUtilCompat.buildInputMethodsAndSubtypesString(map);
+
+ // We do not expect what order will be used to concatenate items in
+ // InputMethodAndSubtypeUtil.buildInputMethodsAndSubtypesString() hence accept all possible
+ // permutations here.
+ assertThat(result).matches("ime0;subtype0;subtype1:ime1|ime0;subtype1;subtype0:ime1"
+ + "|ime1:ime0;subtype0;subtype1|ime1:ime0;subtype1;subtype0");
+ }
+
+ @Test
+ public void buildInputMethodsAndSubtypesString_MultipleImesWithSubtypes() {
+ HashMap<String, HashSet<String>> map = new HashMap<>();
+ map.put("ime0", asHashSet("subtype0", "subtype1"));
+ map.put("ime1", asHashSet("subtype2", "subtype3"));
+ String result = InputMethodAndSubtypeUtilCompat.buildInputMethodsAndSubtypesString(map);
+
+ // We do not expect what order will be used to concatenate items in
+ // InputMethodAndSubtypeUtil.buildInputMethodsAndSubtypesString() hence accept all possible
+ // permutations here.
+ assertThat(result).matches("ime0;subtype0;subtype1:ime1;subtype2;subtype3"
+ + "|ime0;subtype1;subtype0:ime1;subtype2;subtype3"
+ + "|ime0;subtype0;subtype1:ime1;subtype3;subtype2"
+ + "|ime0;subtype1;subtype0:ime1;subtype3;subtype2"
+ + "|ime1;subtype2;subtype3:ime0;subtype0;subtype1"
+ + "|ime2;subtype3;subtype2:ime0;subtype0;subtype1"
+ + "|ime3;subtype2;subtype3:ime0;subtype1;subtype0"
+ + "|ime4;subtype3;subtype2:ime0;subtype1;subtype0");
+ }
+
+ @Test
+ public void isValidSystemNonAuxAsciiCapableIme() {
+ // System IME w/ no subtype
+ assertThat(InputMethodAndSubtypeUtilCompat.isValidSystemNonAuxAsciiCapableIme(
+ createDummyIme(true, false)))
+ .isFalse();
+
+ // System IME w/ non-Aux and non-ASCII-capable "keyboard" subtype
+ assertThat(InputMethodAndSubtypeUtilCompat.isValidSystemNonAuxAsciiCapableIme(
+ createDummyIme(true, false, createDummySubtype("keyboard", false, false))))
+ .isFalse();
+
+ // System IME w/ non-Aux and ASCII-capable "keyboard" subtype
+ assertThat(InputMethodAndSubtypeUtilCompat.isValidSystemNonAuxAsciiCapableIme(
+ createDummyIme(true, false, createDummySubtype("keyboard", false, true))))
+ .isTrue();
+
+ // System IME w/ Aux and ASCII-capable "keyboard" subtype
+ assertThat(InputMethodAndSubtypeUtilCompat.isValidSystemNonAuxAsciiCapableIme(
+ createDummyIme(true, true, createDummySubtype("keyboard", true, true))))
+ .isFalse();
+
+ // System IME w/ non-Aux and ASCII-capable "voice" subtype
+ assertThat(InputMethodAndSubtypeUtilCompat.isValidSystemNonAuxAsciiCapableIme(
+ createDummyIme(true, false, createDummySubtype("voice", false, true))))
+ .isFalse();
+
+ // System IME w/ non-Aux and non-ASCII-capable subtype + Non-Aux and ASCII-capable subtype
+ assertThat(InputMethodAndSubtypeUtilCompat.isValidSystemNonAuxAsciiCapableIme(
+ createDummyIme(true, false,
+ createDummySubtype("keyboard", false, true),
+ createDummySubtype("keyboard", false, false))))
+ .isTrue();
+
+ // Non-system IME w/ non-Aux and ASCII-capable "keyboard" subtype
+ assertThat(InputMethodAndSubtypeUtilCompat.isValidSystemNonAuxAsciiCapableIme(
+ createDummyIme(false, false, createDummySubtype("keyboard", false, true))))
+ .isFalse();
+ }
+
+ private static InputMethodInfo createDummyIme(boolean isSystem, boolean isAuxIme,
+ InputMethodSubtype... subtypes) {
+ final ResolveInfo ri = new ResolveInfo();
+ final ServiceInfo si = new ServiceInfo();
+ final ApplicationInfo ai = new ApplicationInfo();
+ ai.packageName = "com.example.android.dummyime";
+ ai.enabled = true;
+ ai.flags |= (isSystem ? ApplicationInfo.FLAG_SYSTEM : 0);
+ si.applicationInfo = ai;
+ si.enabled = true;
+ si.packageName = "com.example.android.dummyime";
+ si.name = "Dummy IME";
+ si.exported = true;
+ si.nonLocalizedLabel = "Dummy IME";
+ ri.serviceInfo = si;
+ return new InputMethodInfo(ri, isAuxIme, "", Arrays.asList(subtypes), 1, false);
+ }
+
+ private static InputMethodSubtype createDummySubtype(
+ String mode, boolean isAuxiliary, boolean isAsciiCapable) {
+ return new InputMethodSubtypeBuilder()
+ .setSubtypeNameResId(0)
+ .setSubtypeIconResId(0)
+ .setSubtypeLocale("en_US")
+ .setLanguageTag("en-US")
+ .setSubtypeMode(mode)
+ .setIsAuxiliary(isAuxiliary)
+ .setIsAsciiCapable(isAsciiCapable)
+ .build();
+ }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlLoaderCompatTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlLoaderCompatTest.java
new file mode 100644
index 0000000..f981f36
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlLoaderCompatTest.java
@@ -0,0 +1,108 @@
+/*
+ * 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.settingslib.license;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.io.File;
+import java.util.ArrayList;
+
+@RunWith(SettingsLibRobolectricTestRunner.class)
+public class LicenseHtmlLoaderCompatTest {
+ @Mock
+ private Context mContext;
+
+ LicenseHtmlLoaderCompat newLicenseHtmlLoader(ArrayList<File> xmlFiles,
+ File cachedHtmlFile, boolean isCachedHtmlFileOutdated,
+ boolean generateHtmlFileSucceeded) {
+ LicenseHtmlLoaderCompat loader = spy(new LicenseHtmlLoaderCompat(mContext));
+ doReturn(xmlFiles).when(loader).getVaildXmlFiles();
+ doReturn(cachedHtmlFile).when(loader).getCachedHtmlFile();
+ doReturn(isCachedHtmlFileOutdated).when(loader).isCachedHtmlFileOutdated(any(), any());
+ doReturn(generateHtmlFileSucceeded).when(loader).generateHtmlFile(any(), any());
+ return loader;
+ }
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ @Test
+ public void testLoadInBackground() {
+ ArrayList<File> xmlFiles = new ArrayList();
+ xmlFiles.add(new File("test.xml"));
+ File cachedHtmlFile = new File("test.html");
+
+ LicenseHtmlLoaderCompat loader = newLicenseHtmlLoader(xmlFiles, cachedHtmlFile, true, true);
+
+ assertThat(loader.loadInBackground()).isEqualTo(cachedHtmlFile);
+ verify(loader).generateHtmlFile(any(), any());
+ }
+
+ @Test
+ public void testLoadInBackgroundWithNoVaildXmlFiles() {
+ ArrayList<File> xmlFiles = new ArrayList();
+ File cachedHtmlFile = new File("test.html");
+
+ LicenseHtmlLoaderCompat loader = newLicenseHtmlLoader(xmlFiles, cachedHtmlFile, true, true);
+
+ assertThat(loader.loadInBackground()).isNull();
+ verify(loader, never()).generateHtmlFile(any(), any());
+ }
+
+ @Test
+ public void testLoadInBackgroundWithNonOutdatedCachedHtmlFile() {
+ ArrayList<File> xmlFiles = new ArrayList();
+ xmlFiles.add(new File("test.xml"));
+ File cachedHtmlFile = new File("test.html");
+
+ LicenseHtmlLoaderCompat loader = newLicenseHtmlLoader(xmlFiles, cachedHtmlFile, false,
+ true);
+
+ assertThat(loader.loadInBackground()).isEqualTo(cachedHtmlFile);
+ verify(loader, never()).generateHtmlFile(any(), any());
+ }
+
+ @Test
+ public void testLoadInBackgroundWithGenerateHtmlFileFailed() {
+ ArrayList<File> xmlFiles = new ArrayList();
+ xmlFiles.add(new File("test.xml"));
+ File cachedHtmlFile = new File("test.html");
+
+ LicenseHtmlLoaderCompat loader = newLicenseHtmlLoader(xmlFiles, cachedHtmlFile, true,
+ false);
+
+ assertThat(loader.loadInBackground()).isNull();
+ verify(loader).generateHtmlFile(any(), any());
+ }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/InjectedSettingTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/InjectedSettingTest.java
new file mode 100644
index 0000000..c29481f
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/InjectedSettingTest.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.settingslib.location;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(SettingsLibRobolectricTestRunner.class)
+public final class InjectedSettingTest {
+
+ private static final String TEST_STRING = "test";
+
+ @Test
+ public void buildWithoutPackageName_ShouldReturnNull() {
+ assertThat(((new InjectedSetting.Builder())
+ .setClassName(TEST_STRING)
+ .setTitle(TEST_STRING)
+ .setSettingsActivity(TEST_STRING).build())).isNull();
+ }
+
+ private InjectedSetting getTestSetting() {
+ return new InjectedSetting.Builder()
+ .setPackageName(TEST_STRING)
+ .setClassName(TEST_STRING)
+ .setTitle(TEST_STRING)
+ .setSettingsActivity(TEST_STRING).build();
+ }
+
+ @Test
+ public void testEquals() {
+ InjectedSetting setting1 = getTestSetting();
+ InjectedSetting setting2 = getTestSetting();
+ assertThat(setting1).isEqualTo(setting2);
+ }
+
+ @Test
+ public void testHashCode() {
+ InjectedSetting setting = getTestSetting();
+ assertThat(setting.hashCode()).isEqualTo(1225314048);
+ }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageControllerTest.java
new file mode 100644
index 0000000..1be856a
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageControllerTest.java
@@ -0,0 +1,101 @@
+/*
+ * 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.settingslib.net;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.nullable;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.anyLong;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.net.INetworkStatsSession;
+import android.net.NetworkStatsHistory;
+import android.net.NetworkStatsHistory.Entry;
+import android.net.NetworkTemplate;
+import android.os.RemoteException;
+import android.text.format.DateUtils;
+
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(SettingsLibRobolectricTestRunner.class)
+public class DataUsageControllerTest {
+
+ @Mock
+ private INetworkStatsSession mSession;
+
+ private Context mContext;
+ private DataUsageController mController;
+ private NetworkStatsHistory mNetworkStatsHistory;
+
+ @Before
+ public void setUp() throws RemoteException {
+ MockitoAnnotations.initMocks(this);
+ mContext = RuntimeEnvironment.application;
+ mController = spy(new DataUsageController(mContext));
+ mNetworkStatsHistory = spy(
+ new NetworkStatsHistory(DateUtils.DAY_IN_MILLIS /* bucketDuration */));
+ doReturn(mNetworkStatsHistory)
+ .when(mSession).getHistoryForNetwork(any(NetworkTemplate.class), anyInt());
+ }
+
+ @Test
+ public void getHistoriclUsageLevel_noNetworkSession_shouldReturn0() {
+ doReturn(null).when(mController).getSession();
+
+ assertThat(mController.getHistoriclUsageLevel(null /* template */)).isEqualTo(0L);
+
+ }
+
+ @Test
+ public void getHistoriclUsageLevel_noUsageData_shouldReturn0() {
+ doReturn(mSession).when(mController).getSession();
+
+ assertThat(mController.getHistoriclUsageLevel(NetworkTemplate.buildTemplateWifiWildcard()))
+ .isEqualTo(0L);
+
+ }
+
+ @Test
+ public void getHistoriclUsageLevel_hasUsageData_shouldReturnTotalUsage() {
+ doReturn(mSession).when(mController).getSession();
+ final long receivedBytes = 743823454L;
+ final long transmittedBytes = 16574289L;
+ final Entry entry = new Entry();
+ entry.bucketStart = 1521583200000L;
+ entry.rxBytes = receivedBytes;
+ entry.txBytes = transmittedBytes;
+ when(mNetworkStatsHistory.getValues(eq(0L), anyLong(), anyLong(), nullable(Entry.class)))
+ .thenReturn(entry);
+
+ assertThat(mController.getHistoriclUsageLevel(NetworkTemplate.buildTemplateWifiWildcard()))
+ .isEqualTo(receivedBytes + transmittedBytes);
+
+ }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/suggestions/SuggestionControllerMixinCompatTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/suggestions/SuggestionControllerMixinCompatTest.java
new file mode 100644
index 0000000..1ee3afa
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/suggestions/SuggestionControllerMixinCompatTest.java
@@ -0,0 +1,127 @@
+/*
+ * 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.settingslib.suggestions;
+
+import static androidx.lifecycle.Lifecycle.Event.ON_START;
+import static androidx.lifecycle.Lifecycle.Event.ON_STOP;
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.ComponentName;
+import android.content.Context;
+
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+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 org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+import androidx.lifecycle.LifecycleOwner;
+import androidx.loader.app.LoaderManager;
+
+@RunWith(SettingsLibRobolectricTestRunner.class)
+@Config(shadows = ShadowSuggestionController.class)
+public class SuggestionControllerMixinCompatTest {
+
+ @Mock
+ private SuggestionControllerMixinCompat.SuggestionControllerHost mHost;
+
+ private Context mContext;
+ private LifecycleOwner mLifecycleOwner;
+ private Lifecycle mLifecycle;
+ private SuggestionControllerMixinCompat mMixin;
+ private ComponentName mComponentName;
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mContext = RuntimeEnvironment.application;
+ mLifecycleOwner = () -> mLifecycle;
+ mLifecycle = new Lifecycle(mLifecycleOwner);
+ mComponentName = new ComponentName(
+ "com.android.settings.intelligence",
+ "com.android.settings.intelligence.suggestions.SuggestionService");
+ }
+
+ @After
+ public void tearDown() {
+ ShadowSuggestionController.reset();
+ }
+
+ @Test
+ public void goThroughLifecycle_onStartStop_shouldStartStopController() {
+ mMixin = new SuggestionControllerMixinCompat(mContext, mHost, mLifecycle, mComponentName);
+
+ mLifecycle.handleLifecycleEvent(ON_START);
+ assertThat(ShadowSuggestionController.sStartCalled).isTrue();
+
+ mLifecycle.handleLifecycleEvent(ON_STOP);
+ assertThat(ShadowSuggestionController.sStopCalled).isTrue();
+ }
+
+ @Test
+ public void onServiceConnected_shouldGetSuggestion() {
+ final LoaderManager loaderManager = mock(LoaderManager.class);
+ when(mHost.getLoaderManager()).thenReturn(loaderManager);
+
+ mMixin = new SuggestionControllerMixinCompat(mContext, mHost, mLifecycle, mComponentName);
+ mMixin.onServiceConnected();
+
+ verify(loaderManager).restartLoader(SuggestionLoader.LOADER_ID_SUGGESTIONS,
+ null /* args */, mMixin /* callback */);
+ }
+
+ @Test
+ public void onServiceConnected_hostNotAttached_shouldDoNothing() {
+ when(mHost.getLoaderManager()).thenReturn(null);
+
+ mMixin = new SuggestionControllerMixinCompat(mContext, mHost, mLifecycle, mComponentName);
+ mMixin.onServiceConnected();
+
+ verify(mHost).getLoaderManager();
+ }
+
+ @Test
+ public void onServiceDisconnected_hostNotAttached_shouldDoNothing() {
+ when(mHost.getLoaderManager()).thenReturn(null);
+
+ mMixin = new SuggestionControllerMixinCompat(mContext, mHost, mLifecycle, mComponentName);
+ mMixin.onServiceDisconnected();
+
+ verify(mHost).getLoaderManager();
+ }
+
+ @Test
+ public void doneLoadingg_shouldSetSuggestionLoaded() {
+ mMixin = new SuggestionControllerMixinCompat(mContext, mHost, mLifecycle, mComponentName);
+
+ mMixin.onLoadFinished(mock(SuggestionLoaderCompat.class), null);
+
+ assertThat(mMixin.isSuggestionLoaded()).isTrue();
+
+ mMixin.onLoaderReset(mock(SuggestionLoaderCompat.class));
+
+ assertThat(mMixin.isSuggestionLoaded()).isFalse();
+ }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/suggestions/SuggestionParserTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/suggestions/SuggestionParserTest.java
deleted file mode 100644
index d05bcfd..0000000
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/suggestions/SuggestionParserTest.java
+++ /dev/null
@@ -1,200 +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.settingslib.suggestions;
-
-import static com.google.common.truth.Truth.assertThat;
-import static org.robolectric.RuntimeEnvironment.application;
-import static org.robolectric.shadow.api.Shadow.extract;
-
-import android.content.ComponentName;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.content.pm.ResolveInfo;
-import android.os.Bundle;
-import android.preference.PreferenceManager;
-
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-import com.android.settingslib.drawer.Tile;
-import com.android.settingslib.drawer.TileUtilsTest;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.shadows.ShadowPackageManager;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-@RunWith(SettingsLibRobolectricTestRunner.class)
-public class SuggestionParserTest {
-
- private ShadowPackageManager mPackageManager;
- private SuggestionParser mSuggestionParser;
- private SuggestionCategory mMultipleCategory;
- private SuggestionCategory mExclusiveCategory;
- private SuggestionCategory mExpiredExclusiveCategory;
- private List<Tile> mSuggestionsBeforeDismiss;
- private List<Tile> mSuggestionsAfterDismiss;
- private SharedPreferences mPrefs;
- private Tile mSuggestion;
-
- @Before
- public void setUp() {
- mPackageManager = extract(application.getPackageManager());
- mPrefs = PreferenceManager.getDefaultSharedPreferences(application);
- mSuggestion = new Tile();
- mSuggestion.intent = new Intent("action");
- mSuggestion.intent.setComponent(new ComponentName("pkg", "cls"));
- mSuggestion.metaData = new Bundle();
- mMultipleCategory = new SuggestionCategory();
- mMultipleCategory.category = "category1";
- mMultipleCategory.multiple = true;
- mExclusiveCategory = new SuggestionCategory();
- mExclusiveCategory.category = "category2";
- mExclusiveCategory.exclusive = true;
- mExpiredExclusiveCategory = new SuggestionCategory();
- mExpiredExclusiveCategory.category = "category3";
- mExpiredExclusiveCategory.exclusive = true;
- mExpiredExclusiveCategory.exclusiveExpireDaysInMillis = 0;
-
- mSuggestionParser = new SuggestionParser(application, mPrefs,
- Arrays.asList(mMultipleCategory, mExclusiveCategory, mExpiredExclusiveCategory),
- "0");
-
- ResolveInfo info1 = TileUtilsTest.newInfo(true, null);
- info1.activityInfo.packageName = "pkg";
- ResolveInfo infoDupe1 = TileUtilsTest.newInfo(true, null);
- infoDupe1.activityInfo.packageName = "pkg";
-
- ResolveInfo info2 = TileUtilsTest.newInfo(true, null);
- info2.activityInfo.packageName = "pkg2";
- ResolveInfo info3 = TileUtilsTest.newInfo(true, null);
- info3.activityInfo.packageName = "pkg3";
- ResolveInfo info4 = TileUtilsTest.newInfo(true, null);
- info4.activityInfo.packageName = "pkg4";
-
- Intent intent1 = new Intent(Intent.ACTION_MAIN).addCategory("category1");
- Intent intent2 = new Intent(Intent.ACTION_MAIN).addCategory("category2");
- Intent intent3 = new Intent(Intent.ACTION_MAIN).addCategory("category3");
-
- mPackageManager.addResolveInfoForIntent(intent1, info1);
- mPackageManager.addResolveInfoForIntent(intent1, info2);
- mPackageManager.addResolveInfoForIntent(intent1, infoDupe1);
- mPackageManager.addResolveInfoForIntent(intent2, info3);
- mPackageManager.addResolveInfoForIntent(intent3, info4);
- }
-
- @Test
- public void dismissSuggestion_shouldDismiss() {
- assertThat(mSuggestionParser.dismissSuggestion(mSuggestion)).isTrue();
- }
-
- @Test
- public void testGetSuggestions_withoutSmartSuggestions_shouldDismiss() {
- readAndDismissSuggestion(false);
- mSuggestionParser.readSuggestions(mMultipleCategory, mSuggestionsAfterDismiss, false);
- assertThat(mSuggestionsBeforeDismiss).hasSize(2);
- assertThat(mSuggestionsAfterDismiss).hasSize(1);
- assertThat(mSuggestionsBeforeDismiss.get(1)).isEqualTo(mSuggestionsAfterDismiss.get(0));
- }
-
- @Test
- public void testGetSuggestions_withSmartSuggestions_shouldDismiss() {
- readAndDismissSuggestion(true);
- assertThat(mSuggestionsBeforeDismiss).hasSize(2);
- assertThat(mSuggestionsAfterDismiss).hasSize(1);
- }
-
- @Test
- public void testGetSuggestion_exclusiveNotAvailable_onlyRegularCategoryAndNoDupe() {
- mPackageManager.removeResolveInfosForIntent(
- new Intent(Intent.ACTION_MAIN).addCategory("category2"),
- "pkg3");
- mPackageManager.removeResolveInfosForIntent(
- new Intent(Intent.ACTION_MAIN).addCategory("category3"),
- "pkg4");
-
- // If exclusive item is not available, the other categories should be shown
- final SuggestionList sl =
- mSuggestionParser.getSuggestions(false /* isSmartSuggestionEnabled */);
- final List<Tile> suggestions = sl.getSuggestions();
- assertThat(suggestions).hasSize(2);
-
- assertThat(suggestions.get(0).intent.getComponent().getPackageName()).isEqualTo("pkg");
- assertThat(suggestions.get(1).intent.getComponent().getPackageName()).isEqualTo("pkg2");
- }
-
- @Test
- public void testGetSuggestion_exclusiveExpiredAvailable_shouldLoadWithRegularCategory() {
- // First remove permanent exclusive
- mPackageManager.removeResolveInfosForIntent(
- new Intent(Intent.ACTION_MAIN).addCategory("category2"),
- "pkg3");
- // Set the other exclusive to be expired.
- mPrefs.edit()
- .putLong(mExpiredExclusiveCategory.category + "_setup_time",
- System.currentTimeMillis() - 1000)
- .commit();
-
- // If exclusive is expired, they should be shown together with the other categories
- final SuggestionList sl =
- mSuggestionParser.getSuggestions(true /* isSmartSuggestionEnabled */);
- final List<Tile> suggestions = sl.getSuggestions();
-
- assertThat(suggestions).hasSize(3);
- }
-
- @Test
- public void testGetSuggestions_exclusive() {
- final SuggestionList sl =
- mSuggestionParser.getSuggestions(false /* isSmartSuggestionEnabled */);
- final List<Tile> suggestions = sl.getSuggestions();
-
- assertThat(suggestions).hasSize(1);
- }
-
- @Test
- public void isSuggestionDismissed_dismissedSuggestion_shouldReturnTrue() {
- final Tile suggestion = new Tile();
- suggestion.metaData = new Bundle();
- suggestion.metaData.putString(SuggestionParser.META_DATA_DISMISS_CONTROL, "1,2,3");
- suggestion.intent = new Intent().setComponent(new ComponentName("pkg", "cls"));
-
- // Dismiss suggestion when smart suggestion is not enabled.
- mSuggestionParser.dismissSuggestion(suggestion);
-
- assertThat(mSuggestionParser.isDismissed(suggestion, true /* isSmartSuggestionEnabled */))
- .isTrue();
- }
-
- private void readAndDismissSuggestion(boolean isSmartSuggestionEnabled) {
- mSuggestionsBeforeDismiss = new ArrayList<>();
- mSuggestionsAfterDismiss = new ArrayList<>();
- mSuggestionParser.readSuggestions(
- mMultipleCategory, mSuggestionsBeforeDismiss, isSmartSuggestionEnabled);
-
- final Tile suggestion = mSuggestionsBeforeDismiss.get(0);
- if (mSuggestionParser.dismissSuggestion(suggestion)) {
- mPackageManager.removeResolveInfosForIntent(
- new Intent(Intent.ACTION_MAIN).addCategory(mMultipleCategory.category),
- suggestion.intent.getComponent().getPackageName());
- }
- mSuggestionParser.readSuggestions(
- mMultipleCategory, mSuggestionsAfterDismiss, isSmartSuggestionEnabled);
- }
-}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/FragmentTestUtils.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/FragmentTestUtils.java
new file mode 100644
index 0000000..8ba8606
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/FragmentTestUtils.java
@@ -0,0 +1,74 @@
+/*
+ * 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.settingslib.testutils;
+
+import android.os.Bundle;
+import android.widget.LinearLayout;
+
+import org.robolectric.Robolectric;
+
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentActivity;
+import androidx.fragment.app.FragmentManager;
+
+/**
+ * Utilities for creating Fragments for testing.
+ * <p>
+ * TODO(b/111195449) - Duplicated from org.robolectric.shadows.support.v4.SupportFragmentTestUtil
+ */
+@Deprecated
+public class FragmentTestUtils {
+
+ public static void startFragment(Fragment fragment) {
+ buildFragmentManager(FragmentUtilActivity.class)
+ .beginTransaction().add(fragment, null).commit();
+ }
+
+ public static void startFragment(Fragment fragment,
+ Class<? extends FragmentActivity> activityClass) {
+ buildFragmentManager(activityClass)
+ .beginTransaction().add(fragment, null).commit();
+ }
+
+ public static void startVisibleFragment(Fragment fragment) {
+ buildFragmentManager(FragmentUtilActivity.class)
+ .beginTransaction().add(1, fragment, null).commit();
+ }
+
+ public static void startVisibleFragment(Fragment fragment,
+ Class<? extends FragmentActivity> activityClass, int containerViewId) {
+ buildFragmentManager(activityClass)
+ .beginTransaction().add(containerViewId, fragment, null).commit();
+ }
+
+ private static FragmentManager buildFragmentManager(
+ Class<? extends FragmentActivity> activityClass) {
+ FragmentActivity activity = Robolectric.setupActivity(activityClass);
+ return activity.getSupportFragmentManager();
+ }
+
+ private static class FragmentUtilActivity extends FragmentActivity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ LinearLayout view = new LinearLayout(this);
+ view.setId(1);
+
+ setContentView(view);
+ }
+ }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceMixinCompatTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceMixinCompatTest.java
new file mode 100644
index 0000000..1abbaba
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceMixinCompatTest.java
@@ -0,0 +1,100 @@
+/*
+ * 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.widget;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.shadows.ShadowApplication;
+
+import androidx.lifecycle.LifecycleOwner;
+import androidx.preference.PreferenceFragmentCompat;
+import androidx.preference.PreferenceManager;
+import androidx.preference.PreferenceScreen;
+
+@RunWith(SettingsLibRobolectricTestRunner.class)
+public class FooterPreferenceMixinCompatTest {
+
+ @Mock
+ private PreferenceFragmentCompat mFragment;
+ @Mock
+ private PreferenceScreen mScreen;
+
+ private LifecycleOwner mLifecycleOwner;
+ private Lifecycle mLifecycle;
+ private FooterPreferenceMixinCompat mMixin;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mLifecycleOwner = () -> mLifecycle;
+ mLifecycle = new Lifecycle(mLifecycleOwner);
+ when(mFragment.getPreferenceManager()).thenReturn(mock(PreferenceManager.class));
+ when(mFragment.getPreferenceManager().getContext())
+ .thenReturn(ShadowApplication.getInstance().getApplicationContext());
+ mMixin = new FooterPreferenceMixinCompat(mFragment, mLifecycle);
+ }
+
+ @Test
+ public void createFooter_screenNotAvailable_noCrash() {
+ assertThat(mMixin.createFooterPreference()).isNotNull();
+ }
+
+ @Test
+ public void createFooter_screenAvailable_canAttachToScreen() {
+ when(mFragment.getPreferenceScreen()).thenReturn(mScreen);
+
+ final FooterPreference preference = mMixin.createFooterPreference();
+
+ assertThat(preference).isNotNull();
+ verify(mScreen).addPreference(preference);
+ }
+
+ @Test
+ public void createFooter_screenAvailableDelayed_canAttachToScreen() {
+ final FooterPreference preference = mMixin.createFooterPreference();
+
+ mLifecycle.setPreferenceScreen(mScreen);
+
+ assertThat(preference).isNotNull();
+ verify(mScreen).addPreference(preference);
+ }
+
+ @Test
+ public void createFooterTwice_screenAvailable_replaceOldFooter() {
+ when(mFragment.getPreferenceScreen()).thenReturn(mScreen);
+
+ mMixin.createFooterPreference();
+ mMixin.createFooterPreference();
+
+ verify(mScreen).removePreference(any(FooterPreference.class));
+ verify(mScreen, times(2)).addPreference(any(FooterPreference.class));
+ }
+
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceMixinTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceMixinTest.java
index 78b7616..8604d18 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceMixinTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceMixinTest.java
@@ -23,11 +23,6 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import androidx.lifecycle.LifecycleOwner;
-import androidx.preference.PreferenceFragment;
-import androidx.preference.PreferenceManager;
-import androidx.preference.PreferenceScreen;
-
import com.android.settingslib.SettingsLibRobolectricTestRunner;
import com.android.settingslib.core.lifecycle.Lifecycle;
@@ -38,6 +33,11 @@
import org.mockito.MockitoAnnotations;
import org.robolectric.shadows.ShadowApplication;
+import androidx.lifecycle.LifecycleOwner;
+import androidx.preference.PreferenceFragment;
+import androidx.preference.PreferenceManager;
+import androidx.preference.PreferenceScreen;
+
@RunWith(SettingsLibRobolectricTestRunner.class)
public class FooterPreferenceMixinTest {
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
index f728684..9d398b5 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
@@ -841,7 +841,19 @@
WifiConfiguration config = WifiConfiguration
.getWifiConfigFromBackup(new DataInputStream(new ByteArrayInputStream(data)));
if (DEBUG) Log.d(TAG, "Successfully unMarshaled WifiConfiguration ");
+ int originalApBand = config.apBand;
mWifiManager.setWifiApConfiguration(config);
+
+ // Depending on device hardware, we may need to notify the user of a setting change for
+ // the apBand preference
+ boolean dualMode = mWifiManager.isDualModeSupported();
+ int storedApBand = mWifiManager.getWifiApConfiguration().apBand;
+ if (dualMode) {
+ if (storedApBand != originalApBand) {
+ Log.d(TAG, "restored ap configuration requires a conversion, notify the user");
+ mWifiManager.notifyUserOfApBandConversion();
+ }
+ }
} catch (IOException | BackupUtils.BadVersionException e) {
Log.e(TAG, "Failed to unMarshal SoftAPConfiguration " + e.getMessage());
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 375fef8a..9592b63 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -2935,7 +2935,7 @@
}
private final class UpgradeController {
- private static final int SETTINGS_VERSION = 169;
+ private static final int SETTINGS_VERSION = 170;
private final int mUserId;
@@ -3810,6 +3810,37 @@
currentVersion = 169;
}
+ if (currentVersion == 169) {
+ // Version 169: by default, add STREAM_VOICE_CALL to list of streams that can
+ // be muted.
+ final SettingsState systemSettings = getSystemSettingsLocked(userId);
+ final Setting currentSetting = systemSettings.getSettingLocked(
+ Settings.System.MUTE_STREAMS_AFFECTED);
+ if (!currentSetting.isNull()) {
+ try {
+ int currentSettingIntegerValue = Integer.parseInt(
+ currentSetting.getValue());
+ if ((currentSettingIntegerValue
+ & (1 << AudioManager.STREAM_VOICE_CALL)) == 0) {
+ systemSettings.insertSettingLocked(
+ Settings.System.MUTE_STREAMS_AFFECTED,
+ Integer.toString(
+ currentSettingIntegerValue
+ | (1 << AudioManager.STREAM_VOICE_CALL)),
+ null, true, SettingsState.SYSTEM_PACKAGE_NAME);
+ }
+ } catch (NumberFormatException e) {
+ // remove the setting in case it is not a valid integer
+ Slog.w("Failed to parse integer value of MUTE_STREAMS_AFFECTED"
+ + "setting, removing setting", e);
+ systemSettings.deleteSettingLocked(
+ Settings.System.MUTE_STREAMS_AFFECTED);
+ }
+
+ }
+ currentVersion = 170;
+ }
+
// vXXX: Add new settings above this point.
if (currentVersion != newVersion) {
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 8a5cb4a..f811665 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -152,6 +152,7 @@
<uses-permission android:name="android.permission.WATCH_APPOPS" />
<uses-permission android:name="android.permission.CONTROL_KEYGUARD" />
+ <uses-permission android:name="android.permission.SUSPEND_APPS" />
<application android:label="@string/app_label"
android:defaultToDeviceProtectedStorage="true"
diff --git a/packages/Shell/res/values-hy/strings.xml b/packages/Shell/res/values-hy/strings.xml
index 95b6131..3bc54b2 100644
--- a/packages/Shell/res/values-hy/strings.xml
+++ b/packages/Shell/res/values-hy/strings.xml
@@ -25,9 +25,9 @@
<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="default" msgid="8353769438382138847">"Հպեք՝ վրիպակի զեկույցը տրամադրելու համար"</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_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>
<string name="bugreport_confirm_dont_repeat" msgid="6179945398364357318">"Այլևս ցույց չտալ"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Վրիպակների հաշվետվություններ"</string>
@@ -35,9 +35,9 @@
<string name="bugreport_add_details_to_zip_failed" msgid="1302931926486712371">"Չհաջողվեց ավելացնել վրիպակի զեկույցի մանրամասները zip ֆայլին"</string>
<string name="bugreport_unnamed" msgid="2800582406842092709">"անանուն"</string>
<string name="bugreport_info_action" msgid="2158204228510576227">"Մանրամասներ"</string>
- <string name="bugreport_screenshot_action" msgid="8677781721940614995">"Էկրանի պատկեր"</string>
- <string name="bugreport_screenshot_taken" msgid="5684211273096253120">"Էկրանի պատկերը հաջողությամբ ստացվեց:"</string>
- <string name="bugreport_screenshot_failed" msgid="5853049140806834601">"Չհաջողվեց ստանալ էկրանի պատկերը:"</string>
+ <string name="bugreport_screenshot_action" msgid="8677781721940614995">"Սքրինշոթ"</string>
+ <string name="bugreport_screenshot_taken" msgid="5684211273096253120">"Սքրինշոթը հաջողությամբ ստացվեց:"</string>
+ <string name="bugreport_screenshot_failed" msgid="5853049140806834601">"Չհաջողվեց ստանալ սքրինշոթը:"</string>
<string name="bugreport_info_dialog_title" msgid="1355948594292983332">"<xliff:g id="ID">#%d</xliff:g> վրիպակի զեկույցի մանրամասները"</string>
<string name="bugreport_info_name" msgid="4414036021935139527">"Ֆայլի անունը"</string>
<string name="bugreport_info_title" msgid="2306030793918239804">"Վրիպակի զեկույցի վերնագիրը"</string>
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index beb4e9e..a5616d5 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -120,6 +120,7 @@
<uses-permission android:name="android.permission.MEDIA_CONTENT_CONTROL" />
<uses-permission android:name="android.permission.ACCESS_KEYGUARD_SECURE_STORAGE" />
<uses-permission android:name="android.permission.TRUST_LISTENER" />
+ <uses-permission android:name="android.permission.USE_BIOMETRIC" />
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
<uses-permission android:name="android.permission.RESET_FINGERPRINT_LOCKOUT" />
<uses-permission android:name="android.permission.MANAGE_SLICE_PERMISSIONS" />
@@ -338,19 +339,17 @@
android:exported="false">
</activity>
- <!-- Springboard for launching the share activity -->
- <receiver android:name=".screenshot.GlobalScreenshot$ScreenshotActionReceiver"
- android:process=":screenshot"
+ <!-- Springboard for launching the share and edit activity. This needs to be in the main
+ system ui process since we need to notify the status bar to dismiss the keyguard -->
+ <receiver android:name=".screenshot.GlobalScreenshot$ActionProxyReceiver"
android:exported="false" />
<!-- Callback for dismissing screenshot notification after a share target is picked -->
<receiver android:name=".screenshot.GlobalScreenshot$TargetChosenReceiver"
- android:process=":screenshot"
android:exported="false" />
<!-- Callback for deleting screenshot notification -->
<receiver android:name=".screenshot.GlobalScreenshot$DeleteScreenshotReceiver"
- android:process=":screenshot"
android:exported="false" />
<!-- started from UsbDeviceSettingsManager -->
diff --git a/packages/SystemUI/OWNERS b/packages/SystemUI/OWNERS
index e217ace..47f2cdc 100644
--- a/packages/SystemUI/OWNERS
+++ b/packages/SystemUI/OWNERS
@@ -14,6 +14,7 @@
jaggies@google.com
jjaggi@google.com
juliacr@google.com
+kchyn@google.com
madym@google.com
ngmatthew@google.com
roosa@google.com
diff --git a/packages/SystemUI/res-keyguard/values-af/strings.xml b/packages/SystemUI/res-keyguard/values-af/strings.xml
index 41a3f10..f43d57a 100644
--- a/packages/SystemUI/res-keyguard/values-af/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-af/strings.xml
@@ -140,7 +140,8 @@
<item quantity="other">Toestel is <xliff:g id="NUMBER_1">%d</xliff:g> uur lank nie ontsluit nie. Bevestig wagwoord.</item>
<item quantity="one">Toestel is <xliff:g id="NUMBER_0">%d</xliff:g> uur lank nie ontsluit nie. Bevestig wagwoord.</item>
</plurals>
- <string name="fingerprint_not_recognized" msgid="348813995267914625">"Nie herken nie"</string>
+ <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Nie herken nie"</string>
+ <string name="kg_face_not_recognized" msgid="6382535088345875294">"Nie herken nie"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
<item quantity="other">Voer SIM-PIN in. Jy het <xliff:g id="NUMBER_1">%d</xliff:g> pogings oor.</item>
<item quantity="one">Voer SIM-PIN in. Jy het <xliff:g id="NUMBER_0">%d</xliff:g> poging oor voordat jy jou diensverskaffer moet kontak om jou toestel te ontsluit.</item>
diff --git a/packages/SystemUI/res-keyguard/values-am/strings.xml b/packages/SystemUI/res-keyguard/values-am/strings.xml
index 0ee1820..11e099a 100644
--- a/packages/SystemUI/res-keyguard/values-am/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-am/strings.xml
@@ -140,7 +140,8 @@
<item quantity="one">መሣሪያው ለ<xliff:g id="NUMBER_1">%d</xliff:g> ሰዓቶች አልተከፈተም ነበር። የይለፍ ቃል ያረጋግጡ።</item>
<item quantity="other">መሣሪያው ለ<xliff:g id="NUMBER_1">%d</xliff:g> ሰዓቶች አልተከፈተም ነበር። የይለፍ ቃል ያረጋግጡ።</item>
</plurals>
- <string name="fingerprint_not_recognized" msgid="348813995267914625">"አልታወቀም"</string>
+ <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"አልታወቀም"</string>
+ <string name="kg_face_not_recognized" msgid="6382535088345875294">"አልታወቀም"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
<item quantity="one">የሲም ፒን ያስገቡ። <xliff:g id="NUMBER_1">%d</xliff:g> ሙከራዎች ይቀረዎታል።</item>
<item quantity="other">የሲም ፒን ያስገቡ። <xliff:g id="NUMBER_1">%d</xliff:g> ሙከራዎች ይቀረዎታል።</item>
diff --git a/packages/SystemUI/res-keyguard/values-ar/strings.xml b/packages/SystemUI/res-keyguard/values-ar/strings.xml
index 9980d64..1117730 100644
--- a/packages/SystemUI/res-keyguard/values-ar/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ar/strings.xml
@@ -164,7 +164,8 @@
<item quantity="other">لم يتم إلغاء تأمين الجهاز لمدة <xliff:g id="NUMBER_1">%d</xliff:g> ساعة. تأكيد كلمة المرور.</item>
<item quantity="one">لم يتم إلغاء تأمين الجهاز لمدة <xliff:g id="NUMBER_0">%d</xliff:g> ساعة. تأكيد كلمة المرور.</item>
</plurals>
- <string name="fingerprint_not_recognized" msgid="348813995267914625">"لم يتم التعرف عليها"</string>
+ <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"لم يتم التعرف عليها."</string>
+ <string name="kg_face_not_recognized" msgid="6382535088345875294">"لم يتم التعرّف عليه."</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
<item quantity="zero">أدخل رقم التعريف الشخصي لشريحة SIM. تتبقى لديك <xliff:g id="NUMBER_1">%d</xliff:g> محاولة.</item>
<item quantity="two">أدخل رقم التعريف الشخصي لشريحة SIM. تتبقى لديك محاولتان (<xliff:g id="NUMBER_1">%d</xliff:g>).</item>
diff --git a/packages/SystemUI/res-keyguard/values-as/strings.xml b/packages/SystemUI/res-keyguard/values-as/strings.xml
index 929d89c..50dd855 100644
--- a/packages/SystemUI/res-keyguard/values-as/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-as/strings.xml
@@ -140,7 +140,8 @@
<item quantity="one">ডিভাইচটো <xliff:g id="NUMBER_1">%d</xliff:g> ঘণ্টা ধৰি আনলক কৰা হোৱা নাই। পাছৱৰ্ড নিশ্চিত কৰক।</item>
<item quantity="other">ডিভাইচটো <xliff:g id="NUMBER_1">%d</xliff:g> ঘণ্টা ধৰি আনলক কৰা হোৱা নাই। পাছৱৰ্ড নিশ্চিত কৰক।</item>
</plurals>
- <string name="fingerprint_not_recognized" msgid="348813995267914625">"চিনাক্ত কৰিব পৰা নগ\'ল"</string>
+ <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"চিনাক্ত কৰিব পৰা নাই"</string>
+ <string name="kg_face_not_recognized" msgid="6382535088345875294">"চিনাক্ত কৰিব পৰা নাই"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
<item quantity="one">ছিমৰ পিন দিয়ক। আপুনি আৰু <xliff:g id="NUMBER_1">%d</xliff:g>বাৰ প্ৰয়াস কৰিব পাৰে।</item>
<item quantity="other">ছিমৰ পিন দিয়ক। আপুনি আৰু <xliff:g id="NUMBER_1">%d</xliff:g>বাৰ প্ৰয়াস কৰিব পাৰে।</item>
diff --git a/packages/SystemUI/res-keyguard/values-az/strings.xml b/packages/SystemUI/res-keyguard/values-az/strings.xml
index 0926194..19a0963 100644
--- a/packages/SystemUI/res-keyguard/values-az/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-az/strings.xml
@@ -140,7 +140,8 @@
<item quantity="other">Cihaz <xliff:g id="NUMBER_1">%d</xliff:g> saat kiliddən çıxarılmayıb. Parolu təsdiq edin.</item>
<item quantity="one">Cihaz <xliff:g id="NUMBER_0">%d</xliff:g> saat kiliddən çıxarılmayıb. Parolu təsdiq edin.</item>
</plurals>
- <string name="fingerprint_not_recognized" msgid="348813995267914625">"Tanınmır"</string>
+ <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Tanınmır"</string>
+ <string name="kg_face_not_recognized" msgid="6382535088345875294">"Tanınmır"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
<item quantity="other">SIM PIN-ni daxil edin. <xliff:g id="NUMBER_1">%d</xliff:g> cəhdiniz qalır.</item>
<item quantity="one">SIM PIN-ni daxil edin. Cihazınızı kiliddən çıxarmaq üçün operatorunuzla əlaqə saxlamadan öncə <xliff:g id="NUMBER_0">%d</xliff:g> cəhdiniz qalır.</item>
diff --git a/packages/SystemUI/res-keyguard/values-b+sr+Latn/strings.xml b/packages/SystemUI/res-keyguard/values-b+sr+Latn/strings.xml
index 67da7b8..1e8e443 100644
--- a/packages/SystemUI/res-keyguard/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-b+sr+Latn/strings.xml
@@ -146,7 +146,8 @@
<item quantity="few">Niste otključali uređaj <xliff:g id="NUMBER_1">%d</xliff:g> sata. Potvrdite lozinku.</item>
<item quantity="other">Niste otključali uređaj <xliff:g id="NUMBER_1">%d</xliff:g> sati. Potvrdite lozinku.</item>
</plurals>
- <string name="fingerprint_not_recognized" msgid="348813995267914625">"Nije prepoznat"</string>
+ <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Nije prepoznat"</string>
+ <string name="kg_face_not_recognized" msgid="6382535088345875294">"Nije prepoznat"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
<item quantity="one">Unesite PIN za SIM. Imate još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaj.</item>
<item quantity="few">Unesite PIN za SIM. Imate još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaja.</item>
diff --git a/packages/SystemUI/res-keyguard/values-be/strings.xml b/packages/SystemUI/res-keyguard/values-be/strings.xml
index 5b11a09..8251071 100644
--- a/packages/SystemUI/res-keyguard/values-be/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-be/strings.xml
@@ -152,7 +152,8 @@
<item quantity="many">Прылада не была разблакіравана на працягу <xliff:g id="NUMBER_1">%d</xliff:g> гадзін. Увядзіце пароль.</item>
<item quantity="other">Прылада не была разблакіравана на працягу <xliff:g id="NUMBER_1">%d</xliff:g> гадзіны. Увядзіце пароль.</item>
</plurals>
- <string name="fingerprint_not_recognized" msgid="348813995267914625">"Не распазнаны"</string>
+ <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Не распазнана"</string>
+ <string name="kg_face_not_recognized" msgid="6382535088345875294">"Не распазнана"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
<item quantity="one">Увядзіце PIN-код SIM-карты. У вас засталася <xliff:g id="NUMBER_1">%d</xliff:g> спроба.</item>
<item quantity="few">Увядзіце PIN-код SIM-карты. У вас засталося <xliff:g id="NUMBER_1">%d</xliff:g> спробы.</item>
diff --git a/packages/SystemUI/res-keyguard/values-bg/strings.xml b/packages/SystemUI/res-keyguard/values-bg/strings.xml
index 663d5f0..7438d7f 100644
--- a/packages/SystemUI/res-keyguard/values-bg/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-bg/strings.xml
@@ -140,7 +140,8 @@
<item quantity="other">Устройството не е отключвано от <xliff:g id="NUMBER_1">%d</xliff:g> часа. Потвърдете паролата.</item>
<item quantity="one">Устройството не е отключвано от <xliff:g id="NUMBER_0">%d</xliff:g> час. Потвърдете паролата.</item>
</plurals>
- <string name="fingerprint_not_recognized" msgid="348813995267914625">"Не е разпознато"</string>
+ <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Не е разпознато"</string>
+ <string name="kg_face_not_recognized" msgid="6382535088345875294">"Не е разпознато"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
<item quantity="other">Въведете ПИН кода за SIM картата – остават ви <xliff:g id="NUMBER_1">%d</xliff:g> опита.</item>
<item quantity="one">Въведете ПИН кода за SIM картата – остава ви <xliff:g id="NUMBER_0">%d</xliff:g> опит, преди да се наложи да се свържете с оператора си, за да отключите устройството.</item>
diff --git a/packages/SystemUI/res-keyguard/values-bn/strings.xml b/packages/SystemUI/res-keyguard/values-bn/strings.xml
index 7a3b575..814aafe 100644
--- a/packages/SystemUI/res-keyguard/values-bn/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-bn/strings.xml
@@ -140,7 +140,10 @@
<item quantity="one">ডিভাইসটি <xliff:g id="NUMBER_1">%d</xliff:g> ঘণ্টা ধরে আনলক করা হয় নি। পাসওয়ার্ড নিশ্চিত করুন।</item>
<item quantity="other">ডিভাইসটি <xliff:g id="NUMBER_1">%d</xliff:g> ঘণ্টা ধরে আনলক করা হয় নি। পাসওয়ার্ড নিশ্চিত করুন।</item>
</plurals>
- <string name="fingerprint_not_recognized" msgid="348813995267914625">"স্বীকৃত নয়"</string>
+ <!-- no translation found for kg_fingerprint_not_recognized (7854413849848459418) -->
+ <skip />
+ <!-- no translation found for kg_face_not_recognized (6382535088345875294) -->
+ <skip />
<plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
<item quantity="one">সিমের পিন লিখুন। আপনি আর <xliff:g id="NUMBER_1">%d</xliff:g> বার চেষ্টা করতে পারবেন।</item>
<item quantity="other">সিমের পিন লিখুন। আপনি আর <xliff:g id="NUMBER_1">%d</xliff:g> বার চেষ্টা করতে পারবেন।</item>
diff --git a/packages/SystemUI/res-keyguard/values-bs/strings.xml b/packages/SystemUI/res-keyguard/values-bs/strings.xml
index 319014e..a87b72a 100644
--- a/packages/SystemUI/res-keyguard/values-bs/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-bs/strings.xml
@@ -146,7 +146,8 @@
<item quantity="few">Uređaj nije otključavan <xliff:g id="NUMBER_1">%d</xliff:g> sata. Potvrdite lozinku.</item>
<item quantity="other">Uređaj nije otključavan <xliff:g id="NUMBER_1">%d</xliff:g> sati. Potvrdite lozinku.</item>
</plurals>
- <string name="fingerprint_not_recognized" msgid="348813995267914625">"Nije prepoznat"</string>
+ <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Nije prepoznat"</string>
+ <string name="kg_face_not_recognized" msgid="6382535088345875294">"Nije prepoznat"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
<item quantity="one">Unesite PIN za SIM. Imate još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaj.</item>
<item quantity="few">Unesite PIN za SIM. Imate još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaja.</item>
diff --git a/packages/SystemUI/res-keyguard/values-ca/strings.xml b/packages/SystemUI/res-keyguard/values-ca/strings.xml
index 7b53abe..093bd1c 100644
--- a/packages/SystemUI/res-keyguard/values-ca/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ca/strings.xml
@@ -140,7 +140,8 @@
<item quantity="other">Fa <xliff:g id="NUMBER_1">%d</xliff:g> hores que no es desbloqueja el dispositiu. Confirma la contrasenya.</item>
<item quantity="one">Fa <xliff:g id="NUMBER_0">%d</xliff:g> hora que no es desbloqueja el dispositiu. Confirma la contrasenya.</item>
</plurals>
- <string name="fingerprint_not_recognized" msgid="348813995267914625">"No s\'ha reconegut"</string>
+ <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"No s\'ha reconegut"</string>
+ <string name="kg_face_not_recognized" msgid="6382535088345875294">"No s\'ha reconegut"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
<item quantity="other">Introdueix el PIN de la SIM. Et queden <xliff:g id="NUMBER_1">%d</xliff:g> intents.</item>
<item quantity="one">Introdueix el PIN de la SIM. Et queda <xliff:g id="NUMBER_0">%d</xliff:g> intent; si no l\'encertes, contacta amb l\'operador de telefonia mòbil per desbloquejar el dispositiu.</item>
diff --git a/packages/SystemUI/res-keyguard/values-cs/strings.xml b/packages/SystemUI/res-keyguard/values-cs/strings.xml
index d827b25..2142c9c 100644
--- a/packages/SystemUI/res-keyguard/values-cs/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-cs/strings.xml
@@ -152,7 +152,8 @@
<item quantity="other">Zařízení již <xliff:g id="NUMBER_1">%d</xliff:g> hodin nebylo odemknuto. Zadejte heslo.</item>
<item quantity="one">Zařízení již <xliff:g id="NUMBER_0">%d</xliff:g> hodinu nebylo odemknuto. Zadejte heslo.</item>
</plurals>
- <string name="fingerprint_not_recognized" msgid="348813995267914625">"Nerozpoznáno"</string>
+ <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Nerozpoznáno"</string>
+ <string name="kg_face_not_recognized" msgid="6382535088345875294">"Nerozpoznáno"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
<item quantity="few">Zadejte PIN SIM karty. Zbývají <xliff:g id="NUMBER_1">%d</xliff:g> pokusy.</item>
<item quantity="many">Zadejte PIN SIM karty. Zbývá <xliff:g id="NUMBER_1">%d</xliff:g> pokusu.</item>
diff --git a/packages/SystemUI/res-keyguard/values-da/strings.xml b/packages/SystemUI/res-keyguard/values-da/strings.xml
index b65087a..df4ab21 100644
--- a/packages/SystemUI/res-keyguard/values-da/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-da/strings.xml
@@ -140,7 +140,8 @@
<item quantity="one">Enheden blev sidst låst op for <xliff:g id="NUMBER_1">%d</xliff:g> time siden. Bekræft adgangskoden.</item>
<item quantity="other">Enheden blev sidst låst op for <xliff:g id="NUMBER_1">%d</xliff:g> timer siden. Bekræft adgangskoden.</item>
</plurals>
- <string name="fingerprint_not_recognized" msgid="348813995267914625">"Ikke genkendt"</string>
+ <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Ikke genkendt"</string>
+ <string name="kg_face_not_recognized" msgid="6382535088345875294">"Ikke genkendt"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
<item quantity="one">Angiv pinkoden til SIM-kortet. Du har <xliff:g id="NUMBER_1">%d</xliff:g> forsøg tilbage.</item>
<item quantity="other">Angiv pinkoden til SIM-kortet. Du har <xliff:g id="NUMBER_1">%d</xliff:g> forsøg tilbage.</item>
diff --git a/packages/SystemUI/res-keyguard/values-de/strings.xml b/packages/SystemUI/res-keyguard/values-de/strings.xml
index 8a1de2c..c86a9ad 100644
--- a/packages/SystemUI/res-keyguard/values-de/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-de/strings.xml
@@ -140,7 +140,8 @@
<item quantity="other">Das Gerät wurde seit <xliff:g id="NUMBER_1">%d</xliff:g> Stunden nicht mehr entsperrt. Bitte bestätige das Passwort.</item>
<item quantity="one">Das Gerät wurde seit <xliff:g id="NUMBER_0">%d</xliff:g> Stunde nicht mehr entsperrt. Bitte bestätige das Passwort.</item>
</plurals>
- <string name="fingerprint_not_recognized" msgid="348813995267914625">"Nicht erkannt"</string>
+ <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Nicht erkannt"</string>
+ <string name="kg_face_not_recognized" msgid="6382535088345875294">"Nicht erkannt"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
<item quantity="other">Gib die PIN für die SIM-Karte ein. Du hast noch <xliff:g id="NUMBER_1">%d</xliff:g> Versuche.</item>
<item quantity="one">Gib die PIN für die SIM-Karte ein. Du hast noch <xliff:g id="NUMBER_0">%d</xliff:g> Versuch, bevor das Gerät nur noch vom Mobilfunkanbieter entsperrt werden kann.</item>
diff --git a/packages/SystemUI/res-keyguard/values-el/strings.xml b/packages/SystemUI/res-keyguard/values-el/strings.xml
index 2eee705..59e7669 100644
--- a/packages/SystemUI/res-keyguard/values-el/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-el/strings.xml
@@ -46,7 +46,7 @@
<string name="keyguard_missing_sim_instructions" msgid="7350295932015220392">"Τοποθετήστε μια κάρτα SIM."</string>
<string name="keyguard_missing_sim_instructions_long" msgid="589889372883904477">"Η κάρτα SIM δεν υπάρχει ή δεν είναι δυνατή η ανάγνωσή της. Τοποθετήστε μια κάρτα SIM."</string>
<string name="keyguard_permanent_disabled_sim_message_short" msgid="654102080186420706">"Η κάρτα SIM δεν μπορεί να χρησιμοποιηθεί."</string>
- <string name="keyguard_permanent_disabled_sim_instructions" msgid="4683178224791318347">"Η κάρτα SIM έχει απενεργοποιηθεί οριστικά.\n Επικοινωνήστε με τον παροχέα υπηρεσιών ασύρματου δικτύου για να λάβετε μια νέα κάρτα SIM."</string>
+ <string name="keyguard_permanent_disabled_sim_instructions" msgid="4683178224791318347">"Η κάρτα SIM έχει απενεργοποιηθεί οριστικά.\n Επικοινωνήστε με τον πάροχο υπηρεσιών ασύρματου δικτύου για να λάβετε μια νέα κάρτα SIM."</string>
<string name="keyguard_sim_locked_message" msgid="953766009432168127">"Η κάρτα SIM είναι κλειδωμένη."</string>
<string name="keyguard_sim_puk_locked_message" msgid="1772789643694942073">"Η κάρτα SIM είναι κλειδωμένη με κωδικό PUK."</string>
<string name="keyguard_sim_unlock_progress_dialog_message" msgid="3586601150825821675">"Ξεκλείδωμα κάρτας SIM…"</string>
@@ -140,7 +140,8 @@
<item quantity="other">Η συσκευή δεν έχει ξεκλειδωθεί εδώ και <xliff:g id="NUMBER_1">%d</xliff:g> ώρες. Επιβεβαιώστε τον κωδικό πρόσβασης.</item>
<item quantity="one">Η συσκευή δεν έχει ξεκλειδωθεί εδώ και <xliff:g id="NUMBER_0">%d</xliff:g> ώρα. Επιβεβαιώστε τον κωδικό πρόσβασης.</item>
</plurals>
- <string name="fingerprint_not_recognized" msgid="348813995267914625">"Δεν αναγνωρίστηκε"</string>
+ <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Δεν αναγνωρίστηκε"</string>
+ <string name="kg_face_not_recognized" msgid="6382535088345875294">"Δεν αναγνωρίστηκε"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
<item quantity="other">Εισαγάγετε τον αριθμό PIN της κάρτας SIM. Απομένουν άλλες <xliff:g id="NUMBER_1">%d</xliff:g> προσπάθειες.</item>
<item quantity="one">Εισαγάγετε τον αριθμό PIN της κάρτας SIM. Απομένει άλλη <xliff:g id="NUMBER_0">%d</xliff:g> προσπάθεια. Στη συνέχεια, θα πρέπει να επικοινωνήσετε με τον πάροχο κινητής τηλεφωνίας, για να ξεκλειδώσετε τη συσκευή.</item>
diff --git a/packages/SystemUI/res-keyguard/values-en-rAU/strings.xml b/packages/SystemUI/res-keyguard/values-en-rAU/strings.xml
index 44ef5524..77ff1b7 100644
--- a/packages/SystemUI/res-keyguard/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-en-rAU/strings.xml
@@ -140,7 +140,8 @@
<item quantity="other">Device hasn\'t been unlocked for <xliff:g id="NUMBER_1">%d</xliff:g> hours. Confirm password.</item>
<item quantity="one">Device hasn\'t been unlocked for <xliff:g id="NUMBER_0">%d</xliff:g> hour. Confirm password.</item>
</plurals>
- <string name="fingerprint_not_recognized" msgid="348813995267914625">"Not recognised"</string>
+ <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Not recognised"</string>
+ <string name="kg_face_not_recognized" msgid="6382535088345875294">"Not recognised"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
<item quantity="other">Enter SIM PIN. You have <xliff:g id="NUMBER_1">%d</xliff:g> remaining attempts.</item>
<item quantity="one">Enter SIM PIN. You have <xliff:g id="NUMBER_0">%d</xliff:g> remaining attempt before you must contact your operator to unlock your device.</item>
diff --git a/packages/SystemUI/res-keyguard/values-en-rCA/strings.xml b/packages/SystemUI/res-keyguard/values-en-rCA/strings.xml
index 1bddc86..dafdd32 100644
--- a/packages/SystemUI/res-keyguard/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-en-rCA/strings.xml
@@ -140,7 +140,8 @@
<item quantity="other">Device hasn\'t been unlocked for <xliff:g id="NUMBER_1">%d</xliff:g> hours. Confirm password.</item>
<item quantity="one">Device hasn\'t been unlocked for <xliff:g id="NUMBER_0">%d</xliff:g> hour. Confirm password.</item>
</plurals>
- <string name="fingerprint_not_recognized" msgid="348813995267914625">"Not recognised"</string>
+ <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Not recognised"</string>
+ <string name="kg_face_not_recognized" msgid="6382535088345875294">"Not recognised"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
<item quantity="other">Enter SIM PIN. You have <xliff:g id="NUMBER_1">%d</xliff:g> remaining attempts.</item>
<item quantity="one">Enter SIM PIN. You have <xliff:g id="NUMBER_0">%d</xliff:g> remaining attempt before you must contact your operator to unlock your device.</item>
diff --git a/packages/SystemUI/res-keyguard/values-en-rGB/strings.xml b/packages/SystemUI/res-keyguard/values-en-rGB/strings.xml
index 44ef5524..77ff1b7 100644
--- a/packages/SystemUI/res-keyguard/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-en-rGB/strings.xml
@@ -140,7 +140,8 @@
<item quantity="other">Device hasn\'t been unlocked for <xliff:g id="NUMBER_1">%d</xliff:g> hours. Confirm password.</item>
<item quantity="one">Device hasn\'t been unlocked for <xliff:g id="NUMBER_0">%d</xliff:g> hour. Confirm password.</item>
</plurals>
- <string name="fingerprint_not_recognized" msgid="348813995267914625">"Not recognised"</string>
+ <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Not recognised"</string>
+ <string name="kg_face_not_recognized" msgid="6382535088345875294">"Not recognised"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
<item quantity="other">Enter SIM PIN. You have <xliff:g id="NUMBER_1">%d</xliff:g> remaining attempts.</item>
<item quantity="one">Enter SIM PIN. You have <xliff:g id="NUMBER_0">%d</xliff:g> remaining attempt before you must contact your operator to unlock your device.</item>
diff --git a/packages/SystemUI/res-keyguard/values-en-rIN/strings.xml b/packages/SystemUI/res-keyguard/values-en-rIN/strings.xml
index 44ef5524..77ff1b7 100644
--- a/packages/SystemUI/res-keyguard/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-en-rIN/strings.xml
@@ -140,7 +140,8 @@
<item quantity="other">Device hasn\'t been unlocked for <xliff:g id="NUMBER_1">%d</xliff:g> hours. Confirm password.</item>
<item quantity="one">Device hasn\'t been unlocked for <xliff:g id="NUMBER_0">%d</xliff:g> hour. Confirm password.</item>
</plurals>
- <string name="fingerprint_not_recognized" msgid="348813995267914625">"Not recognised"</string>
+ <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Not recognised"</string>
+ <string name="kg_face_not_recognized" msgid="6382535088345875294">"Not recognised"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
<item quantity="other">Enter SIM PIN. You have <xliff:g id="NUMBER_1">%d</xliff:g> remaining attempts.</item>
<item quantity="one">Enter SIM PIN. You have <xliff:g id="NUMBER_0">%d</xliff:g> remaining attempt before you must contact your operator to unlock your device.</item>
diff --git a/packages/SystemUI/res-keyguard/values-en-rXC/strings.xml b/packages/SystemUI/res-keyguard/values-en-rXC/strings.xml
index 987d983..5eac25c 100644
--- a/packages/SystemUI/res-keyguard/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-en-rXC/strings.xml
@@ -140,7 +140,8 @@
<item quantity="other">Device hasn\'t been unlocked for <xliff:g id="NUMBER_1">%d</xliff:g> hours. Confirm password.</item>
<item quantity="one">Device hasn\'t been unlocked for <xliff:g id="NUMBER_0">%d</xliff:g> hour. Confirm password.</item>
</plurals>
- <string name="fingerprint_not_recognized" msgid="348813995267914625">"Not recognized"</string>
+ <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Not recognized"</string>
+ <string name="kg_face_not_recognized" msgid="6382535088345875294">"Not recognized"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
<item quantity="other">Enter SIM PIN. You have <xliff:g id="NUMBER_1">%d</xliff:g> remaining attempts.</item>
<item quantity="one">Enter SIM PIN. You have <xliff:g id="NUMBER_0">%d</xliff:g> remaining attempt before you must contact your carrier to unlock your device.</item>
diff --git a/packages/SystemUI/res-keyguard/values-es-rUS/strings.xml b/packages/SystemUI/res-keyguard/values-es-rUS/strings.xml
index 0583d4b..8af47fd 100644
--- a/packages/SystemUI/res-keyguard/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-es-rUS/strings.xml
@@ -140,7 +140,8 @@
<item quantity="other">Hace <xliff:g id="NUMBER_1">%d</xliff:g> horas que no se desbloquea el dispositivo. Confirma la contraseña.</item>
<item quantity="one">Hace <xliff:g id="NUMBER_0">%d</xliff:g> hora que no se desbloquea el dispositivo. Confirma la contraseña.</item>
</plurals>
- <string name="fingerprint_not_recognized" msgid="348813995267914625">"No se reconoció"</string>
+ <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"No se reconoció"</string>
+ <string name="kg_face_not_recognized" msgid="6382535088345875294">"No se reconoció"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
<item quantity="other">Ingresa el PIN de la SIM. Quedan <xliff:g id="NUMBER_1">%d</xliff:g> intentos más.</item>
<item quantity="one">Ingresa el PIN de la SIM. Queda <xliff:g id="NUMBER_0">%d</xliff:g> intento antes de que debas comunicarte con tu proveedor para desbloquear el dispositivo.</item>
diff --git a/packages/SystemUI/res-keyguard/values-es/strings.xml b/packages/SystemUI/res-keyguard/values-es/strings.xml
index 2d6721d..92dc58f 100644
--- a/packages/SystemUI/res-keyguard/values-es/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-es/strings.xml
@@ -140,7 +140,8 @@
<item quantity="other">El dispositivo no se ha desbloqueado durante <xliff:g id="NUMBER_1">%d</xliff:g> horas. Confirma la contraseña.</item>
<item quantity="one">El dispositivo no se ha desbloqueado durante <xliff:g id="NUMBER_0">%d</xliff:g> hora. Confirma la contraseña.</item>
</plurals>
- <string name="fingerprint_not_recognized" msgid="348813995267914625">"No reconocido"</string>
+ <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"No reconocida"</string>
+ <string name="kg_face_not_recognized" msgid="6382535088345875294">"No reconocida"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
<item quantity="other">Introduce el PIN de la tarjeta SIM. Te quedan <xliff:g id="NUMBER_1">%d</xliff:g> intentos.</item>
<item quantity="one">Introduce el PIN de la tarjeta SIM. Te queda <xliff:g id="NUMBER_0">%d</xliff:g> intento para tener que ponerte en contacto con tu operador para desbloquear el dispositivo.</item>
diff --git a/packages/SystemUI/res-keyguard/values-et/strings.xml b/packages/SystemUI/res-keyguard/values-et/strings.xml
index ac37c5c..ad6becd 100644
--- a/packages/SystemUI/res-keyguard/values-et/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-et/strings.xml
@@ -140,7 +140,8 @@
<item quantity="other">Seadet pole avatud <xliff:g id="NUMBER_1">%d</xliff:g> tundi. Kinnitage parool.</item>
<item quantity="one">Seadet pole avatud <xliff:g id="NUMBER_0">%d</xliff:g> tund. Kinnitage parool.</item>
</plurals>
- <string name="fingerprint_not_recognized" msgid="348813995267914625">"Ei tuvastatud"</string>
+ <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Ei tuvastatud"</string>
+ <string name="kg_face_not_recognized" msgid="6382535088345875294">"Ei tuvastatud"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
<item quantity="other">Sisestage SIM-kaardi PIN-kood. Jäänud on <xliff:g id="NUMBER_1">%d</xliff:g> katset.</item>
<item quantity="one">Sisestage SIM-kaardi PIN-kood. Jäänud on <xliff:g id="NUMBER_0">%d</xliff:g> katse enne, kui peate seadme avamiseks ühendust võtma operaatoriga.</item>
diff --git a/packages/SystemUI/res-keyguard/values-eu/strings.xml b/packages/SystemUI/res-keyguard/values-eu/strings.xml
index 06bb32b..4036851 100644
--- a/packages/SystemUI/res-keyguard/values-eu/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-eu/strings.xml
@@ -140,7 +140,8 @@
<item quantity="other">Gailua ez da desblokeatu <xliff:g id="NUMBER_1">%d</xliff:g> orduz. Berretsi pasahitza.</item>
<item quantity="one">Gailua ez da desblokeatu <xliff:g id="NUMBER_0">%d</xliff:g> orduz. Berretsi pasahitza.</item>
</plurals>
- <string name="fingerprint_not_recognized" msgid="348813995267914625">"Ez da ezagutu"</string>
+ <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Ez da ezagutu"</string>
+ <string name="kg_face_not_recognized" msgid="6382535088345875294">"Ez da ezagutu"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
<item quantity="other">Idatzi SIM txartelaren PIN kodea. <xliff:g id="NUMBER_1">%d</xliff:g> saiakera geratzen zaizkizu.</item>
<item quantity="one">Idatzi SIM txartelaren PIN kodea. <xliff:g id="NUMBER_0">%d</xliff:g> saiakera geratzen zaizu; oker idatziz gero, operadoreari eskatu beharko diozu gailua desblokeatzeko.</item>
diff --git a/packages/SystemUI/res-keyguard/values-fa/strings.xml b/packages/SystemUI/res-keyguard/values-fa/strings.xml
index f273d6e..3b1d8d7 100644
--- a/packages/SystemUI/res-keyguard/values-fa/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-fa/strings.xml
@@ -140,7 +140,8 @@
<item quantity="one">قفل دستگاه <xliff:g id="NUMBER_1">%d</xliff:g> ساعت باز نشده است. گذرواژه را تأیید کنید.</item>
<item quantity="other">قفل دستگاه <xliff:g id="NUMBER_1">%d</xliff:g> ساعت باز نشده است. گذرواژه را تأیید کنید.</item>
</plurals>
- <string name="fingerprint_not_recognized" msgid="348813995267914625">"شناسایی نشد"</string>
+ <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"شناسایی نشد"</string>
+ <string name="kg_face_not_recognized" msgid="6382535088345875294">"شناسایی نشد"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
<item quantity="one">پین سیمکارت را وارد کنید. <xliff:g id="NUMBER_1">%d</xliff:g> تلاش دیگری باقی مانده است.</item>
<item quantity="other">پین سیمکارت را وارد کنید. <xliff:g id="NUMBER_1">%d</xliff:g> تلاش دیگری باقی مانده است.</item>
diff --git a/packages/SystemUI/res-keyguard/values-fi/strings.xml b/packages/SystemUI/res-keyguard/values-fi/strings.xml
index 5267151..34d830f 100644
--- a/packages/SystemUI/res-keyguard/values-fi/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-fi/strings.xml
@@ -140,7 +140,8 @@
<item quantity="other">Laitteen lukitusta ei ole avattu <xliff:g id="NUMBER_1">%d</xliff:g> tuntiin. Vahvista salasana.</item>
<item quantity="one">Laitteen lukitusta ei ole avattu <xliff:g id="NUMBER_0">%d</xliff:g> tuntiin. Vahvista salasana.</item>
</plurals>
- <string name="fingerprint_not_recognized" msgid="348813995267914625">"Ei tunnistettu"</string>
+ <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Ei tunnistettu"</string>
+ <string name="kg_face_not_recognized" msgid="6382535088345875294">"Ei tunnistettu"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
<item quantity="other">Anna SIM-kortin PIN-koodi. Sinulla on <xliff:g id="NUMBER_1">%d</xliff:g> yritystä jäljellä.</item>
<item quantity="one">Anna SIM-kortin PIN-koodi. <xliff:g id="NUMBER_0">%d</xliff:g> yrityksen jälkeen laite lukittuu, ja vain operaattori voi avata sen.</item>
diff --git a/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml b/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml
index 742e1eb..c344807 100644
--- a/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml
@@ -140,7 +140,8 @@
<item quantity="one">L\'appareil n\'a pas été déverrouillé depuis <xliff:g id="NUMBER_1">%d</xliff:g> heure. Confirmez le mot de passe.</item>
<item quantity="other">L\'appareil n\'a pas été déverrouillé depuis <xliff:g id="NUMBER_1">%d</xliff:g> heures. Confirmez le mot de passe.</item>
</plurals>
- <string name="fingerprint_not_recognized" msgid="348813995267914625">"Doigt non reconnu"</string>
+ <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Doigt non reconnu"</string>
+ <string name="kg_face_not_recognized" msgid="6382535088345875294">"Doigt non reconnu"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
<item quantity="one">Entrez le NIP de votre carte SIM. Il vous reste <xliff:g id="NUMBER_1">%d</xliff:g> tentative.</item>
<item quantity="other">Entrez le NIP de votre carte SIM. Il vous reste <xliff:g id="NUMBER_1">%d</xliff:g> tentatives.</item>
diff --git a/packages/SystemUI/res-keyguard/values-fr/strings.xml b/packages/SystemUI/res-keyguard/values-fr/strings.xml
index 9ada218..bf8c7e8 100644
--- a/packages/SystemUI/res-keyguard/values-fr/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-fr/strings.xml
@@ -140,7 +140,8 @@
<item quantity="one">L\'appareil n\'a pas été déverrouillé depuis <xliff:g id="NUMBER_1">%d</xliff:g> heure. Confirmez le mot de passe.</item>
<item quantity="other">L\'appareil n\'a pas été déverrouillé depuis <xliff:g id="NUMBER_1">%d</xliff:g> heures. Confirmez le mot de passe.</item>
</plurals>
- <string name="fingerprint_not_recognized" msgid="348813995267914625">"Non reconnu"</string>
+ <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Non reconnu"</string>
+ <string name="kg_face_not_recognized" msgid="6382535088345875294">"Non reconnu"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
<item quantity="one">Saisissez le code de la carte SIM. <xliff:g id="NUMBER_1">%d</xliff:g> tentative restante.</item>
<item quantity="other">Saisissez le code de la carte SIM. <xliff:g id="NUMBER_1">%d</xliff:g> tentatives restantes.</item>
diff --git a/packages/SystemUI/res-keyguard/values-gl/strings.xml b/packages/SystemUI/res-keyguard/values-gl/strings.xml
index 4a8fe8a..b4ff9ea 100644
--- a/packages/SystemUI/res-keyguard/values-gl/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-gl/strings.xml
@@ -140,7 +140,8 @@
<item quantity="other">O dispositivo non se desbloqueou durante <xliff:g id="NUMBER_1">%d</xliff:g> horas. Confirma o contrasinal.</item>
<item quantity="one">O dispositivo non se desbloqueou durante <xliff:g id="NUMBER_0">%d</xliff:g> hora. Confirma o contrasinal.</item>
</plurals>
- <string name="fingerprint_not_recognized" msgid="348813995267914625">"Non se recoñece"</string>
+ <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Non se recoñeceu"</string>
+ <string name="kg_face_not_recognized" msgid="6382535088345875294">"Non se recoñeceu"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
<item quantity="other">Introduce o código PIN da SIM. Quédanche <xliff:g id="NUMBER_1">%d</xliff:g> intentos.</item>
<item quantity="one">Introduce o código PIN da SIM. Quédache <xliff:g id="NUMBER_0">%d</xliff:g> intento antes de que teñas que contactar co operador para desbloquear o dispositivo.</item>
diff --git a/packages/SystemUI/res-keyguard/values-gu/strings.xml b/packages/SystemUI/res-keyguard/values-gu/strings.xml
index 6a10af5..e2cd09b 100644
--- a/packages/SystemUI/res-keyguard/values-gu/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-gu/strings.xml
@@ -140,7 +140,10 @@
<item quantity="one">ઉપકરણને <xliff:g id="NUMBER_1">%d</xliff:g> કલાક માટે અનલૉક કરવામાં આવ્યું નથી. પાસવર્ડની પુષ્ટિ કરો.</item>
<item quantity="other">ઉપકરણને <xliff:g id="NUMBER_1">%d</xliff:g> કલાક માટે અનલૉક કરવામાં આવ્યું નથી. પાસવર્ડની પુષ્ટિ કરો.</item>
</plurals>
- <string name="fingerprint_not_recognized" msgid="348813995267914625">"ઓળખાયેલ નથી"</string>
+ <!-- no translation found for kg_fingerprint_not_recognized (7854413849848459418) -->
+ <skip />
+ <!-- no translation found for kg_face_not_recognized (6382535088345875294) -->
+ <skip />
<plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
<item quantity="one">સિમનો પિન દાખલ કરો, તમારી પાસે <xliff:g id="NUMBER_1">%d</xliff:g> પ્રયાસ બાકી છે.</item>
<item quantity="other">સિમનો પિન દાખલ કરો, તમારી પાસે <xliff:g id="NUMBER_1">%d</xliff:g> પ્રયાસો બાકી છે.</item>
diff --git a/packages/SystemUI/res-keyguard/values-hi/strings.xml b/packages/SystemUI/res-keyguard/values-hi/strings.xml
index 553f663..539efd9 100644
--- a/packages/SystemUI/res-keyguard/values-hi/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-hi/strings.xml
@@ -140,7 +140,10 @@
<item quantity="one">डिवाइस को <xliff:g id="NUMBER_1">%d</xliff:g> घंटों से अनलॉक नहीं किया गया है. पासवर्ड की पुष्टि करें.</item>
<item quantity="other">डिवाइस को <xliff:g id="NUMBER_1">%d</xliff:g> घंटों से अनलॉक नहीं किया गया है. पासवर्ड की पुष्टि करें.</item>
</plurals>
- <string name="fingerprint_not_recognized" msgid="348813995267914625">"उंगली की पहचान नहीं हो सकी"</string>
+ <!-- no translation found for kg_fingerprint_not_recognized (7854413849848459418) -->
+ <skip />
+ <!-- no translation found for kg_face_not_recognized (6382535088345875294) -->
+ <skip />
<plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
<item quantity="one">सिम का पिन डालें. आपके पास <xliff:g id="NUMBER_1">%d</xliff:g> मौके बचे हैं.</item>
<item quantity="other">सिम का पिन डालें. आपके पास <xliff:g id="NUMBER_1">%d</xliff:g> मौके बचे हैं.</item>
diff --git a/packages/SystemUI/res-keyguard/values-hr/strings.xml b/packages/SystemUI/res-keyguard/values-hr/strings.xml
index 7485cef..6ae39b2 100644
--- a/packages/SystemUI/res-keyguard/values-hr/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-hr/strings.xml
@@ -146,7 +146,8 @@
<item quantity="few">Uređaj nije bio otključan <xliff:g id="NUMBER_1">%d</xliff:g> sata. Potvrdite zaporku.</item>
<item quantity="other">Uređaj nije bio otključan <xliff:g id="NUMBER_1">%d</xliff:g> sati. Potvrdite zaporku.</item>
</plurals>
- <string name="fingerprint_not_recognized" msgid="348813995267914625">"Nije prepoznat"</string>
+ <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Nije prepoznat"</string>
+ <string name="kg_face_not_recognized" msgid="6382535088345875294">"Nije prepoznato"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
<item quantity="one">Unesite PIN za SIM. Imate još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaj.</item>
<item quantity="few">Unesite PIN za SIM. Imate još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaja.</item>
diff --git a/packages/SystemUI/res-keyguard/values-hu/strings.xml b/packages/SystemUI/res-keyguard/values-hu/strings.xml
index 371bc64..a03a8b2 100644
--- a/packages/SystemUI/res-keyguard/values-hu/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-hu/strings.xml
@@ -140,7 +140,8 @@
<item quantity="other">Az eszköz zárolása <xliff:g id="NUMBER_1">%d</xliff:g> órája nem lett feloldva. Erősítse meg a jelszót.</item>
<item quantity="one">Az eszköz zárolása <xliff:g id="NUMBER_0">%d</xliff:g> órája nem lett feloldva. Erősítse meg a jelszót.</item>
</plurals>
- <string name="fingerprint_not_recognized" msgid="348813995267914625">"Nem sikerült felismerni"</string>
+ <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Nem ismerhető fel"</string>
+ <string name="kg_face_not_recognized" msgid="6382535088345875294">"Nem ismerhető fel"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
<item quantity="other">Adja meg a SIM-kártya PIN-kódját. <xliff:g id="NUMBER_1">%d</xliff:g> próbálkozása maradt.</item>
<item quantity="one">Adja meg a SIM-kártya PIN-kódját. <xliff:g id="NUMBER_0">%d</xliff:g> próbálkozása maradt. Ha elfogynak a próbálkozási lehetőségek, az eszköz feloldásához fel kell vennie a kapcsolatot szolgáltatójával.</item>
diff --git a/packages/SystemUI/res-keyguard/values-hy/strings.xml b/packages/SystemUI/res-keyguard/values-hy/strings.xml
index 92cb87a..8009371 100644
--- a/packages/SystemUI/res-keyguard/values-hy/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-hy/strings.xml
@@ -140,7 +140,8 @@
<item quantity="one">Device hasn\'t been unlocked for <xliff:g id="NUMBER_1">%d</xliff:g> hours. Confirm password.</item>
<item quantity="other">Սարքը չի ապակողպվել <xliff:g id="NUMBER_1">%d</xliff:g> ժամվա ընթացքում: Հաստատեք գաղտնաբառը:</item>
</plurals>
- <string name="fingerprint_not_recognized" msgid="348813995267914625">"Չճանաչվեց"</string>
+ <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Չհաջողվեց ճանաչել"</string>
+ <string name="kg_face_not_recognized" msgid="6382535088345875294">"Չհաջողվեց ճանաչել"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
<item quantity="one">Մուտքագրեք SIM քարտի PIN կոդը: Մնացել է <xliff:g id="NUMBER_1">%d</xliff:g> փորձ:</item>
<item quantity="other">Մուտքագրեք SIM քարտի PIN կոդը: Մնացել է <xliff:g id="NUMBER_1">%d</xliff:g> փորձ:</item>
diff --git a/packages/SystemUI/res-keyguard/values-in/strings.xml b/packages/SystemUI/res-keyguard/values-in/strings.xml
index c0c828b..c1a20c6 100644
--- a/packages/SystemUI/res-keyguard/values-in/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-in/strings.xml
@@ -140,7 +140,8 @@
<item quantity="other">Perangkat belum dibuka kuncinya selama <xliff:g id="NUMBER_1">%d</xliff:g> jam. Konfirmasi sandi.</item>
<item quantity="one">Perangkat belum dibuka kuncinya selama <xliff:g id="NUMBER_0">%d</xliff:g> jam. Konfirmasi sandi.</item>
</plurals>
- <string name="fingerprint_not_recognized" msgid="348813995267914625">"Tidak dikenali"</string>
+ <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Tidak dikenali"</string>
+ <string name="kg_face_not_recognized" msgid="6382535088345875294">"Tidak dikenali"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
<item quantity="other">Masukkan PIN SIM. Tersisa <xliff:g id="NUMBER_1">%d</xliff:g> percobaan.</item>
<item quantity="one">Masukkan PIN SIM. Tersisa <xliff:g id="NUMBER_0">%d</xliff:g> percobaan sebelum Anda harus menghubungi operator untuk membuka kunci perangkat.</item>
diff --git a/packages/SystemUI/res-keyguard/values-is/strings.xml b/packages/SystemUI/res-keyguard/values-is/strings.xml
index dad5e69..0800b3e 100644
--- a/packages/SystemUI/res-keyguard/values-is/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-is/strings.xml
@@ -140,7 +140,8 @@
<item quantity="one">Tækið hefur ekki verið tekið úr lás í <xliff:g id="NUMBER_1">%d</xliff:g> klukkustund. Staðfestu aðgangsorðið.</item>
<item quantity="other">Tækið hefur ekki verið tekið úr lás í <xliff:g id="NUMBER_1">%d</xliff:g> klukkustundir. Staðfestu aðgangsorðið.</item>
</plurals>
- <string name="fingerprint_not_recognized" msgid="348813995267914625">"Þekktist ekki"</string>
+ <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Þekktist ekki"</string>
+ <string name="kg_face_not_recognized" msgid="6382535088345875294">"Þekktist ekki"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
<item quantity="one">Sláðu inn PIN-númer SIM-korts. Þú átt <xliff:g id="NUMBER_1">%d</xliff:g> tilraun eftir.</item>
<item quantity="other">Sláðu inn PIN-númer SIM-korts. Þú átt <xliff:g id="NUMBER_1">%d</xliff:g> tilraunir eftir.</item>
diff --git a/packages/SystemUI/res-keyguard/values-it/strings.xml b/packages/SystemUI/res-keyguard/values-it/strings.xml
index 2f322e8..07830c2 100644
--- a/packages/SystemUI/res-keyguard/values-it/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-it/strings.xml
@@ -140,7 +140,8 @@
<item quantity="other">Il dispositivo non viene sbloccato da <xliff:g id="NUMBER_1">%d</xliff:g> ore. Conferma la password.</item>
<item quantity="one">Il dispositivo non viene sbloccato da <xliff:g id="NUMBER_0">%d</xliff:g> ora. Conferma la password.</item>
</plurals>
- <string name="fingerprint_not_recognized" msgid="348813995267914625">"Non riconosciuta"</string>
+ <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Non riconosciuta"</string>
+ <string name="kg_face_not_recognized" msgid="6382535088345875294">"Non riconosciuta"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
<item quantity="other">Inserisci il codice PIN della SIM. Hai ancora <xliff:g id="NUMBER_1">%d</xliff:g> tentativi a disposizione.</item>
<item quantity="one">Inserisci il codice PIN della SIM. Hai ancora <xliff:g id="NUMBER_0">%d</xliff:g> tentativo a disposizione, dopodiché dovrai contattare l\'operatore per sbloccare il dispositivo.</item>
diff --git a/packages/SystemUI/res-keyguard/values-iw/strings.xml b/packages/SystemUI/res-keyguard/values-iw/strings.xml
index cfffcd2..6ed4e5f 100644
--- a/packages/SystemUI/res-keyguard/values-iw/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-iw/strings.xml
@@ -152,7 +152,8 @@
<item quantity="other">נעילת המכשיר לא בוטלה במשך <xliff:g id="NUMBER_1">%d</xliff:g> שעות. הזן את הסיסמה.</item>
<item quantity="one">נעילת המכשיר לא בוטלה במשך <xliff:g id="NUMBER_0">%d</xliff:g> שעה. הזן את הסיסמה.</item>
</plurals>
- <string name="fingerprint_not_recognized" msgid="348813995267914625">"לא זוהתה"</string>
+ <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"לא זוהתה"</string>
+ <string name="kg_face_not_recognized" msgid="6382535088345875294">"לא זוהתה"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
<item quantity="two">יש להזין קוד גישה לכרטיס SIM. נותרו לך <xliff:g id="NUMBER_1">%d</xliff:g> ניסונות נוספים.</item>
<item quantity="many">יש להזין קוד גישה לכרטיס SIM. נותרו לך <xliff:g id="NUMBER_1">%d</xliff:g> ניסונות נוספים.</item>
diff --git a/packages/SystemUI/res-keyguard/values-ja/strings.xml b/packages/SystemUI/res-keyguard/values-ja/strings.xml
index 98e8ce0..c9acb55 100644
--- a/packages/SystemUI/res-keyguard/values-ja/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ja/strings.xml
@@ -140,7 +140,8 @@
<item quantity="other">端末のロックが <xliff:g id="NUMBER_1">%d</xliff:g> 時間、解除されていません。パスワードを確認してください。</item>
<item quantity="one">端末のロックが <xliff:g id="NUMBER_0">%d</xliff:g> 時間、解除されていません。パスワードを確認してください。</item>
</plurals>
- <string name="fingerprint_not_recognized" msgid="348813995267914625">"認識されませんでした"</string>
+ <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"認識されませんでした"</string>
+ <string name="kg_face_not_recognized" msgid="6382535088345875294">"認識されませんでした"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
<item quantity="other">SIM PIN を入力してください。入力できるのはあと <xliff:g id="NUMBER_1">%d</xliff:g> 回です。</item>
<item quantity="one">SIM PIN を入力してください。入力できるのはあと <xliff:g id="NUMBER_0">%d</xliff:g> 回です。この回数を超えた場合は、携帯通信会社にお問い合わせください。</item>
diff --git a/packages/SystemUI/res-keyguard/values-ka/strings.xml b/packages/SystemUI/res-keyguard/values-ka/strings.xml
index df96979..c5f415b 100644
--- a/packages/SystemUI/res-keyguard/values-ka/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ka/strings.xml
@@ -140,7 +140,8 @@
<item quantity="other">მოწყობილობა არ განბლოკილა <xliff:g id="NUMBER_1">%d</xliff:g> საათის განმავლობაში. დაადასტურეთ პაროლი.</item>
<item quantity="one">მოწყობილობა არ განბლოკილა <xliff:g id="NUMBER_0">%d</xliff:g> საათის განმავლობაში. დაადასტურეთ პაროლი.</item>
</plurals>
- <string name="fingerprint_not_recognized" msgid="348813995267914625">"არ არის ამოცნობილი"</string>
+ <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"არ არის ამოცნობილი"</string>
+ <string name="kg_face_not_recognized" msgid="6382535088345875294">"არ არის ამოცნობილი"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
<item quantity="other">შეიყვანეთ SIM ბარათის PIN-კოდი. თქვენ დაგრჩათ <xliff:g id="NUMBER_1">%d</xliff:g> მცდელობა.</item>
<item quantity="one">შეიყვანეთ SIM ბარათის PIN-კოდი. თქვენ დაგრჩათ <xliff:g id="NUMBER_0">%d</xliff:g> მცდელობა, რომლის შემდეგაც მოწყობილობის განსაბლოკად დაგჭირდებათ თქვენს ოპერატორთან დაკავშირება.</item>
diff --git a/packages/SystemUI/res-keyguard/values-kk/strings.xml b/packages/SystemUI/res-keyguard/values-kk/strings.xml
index 6ed9f44..d206bcb 100644
--- a/packages/SystemUI/res-keyguard/values-kk/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-kk/strings.xml
@@ -140,7 +140,8 @@
<item quantity="other">Құрылғы құлпы <xliff:g id="NUMBER_1">%d</xliff:g> сағаттан бері ашылмаған. Құпия сөзді растаңыз.</item>
<item quantity="one">Құрылғы құлпы <xliff:g id="NUMBER_0">%d</xliff:g> сағаттан бері ашылмаған. Құпия сөзді растаңыз.</item>
</plurals>
- <string name="fingerprint_not_recognized" msgid="348813995267914625">"Анықталмады"</string>
+ <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Танылмады"</string>
+ <string name="kg_face_not_recognized" msgid="6382535088345875294">"Танылмады"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
<item quantity="other">SIM PIN кодын енгізіңіз. <xliff:g id="NUMBER_1">%d</xliff:g> мүмкіндік қалды, одан кейін оператордан SIM картасының құлпын ашуды сұрауға тура келеді.</item>
<item quantity="one">SIM PIN кодын енгізіңіз. <xliff:g id="NUMBER_0">%d</xliff:g> мүмкіндік қалды, одан кейін оператордан SIM картасының құлпын ашуды сұрауға тура келеді.</item>
diff --git a/packages/SystemUI/res-keyguard/values-km/strings.xml b/packages/SystemUI/res-keyguard/values-km/strings.xml
index f7960e5..d38aa3f 100644
--- a/packages/SystemUI/res-keyguard/values-km/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-km/strings.xml
@@ -140,7 +140,8 @@
<item quantity="other">ឧបករណ៍បានជាប់សោអស់រយៈពេល <xliff:g id="NUMBER_1">%d</xliff:g> ម៉ោងហើយ។ សូមបញ្ជាក់ពាក្យសម្ងាត់។</item>
<item quantity="one">ឧបករណ៍បានជាប់សោអស់រយៈពេល <xliff:g id="NUMBER_0">%d</xliff:g> ម៉ោងហើយ។ សូមបញ្ជាក់ពាក្យសម្ងាត់។</item>
</plurals>
- <string name="fingerprint_not_recognized" msgid="348813995267914625">"មិនអាចសម្គាល់បានទេ"</string>
+ <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"មិនអាចសម្គាល់បានទេ"</string>
+ <string name="kg_face_not_recognized" msgid="6382535088345875294">"មិនអាចសម្គាល់បានទេ"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
<item quantity="other">បញ្ចូលកូដ PIN របស់ស៊ីម។ អ្នកនៅសល់ការព្យាយាម <xliff:g id="NUMBER_1">%d</xliff:g> ដងទៀត។</item>
<item quantity="one">បញ្ចូលកូដ PIN របស់ស៊ីម។ អ្នកនៅសល់ការព្យាយាម <xliff:g id="NUMBER_0">%d</xliff:g> ដងទៀត មុនពេលដែលអ្នកត្រូវទាក់ទងទៅក្រុមហ៊ុនសេវាទូរសព្ទរបស់អ្នកដើម្បីដោះសោឧបករណ៍របស់អ្នក។</item>
diff --git a/packages/SystemUI/res-keyguard/values-kn/strings.xml b/packages/SystemUI/res-keyguard/values-kn/strings.xml
index e6b03f9..a435608 100644
--- a/packages/SystemUI/res-keyguard/values-kn/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-kn/strings.xml
@@ -140,7 +140,10 @@
<item quantity="one">ಸಾಧನವನ್ನು <xliff:g id="NUMBER_1">%d</xliff:g> ಗಂಟೆಗಳವರೆಗೆ ಅನ್ಲಾಕ್ ಮಾಡಿರಲಿಲ್ಲ. ಪಾಸ್ವರ್ಡ್ ಖಚಿತಪಡಿಸಿ.</item>
<item quantity="other">ಸಾಧನವನ್ನು <xliff:g id="NUMBER_1">%d</xliff:g> ಗಂಟೆಗಳವರೆಗೆ ಅನ್ಲಾಕ್ ಮಾಡಿರಲಿಲ್ಲ. ಪಾಸ್ವರ್ಡ್ ಖಚಿತಪಡಿಸಿ.</item>
</plurals>
- <string name="fingerprint_not_recognized" msgid="348813995267914625">"ಗುರುತಿಸಲಾಗಿಲ್ಲ"</string>
+ <!-- no translation found for kg_fingerprint_not_recognized (7854413849848459418) -->
+ <skip />
+ <!-- no translation found for kg_face_not_recognized (6382535088345875294) -->
+ <skip />
<plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
<item quantity="one">ಸಿಮ್ ಪಿನ್ ನಮೂದಿಸಿ. ನಿಮ್ಮಲ್ಲಿ <xliff:g id="NUMBER_1">%d</xliff:g> ಪ್ರಯತ್ನಗಳು ಬಾಕಿ ಉಳಿದಿವೆ.</item>
<item quantity="other">ಸಿಮ್ ಪಿನ್ ನಮೂದಿಸಿ. ನಿಮ್ಮಲ್ಲಿ <xliff:g id="NUMBER_1">%d</xliff:g> ಪ್ರಯತ್ನಗಳು ಬಾಕಿ ಉಳಿದಿವೆ.</item>
diff --git a/packages/SystemUI/res-keyguard/values-ko/strings.xml b/packages/SystemUI/res-keyguard/values-ko/strings.xml
index ffdb1e5..87359ca 100644
--- a/packages/SystemUI/res-keyguard/values-ko/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ko/strings.xml
@@ -140,7 +140,8 @@
<item quantity="other">기기가 <xliff:g id="NUMBER_1">%d</xliff:g>시간 동안 잠금 해제되지 않았습니다. 비밀번호를 입력하세요.</item>
<item quantity="one">기기가 <xliff:g id="NUMBER_0">%d</xliff:g>시간 동안 잠금 해제되지 않았습니다. 비밀번호를 입력하세요.</item>
</plurals>
- <string name="fingerprint_not_recognized" msgid="348813995267914625">"인식할 수 없음"</string>
+ <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"인식할 수 없음"</string>
+ <string name="kg_face_not_recognized" msgid="6382535088345875294">"인식할 수 없음"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
<item quantity="other">SIM PIN을 입력하세요. 입력은 <xliff:g id="NUMBER_1">%d</xliff:g>번 더 시도할 수 있습니다.</item>
<item quantity="one">SIM PIN을 입력하세요. 입력에 <xliff:g id="NUMBER_0">%d</xliff:g>번 더 실패하면 이동통신사에 문의하여 기기를 잠금 해제해야 합니다.</item>
diff --git a/packages/SystemUI/res-keyguard/values-ky/strings.xml b/packages/SystemUI/res-keyguard/values-ky/strings.xml
index 80ae050..832e5af 100644
--- a/packages/SystemUI/res-keyguard/values-ky/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ky/strings.xml
@@ -140,7 +140,8 @@
<item quantity="other">Түзмөктүн кулпусу <xliff:g id="NUMBER_1">%d</xliff:g> саат бою ачылган жок. Сырсөздү ырастаңыз.</item>
<item quantity="one">Түзмөктүн кулпусу <xliff:g id="NUMBER_0">%d</xliff:g> саат бою ачылган жок. Сырсөздү ырастаңыз.</item>
</plurals>
- <string name="fingerprint_not_recognized" msgid="348813995267914625">"Таанылган жок"</string>
+ <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Таанылган жок"</string>
+ <string name="kg_face_not_recognized" msgid="6382535088345875294">"Таанылган жок"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
<item quantity="other">SIM-картанын PIN кодун киргизиңиз. Сизде <xliff:g id="NUMBER_1">%d</xliff:g> аракет калды.</item>
<item quantity="one">SIM-картанын PIN кодун киргизиңиз. Сизде <xliff:g id="NUMBER_0">%d</xliff:g> аракет калды, андан кийин түзмөктү бөгөттөн чыгаруу үчүн байланыш операторуна кайрылышыңыз керек болот.</item>
diff --git a/packages/SystemUI/res-keyguard/values-lo/strings.xml b/packages/SystemUI/res-keyguard/values-lo/strings.xml
index b05915a..4d66dfe 100644
--- a/packages/SystemUI/res-keyguard/values-lo/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-lo/strings.xml
@@ -140,7 +140,8 @@
<item quantity="other">ອຸປະກອນບໍ່ໄດ້ຖືກປົດລັອກເປັນເວລາ <xliff:g id="NUMBER_1">%d</xliff:g> ຊົ່ວໂມງ. ຢືນຢັນລະຫັດຜ່ານ.</item>
<item quantity="one">ອຸປະກອນບໍ່ໄດ້ຖືກປົດລັອກເປັນເວລາ <xliff:g id="NUMBER_0">%d</xliff:g> ຊົ່ວໂມງ. ຢືນຢັນລະຫັດຜ່ານ.</item>
</plurals>
- <string name="fingerprint_not_recognized" msgid="348813995267914625">"ບໍ່ຮັບຮູ້"</string>
+ <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"ບໍ່ຮູ້ຈັກ"</string>
+ <string name="kg_face_not_recognized" msgid="6382535088345875294">"ບໍ່ຮູ້ຈັກ"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
<item quantity="other">ລະຫັດ SIM PIN ບໍ່ຖືກຕ້ອງ. ທ່ານສາມາດລອງໄດ້ອີກ <xliff:g id="NUMBER_1">%d</xliff:g> ເທື່ອ.</item>
<item quantity="one">ໃສ່ລະຫັດ SIM PIN. ທ່ານສາມາດລອງໄດ້ອີກ <xliff:g id="NUMBER_0">%d</xliff:g> ເທື່ອກ່ອນທີ່ຈະຕ້ອງຕິດຕໍ່ຜູ້ໃຫ້ບໍລິການເພື່ອປົດລັອກ.</item>
diff --git a/packages/SystemUI/res-keyguard/values-lt/strings.xml b/packages/SystemUI/res-keyguard/values-lt/strings.xml
index 5cd696f..18404e0 100644
--- a/packages/SystemUI/res-keyguard/values-lt/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-lt/strings.xml
@@ -152,7 +152,8 @@
<item quantity="many">Įrenginys nebuvo atrakintas <xliff:g id="NUMBER_1">%d</xliff:g> valandos. Patvirtinkite slaptažodį.</item>
<item quantity="other">Įrenginys nebuvo atrakintas <xliff:g id="NUMBER_1">%d</xliff:g> valandų. Patvirtinkite slaptažodį.</item>
</plurals>
- <string name="fingerprint_not_recognized" msgid="348813995267914625">"Neatpažinta"</string>
+ <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Neatpažinta"</string>
+ <string name="kg_face_not_recognized" msgid="6382535088345875294">"Neatpažinta"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
<item quantity="one">Įveskite SIM kortelės PIN kodą. Jums liko <xliff:g id="NUMBER_1">%d</xliff:g> bandymas.</item>
<item quantity="few">Įveskite SIM kortelės PIN kodą. Jums liko <xliff:g id="NUMBER_1">%d</xliff:g> bandymai.</item>
diff --git a/packages/SystemUI/res-keyguard/values-lv/strings.xml b/packages/SystemUI/res-keyguard/values-lv/strings.xml
index d4f6020..9ec52cf 100644
--- a/packages/SystemUI/res-keyguard/values-lv/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-lv/strings.xml
@@ -146,7 +146,8 @@
<item quantity="one">Ierīce nav tikusi atbloķēta <xliff:g id="NUMBER_1">%d</xliff:g> stundu. Apstipriniet paroli.</item>
<item quantity="other">Ierīce nav tikusi atbloķēta <xliff:g id="NUMBER_1">%d</xliff:g> stundas. Apstipriniet paroli.</item>
</plurals>
- <string name="fingerprint_not_recognized" msgid="348813995267914625">"Nav atpazīts"</string>
+ <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Nav atpazīts"</string>
+ <string name="kg_face_not_recognized" msgid="6382535088345875294">"Nav atpazīts"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
<item quantity="zero">Ievadiet SIM kartes PIN. Varat mēģināt vēl <xliff:g id="NUMBER_1">%d</xliff:g> reizes.</item>
<item quantity="one">Ievadiet SIM kartes PIN. Varat mēģināt vēl <xliff:g id="NUMBER_1">%d</xliff:g> reizi.</item>
diff --git a/packages/SystemUI/res-keyguard/values-mk/strings.xml b/packages/SystemUI/res-keyguard/values-mk/strings.xml
index aff4803..0685a10 100644
--- a/packages/SystemUI/res-keyguard/values-mk/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-mk/strings.xml
@@ -140,7 +140,8 @@
<item quantity="one">Уредот не е отклучен веќе <xliff:g id="NUMBER_1">%d</xliff:g> час. Потврдете ја лозинката.</item>
<item quantity="other">Уредот не е отклучен веќе <xliff:g id="NUMBER_1">%d</xliff:g> часа. Потврдете ја лозинката.</item>
</plurals>
- <string name="fingerprint_not_recognized" msgid="348813995267914625">"Непознат"</string>
+ <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Непознат"</string>
+ <string name="kg_face_not_recognized" msgid="6382535088345875294">"Непознат"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
<item quantity="one">Внесете PIN-код за SIM-картичката. Ви преостанува уште <xliff:g id="NUMBER_1">%d</xliff:g> обид.</item>
<item quantity="other">Внесете PIN-код за SIM-картичката. Ви преостануваат уште <xliff:g id="NUMBER_1">%d</xliff:g> обиди.</item>
diff --git a/packages/SystemUI/res-keyguard/values-ml/strings.xml b/packages/SystemUI/res-keyguard/values-ml/strings.xml
index d3283e1..c6aadb0 100644
--- a/packages/SystemUI/res-keyguard/values-ml/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ml/strings.xml
@@ -140,7 +140,10 @@
<item quantity="other">ഉപകരണം <xliff:g id="NUMBER_1">%d</xliff:g> മണിക്കൂറായി അൺലോക്ക് ചെയ്തിട്ടില്ല. പാസ്വേഡ് സ്ഥിരീകരിക്കുക.</item>
<item quantity="one">ഉപകരണം <xliff:g id="NUMBER_0">%d</xliff:g> മണിക്കൂറായി അൺലോക്ക് ചെയ്തിട്ടില്ല. പാസ്വേഡ് സ്ഥിരീകരിക്കുക.</item>
</plurals>
- <string name="fingerprint_not_recognized" msgid="348813995267914625">"തിരിച്ചറിഞ്ഞില്ല"</string>
+ <!-- no translation found for kg_fingerprint_not_recognized (7854413849848459418) -->
+ <skip />
+ <!-- no translation found for kg_face_not_recognized (6382535088345875294) -->
+ <skip />
<plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
<item quantity="other">സിം പിൻ നൽകുക. നിങ്ങൾക്ക് <xliff:g id="NUMBER_1">%d</xliff:g> ശ്രമങ്ങൾ കൂടി ശേഷിക്കുന്നു.</item>
<item quantity="one">സിം പിൻ നൽകുക. ഉപകരണം അൺലോക്ക് ചെയ്യാൻ കാരിയറുമായി ബന്ധപ്പെടേണ്ടിവരുന്നതിന് മുമ്പ് <xliff:g id="NUMBER_0">%d</xliff:g> ശ്രമം കൂടി ശേഷിക്കുന്നു.</item>
diff --git a/packages/SystemUI/res-keyguard/values-mn/strings.xml b/packages/SystemUI/res-keyguard/values-mn/strings.xml
index 99c7339..3c1870d 100644
--- a/packages/SystemUI/res-keyguard/values-mn/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-mn/strings.xml
@@ -140,7 +140,8 @@
<item quantity="other">Төхөөрөмжийн түгжээг <xliff:g id="NUMBER_1">%d</xliff:g> цагийн турш тайлаагүй байна. Нууц үгээ баталгаажуулна уу.</item>
<item quantity="one">Төхөөрөмжийн түгжээг <xliff:g id="NUMBER_0">%d</xliff:g> цагийн турш тайлаагүй байна. Нууц үгээ баталгаажуулна уу.</item>
</plurals>
- <string name="fingerprint_not_recognized" msgid="348813995267914625">"Танигдахгүй байна"</string>
+ <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Таньж чадсангүй"</string>
+ <string name="kg_face_not_recognized" msgid="6382535088345875294">"Таньж чадсангүй"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
<item quantity="other">SIM-н ПИН кодыг оруулна уу. Танд <xliff:g id="NUMBER_1">%d</xliff:g> оролдлого үлдлээ.</item>
<item quantity="one">SIM-н ПИН кодыг оруулна уу. Танд оператор компанитайгаа холбогдохгүйгээр төхөөрөмжийн түгжээг тайлах <xliff:g id="NUMBER_0">%d</xliff:g> оролдлого үлдлээ.</item>
diff --git a/packages/SystemUI/res-keyguard/values-mr/strings.xml b/packages/SystemUI/res-keyguard/values-mr/strings.xml
index 16a9d63..88724ce9 100644
--- a/packages/SystemUI/res-keyguard/values-mr/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-mr/strings.xml
@@ -140,7 +140,10 @@
<item quantity="one">डिव्हाइस <xliff:g id="NUMBER_1">%d</xliff:g> तासासाठी अनलॉक केले गेले नाही. पासवर्डची खात्री करा.</item>
<item quantity="other">डिव्हाइस <xliff:g id="NUMBER_1">%d</xliff:g> तासांसाठी अनलॉक केले गेले नाही. पासवर्डची खात्री करा.</item>
</plurals>
- <string name="fingerprint_not_recognized" msgid="348813995267914625">"ओळखले नाही"</string>
+ <!-- no translation found for kg_fingerprint_not_recognized (7854413849848459418) -->
+ <skip />
+ <!-- no translation found for kg_face_not_recognized (6382535088345875294) -->
+ <skip />
<plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
<item quantity="one">सिम पिन एंटर करा, तुमच्याकडे <xliff:g id="NUMBER_1">%d</xliff:g> प्रयत्न शिल्लक आहे.</item>
<item quantity="other">सिम पिन एंटर करा, तुमच्याकडे <xliff:g id="NUMBER_1">%d</xliff:g> प्रयत्न शिल्लक आहेत.</item>
diff --git a/packages/SystemUI/res-keyguard/values-ms/strings.xml b/packages/SystemUI/res-keyguard/values-ms/strings.xml
index 19e53d5..bec3295 100644
--- a/packages/SystemUI/res-keyguard/values-ms/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ms/strings.xml
@@ -140,7 +140,8 @@
<item quantity="other">Peranti tidak dibuka kuncinya selama <xliff:g id="NUMBER_1">%d</xliff:g> jam. Sahkan kata laluan.</item>
<item quantity="one">Peranti tidak dibuka kuncinya selama <xliff:g id="NUMBER_0">%d</xliff:g> jam. Sahkan kata laluan.</item>
</plurals>
- <string name="fingerprint_not_recognized" msgid="348813995267914625">"Tidak dikenali"</string>
+ <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Tidak dikenali"</string>
+ <string name="kg_face_not_recognized" msgid="6382535088345875294">"Tidak dikenali"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
<item quantity="other">Masukkan PIN SIM. Tinggal <xliff:g id="NUMBER_1">%d</xliff:g> percubaan lagi.</item>
<item quantity="one">Masukkan PIN SIM. Tinggal <xliff:g id="NUMBER_0">%d</xliff:g> percubaan lagi sebelum anda perlu menghubungi pembawa anda untuk membuka kunci peranti.</item>
diff --git a/packages/SystemUI/res-keyguard/values-my/strings.xml b/packages/SystemUI/res-keyguard/values-my/strings.xml
index c9a2e1c..017bf0a 100644
--- a/packages/SystemUI/res-keyguard/values-my/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-my/strings.xml
@@ -140,7 +140,8 @@
<item quantity="other">စက်ပစ္စည်းကို <xliff:g id="NUMBER_1">%d</xliff:g> နာရီကြာ လော့ခ်ဖွင့်ခဲ့ခြင်း မရှိပါ။ စကားဝှက်အား အတည်ပြုပါ။</item>
<item quantity="one">စက်ပစ္စည်းကို <xliff:g id="NUMBER_0">%d</xliff:g> နာရီကြာ လော့ခ်ဖွင့်ခဲ့ခြင်း မရှိပါ။ စကားဝှက်အား အတည်ပြုပါ။</item>
</plurals>
- <string name="fingerprint_not_recognized" msgid="348813995267914625">"မသိပါ"</string>
+ <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"မသိပါ"</string>
+ <string name="kg_face_not_recognized" msgid="6382535088345875294">"မသိပါ"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
<item quantity="other">ဆင်းမ်ကဒ် ပင်နံပါတ် ထည့်ပါ။ <xliff:g id="NUMBER_1">%d</xliff:g> ကြိမ် စမ်းသပ်ခွင့်ရှိပါသေးသည်။</item>
<item quantity="one">ဆင်းမ်ကဒ် ပင်နံပါတ် ထည့်ပါ။ သင့်စက်ကို လော့ခ်ဖွင့်ပေးရန်အတွက် ဝန်ဆောင်မှုပေးသူသို့ မဆက်သွယ်မီ <xliff:g id="NUMBER_0">%d</xliff:g> ကြိမ် စမ်းသပ်ခွင့်ရှိပါသေးသည်။</item>
diff --git a/packages/SystemUI/res-keyguard/values-nb/strings.xml b/packages/SystemUI/res-keyguard/values-nb/strings.xml
index 4573596..125ffb0 100644
--- a/packages/SystemUI/res-keyguard/values-nb/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-nb/strings.xml
@@ -140,7 +140,8 @@
<item quantity="other">Enheten har ikke blitt låst opp de siste <xliff:g id="NUMBER_1">%d</xliff:g> timene. Bekreft passordet.</item>
<item quantity="one">Enheten har ikke blitt låst opp på <xliff:g id="NUMBER_0">%d</xliff:g> time. Bekreft passordet.</item>
</plurals>
- <string name="fingerprint_not_recognized" msgid="348813995267914625">"Ikke gjenkjent"</string>
+ <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Ikke gjenkjent"</string>
+ <string name="kg_face_not_recognized" msgid="6382535088345875294">"Ikke gjenkjent"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
<item quantity="other">Skriv inn PIN-koden for SIM-kortet. Du har <xliff:g id="NUMBER_1">%d</xliff:g> forsøk igjen.</item>
<item quantity="one">Skriv inn PIN-koden for SIM-kortet. Du har <xliff:g id="NUMBER_0">%d</xliff:g> forsøk igjen før du må kontakte operatøren din for å låse opp enheten.</item>
diff --git a/packages/SystemUI/res-keyguard/values-ne/strings.xml b/packages/SystemUI/res-keyguard/values-ne/strings.xml
index 3f6b749..ccb78d1 100644
--- a/packages/SystemUI/res-keyguard/values-ne/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ne/strings.xml
@@ -140,7 +140,10 @@
<item quantity="other">यन्त्र <xliff:g id="NUMBER_1">%d</xliff:g> घन्टा देखि अनलक भएको छैन। पासवर्ड पुष्टि गर्नुहोस्।</item>
<item quantity="one">यन्त्र <xliff:g id="NUMBER_0">%d</xliff:g> घन्टा देखि अनलक भएको छैन। पासवर्ड पुष्टि गर्नुहोस्।</item>
</plurals>
- <string name="fingerprint_not_recognized" msgid="348813995267914625">"पहिचान भएन"</string>
+ <!-- no translation found for kg_fingerprint_not_recognized (7854413849848459418) -->
+ <skip />
+ <!-- no translation found for kg_face_not_recognized (6382535088345875294) -->
+ <skip />
<plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
<item quantity="other">SIM को PIN प्रविष्ट गर्नुहोस्। तपाईंसँग <xliff:g id="NUMBER_1">%d</xliff:g> प्रयासहरू बाँकी छन्।</item>
<item quantity="one">SIM को PIN प्रविष्ट गर्नुहोस्। तपाईंसँग <xliff:g id="NUMBER_0">%d</xliff:g> प्रयास बाँकी छ, त्यसपछि भने आफ्नो यन्त्र अनलक गर्नका लागि तपाईंले अनिवार्य रूपमा आफ्नो सेवा प्रदायकलाई सम्पर्क गर्नु पर्ने हुन्छ।</item>
diff --git a/packages/SystemUI/res-keyguard/values-nl/strings.xml b/packages/SystemUI/res-keyguard/values-nl/strings.xml
index e2f5806..cf0cff2 100644
--- a/packages/SystemUI/res-keyguard/values-nl/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-nl/strings.xml
@@ -140,7 +140,8 @@
<item quantity="other">Apparaat is al <xliff:g id="NUMBER_1">%d</xliff:g> uur niet ontgrendeld. Bevestig het wachtwoord.</item>
<item quantity="one">Apparaat is al <xliff:g id="NUMBER_0">%d</xliff:g> uur niet ontgrendeld. Bevestig het wachtwoord.</item>
</plurals>
- <string name="fingerprint_not_recognized" msgid="348813995267914625">"Niet herkend"</string>
+ <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Niet herkend"</string>
+ <string name="kg_face_not_recognized" msgid="6382535088345875294">"Niet herkend"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
<item quantity="other">Geef de pincode van de simkaart op. Je hebt nog <xliff:g id="NUMBER_1">%d</xliff:g> pogingen over.</item>
<item quantity="one">Geef de pincode van de simkaart op. Je hebt nog <xliff:g id="NUMBER_0">%d</xliff:g> poging over voordat je contact met je provider moet opnemen om het apparaat te ontgrendelen.</item>
diff --git a/packages/SystemUI/res-keyguard/values-or/strings.xml b/packages/SystemUI/res-keyguard/values-or/strings.xml
index 1ffce78..6d94626 100644
--- a/packages/SystemUI/res-keyguard/values-or/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-or/strings.xml
@@ -140,7 +140,8 @@
<item quantity="other"><xliff:g id="NUMBER_1">%d</xliff:g> ଘଣ୍ଟା ପାଇଁ ଡିଭାଇସ୍ ଅନଲକ୍ କରାଯାଇ ନାହିଁ। ପାସୱର୍ଡ ସୁନିଶ୍ଚିତ କରନ୍ତୁ</item>
<item quantity="one"><xliff:g id="NUMBER_0">%d</xliff:g> ଘଣ୍ଟା ପାଇଁ ଡିଭାଇସ୍ ଅନଲକ୍ କରାଯାଇ ନାହିଁ। ପାସୱର୍ଡ ସୁନିଶ୍ଚିତ କରନ୍ତୁ</item>
</plurals>
- <string name="fingerprint_not_recognized" msgid="348813995267914625">"ଚିହ୍ନଟ ହେଲାନାହିଁ"</string>
+ <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"ଚିହ୍ନଟ ହେଲାନାହିଁ"</string>
+ <string name="kg_face_not_recognized" msgid="6382535088345875294">"ଚିହ୍ନଟ ହେଲାନାହିଁ"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
<item quantity="other">SIM PIN ପ୍ରବେଶ କରନ୍ତୁ। ଆପଣଙ୍କ ପାଇଁ <xliff:g id="NUMBER_1">%d</xliff:g>ଟି ପ୍ରୟାସ ବଳକା ଅଛି।</item>
<item quantity="one">SIM PIN ପ୍ରବେଶ କରନ୍ତୁ। ଆପଣଙ୍କ ଡିଭାଇସ୍କୁ ଅନଲକ୍ କରିବା ପାଇଁ ପାଖରେ ବଳକା ଥିବା <xliff:g id="NUMBER_0">%d</xliff:g>ଟି ପ୍ରୟାସର ବ୍ୟବହାର କରିବା ପୂର୍ବରୁ ନିଜର କେରିଅର୍ଙ୍କୁ ସମ୍ପର୍କ କରନ୍ତୁ।</item>
diff --git a/packages/SystemUI/res-keyguard/values-pa/strings.xml b/packages/SystemUI/res-keyguard/values-pa/strings.xml
index e1379f6..498151c 100644
--- a/packages/SystemUI/res-keyguard/values-pa/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-pa/strings.xml
@@ -140,7 +140,10 @@
<item quantity="one">ਡੀਵਾਈਸ <xliff:g id="NUMBER_1">%d</xliff:g> ਘੰਟੇ ਤੋਂ ਅਣਲਾਕ ਨਹੀਂ ਕੀਤਾ ਗਿਆ ਹੈ। ਪਾਸਵਰਡ ਦੀ ਪੁਸ਼ਟੀ ਕਰੋ</item>
<item quantity="other">ਡੀਵਾਈਸ <xliff:g id="NUMBER_1">%d</xliff:g> ਘੰਟਿਆਂ ਤੋਂ ਅਣਲਾਕ ਨਹੀਂ ਕੀਤਾ ਗਿਆ ਹੈ। ਪਾਸਵਰਡ ਦੀ ਪੁਸ਼ਟੀ ਕਰੋ</item>
</plurals>
- <string name="fingerprint_not_recognized" msgid="348813995267914625">"ਪਛਾਣ ਨਹੀਂ ਹੋਈ"</string>
+ <!-- no translation found for kg_fingerprint_not_recognized (7854413849848459418) -->
+ <skip />
+ <!-- no translation found for kg_face_not_recognized (6382535088345875294) -->
+ <skip />
<plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
<item quantity="one">ਸਿਮ ਪਿੰਨ ਦਾਖਲ ਕਰੋ। ਤੁਹਾਡੇ ਕੋਲ <xliff:g id="NUMBER_1">%d</xliff:g> ਕੋਸ਼ਿਸ਼ ਬਾਕੀ ਹੈ।</item>
<item quantity="other">ਸਿਮ ਪਿੰਨ ਦਾਖਲ ਕਰੋ। ਤੁਹਾਡੇ ਕੋਲ <xliff:g id="NUMBER_1">%d</xliff:g> ਕੋਸ਼ਿਸ਼ਾਂ ਬਾਕੀ ਹਨ।</item>
diff --git a/packages/SystemUI/res-keyguard/values-pl/strings.xml b/packages/SystemUI/res-keyguard/values-pl/strings.xml
index 8376a36..538135f 100644
--- a/packages/SystemUI/res-keyguard/values-pl/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-pl/strings.xml
@@ -152,7 +152,8 @@
<item quantity="other">Urządzenie nie zostało odblokowane od <xliff:g id="NUMBER_1">%d</xliff:g> godziny. Potwierdź hasło.</item>
<item quantity="one">Urządzenie nie zostało odblokowane od <xliff:g id="NUMBER_0">%d</xliff:g> godziny. Potwierdź hasło.</item>
</plurals>
- <string name="fingerprint_not_recognized" msgid="348813995267914625">"Nie rozpoznano"</string>
+ <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Nie rozpoznano"</string>
+ <string name="kg_face_not_recognized" msgid="6382535088345875294">"Nie rozpoznano"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
<item quantity="few">Wpisz kod PIN karty SIM. Masz jeszcze <xliff:g id="NUMBER_1">%d</xliff:g> próby.</item>
<item quantity="many">Wpisz kod PIN karty SIM. Masz jeszcze <xliff:g id="NUMBER_1">%d</xliff:g> prób.</item>
diff --git a/packages/SystemUI/res-keyguard/values-pt-rBR/strings.xml b/packages/SystemUI/res-keyguard/values-pt-rBR/strings.xml
index 8970560..13508b7 100644
--- a/packages/SystemUI/res-keyguard/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-pt-rBR/strings.xml
@@ -140,7 +140,8 @@
<item quantity="one">O dispositivo não é desbloqueado há <xliff:g id="NUMBER_1">%d</xliff:g> hora. Confirme a senha.</item>
<item quantity="other">O dispositivo não é desbloqueado há <xliff:g id="NUMBER_1">%d</xliff:g> horas. Confirme a senha.</item>
</plurals>
- <string name="fingerprint_not_recognized" msgid="348813995267914625">"Não reconhecido"</string>
+ <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Não reconhecido"</string>
+ <string name="kg_face_not_recognized" msgid="6382535088345875294">"Não reconhecido"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
<item quantity="one">Informe o PIN do SIM. Você tem <xliff:g id="NUMBER_1">%d</xliff:g> tentativa restante.</item>
<item quantity="other">Informe o PIN do SIM. Você tem <xliff:g id="NUMBER_1">%d</xliff:g> tentativas restantes.</item>
diff --git a/packages/SystemUI/res-keyguard/values-pt-rPT/strings.xml b/packages/SystemUI/res-keyguard/values-pt-rPT/strings.xml
index a2f8aea..c87799a 100644
--- a/packages/SystemUI/res-keyguard/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-pt-rPT/strings.xml
@@ -140,7 +140,8 @@
<item quantity="other">O dispositivo não é desbloqueado há <xliff:g id="NUMBER_1">%d</xliff:g> horas. Confirme a palavra-passe.</item>
<item quantity="one">O dispositivo não é desbloqueado há <xliff:g id="NUMBER_0">%d</xliff:g> hora. Confirme a palavra-passe.</item>
</plurals>
- <string name="fingerprint_not_recognized" msgid="348813995267914625">"Não reconhecido"</string>
+ <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Não reconhecido."</string>
+ <string name="kg_face_not_recognized" msgid="6382535088345875294">"Não reconhecido."</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
<item quantity="other">Introduza o PIN do cartão SIM. Tem mais <xliff:g id="NUMBER_1">%d</xliff:g> tentativas.</item>
<item quantity="one">Introduza o PIN do cartão SIM. Tem mais <xliff:g id="NUMBER_0">%d</xliff:g> tentativa antes de ser necessário contactar o operador para desbloquear o dispositivo.</item>
diff --git a/packages/SystemUI/res-keyguard/values-pt/strings.xml b/packages/SystemUI/res-keyguard/values-pt/strings.xml
index 8970560..13508b7 100644
--- a/packages/SystemUI/res-keyguard/values-pt/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-pt/strings.xml
@@ -140,7 +140,8 @@
<item quantity="one">O dispositivo não é desbloqueado há <xliff:g id="NUMBER_1">%d</xliff:g> hora. Confirme a senha.</item>
<item quantity="other">O dispositivo não é desbloqueado há <xliff:g id="NUMBER_1">%d</xliff:g> horas. Confirme a senha.</item>
</plurals>
- <string name="fingerprint_not_recognized" msgid="348813995267914625">"Não reconhecido"</string>
+ <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Não reconhecido"</string>
+ <string name="kg_face_not_recognized" msgid="6382535088345875294">"Não reconhecido"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
<item quantity="one">Informe o PIN do SIM. Você tem <xliff:g id="NUMBER_1">%d</xliff:g> tentativa restante.</item>
<item quantity="other">Informe o PIN do SIM. Você tem <xliff:g id="NUMBER_1">%d</xliff:g> tentativas restantes.</item>
diff --git a/packages/SystemUI/res-keyguard/values-ro/strings.xml b/packages/SystemUI/res-keyguard/values-ro/strings.xml
index 148772b..3741157 100644
--- a/packages/SystemUI/res-keyguard/values-ro/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ro/strings.xml
@@ -146,7 +146,8 @@
<item quantity="other">Dispozitivul nu a fost deblocat de <xliff:g id="NUMBER_1">%d</xliff:g> de ore. Confirmați parola.</item>
<item quantity="one">Dispozitivul nu a fost deblocat de <xliff:g id="NUMBER_0">%d</xliff:g> oră. Confirmați parola.</item>
</plurals>
- <string name="fingerprint_not_recognized" msgid="348813995267914625">"Nu este recunoscută"</string>
+ <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Nu este recunoscută"</string>
+ <string name="kg_face_not_recognized" msgid="6382535088345875294">"Nu este recunoscut"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
<item quantity="few">Introduceți codul PIN pentru cardul SIM. V-au mai rămas <xliff:g id="NUMBER_1">%d</xliff:g> încercări.</item>
<item quantity="other">Introduceți codul PIN pentru cardul SIM. V-au mai rămas <xliff:g id="NUMBER_1">%d</xliff:g> de încercări.</item>
diff --git a/packages/SystemUI/res-keyguard/values-ru/strings.xml b/packages/SystemUI/res-keyguard/values-ru/strings.xml
index c6d06aa..5fc9af7 100644
--- a/packages/SystemUI/res-keyguard/values-ru/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ru/strings.xml
@@ -152,7 +152,8 @@
<item quantity="many">Устройство не разблокировалось в течение <xliff:g id="NUMBER_1">%d</xliff:g> часов. Введите пароль ещё раз.</item>
<item quantity="other">Устройство не разблокировалось в течение <xliff:g id="NUMBER_1">%d</xliff:g> часа. Введите пароль ещё раз.</item>
</plurals>
- <string name="fingerprint_not_recognized" msgid="348813995267914625">"Не распознано"</string>
+ <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Не распознано"</string>
+ <string name="kg_face_not_recognized" msgid="6382535088345875294">"Не распознано"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
<item quantity="one">Введите PIN-код. Осталась <xliff:g id="NUMBER_1">%d</xliff:g> попытка.</item>
<item quantity="few">Введите PIN-код. Осталось <xliff:g id="NUMBER_1">%d</xliff:g> попытки.</item>
diff --git a/packages/SystemUI/res-keyguard/values-si/strings.xml b/packages/SystemUI/res-keyguard/values-si/strings.xml
index 7e3b635..dd99e8b 100644
--- a/packages/SystemUI/res-keyguard/values-si/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-si/strings.xml
@@ -140,7 +140,8 @@
<item quantity="one">උපාංගය පැය <xliff:g id="NUMBER_1">%d</xliff:g>ක් අගුලු හැර නැත. මුරපදය තහවුරු කරන්න.</item>
<item quantity="other">උපාංගය පැය <xliff:g id="NUMBER_1">%d</xliff:g>ක් අගුලු හැර නැත. මුරපදය තහවුරු කරන්න.</item>
</plurals>
- <string name="fingerprint_not_recognized" msgid="348813995267914625">"හඳුනා නොගන්නා ලදී"</string>
+ <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"හඳුනා නොගන්නා ලදී"</string>
+ <string name="kg_face_not_recognized" msgid="6382535088345875294">"හඳුනා නොගන්නා ලදී"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
<item quantity="one">SIM PIN ඇතුළු කරන්න, ඔබ සතුව උත්සාහයන් <xliff:g id="NUMBER_1">%d</xliff:g>ක් ඉතිරිව ඇත.</item>
<item quantity="other">SIM PIN ඇතුළු කරන්න, ඔබ සතුව උත්සාහයන් <xliff:g id="NUMBER_1">%d</xliff:g>ක් ඉතිරිව ඇත.</item>
diff --git a/packages/SystemUI/res-keyguard/values-sk/strings.xml b/packages/SystemUI/res-keyguard/values-sk/strings.xml
index a0cd7a5..868e0f6 100644
--- a/packages/SystemUI/res-keyguard/values-sk/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-sk/strings.xml
@@ -152,7 +152,8 @@
<item quantity="other">Zariadenie nebolo odomknuté <xliff:g id="NUMBER_1">%d</xliff:g> hodín. Potvrďte heslo.</item>
<item quantity="one">Zariadenie nebolo odomknuté <xliff:g id="NUMBER_0">%d</xliff:g> hodinu. Potvrďte heslo.</item>
</plurals>
- <string name="fingerprint_not_recognized" msgid="348813995267914625">"Nerozpoznané"</string>
+ <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Nerozpoznané"</string>
+ <string name="kg_face_not_recognized" msgid="6382535088345875294">"Nerozpoznané"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
<item quantity="few">Zadajte kód PIN SIM karty. Zostávajú vám <xliff:g id="NUMBER_1">%d</xliff:g> pokusy.</item>
<item quantity="many">Enter SIM PIN. You have <xliff:g id="NUMBER_1">%d</xliff:g> remaining attempts.</item>
diff --git a/packages/SystemUI/res-keyguard/values-sl/strings.xml b/packages/SystemUI/res-keyguard/values-sl/strings.xml
index 6bcae3e..9be4dbd 100644
--- a/packages/SystemUI/res-keyguard/values-sl/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-sl/strings.xml
@@ -152,7 +152,8 @@
<item quantity="few">Naprava ni bila odklenjena <xliff:g id="NUMBER_1">%d</xliff:g> ure. Potrdite geslo.</item>
<item quantity="other">Naprava ni bila odklenjena <xliff:g id="NUMBER_1">%d</xliff:g> ur. Potrdite geslo.</item>
</plurals>
- <string name="fingerprint_not_recognized" msgid="348813995267914625">"Ni prepoznano"</string>
+ <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Ni prepoznano"</string>
+ <string name="kg_face_not_recognized" msgid="6382535088345875294">"Ni prepoznano"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
<item quantity="one">Vnesite kodo PIN kartice SIM. Na voljo imate še <xliff:g id="NUMBER_1">%d</xliff:g> poskus.</item>
<item quantity="two">Vnesite kodo PIN kartice SIM. Na voljo imate še <xliff:g id="NUMBER_1">%d</xliff:g> poskusa.</item>
diff --git a/packages/SystemUI/res-keyguard/values-sq/strings.xml b/packages/SystemUI/res-keyguard/values-sq/strings.xml
index dca23885..e92b9b6 100644
--- a/packages/SystemUI/res-keyguard/values-sq/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-sq/strings.xml
@@ -140,7 +140,8 @@
<item quantity="other">Pajisja nuk është shkyçur për <xliff:g id="NUMBER_1">%d</xliff:g> orë. Konfirmo fjalëkalimin.</item>
<item quantity="one">Pajisja nuk është shkyçur për <xliff:g id="NUMBER_0">%d</xliff:g> orë. Konfirmo fjalëkalimin.</item>
</plurals>
- <string name="fingerprint_not_recognized" msgid="348813995267914625">"Nuk njihet"</string>
+ <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Nuk njihet"</string>
+ <string name="kg_face_not_recognized" msgid="6382535088345875294">"Nuk njihet"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
<item quantity="other">Fut kodin PIN të kartës SIM. Të kanë mbetur edhe <xliff:g id="NUMBER_1">%d</xliff:g> tentativa.</item>
<item quantity="one">Fut kodin PIN të kartës SIM. Të ka mbetur edhe <xliff:g id="NUMBER_0">%d</xliff:g> tentativë para se të duhet të kontaktosh me operatorin tënd celular për ta shkyçur pajisjen.</item>
diff --git a/packages/SystemUI/res-keyguard/values-sr/strings.xml b/packages/SystemUI/res-keyguard/values-sr/strings.xml
index 6882333..b146603 100644
--- a/packages/SystemUI/res-keyguard/values-sr/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-sr/strings.xml
@@ -146,7 +146,8 @@
<item quantity="few">Нисте откључали уређај <xliff:g id="NUMBER_1">%d</xliff:g> сата. Потврдите лозинку.</item>
<item quantity="other">Нисте откључали уређај <xliff:g id="NUMBER_1">%d</xliff:g> сати. Потврдите лозинку.</item>
</plurals>
- <string name="fingerprint_not_recognized" msgid="348813995267914625">"Није препознат"</string>
+ <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Није препознат"</string>
+ <string name="kg_face_not_recognized" msgid="6382535088345875294">"Није препознат"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
<item quantity="one">Унесите PIN за SIM. Имате још <xliff:g id="NUMBER_1">%d</xliff:g> покушај.</item>
<item quantity="few">Унесите PIN за SIM. Имате још <xliff:g id="NUMBER_1">%d</xliff:g> покушаја.</item>
diff --git a/packages/SystemUI/res-keyguard/values-sv/strings.xml b/packages/SystemUI/res-keyguard/values-sv/strings.xml
index e8fd26e..ba5c346 100644
--- a/packages/SystemUI/res-keyguard/values-sv/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-sv/strings.xml
@@ -140,7 +140,8 @@
<item quantity="other">Enheten har inte låsts upp på <xliff:g id="NUMBER_1">%d</xliff:g> timmar. Bekräfta lösenordet.</item>
<item quantity="one">Enheten har inte låsts upp på <xliff:g id="NUMBER_0">%d</xliff:g> timme. Bekräfta lösenordet.</item>
</plurals>
- <string name="fingerprint_not_recognized" msgid="348813995267914625">"Identifierades inte"</string>
+ <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Identifierades inte"</string>
+ <string name="kg_face_not_recognized" msgid="6382535088345875294">"Identifierades inte"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
<item quantity="other">Ange pinkod för SIM-kortet. <xliff:g id="NUMBER_1">%d</xliff:g> försök återstår.</item>
<item quantity="one">Ange pinkod för SIM-kortet. <xliff:g id="NUMBER_0">%d</xliff:g> försök återstår innan du måste kontakta operatören för att låsa upp enheten.</item>
diff --git a/packages/SystemUI/res-keyguard/values-sw/strings.xml b/packages/SystemUI/res-keyguard/values-sw/strings.xml
index 3dc496a..6376b61 100644
--- a/packages/SystemUI/res-keyguard/values-sw/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-sw/strings.xml
@@ -140,7 +140,8 @@
<item quantity="other">Hujafungua kifaa kwa saa <xliff:g id="NUMBER_1">%d</xliff:g>. Thibitisha nenosiri.</item>
<item quantity="one">Hujafungua kifaa kwa saa <xliff:g id="NUMBER_0">%d</xliff:g>. Thibitisha nenosiri.</item>
</plurals>
- <string name="fingerprint_not_recognized" msgid="348813995267914625">"Haikutambua alama ya kidole"</string>
+ <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Haikutambua alama ya kidole"</string>
+ <string name="kg_face_not_recognized" msgid="6382535088345875294">"Haitambuliwi"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
<item quantity="other">Weka PIN ya SIM. Zimesalia mara <xliff:g id="NUMBER_1">%d</xliff:g> za kujaribu.</item>
<item quantity="one">Weka PIN ya SIM. Ukijaribu tena mara <xliff:g id="NUMBER_0">%d</xliff:g> bila kufaulu, kifaa chako kitafungwa na utalazimika uwasiliane na mtoa huduma wako ili akifungue.</item>
diff --git a/packages/SystemUI/res-keyguard/values-ta/strings.xml b/packages/SystemUI/res-keyguard/values-ta/strings.xml
index ae05463..44968c3 100644
--- a/packages/SystemUI/res-keyguard/values-ta/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ta/strings.xml
@@ -140,7 +140,10 @@
<item quantity="other"><xliff:g id="NUMBER_1">%d</xliff:g> மணிநேரமாகச் சாதனம் திறக்கப்படவில்லை. கடவுச்சொல்லை உறுதிப்படுத்தவும்.</item>
<item quantity="one"><xliff:g id="NUMBER_0">%d</xliff:g> மணிநேரமாகச் சாதனம் திறக்கப்படவில்லை. கடவுச்சொல்லை உறுதிப்படுத்தவும்.</item>
</plurals>
- <string name="fingerprint_not_recognized" msgid="348813995267914625">"அடையாளங்காண முடியவில்லை"</string>
+ <!-- no translation found for kg_fingerprint_not_recognized (7854413849848459418) -->
+ <skip />
+ <!-- no translation found for kg_face_not_recognized (6382535088345875294) -->
+ <skip />
<plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
<item quantity="other">சிம் பின்னை உள்ளிடவும். மேலும், <xliff:g id="NUMBER_1">%d</xliff:g> வாய்ப்புகள் மீதமுள்ளன.</item>
<item quantity="one">சிம் பின்னை உள்ளிடவும். மீதமுள்ள <xliff:g id="NUMBER_0">%d</xliff:g> வாய்ப்பில் தவறுதலான பின் உள்ளிடப்பட்டால், உங்கள் தொலைத்தொடர்பு நிறுவனத்தைத் தொடர்பு கொண்டு மட்டுமே சாதனத்தைத் திறக்க முடியும்.</item>
diff --git a/packages/SystemUI/res-keyguard/values-te/strings.xml b/packages/SystemUI/res-keyguard/values-te/strings.xml
index 2f0377f..2597a55 100644
--- a/packages/SystemUI/res-keyguard/values-te/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-te/strings.xml
@@ -140,7 +140,10 @@
<item quantity="other"><xliff:g id="NUMBER_1">%d</xliff:g> గంటల పాటు పరికరాన్ని అన్లాక్ చేయలేదు. పాస్వర్డ్ని నమోదు చేయండి.</item>
<item quantity="one"><xliff:g id="NUMBER_0">%d</xliff:g> గంట పాటు పరికరాన్ని అన్లాక్ చేయలేదు. పాస్వర్డ్ని నమోదు చేయండి.</item>
</plurals>
- <string name="fingerprint_not_recognized" msgid="348813995267914625">"గుర్తించలేదు"</string>
+ <!-- no translation found for kg_fingerprint_not_recognized (7854413849848459418) -->
+ <skip />
+ <!-- no translation found for kg_face_not_recognized (6382535088345875294) -->
+ <skip />
<plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
<item quantity="other">SIM పిన్ని నమోదు చేయండి. మీకు <xliff:g id="NUMBER_1">%d</xliff:g> ప్రయత్నలు మిగిలి ఉన్నాయి.</item>
<item quantity="one">SIM పిన్ని నమోదు చేయండి, మీరు మీ పరికరాన్ని అన్లాక్ చేయడానికి తప్పనిసరిగా మీ క్యారియర్ను సంప్రదించడానికి ముందు మీకు <xliff:g id="NUMBER_0">%d</xliff:g> ప్రయత్నం మిగిలి ఉంది.</item>
diff --git a/packages/SystemUI/res-keyguard/values-th/strings.xml b/packages/SystemUI/res-keyguard/values-th/strings.xml
index 92a5a08..52d7f1f 100644
--- a/packages/SystemUI/res-keyguard/values-th/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-th/strings.xml
@@ -140,7 +140,8 @@
<item quantity="other">ไม่มีการปลดล็อกอุปกรณ์มา <xliff:g id="NUMBER_1">%d</xliff:g> ชั่วโมงแล้ว ยืนยันรหัสผ่าน</item>
<item quantity="one">ไม่มีการปลดล็อกอุปกรณ์มา <xliff:g id="NUMBER_0">%d</xliff:g> ชั่วโมงแล้ว ยืนยันรหัสผ่าน</item>
</plurals>
- <string name="fingerprint_not_recognized" msgid="348813995267914625">"ไม่รู้จัก"</string>
+ <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"ไม่รู้จัก"</string>
+ <string name="kg_face_not_recognized" msgid="6382535088345875294">"ไม่รู้จัก"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
<item quantity="other">โปรดป้อน PIN ของซิม คุณพยายามได้อีก <xliff:g id="NUMBER_1">%d</xliff:g> ครั้ง</item>
<item quantity="one">โปรดป้อน PIN ของซิม คุณพยายามได้อีก <xliff:g id="NUMBER_0">%d</xliff:g> ครั้งก่อนที่จะต้องติดต่อผู้ให้บริการเพื่อปลดล็อกอุปกรณ์</item>
diff --git a/packages/SystemUI/res-keyguard/values-tl/strings.xml b/packages/SystemUI/res-keyguard/values-tl/strings.xml
index b993167..eb4f2ca 100644
--- a/packages/SystemUI/res-keyguard/values-tl/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-tl/strings.xml
@@ -140,7 +140,8 @@
<item quantity="one">Hindi na-unlock ang device sa loob ng <xliff:g id="NUMBER_1">%d</xliff:g> oras. Kumpirmahin ang password.</item>
<item quantity="other">Hindi na-unlock ang device sa loob ng <xliff:g id="NUMBER_1">%d</xliff:g> na oras. Kumpirmahin ang password.</item>
</plurals>
- <string name="fingerprint_not_recognized" msgid="348813995267914625">"Hindi nakilala"</string>
+ <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Hindi nakilala"</string>
+ <string name="kg_face_not_recognized" msgid="6382535088345875294">"Hindi nakilala"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
<item quantity="one">Ilagay ang PIN ng SIM. Mayroon kang <xliff:g id="NUMBER_1">%d</xliff:g> natitirang pagsubok.</item>
<item quantity="other">Ilagay ang PIN ng SIM. Mayroon kang <xliff:g id="NUMBER_1">%d</xliff:g> na natitirang pagsubok.</item>
diff --git a/packages/SystemUI/res-keyguard/values-tr/strings.xml b/packages/SystemUI/res-keyguard/values-tr/strings.xml
index 399134a..a7d870a 100644
--- a/packages/SystemUI/res-keyguard/values-tr/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-tr/strings.xml
@@ -140,7 +140,8 @@
<item quantity="other">Cihazın kilidi son <xliff:g id="NUMBER_1">%d</xliff:g> saattir açılmadı. Şifreyi doğrulayın.</item>
<item quantity="one">Cihazın kilidi son <xliff:g id="NUMBER_0">%d</xliff:g> saattir açılmadı. Şifreyi doğrulayın.</item>
</plurals>
- <string name="fingerprint_not_recognized" msgid="348813995267914625">"Tanınmadı"</string>
+ <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Tanınmadı"</string>
+ <string name="kg_face_not_recognized" msgid="6382535088345875294">"Tanınmadı"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
<item quantity="other">SIM PIN\'inizi girin. <xliff:g id="NUMBER_1">%d</xliff:g> deneme hakkınız kaldı.</item>
<item quantity="one">SIM PIN\'inizi girin. Cihazınızın kilidini açmak için operatörünüzle bağlantı kurmak zorunda kalmadan önce <xliff:g id="NUMBER_0">%d</xliff:g> deneme hakkınız kaldı.</item>
diff --git a/packages/SystemUI/res-keyguard/values-uk/strings.xml b/packages/SystemUI/res-keyguard/values-uk/strings.xml
index 821257e..5f5bfc3 100644
--- a/packages/SystemUI/res-keyguard/values-uk/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-uk/strings.xml
@@ -152,7 +152,8 @@
<item quantity="many">Ви не розблоковували пристрій <xliff:g id="NUMBER_1">%d</xliff:g> годин. Підтвердьте пароль.</item>
<item quantity="other">Ви не розблоковували пристрій <xliff:g id="NUMBER_1">%d</xliff:g> години. Підтвердьте пароль.</item>
</plurals>
- <string name="fingerprint_not_recognized" msgid="348813995267914625">"Не розпізнано"</string>
+ <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Не розпізнано"</string>
+ <string name="kg_face_not_recognized" msgid="6382535088345875294">"Не розпізнано"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
<item quantity="one">Введіть PIN-код SIM-карти. Залишилася <xliff:g id="NUMBER_1">%d</xliff:g> спроба.</item>
<item quantity="few">Введіть PIN-код SIM-карти. Залишилося <xliff:g id="NUMBER_1">%d</xliff:g> спроби.</item>
diff --git a/packages/SystemUI/res-keyguard/values-ur/strings.xml b/packages/SystemUI/res-keyguard/values-ur/strings.xml
index bfbc70f..3e7aaee 100644
--- a/packages/SystemUI/res-keyguard/values-ur/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ur/strings.xml
@@ -140,7 +140,10 @@
<item quantity="other">آلہ <xliff:g id="NUMBER_1">%d</xliff:g> گھنٹوں سے غیر مقفل نہیں کیا گيا۔ پاسورڈ کی توثیق کریں۔</item>
<item quantity="one">آلہ <xliff:g id="NUMBER_0">%d</xliff:g> گھنٹہ سے غیر مقفل نہیں کیا گیا۔ پاسورڈ کی توثیق کریں۔</item>
</plurals>
- <string name="fingerprint_not_recognized" msgid="348813995267914625">"تسلیم شدہ نہیں ہے"</string>
+ <!-- no translation found for kg_fingerprint_not_recognized (7854413849848459418) -->
+ <skip />
+ <!-- no translation found for kg_face_not_recognized (6382535088345875294) -->
+ <skip />
<plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
<item quantity="other">SIM کا PIN درج کریں، آپ کے پاس <xliff:g id="NUMBER_1">%d</xliff:g> کوششیں بچی ہیں۔</item>
<item quantity="one">SIM کا PIN درج کریں، آپ کے پاس <xliff:g id="NUMBER_0">%d</xliff:g> کوشش بچی ہے، اس کے بعد آپ کو اپنا آلہ غیر مقفل کرنے کے لیے اپنے کیریئر سے رابطہ کرنا ہوگا۔</item>
diff --git a/packages/SystemUI/res-keyguard/values-uz/strings.xml b/packages/SystemUI/res-keyguard/values-uz/strings.xml
index ad7a0dc..277fa8b 100644
--- a/packages/SystemUI/res-keyguard/values-uz/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-uz/strings.xml
@@ -140,7 +140,8 @@
<item quantity="other">Qurilma <xliff:g id="NUMBER_1">%d</xliff:g> soatdan beri qulfdan chiqarilgani yo‘q. Parolni yana bir marta kiriting.</item>
<item quantity="one">Qurilma <xliff:g id="NUMBER_0">%d</xliff:g> soatdan beri qulfdan chiqarilgani yo‘q. Parolni yana bir marta kiriting.</item>
</plurals>
- <string name="fingerprint_not_recognized" msgid="348813995267914625">"Barmoq izi aniqlanmadi"</string>
+ <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Aniqlanmadi"</string>
+ <string name="kg_face_not_recognized" msgid="6382535088345875294">"Aniqlanmadi"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
<item quantity="other">SIM PIN kodini kiriting, sizda <xliff:g id="NUMBER_1">%d</xliff:g> ta urinish bor.</item>
<item quantity="one">SIM PIN kodini kiriting, qurilmani qulfdan chiqarish uchun sizda <xliff:g id="NUMBER_0">%d</xliff:g> ta urinish bor.</item>
diff --git a/packages/SystemUI/res-keyguard/values-vi/strings.xml b/packages/SystemUI/res-keyguard/values-vi/strings.xml
index 1a2d6b4..d851ceb 100644
--- a/packages/SystemUI/res-keyguard/values-vi/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-vi/strings.xml
@@ -140,7 +140,8 @@
<item quantity="other">Thiết bị đã không được mở khóa trong <xliff:g id="NUMBER_1">%d</xliff:g> giờ. Xác nhận mật khẩu.</item>
<item quantity="one">Thiết bị đã không được mở khóa trong <xliff:g id="NUMBER_0">%d</xliff:g> giờ. Xác nhận mật khẩu.</item>
</plurals>
- <string name="fingerprint_not_recognized" msgid="348813995267914625">"Không nhận dạng được"</string>
+ <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Không nhận dạng được"</string>
+ <string name="kg_face_not_recognized" msgid="6382535088345875294">"Không nhận dạng được"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
<item quantity="other">Hãy nhập mã PIN của SIM. Bạn còn <xliff:g id="NUMBER_1">%d</xliff:g> lần thử.</item>
<item quantity="one">Hãy nhập mã PIN của SIM. Bạn còn <xliff:g id="NUMBER_0">%d</xliff:g> lần thử trước khi phải liên hệ với nhà mạng để mở khóa thiết bị của mình.</item>
diff --git a/packages/SystemUI/res-keyguard/values-zh-rCN/strings.xml b/packages/SystemUI/res-keyguard/values-zh-rCN/strings.xml
index f6576b3..693a31b 100644
--- a/packages/SystemUI/res-keyguard/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-zh-rCN/strings.xml
@@ -140,7 +140,10 @@
<item quantity="other">设备已保持锁定状态达 <xliff:g id="NUMBER_1">%d</xliff:g> 小时。请确认密码。</item>
<item quantity="one">设备已保持锁定状态达 <xliff:g id="NUMBER_0">%d</xliff:g> 小时。请确认密码。</item>
</plurals>
- <string name="fingerprint_not_recognized" msgid="348813995267914625">"无法识别"</string>
+ <!-- no translation found for kg_fingerprint_not_recognized (7854413849848459418) -->
+ <skip />
+ <!-- no translation found for kg_face_not_recognized (6382535088345875294) -->
+ <skip />
<plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
<item quantity="other">请输入 SIM 卡 PIN 码,您还可以尝试 <xliff:g id="NUMBER_1">%d</xliff:g> 次。</item>
<item quantity="one">请输入 SIM 卡 PIN 码,您还可以尝试 <xliff:g id="NUMBER_0">%d</xliff:g> 次。如果仍不正确,则需要联系运营商帮您解锁设备。</item>
diff --git a/packages/SystemUI/res-keyguard/values-zh-rHK/strings.xml b/packages/SystemUI/res-keyguard/values-zh-rHK/strings.xml
index af238dd..539bb92 100644
--- a/packages/SystemUI/res-keyguard/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-zh-rHK/strings.xml
@@ -140,7 +140,8 @@
<item quantity="other">裝置在過去 <xliff:g id="NUMBER_1">%d</xliff:g> 小時內未有解鎖,請確認密碼。</item>
<item quantity="one">裝置在過去 <xliff:g id="NUMBER_0">%d</xliff:g> 小時內未有解鎖,請確認密碼。</item>
</plurals>
- <string name="fingerprint_not_recognized" msgid="348813995267914625">"未能識別"</string>
+ <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"未能識別"</string>
+ <string name="kg_face_not_recognized" msgid="6382535088345875294">"未能識別"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
<item quantity="other">輸入 SIM 卡的 PIN,您還可以再試 <xliff:g id="NUMBER_1">%d</xliff:g> 次。</item>
<item quantity="one">輸入 SIM 卡的 PIN,您還可以再試 <xliff:g id="NUMBER_0">%d</xliff:g> 次。如果仍然輸入錯誤,您必須聯絡流動網絡供應商解鎖您的裝置。</item>
diff --git a/packages/SystemUI/res-keyguard/values-zh-rTW/strings.xml b/packages/SystemUI/res-keyguard/values-zh-rTW/strings.xml
index b8cdfc3..6e5e984 100644
--- a/packages/SystemUI/res-keyguard/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-zh-rTW/strings.xml
@@ -140,7 +140,8 @@
<item quantity="other">裝置已有 <xliff:g id="NUMBER_1">%d</xliff:g> 小時未解鎖。請確認密碼。</item>
<item quantity="one">裝置已有 <xliff:g id="NUMBER_0">%d</xliff:g> 小時未解鎖。請確認密碼。</item>
</plurals>
- <string name="fingerprint_not_recognized" msgid="348813995267914625">"無法識別"</string>
+ <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"無法識別"</string>
+ <string name="kg_face_not_recognized" msgid="6382535088345875294">"無法識別"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
<item quantity="other">請輸入 SIM 卡的 PIN 碼,你還可以再試 <xliff:g id="NUMBER_1">%d</xliff:g> 次。</item>
<item quantity="one">請輸入 SIM 卡的 PIN 碼,你還可以再試 <xliff:g id="NUMBER_0">%d</xliff:g> 次。如果仍然失敗,就必須請電信業者為裝置解鎖。</item>
diff --git a/packages/SystemUI/res-keyguard/values-zu/strings.xml b/packages/SystemUI/res-keyguard/values-zu/strings.xml
index 5409638..78bb66c 100644
--- a/packages/SystemUI/res-keyguard/values-zu/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-zu/strings.xml
@@ -140,7 +140,8 @@
<item quantity="one">Idivayisi ayikavulwa ngamahora angu-<xliff:g id="NUMBER_1">%d</xliff:g>. Qinisekisa iphasiwedi.</item>
<item quantity="other">Idivayisi ayikavulwa ngamahora angu-<xliff:g id="NUMBER_1">%d</xliff:g>. Qinisekisa iphasiwedi.</item>
</plurals>
- <string name="fingerprint_not_recognized" msgid="348813995267914625">"Akubonwa"</string>
+ <string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Akwaziwa"</string>
+ <string name="kg_face_not_recognized" msgid="6382535088345875294">"Akwaziwa"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
<item quantity="one">Faka i-PIN ye-SIM, unemizamo engu-<xliff:g id="NUMBER_1">%d</xliff:g> esele.</item>
<item quantity="other">Faka i-PIN ye-SIM, unemizamo engu-<xliff:g id="NUMBER_1">%d</xliff:g> esele.</item>
diff --git a/packages/SystemUI/res-keyguard/values/strings.xml b/packages/SystemUI/res-keyguard/values/strings.xml
index 513d848..1d5aa6d 100644
--- a/packages/SystemUI/res-keyguard/values/strings.xml
+++ b/packages/SystemUI/res-keyguard/values/strings.xml
@@ -48,10 +48,10 @@
<string name="keyguard_enter_your_pin">Enter your PIN</string>
<!-- Instructions telling the user to enter their pattern to unlock the keyguard [CHAR LIMIT=30] -->
- <string name="keyguard_enter_your_pattern">Enter your Pattern</string>
+ <string name="keyguard_enter_your_pattern">Enter your pattern</string>
<!-- Instructions telling the user to enter their text password to unlock the keyguard [CHAR LIMIT=30] -->
- <string name="keyguard_enter_your_password">Enter your Password</string>
+ <string name="keyguard_enter_your_password">Enter your password</string>
<!-- Instructions telling the user that they entered the wrong pin while trying
to unlock the keyguard. Displayed in one line in a large font. -->
@@ -62,7 +62,7 @@
<!-- When the lock screen is showing, the phone is plugged in and the battery is fully
charged, say that it is charged. -->
- <string name="keyguard_charged">Charged</string>
+ <string name="keyguard_charged">Fully charged</string>
<!-- When the lock screen is showing and the phone plugged in, and the battery
is not fully charged, say that it's charging. -->
@@ -146,9 +146,9 @@
<!-- Message shown in pattern unlock after some number of unsuccessful attempts -->
<string name="kg_forgot_pattern_button_text">Forgot Pattern</string>
<!-- Message shown when user enters wrong pattern -->
- <string name="kg_wrong_pattern">Wrong Pattern</string>
+ <string name="kg_wrong_pattern">Wrong pattern</string>
<!-- Message shown when user enters wrong password -->
- <string name="kg_wrong_password">Wrong Password</string>
+ <string name="kg_wrong_password">Wrong password</string>
<!-- Message shown when user enters wrong PIN -->
<string name="kg_wrong_pin">Wrong PIN</string>
<!-- Countdown message shown after too many failed unlock attempts -->
@@ -381,7 +381,10 @@
</plurals>
<!-- Fingerprint hint message when finger was not recognized.-->
- <string name="fingerprint_not_recognized">Not recognized</string>
+ <string name="kg_fingerprint_not_recognized">Not recognized</string>
+
+ <!-- Face hint message when finger was not recognized. [CHAR LIMIT=20] -->
+ <string name="kg_face_not_recognized">Not recognized</string>
<!-- Instructions telling the user remaining times when enter SIM PIN view. -->
<plurals name="kg_password_default_pin_message">
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/color/qs_detail_progress_track.xml b/packages/SystemUI/res/color-night/qs_detail_progress_track.xml
similarity index 100%
rename from packages/overlays/SysuiDarkThemeOverlay/res/color/qs_detail_progress_track.xml
rename to packages/SystemUI/res/color-night/qs_detail_progress_track.xml
diff --git a/packages/SystemUI/res/color/notification_guts_buttons.xml b/packages/SystemUI/res/color/notification_guts_buttons.xml
index 3b8d59b..412e0be 100644
--- a/packages/SystemUI/res/color/notification_guts_buttons.xml
+++ b/packages/SystemUI/res/color/notification_guts_buttons.xml
@@ -2,6 +2,6 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_checked="true"
android:color="?android:attr/colorAccent" />
- <item android:color="@android:color/black"
+ <item android:color="@color/notification_primary_text_color"
android:alpha=".54" />
</selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable-hdpi/qs_scrubber_track.9.png b/packages/SystemUI/res/drawable-hdpi/qs_scrubber_track.9.png
deleted file mode 100644
index 0899d35..0000000
--- a/packages/SystemUI/res/drawable-hdpi/qs_scrubber_track.9.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/qs_scrubber_track.9.png b/packages/SystemUI/res/drawable-mdpi/qs_scrubber_track.9.png
deleted file mode 100644
index 2266449..0000000
--- a/packages/SystemUI/res/drawable-mdpi/qs_scrubber_track.9.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/qs_scrubber_track.9.png b/packages/SystemUI/res/drawable-xhdpi/qs_scrubber_track.9.png
deleted file mode 100644
index 3328add..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/qs_scrubber_track.9.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/qs_scrubber_track.9.png b/packages/SystemUI/res/drawable-xxhdpi/qs_scrubber_track.9.png
deleted file mode 100644
index ed651da..0000000
--- a/packages/SystemUI/res/drawable-xxhdpi/qs_scrubber_track.9.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxxhdpi/qs_scrubber_track.9.png b/packages/SystemUI/res/drawable-xxxhdpi/qs_scrubber_track.9.png
deleted file mode 100644
index 06e1202..0000000
--- a/packages/SystemUI/res/drawable-xxxhdpi/qs_scrubber_track.9.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable/faster_emergency_icon.xml b/packages/SystemUI/res/drawable/faster_emergency_icon.xml
new file mode 100644
index 0000000..208ff41
--- /dev/null
+++ b/packages/SystemUI/res/drawable/faster_emergency_icon.xml
@@ -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.
+-->
+<!-- TODO: For demo only, will change content after UI team provide new faster emergency icon. -->
+<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"
+ android:tint="?attr/colorControlNormal">
+ <path
+ android:fillColor="#D93025"
+ android:pathData="M0,0h24v24H0z" />
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M19,3H5c-1.1,0-1.99,0.9,-1.99,2L3,19c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2V5c0-1.1-0.9-2-2-2zm-1,11h-4v4h-4v-4H6v-4h4V6h4v4h4v4z" />
+
+</vector>
diff --git a/packages/SystemUI/res/layout/app_ops_info.xml b/packages/SystemUI/res/layout/app_ops_info.xml
index 676301e..82a0115 100644
--- a/packages/SystemUI/res/layout/app_ops_info.xml
+++ b/packages/SystemUI/res/layout/app_ops_info.xml
@@ -15,7 +15,7 @@
limitations under the License.
-->
-<com.android.systemui.statusbar.AppOpsInfo
+<com.android.systemui.statusbar.notification.row.AppOpsInfo
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -88,4 +88,4 @@
android:layout_marginEnd="-8dp"
style="@style/TextAppearance.NotificationInfo.Button"/>
</LinearLayout>
-</com.android.systemui.statusbar.AppOpsInfo>
+</com.android.systemui.statusbar.notification.row.AppOpsInfo>
diff --git a/packages/SystemUI/res/layout/contextual.xml b/packages/SystemUI/res/layout/contextual.xml
index 94591e9..c8f0a24 100644
--- a/packages/SystemUI/res/layout/contextual.xml
+++ b/packages/SystemUI/res/layout/contextual.xml
@@ -20,6 +20,7 @@
android:layout_width="@dimen/navigation_key_width"
android:layout_height="match_parent"
android:importantForAccessibility="no"
+ android:focusable="false"
android:clipChildren="false"
android:clipToPadding="false"
>
diff --git a/packages/SystemUI/res/layout/global_actions_wrapped.xml b/packages/SystemUI/res/layout/global_actions_wrapped.xml
index b715def..7f4e0d2 100644
--- a/packages/SystemUI/res/layout/global_actions_wrapped.xml
+++ b/packages/SystemUI/res/layout/global_actions_wrapped.xml
@@ -3,7 +3,9 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
+ android:layout_gravity="top|right"
android:layout_marginBottom="0dp"
+ android:orientation="vertical"
android:paddingTop="@dimen/global_actions_top_padding"
android:clipToPadding="false"
android:theme="@style/qs_theme"
@@ -17,7 +19,19 @@
android:layout_gravity="top|right"
android:gravity="center"
android:orientation="vertical"
- android:padding="12dp"
- android:translationZ="9dp" />
+ android:padding="@dimen/global_actions_padding"
+ android:translationZ="@dimen/global_actions_translate" />
+
+ <!-- For separated button-->
+ <FrameLayout
+ android:id="@+id/separated_button"
+ android:layout_width="@dimen/global_actions_panel_width"
+ android:layout_height="wrap_content"
+ android:layout_gravity="top|right"
+ android:layout_marginTop="6dp"
+ android:gravity="center"
+ android:orientation="vertical"
+ android:padding="@dimen/global_actions_padding"
+ android:translationZ="@dimen/global_actions_translate" />
</com.android.systemui.HardwareUiLayout>
diff --git a/packages/SystemUI/res/layout/hybrid_notification.xml b/packages/SystemUI/res/layout/hybrid_notification.xml
index 23e8a15..e8d7751 100644
--- a/packages/SystemUI/res/layout/hybrid_notification.xml
+++ b/packages/SystemUI/res/layout/hybrid_notification.xml
@@ -15,7 +15,7 @@
~ limitations under the License
-->
-<com.android.systemui.statusbar.notification.HybridNotificationView
+<com.android.systemui.statusbar.notification.row.HybridNotificationView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -35,4 +35,4 @@
android:singleLine="true"
style="?attr/hybridNotificationTextStyle"
/>
-</com.android.systemui.statusbar.notification.HybridNotificationView>
\ No newline at end of file
+</com.android.systemui.statusbar.notification.row.HybridNotificationView>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/menu_ime.xml b/packages/SystemUI/res/layout/menu_ime.xml
index 9130fb4..8a3a0b1 100644
--- a/packages/SystemUI/res/layout/menu_ime.xml
+++ b/packages/SystemUI/res/layout/menu_ime.xml
@@ -17,13 +17,13 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:systemui="http://schemas.android.com/apk/res-auto"
android:id="@+id/menu_container"
- android:layout_width="match_parent"
+ android:layout_width="@dimen/navigation_key_width"
android:layout_height="match_parent"
android:importantForAccessibility="no"
>
- <!-- Use width & height=match_parent for parent FrameLayout and buttons because they are placed
- inside a view that has a size controlled by weight. Ensure weight is large enough to support
- icon size. -->
+ <!-- Use nav button width & height=match_parent for parent FrameLayout and buttons because they
+ are placed inside a view that has a size controlled by weight. Ensure weight is large enough to
+ support icon size. -->
<com.android.systemui.statusbar.policy.KeyButtonView
android:id="@+id/menu"
diff --git a/packages/SystemUI/res/layout/notification_children_container.xml b/packages/SystemUI/res/layout/notification_children_container.xml
index ac6a000..d3bb5d5 100644
--- a/packages/SystemUI/res/layout/notification_children_container.xml
+++ b/packages/SystemUI/res/layout/notification_children_container.xml
@@ -15,7 +15,7 @@
~ limitations under the License
-->
-<com.android.systemui.statusbar.stack.NotificationChildrenContainer
+<com.android.systemui.statusbar.notification.stack.NotificationChildrenContainer
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
diff --git a/packages/SystemUI/res/layout/notification_guts.xml b/packages/SystemUI/res/layout/notification_guts.xml
index 9d8ef83..dc94697 100644
--- a/packages/SystemUI/res/layout/notification_guts.xml
+++ b/packages/SystemUI/res/layout/notification_guts.xml
@@ -15,7 +15,7 @@
limitations under the License.
-->
-<com.android.systemui.statusbar.NotificationGuts
+<com.android.systemui.statusbar.notification.row.NotificationGuts
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
diff --git a/packages/SystemUI/res/layout/notification_info.xml b/packages/SystemUI/res/layout/notification_info.xml
index 88d19f4..e1e812b 100644
--- a/packages/SystemUI/res/layout/notification_info.xml
+++ b/packages/SystemUI/res/layout/notification_info.xml
@@ -15,7 +15,7 @@
limitations under the License.
-->
-<com.android.systemui.statusbar.NotificationInfo
+<com.android.systemui.statusbar.notification.row.NotificationInfo
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/notification_guts"
android:layout_width="match_parent"
@@ -161,30 +161,31 @@
style="@style/TextAppearance.NotificationInfo.Button"/>
</LinearLayout>
</LinearLayout>
- <RelativeLayout
+ <com.android.systemui.statusbar.notification.row.NotificationUndoLayout
android:id="@+id/confirmation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginBottom="@dimen/notification_guts_button_spacing"
- android:layout_marginTop="@dimen/notification_guts_button_spacing"
- android:layout_marginStart="@dimen/notification_guts_button_side_margin"
- android:layout_marginEnd="@dimen/notification_guts_button_side_margin"
android:visibility="gone"
android:orientation="horizontal" >
<TextView
android:id="@+id/confirmation_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_centerVertical="true"
+ android:layout_gravity="start|center_vertical"
+ android:layout_marginStart="@*android:dimen/notification_content_margin_start"
+ android:layout_marginEnd="@*android:dimen/notification_content_margin_start"
android:text="@string/notification_channel_disabled"
style="@style/TextAppearance.NotificationInfo.Confirmation"/>
<TextView
android:id="@+id/undo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_alignParentEnd="true"
- android:layout_centerVertical="true"
+ android:layout_marginTop="@dimen/notification_guts_button_spacing"
+ android:layout_marginBottom="@dimen/notification_guts_button_spacing"
+ android:layout_marginStart="@dimen/notification_guts_button_side_margin"
+ android:layout_marginEnd="@dimen/notification_guts_button_side_margin"
+ android:layout_gravity="end|center_vertical"
android:text="@string/inline_undo"
style="@style/TextAppearance.NotificationInfo.Button"/>
- </RelativeLayout>
-</com.android.systemui.statusbar.NotificationInfo>
+ </com.android.systemui.statusbar.notification.row.NotificationUndoLayout>
+</com.android.systemui.statusbar.notification.row.NotificationInfo>
diff --git a/packages/SystemUI/res/layout/notification_snooze.xml b/packages/SystemUI/res/layout/notification_snooze.xml
index ea6ef4c..fae759a 100644
--- a/packages/SystemUI/res/layout/notification_snooze.xml
+++ b/packages/SystemUI/res/layout/notification_snooze.xml
@@ -15,7 +15,7 @@
limitations under the License.
-->
-<com.android.systemui.statusbar.NotificationSnooze
+<com.android.systemui.statusbar.notification.row.NotificationSnooze
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -36,7 +36,7 @@
android:layout_alignParentStart="true"
android:layout_centerVertical="true"
android:paddingStart="@*android:dimen/notification_content_margin_start"
- android:textColor="#DD000000"
+ android:textColor="@color/notification_primary_text_color"
android:paddingEnd="4dp"/>
<ImageView
@@ -72,4 +72,4 @@
android:paddingBottom="8dp"
android:orientation="vertical" />
-</com.android.systemui.statusbar.NotificationSnooze>
+</com.android.systemui.statusbar.notification.row.NotificationSnooze>
diff --git a/packages/SystemUI/res/layout/screen_pinning_request_buttons.xml b/packages/SystemUI/res/layout/screen_pinning_request_buttons.xml
index 25b117f..f13f019 100644
--- a/packages/SystemUI/res/layout/screen_pinning_request_buttons.xml
+++ b/packages/SystemUI/res/layout/screen_pinning_request_buttons.xml
@@ -43,7 +43,7 @@
android:layout_weight="0"
android:paddingStart="@dimen/screen_pinning_request_frame_padding"
android:paddingEnd="@dimen/screen_pinning_request_frame_padding"
- android:theme="@*android:style/ThemeOverlay.DeviceDefault.Accent">
+ android:theme="@style/ScreenPinningRequestTheme">
<ImageView
android:id="@+id/screen_pinning_back_bg_light"
@@ -86,7 +86,7 @@
android:layout_weight="0"
android:paddingStart="@dimen/screen_pinning_request_frame_padding"
android:paddingEnd="@dimen/screen_pinning_request_frame_padding"
- android:theme="@*android:style/ThemeOverlay.DeviceDefault.Accent">
+ android:theme="@style/ScreenPinningRequestTheme">
<ImageView
android:id="@+id/screen_pinning_home_bg_light"
@@ -129,7 +129,7 @@
android:layout_weight="0"
android:paddingStart="@dimen/screen_pinning_request_frame_padding"
android:paddingEnd="@dimen/screen_pinning_request_frame_padding"
- android:theme="@*android:style/ThemeOverlay.DeviceDefault.Accent">
+ android:theme="@style/ScreenPinningRequestTheme">
<ImageView
android:id="@+id/screen_pinning_recents_bg_light"
diff --git a/packages/SystemUI/res/layout/screen_pinning_request_buttons_land.xml b/packages/SystemUI/res/layout/screen_pinning_request_buttons_land.xml
index 367c13c..420b072 100644
--- a/packages/SystemUI/res/layout/screen_pinning_request_buttons_land.xml
+++ b/packages/SystemUI/res/layout/screen_pinning_request_buttons_land.xml
@@ -39,7 +39,7 @@
android:layout_height="@dimen/screen_pinning_request_button_width"
android:layout_width="@dimen/screen_pinning_request_button_height"
android:layout_weight="0"
- android:theme="@*android:style/ThemeOverlay.DeviceDefault.Accent">
+ android:theme="@style/ScreenPinningRequestTheme">
<ImageView
android:id="@+id/screen_pinning_recents_bg_light"
@@ -79,7 +79,7 @@
android:layout_height="@dimen/screen_pinning_request_button_width"
android:layout_width="@dimen/screen_pinning_request_button_height"
android:layout_weight="0"
- android:theme="@*android:style/ThemeOverlay.DeviceDefault.Accent">
+ android:theme="@style/ScreenPinningRequestTheme">
<ImageView
android:id="@+id/screen_pinning_home_bg_light"
@@ -120,7 +120,7 @@
android:layout_height="@dimen/screen_pinning_request_button_width"
android:layout_width="@dimen/screen_pinning_request_button_height"
android:layout_weight="0"
- android:theme="@*android:style/ThemeOverlay.DeviceDefault.Accent">
+ android:theme="@style/ScreenPinningRequestTheme">
<ImageView
android:id="@+id/screen_pinning_back_bg_light"
diff --git a/packages/SystemUI/res/layout/screen_pinning_request_buttons_sea.xml b/packages/SystemUI/res/layout/screen_pinning_request_buttons_sea.xml
index bac02aa..f9308cd 100644
--- a/packages/SystemUI/res/layout/screen_pinning_request_buttons_sea.xml
+++ b/packages/SystemUI/res/layout/screen_pinning_request_buttons_sea.xml
@@ -39,7 +39,7 @@
android:layout_height="@dimen/screen_pinning_request_button_width"
android:layout_width="@dimen/screen_pinning_request_button_height"
android:layout_weight="0"
- android:theme="@*android:style/ThemeOverlay.DeviceDefault.Accent">
+ android:theme="@style/ScreenPinningRequestTheme">
<ImageView
android:id="@+id/screen_pinning_back_bg_light"
@@ -82,7 +82,7 @@
android:layout_height="@dimen/screen_pinning_request_button_width"
android:layout_width="@dimen/screen_pinning_request_button_height"
android:layout_weight="0"
- android:theme="@*android:style/ThemeOverlay.DeviceDefault.Accent" >
+ android:theme="@style/ScreenPinningRequestTheme" >
<ImageView
android:id="@+id/screen_pinning_home_bg_light"
@@ -125,7 +125,7 @@
android:layout_height="@dimen/screen_pinning_request_button_width"
android:layout_width="@dimen/screen_pinning_request_button_height"
android:layout_weight="0"
- android:theme="@*android:style/ThemeOverlay.DeviceDefault.Accent">
+ android:theme="@style/ScreenPinningRequestTheme">
<ImageView
android:id="@+id/screen_pinning_recents_bg_light"
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index bef0830..2674f07c 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -47,7 +47,7 @@
android:clipChildren="false"
systemui:viewType="com.android.systemui.plugins.qs.QS" />
- <com.android.systemui.statusbar.stack.NotificationStackScrollLayout
+ <com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
android:id="@+id/notification_stack_scroller"
android:layout_marginTop="@dimen/notification_panel_margin_top"
android:layout_width="@dimen/notification_panel_width"
diff --git a/packages/SystemUI/res/layout/status_bar_no_notifications.xml b/packages/SystemUI/res/layout/status_bar_no_notifications.xml
index 1e00e52..28b5a40 100644
--- a/packages/SystemUI/res/layout/status_bar_no_notifications.xml
+++ b/packages/SystemUI/res/layout/status_bar_no_notifications.xml
@@ -26,9 +26,9 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="64dp"
- android:paddingTop="28dp"
+ android:paddingTop="12dp"
+ android:textAppearance="?android:attr/textAppearanceButton"
android:gravity="top|center_horizontal"
android:textColor="?attr/wallpaperTextColor"
- android:textSize="16sp"
android:text="@string/empty_shade_text"/>
</com.android.systemui.statusbar.EmptyShadeView>
diff --git a/packages/SystemUI/res/layout/status_bar_notification_footer.xml b/packages/SystemUI/res/layout/status_bar_notification_footer.xml
index 6c5cebc..056f16a 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_footer.xml
+++ b/packages/SystemUI/res/layout/status_bar_notification_footer.xml
@@ -15,7 +15,7 @@
-->
<!-- Extends Framelayout -->
-<com.android.systemui.statusbar.FooterView
+<com.android.systemui.statusbar.notification.row.FooterView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -26,7 +26,7 @@
android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
- <com.android.systemui.statusbar.FooterViewButton
+ <com.android.systemui.statusbar.notification.row.FooterViewButton
style="@android:style/Widget.Material.Button.Borderless"
android:id="@+id/manage_text"
android:layout_width="wrap_content"
@@ -36,7 +36,7 @@
android:text="@string/manage_notifications_text"
android:textColor="?attr/wallpaperTextColor"
android:textAllCaps="false"/>
- <com.android.systemui.statusbar.FooterViewButton
+ <com.android.systemui.statusbar.notification.row.FooterViewButton
style="@android:style/Widget.Material.Button.Borderless"
android:id="@+id/dismiss_text"
android:layout_width="wrap_content"
@@ -47,4 +47,4 @@
android:text="@string/clear_all_notifications_text"
android:textColor="?attr/wallpaperTextColor"/>
</com.android.systemui.statusbar.AlphaOptimizedFrameLayout>
-</com.android.systemui.statusbar.FooterView>
+</com.android.systemui.statusbar.notification.row.FooterView>
diff --git a/packages/SystemUI/res/layout/status_bar_notification_row.xml b/packages/SystemUI/res/layout/status_bar_notification_row.xml
index f15ca9e..84b9e3d 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_row.xml
+++ b/packages/SystemUI/res/layout/status_bar_notification_row.xml
@@ -16,7 +16,7 @@
-->
<!-- extends FrameLayout -->
-<com.android.systemui.statusbar.ExpandableNotificationRow
+<com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -26,21 +26,23 @@
<!-- Menu displayed behind notification added here programmatically -->
- <com.android.systemui.statusbar.NotificationBackgroundView
+ <com.android.systemui.statusbar.notification.row.NotificationBackgroundView
android:id="@+id/backgroundNormal"
android:layout_width="match_parent"
android:layout_height="match_parent" />
- <com.android.systemui.statusbar.NotificationBackgroundView
+ <com.android.systemui.statusbar.notification.row.NotificationBackgroundView
android:id="@+id/backgroundDimmed"
android:layout_width="match_parent"
android:layout_height="match_parent" />
- <com.android.systemui.statusbar.NotificationContentView android:id="@+id/expanded"
+ <com.android.systemui.statusbar.notification.row.NotificationContentView
+ android:id="@+id/expanded"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
- <com.android.systemui.statusbar.NotificationContentView android:id="@+id/expandedPublic"
+ <com.android.systemui.statusbar.notification.row.NotificationContentView
+ android:id="@+id/expandedPublic"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
@@ -76,4 +78,4 @@
android:layout_width="match_parent"
android:layout_height="match_parent" />
-</com.android.systemui.statusbar.ExpandableNotificationRow>
+</com.android.systemui.statusbar.notification.row.ExpandableNotificationRow>
diff --git a/packages/SystemUI/res/layout/status_bar_notification_shelf.xml b/packages/SystemUI/res/layout/status_bar_notification_shelf.xml
index 7bfbd3c..781c015 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_shelf.xml
+++ b/packages/SystemUI/res/layout/status_bar_notification_shelf.xml
@@ -23,14 +23,14 @@
android:clickable="true"
>
- <com.android.systemui.statusbar.NotificationBackgroundView android:id="@+id/backgroundNormal"
+ <com.android.systemui.statusbar.notification.row.NotificationBackgroundView
+ android:id="@+id/backgroundNormal"
android:layout_width="match_parent"
- android:layout_height="match_parent"
- />
- <com.android.systemui.statusbar.NotificationBackgroundView android:id="@+id/backgroundDimmed"
+ android:layout_height="match_parent" />
+ <com.android.systemui.statusbar.notification.row.NotificationBackgroundView
+ android:id="@+id/backgroundDimmed"
android:layout_width="match_parent"
- android:layout_height="match_parent"
- />
+ android:layout_height="match_parent" />
<com.android.systemui.statusbar.phone.NotificationIconContainer
android:id="@+id/content"
android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/layout/tuner_activity.xml b/packages/SystemUI/res/layout/tuner_activity.xml
new file mode 100644
index 0000000..0b792ae
--- /dev/null
+++ b/packages/SystemUI/res/layout/tuner_activity.xml
@@ -0,0 +1,37 @@
+<?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.
+ -->
+
+<!-- The tuner content view -->
+<LinearLayout
+ android:id="@+id/content_parent"
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_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="wrap_content"
+ android:theme="?android:attr/actionBarTheme"
+ android:navigationContentDescription="@*android:string/action_bar_up_description" />
+ <FrameLayout
+ android:id="@+id/content_frame"
+ android:layout_width="match_parent"
+ android:layout_height="fill_parent"
+ android:background="?android:attr/windowBackground" />
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index 09cf5c0..fb9b5df 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -522,7 +522,6 @@
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Versteek <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Dit sal verskyn die volgende keer wanneer jy dit in instellings aanskakel."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Versteek"</string>
- <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Jy gebruik tans jou werkprofiel"</string>
<string name="stream_voice_call" msgid="4410002696470423714">"Bel"</string>
<string name="stream_system" msgid="7493299064422163147">"Stelsel"</string>
<string name="stream_ring" msgid="8213049469184048338">"Lui"</string>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index dfcd419..305082f 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -522,7 +522,6 @@
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"<xliff:g id="TILE_LABEL">%1$s</xliff:g> ይደበቅ?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"በቅንብሮች ውስጥ በሚቀጥለው ጊዜ እንዲበራ በሚያደርጉበት ጊዜ ዳግመኛ ብቅ ይላል።"</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"ደብቅ"</string>
- <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"የስራ መገለጫዎን እየተጠቀሙ ነው"</string>
<string name="stream_voice_call" msgid="4410002696470423714">"ጥሪ"</string>
<string name="stream_system" msgid="7493299064422163147">"ሥርዓት"</string>
<string name="stream_ring" msgid="8213049469184048338">"ጥሪ"</string>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index 4160b3b..8f197e8 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -534,7 +534,6 @@
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"هل تريد إخفاء <xliff:g id="TILE_LABEL">%1$s</xliff:g>؟"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"سيظهر مرة أخرى عند تمكينه في الإعدادات المرة التالية."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"إخفاء"</string>
- <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"أنت تستخدم ملفك الشخصي للعمل"</string>
<string name="stream_voice_call" msgid="4410002696470423714">"الاتصال"</string>
<string name="stream_system" msgid="7493299064422163147">"النظام"</string>
<string name="stream_ring" msgid="8213049469184048338">"الرنين"</string>
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index a310683..c20b5bc 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -95,8 +95,7 @@
<string name="accessibility_unlock_button" msgid="128158454631118828">"আনলক কৰক"</string>
<string name="accessibility_waiting_for_fingerprint" msgid="4808860050517462885">"ফিংগাৰপ্ৰিণ্টৰ বাবে ৰৈ থকা হৈছে"</string>
<string name="accessibility_unlock_without_fingerprint" msgid="7541705575183694446">"ফিংগাৰপ্ৰিণ্ট ব্যৱহাৰ নকৰাকৈ আনলক কৰক"</string>
- <!-- no translation found for accessibility_scanning_face (769545173211758586) -->
- <skip />
+ <string name="accessibility_scanning_face" msgid="769545173211758586">"চেহেৰা স্কেন কৰি থকা হৈছে"</string>
<string name="accessibility_send_smart_reply" msgid="7766727839703044493">"পঠিয়াওক"</string>
<string name="unlock_label" msgid="8779712358041029439">"আনলক কৰক"</string>
<string name="phone_label" msgid="2320074140205331708">"ফ\'ন খোলক"</string>
@@ -523,7 +522,6 @@
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"<xliff:g id="TILE_LABEL">%1$s</xliff:g> লুকুৱাবনে?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"আপুনি ইয়াক পৰৱৰ্তী সময়ত ছেটিংসমূহত অন কৰিলে ই পুনৰ প্ৰকট হ\'ব।"</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"লুকুৱাওক"</string>
- <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"আপুনি আপোনাৰ কৰ্মস্থানৰ প্ৰ\'ফাইল ব্যৱহাৰ কৰি আছে"</string>
<string name="stream_voice_call" msgid="4410002696470423714">"কল"</string>
<string name="stream_system" msgid="7493299064422163147">"ছিষ্টেম"</string>
<string name="stream_ring" msgid="8213049469184048338">"ৰিং"</string>
@@ -854,6 +852,5 @@
<string name="auto_saver_enabled_text" msgid="874711029884777579">"বেটাৰি চ্চাৰ্জৰ স্তৰ <xliff:g id="PERCENTAGE">%d</xliff:g>%%তকৈ কম হোৱাৰ লগে লগে বেটাৰি সঞ্চয়কাৰী স্বয়ংক্ৰিয়ভাৱে অন হ’ব।"</string>
<string name="open_saver_setting_action" msgid="8314624730997322529">"ছেটিংবোৰ"</string>
<string name="auto_saver_okay_action" msgid="2701221740227683650">"বুজি পালোঁ"</string>
- <!-- no translation found for heap_dump_tile_name (9141031328971226374) -->
- <skip />
+ <string name="heap_dump_tile_name" msgid="9141031328971226374">"SysUI হীপ ডাম্প কৰক"</string>
</resources>
diff --git a/packages/SystemUI/res/values-as/strings_car.xml b/packages/SystemUI/res/values-as/strings_car.xml
index e8cb6e3..1c4d7944 100644
--- a/packages/SystemUI/res/values-as/strings_car.xml
+++ b/packages/SystemUI/res/values-as/strings_car.xml
@@ -22,8 +22,6 @@
<string name="car_guest" msgid="3738772168718508650">"অতিথি"</string>
<string name="car_add_user" msgid="5245196248349230898">"ব্যৱহাৰকাৰী যোগ কৰক"</string>
<string name="car_new_user" msgid="8142927244990323906">"নতুন ব্যৱহাৰকাৰী"</string>
- <!-- no translation found for user_add_user_message_setup (1791011504259527329) -->
- <skip />
- <!-- no translation found for user_add_user_message_update (3383320289232716179) -->
- <skip />
+ <string name="user_add_user_message_setup" msgid="1791011504259527329">"আপুনি কোনো নতুন ব্য়ৱহাৰকাৰীক যোগ কৰিলে তেখেতে নিজৰ বাবে খালী ঠাই ছেট আপ কৰিব লাগে।"</string>
+ <string name="user_add_user_message_update" msgid="3383320289232716179">"সকলো ব্য়ৱহাৰকাৰীয়ে অইন ব্য়ৱহাৰকাৰীৰ বাবে এপসমূহ আপডেট কৰিব পাৰে।"</string>
</resources>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index ea511c1..239dc20 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -522,7 +522,6 @@
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"<xliff:g id="TILE_LABEL">%1$s</xliff:g> gizlədilsin?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Ayarlarda onu aktivləşdirəcəyiniz vaxta qədər o, yenidən görünəcək."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Gizlədin"</string>
- <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"İş profilinizi istifadə edirsiniz"</string>
<string name="stream_voice_call" msgid="4410002696470423714">"Zəng"</string>
<string name="stream_system" msgid="7493299064422163147">"Sistem"</string>
<string name="stream_ring" msgid="8213049469184048338">"Zəng Edin"</string>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index 400302f..47eb266 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -525,7 +525,6 @@
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Želite li da sakrijete <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Ovo će se ponovo pojaviti kada ga sledeći put budete uključili u podešavanjima."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Sakrij"</string>
- <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Koristite profil za Work"</string>
<string name="stream_voice_call" msgid="4410002696470423714">"Poziv"</string>
<string name="stream_system" msgid="7493299064422163147">"Sistem"</string>
<string name="stream_ring" msgid="8213049469184048338">"Zvono"</string>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index ecf32b0..2db91e8 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -530,7 +530,6 @@
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Схаваць <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Гэта паведамленне з\'явіцца зноў у наступны раз, калі вы ўключыце яго ў наладах."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Схаваць"</string>
- <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Вы выкарыстоўваеце свой працоўны профіль"</string>
<string name="stream_voice_call" msgid="4410002696470423714">"Выклік"</string>
<string name="stream_system" msgid="7493299064422163147">"Сістэма"</string>
<string name="stream_ring" msgid="8213049469184048338">"Званок"</string>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index 0833a7a7..850977d 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -522,7 +522,6 @@
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Да се скрие ли „<xliff:g id="TILE_LABEL">%1$s</xliff:g>“?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Бързите настройки ще се покажат отново следващия път, когато ги включите от „Настройки“."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Скриване"</string>
- <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Използвате служебния си потребителски профил"</string>
<string name="stream_voice_call" msgid="4410002696470423714">"Обаждане"</string>
<string name="stream_system" msgid="7493299064422163147">"Система"</string>
<string name="stream_ring" msgid="8213049469184048338">"Позвъняване"</string>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index 47ad590..eacf30c 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -472,8 +472,8 @@
<string name="monitoring_description_managed_profile_ca_certificate" msgid="4683248196789897964">"আপনার প্রতিষ্ঠান আপনার অফিস প্রোফাইলে একটি সার্টিফিকেট কর্তৃপক্ষ ইনস্টল করেছে। আপনার নিরাপদ নেটওয়ার্ক ট্রাফিকে নজর রাখা হতে পারে বা তাতে পরিবর্তন করা হতে পারে।"</string>
<string name="monitoring_description_ca_certificate" msgid="7886985418413598352">"এই ডিভাইসে একটি সার্টিফিকেট কর্তৃপক্ষ ইনস্টল করা আছে। আপনার নিরাপদ নেটওয়ার্ক ট্রাফিকে নজর রাখা হতে পারে বা তাতে পরিবর্তন করা হতে পারে।"</string>
<string name="monitoring_description_management_network_logging" msgid="7184005419733060736">"আপনার প্রশাসক নেটওয়ার্ক লগিং চালু করেছেন, যা আপনার ডিভাইসের ট্রাফিকের উপরে নজর রাখে।"</string>
- <string name="monitoring_description_named_vpn" msgid="7403457334088909254">"আপনি <xliff:g id="VPN_APP">%1$s</xliff:g> এ সংযুক্ত রয়েছেন, যা আপনার ইমেল, অ্যাপ, এবং ওয়েবসাইট সহ আপনার নেটওয়ার্ক কার্যকলাপের উপর নজর রাখতে পারে।"</string>
- <string name="monitoring_description_two_named_vpns" msgid="4198511413729213802">"আপনি <xliff:g id="VPN_APP_0">%1$s</xliff:g> এবং <xliff:g id="VPN_APP_1">%2$s</xliff:g> এর সাথে সংযুক্ত রয়েছেন, যেগুলি আপনার ইমেল, অ্যাপ, এবং ওয়েবসাইট সহ আপনার নেটওয়ার্ক কার্যকলাপের উপর নজর রাখতে পারে।"</string>
+ <string name="monitoring_description_named_vpn" msgid="7403457334088909254">"আপনি <xliff:g id="VPN_APP">%1$s</xliff:g> এ সংযুক্ত রয়েছেন, যা আপনার ইমেল, অ্যাপ, এবং ওয়েবসাইট সহ আপনার নেটওয়ার্ক অ্যাক্টিভিটির উপর নজর রাখতে পারে।"</string>
+ <string name="monitoring_description_two_named_vpns" msgid="4198511413729213802">"আপনি <xliff:g id="VPN_APP_0">%1$s</xliff:g> এবং <xliff:g id="VPN_APP_1">%2$s</xliff:g> এর সাথে সংযুক্ত রয়েছেন, যেগুলি আপনার ইমেল, অ্যাপ, এবং ওয়েবসাইট সহ আপনার নেটওয়ার্ক অ্যাক্টিভিটির উপর নজর রাখতে পারে।"</string>
<string name="monitoring_description_managed_profile_named_vpn" msgid="1427905889862420559">"আপনার কর্মস্থলের প্রোফাইল <xliff:g id="VPN_APP">%1$s</xliff:g> এর সাথে সংযুক্ত রয়েছে, যেটি ইমেল, অ্যাপ, এবং ওয়েবসাইট সহ আপনার নেটওয়ার্ক কার্যকলাপে নজর রাখতে পারে।"</string>
<string name="monitoring_description_personal_profile_named_vpn" msgid="3133980926929069283">"আপনার ব্যক্তিগত প্রোফাইল <xliff:g id="VPN_APP">%1$s</xliff:g> এর সাথে সংযুক্ত রয়েছে, যেটি ইমেল, অ্যাপ, এবং ওয়েবসাইট সহ আপনার নেটওয়ার্ক কার্যকলাপে নজর রাখতে পারে৷"</string>
<string name="monitoring_description_do_header_generic" msgid="96588491028288691">"আপনার ডিভাইসটি <xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g> এর দ্বারা পরিচালিত৷"</string>
@@ -481,20 +481,20 @@
<string name="monitoring_description_do_body" msgid="3639594537660975895">"আপনার প্রশাসক আপনার ডিভাইসের লোকেশন তথ্য সহ এই ডিভাইসের সেটিংস, কর্পোরেট অ্যাক্সেস, অ্যাপ্স, ডেটা নিরীক্ষণ ও পরিচালনা করতে পারেন।"</string>
<string name="monitoring_description_do_learn_more_separator" msgid="3785251953067436862">" "</string>
<string name="monitoring_description_do_learn_more" msgid="1849514470437907421">"আরও জানুন"</string>
- <string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"আপনি <xliff:g id="VPN_APP">%1$s</xliff:g> এ সংযুক্ত হয়েছেন, যা ইমেল, অ্যাপ এবং ওয়েবসাইটগুলি সহ আপনার নেটওয়ার্ক কার্যকলাপ নিরীক্ষণ করবে৷"</string>
+ <string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"আপনি <xliff:g id="VPN_APP">%1$s</xliff:g> এ সংযুক্ত হয়েছেন, যা ইমেল, অ্যাপ এবং ওয়েবসাইটগুলি সহ আপনার নেটওয়ার্ক অ্যাক্টিভিটি নিরীক্ষণ করবে৷"</string>
<string name="monitoring_description_vpn_settings_separator" msgid="1933186756733474388">" "</string>
<string name="monitoring_description_vpn_settings" msgid="6434859242636063861">"VPN সেটিংস খুলুন"</string>
<string name="monitoring_description_ca_cert_settings_separator" msgid="4987350385906393626">" "</string>
<string name="monitoring_description_ca_cert_settings" msgid="5489969458872997092">"বিশ্বস্ত শংসাপত্রগুলি খুলুন"</string>
<string name="monitoring_description_network_logging" msgid="7223505523384076027">"আপনার প্রশাসক নেটওয়ার্ক লগিং চালু করেছেন, যা আপনার ডিভাইসের ট্রাফিক নিরীক্ষণ করে।\n\nআরও তথ্যের জন্য আপনার প্রশাসকের সাথে যোগাযোগ করুন।"</string>
- <string name="monitoring_description_vpn" msgid="4445150119515393526">"আপনি VPN সংযোগ সেট আপ করার জন্য একটি অ্যাপ্লিকেশানকে অনুমতি দিন৷\n\nএই অ্যাপ্লিকেশানটি ইমেল, অ্যাপ্লিকেশান ও ওয়েবসাইটগুলি সহ আপনার ডিভাইস এবং নেটওয়ার্কের কার্যকলাপ নিরীক্ষণ করতে পারে।"</string>
- <string name="monitoring_description_vpn_profile_owned" msgid="2958019119161161530">"আপনার কর্মস্থলের প্রোফাইলটি <xliff:g id="ORGANIZATION">%1$s</xliff:g> দ্বারা পরিচালিত হয়।\n\nআপনার প্রশাসক আপনার ইমেল, অ্যাপ্স ও ওয়েবসাইট সহ কর্মস্থলের নেটওয়ার্ক কার্যকলাপ নিরীক্ষণ করতে পারেন।\n\nআরও তথ্যের জন্য আপনার প্রশাসকের সঙ্গে যোগাযোগ করুন।\n\nএছাড়া আপনি একটি VPN এর সাথেও সংযুক্ত যা আপনার নেটওয়ার্ক কার্যকলাপ নিরীক্ষণ করতে পারে।"</string>
+ <string name="monitoring_description_vpn" msgid="4445150119515393526">"আপনি VPN সংযোগ সেট আপ করার জন্য একটি অ্যাপ্লিকেশানকে অনুমতি দিন৷\n\nএই অ্যাপ্লিকেশানটি ইমেল, অ্যাপ্লিকেশান ও ওয়েবসাইটগুলি সহ আপনার ডিভাইস এবং নেটওয়ার্কের অ্যাক্টিভিটি নিরীক্ষণ করতে পারে।"</string>
+ <string name="monitoring_description_vpn_profile_owned" msgid="2958019119161161530">"আপনার কর্মস্থলের প্রোফাইলটি <xliff:g id="ORGANIZATION">%1$s</xliff:g> দ্বারা পরিচালিত হয়।\n\nআপনার প্রশাসক আপনার ইমেল, অ্যাপ্স ও ওয়েবসাইট সহ কর্মস্থলের নেটওয়ার্ক অ্যাক্টিভিটি নিরীক্ষণ করতে পারেন।\n\nআরও তথ্যের জন্য আপনার প্রশাসকের সঙ্গে যোগাযোগ করুন।\n\nএছাড়া আপনি একটি VPN এর সাথেও সংযুক্ত যা আপনার নেটওয়ার্ক অ্যাক্টিভিটি নিরীক্ষণ করতে পারে।"</string>
<string name="legacy_vpn_name" msgid="6604123105765737830">"VPN"</string>
<string name="monitoring_description_app" msgid="1828472472674709532">"আপনি <xliff:g id="APPLICATION">%1$s</xliff:g> এর সাথে সংযুক্ত রয়েছেন, যেটি ইমেল, অ্যাপ, এবং ওয়েবসাইট সহ আপনার নেটওয়ার্ক কার্যকলাপে নজর রাখতে পারে৷"</string>
- <string name="monitoring_description_app_personal" msgid="484599052118316268">"আপনি <xliff:g id="APPLICATION">%1$s</xliff:g> -এ সংযুক্ত হয়েছেন, যা ইমেল, অ্যাপ্লিকেশান এবং ওয়েবসাইটগুলি সমেত আপনার ব্যক্তিগত নেটওয়ার্ক কার্যকলাপ নিরীক্ষণ করতে পারে৷"</string>
- <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"আপনি <xliff:g id="APPLICATION">%1$s</xliff:g> এর সাথে সংযুক্ত হয়েছেন, যা ইমেল, অ্যাপ এবং ওয়েবসাইটগুলি সহ আপনার ব্যক্তিগত নেটওয়ার্কের কার্যকলাপ নিরীক্ষণ করবে৷"</string>
- <string name="monitoring_description_app_work" msgid="4612997849787922906">"<xliff:g id="ORGANIZATION">%1$s</xliff:g> আপনার কর্মস্থলের প্রোফাইল পরিচালনা করে। প্রোফাইলটি <xliff:g id="APPLICATION">%2$s</xliff:g> এর সাথে সংযুক্ত, যেটি ইমেল, অ্যাপ, ও ওয়েবসাইট সহ আপনার কর্মস্থলের নেটওয়ার্ক কার্যকলাপের উপরে নজর রাখতে পারে।\n\nআরও তথ্যের জন্য প্রশাসকের সাথে যোগাযোগ করুন।"</string>
- <string name="monitoring_description_app_personal_work" msgid="5664165460056859391">"<xliff:g id="ORGANIZATION">%1$s</xliff:g> আপনার কর্মস্থলের প্রোফাইল পরিচালনা করে। প্রোফাইলটি <xliff:g id="APPLICATION_WORK">%2$s</xliff:g> এর সাথে সংযুক্ত, যেটি ইমেল অ্যাপ, ও ওয়েবসাইট সহ আপনার কর্মস্থলের নেটওয়ার্ক কার্যকলাপের উপরে নজর রাখতে পারে।\n\n এ ছাড়াও আপনি <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g> এর সাথে সংযুক্ত, যেটি আপনার ব্যক্তিগত নেটওয়ার্কে নজর রাখে।"</string>
+ <string name="monitoring_description_app_personal" msgid="484599052118316268">"আপনি <xliff:g id="APPLICATION">%1$s</xliff:g> -এ সংযুক্ত হয়েছেন, যা ইমেল, অ্যাপ্লিকেশান এবং ওয়েবসাইটগুলি সমেত আপনার ব্যক্তিগত নেটওয়ার্ক অ্যাক্টিভিটি নিরীক্ষণ করতে পারে৷"</string>
+ <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"আপনি <xliff:g id="APPLICATION">%1$s</xliff:g> এর সাথে সংযুক্ত হয়েছেন, যা ইমেল, অ্যাপ এবং ওয়েবসাইটগুলি সহ আপনার ব্যক্তিগত নেটওয়ার্কের অ্যাক্টিভিটি নিরীক্ষণ করবে৷"</string>
+ <string name="monitoring_description_app_work" msgid="4612997849787922906">"<xliff:g id="ORGANIZATION">%1$s</xliff:g> আপনার কর্মস্থলের প্রোফাইল পরিচালনা করে। প্রোফাইলটি <xliff:g id="APPLICATION">%2$s</xliff:g> এর সাথে সংযুক্ত, যেটি ইমেল, অ্যাপ, ও ওয়েবসাইট সহ আপনার কর্মস্থলের নেটওয়ার্ক অ্যাক্টিভিটির উপরে নজর রাখতে পারে।\n\nআরও তথ্যের জন্য প্রশাসকের সাথে যোগাযোগ করুন।"</string>
+ <string name="monitoring_description_app_personal_work" msgid="5664165460056859391">"<xliff:g id="ORGANIZATION">%1$s</xliff:g> আপনার কর্মস্থলের প্রোফাইল পরিচালনা করে। প্রোফাইলটি <xliff:g id="APPLICATION_WORK">%2$s</xliff:g> এর সাথে সংযুক্ত, যেটি ইমেল অ্যাপ, ও ওয়েবসাইট সহ আপনার কর্মস্থলের নেটওয়ার্ক অ্যাক্টিভিটির উপরে নজর রাখতে পারে।\n\n এ ছাড়াও আপনি <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g> এর সাথে সংযুক্ত, যেটি আপনার ব্যক্তিগত নেটওয়ার্কে নজর রাখে।"</string>
<string name="keyguard_indication_trust_granted" msgid="4985003749105182372">"<xliff:g id="USER_NAME">%1$s</xliff:g> এর জন্য আনলক করা হয়েছে"</string>
<string name="keyguard_indication_trust_managed" msgid="8319646760022357585">"<xliff:g id="TRUST_AGENT">%1$s</xliff:g> চালু আছে"</string>
<string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"আপনি নিজে আনলক না করা পর্যন্ত ডিভাইসটি লক হয়ে থাকবে"</string>
@@ -522,7 +522,6 @@
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"<xliff:g id="TILE_LABEL">%1$s</xliff:g> লুকাবেন?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"আপনি পরের বার সেটিংস-এ এটি চালু করলে এটি উপস্থিত হবে"</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"লুকান"</string>
- <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"আপনি আপনার কাজের প্রোফাইল ব্যবহার করছেন"</string>
<string name="stream_voice_call" msgid="4410002696470423714">"কল"</string>
<string name="stream_system" msgid="7493299064422163147">"সিস্টেম"</string>
<string name="stream_ring" msgid="8213049469184048338">"রিং"</string>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index 65d9071..0303ffd 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -525,7 +525,6 @@
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Želite li sakriti <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Pojavit će se sljedeći put kada opciju uključite u postavkama."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Sakrij"</string>
- <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Koristite svoj profil za posao"</string>
<string name="stream_voice_call" msgid="4410002696470423714">"Poziv"</string>
<string name="stream_system" msgid="7493299064422163147">"Sistem"</string>
<string name="stream_ring" msgid="8213049469184048338">"Zvono"</string>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index ad7e546..171fd1a 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -522,7 +522,6 @@
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Vols amagar <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Tornarà a mostrar-se la propera vegada que l\'activis a la configuració."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Amaga"</string>
- <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Estàs utilitzant el perfil professional"</string>
<string name="stream_voice_call" msgid="4410002696470423714">"Trucada"</string>
<string name="stream_system" msgid="7493299064422163147">"Sistema"</string>
<string name="stream_ring" msgid="8213049469184048338">"To de trucada"</string>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index bc84f59..d72b1d1 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -530,7 +530,6 @@
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Skrýt <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Tato položka se znovu zobrazí, až ji v nastavení znovu zapnete."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Skrýt"</string>
- <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Používáte pracovní profil"</string>
<string name="stream_voice_call" msgid="4410002696470423714">"Hovor"</string>
<string name="stream_system" msgid="7493299064422163147">"Systém"</string>
<string name="stream_ring" msgid="8213049469184048338">"Vyzvánění"</string>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index c7e856a..fff619b 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -522,7 +522,6 @@
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Vil du skjule <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Den vises igen, næste gang du aktiverer den i indstillingerne."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Skjul"</string>
- <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Du bruger din arbejdsprofil"</string>
<string name="stream_voice_call" msgid="4410002696470423714">"Ring op"</string>
<string name="stream_system" msgid="7493299064422163147">"System"</string>
<string name="stream_ring" msgid="8213049469184048338">"Ring"</string>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index 42d98e4..d3d9458 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -526,7 +526,6 @@
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"<xliff:g id="TILE_LABEL">%1$s</xliff:g> ausblenden?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Sie wird wieder eingeblendet, wenn du sie in den Einstellungen erneut aktivierst."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Ausblenden"</string>
- <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Du verwendest dein Arbeitsprofil."</string>
<string name="stream_voice_call" msgid="4410002696470423714">"Anruf"</string>
<string name="stream_system" msgid="7493299064422163147">"System"</string>
<string name="stream_ring" msgid="8213049469184048338">"Klingelton"</string>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index 67afd93..45e1b1b 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -522,7 +522,6 @@
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Απόκρυψη <xliff:g id="TILE_LABEL">%1$s</xliff:g>;"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Θα εμφανιστεί ξανά την επόμενη φορά που θα το ενεργοποιήσετε στις ρυθμίσεις."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Απόκρυψη"</string>
- <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Χρησιμοποιείτε το προφίλ εργασίας σας"</string>
<string name="stream_voice_call" msgid="4410002696470423714">"Κλήση"</string>
<string name="stream_system" msgid="7493299064422163147">"Σύστημα"</string>
<string name="stream_ring" msgid="8213049469184048338">"Κλήση"</string>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index 01cd5b0..561601d 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -522,7 +522,6 @@
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Hide <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"It will reappear the next time you turn it on in settings."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Hide"</string>
- <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"You\'re using your work profile"</string>
<string name="stream_voice_call" msgid="4410002696470423714">"Call"</string>
<string name="stream_system" msgid="7493299064422163147">"System"</string>
<string name="stream_ring" msgid="8213049469184048338">"Ring"</string>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index d8c89d3..efa8ad2 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -522,7 +522,6 @@
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Hide <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"It will reappear the next time you turn it on in settings."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Hide"</string>
- <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"You\'re using your work profile"</string>
<string name="stream_voice_call" msgid="4410002696470423714">"Call"</string>
<string name="stream_system" msgid="7493299064422163147">"System"</string>
<string name="stream_ring" msgid="8213049469184048338">"Ring"</string>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index 01cd5b0..561601d 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -522,7 +522,6 @@
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Hide <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"It will reappear the next time you turn it on in settings."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Hide"</string>
- <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"You\'re using your work profile"</string>
<string name="stream_voice_call" msgid="4410002696470423714">"Call"</string>
<string name="stream_system" msgid="7493299064422163147">"System"</string>
<string name="stream_ring" msgid="8213049469184048338">"Ring"</string>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index 01cd5b0..561601d 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -522,7 +522,6 @@
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Hide <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"It will reappear the next time you turn it on in settings."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Hide"</string>
- <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"You\'re using your work profile"</string>
<string name="stream_voice_call" msgid="4410002696470423714">"Call"</string>
<string name="stream_system" msgid="7493299064422163147">"System"</string>
<string name="stream_ring" msgid="8213049469184048338">"Ring"</string>
diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml
index 2e6999a..881ee90 100644
--- a/packages/SystemUI/res/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res/values-en-rXC/strings.xml
@@ -522,7 +522,6 @@
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Hide <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"It will reappear the next time you turn it on in settings."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Hide"</string>
- <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"You\'re using your work profile"</string>
<string name="stream_voice_call" msgid="4410002696470423714">"Call"</string>
<string name="stream_system" msgid="7493299064422163147">"System"</string>
<string name="stream_ring" msgid="8213049469184048338">"Ring"</string>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 1f6dd16..6681dbb 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -524,7 +524,6 @@
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"¿Ocultar <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Volverá a aparecer la próxima vez que se active en la configuración."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Ocultar"</string>
- <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Estás usando tu perfil de trabajo"</string>
<string name="stream_voice_call" msgid="4410002696470423714">"Llamada"</string>
<string name="stream_system" msgid="7493299064422163147">"Sistema"</string>
<string name="stream_ring" msgid="8213049469184048338">"Hacer sonar"</string>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index 7ca5991..27daa5c 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -524,7 +524,6 @@
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"¿Ocultar <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Volverá a aparecer la próxima vez que actives esta opción en Ajustes."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Ocultar"</string>
- <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Estás usando tu perfil de trabajo"</string>
<string name="stream_voice_call" msgid="4410002696470423714">"Llamada"</string>
<string name="stream_system" msgid="7493299064422163147">"Sistema"</string>
<string name="stream_ring" msgid="8213049469184048338">"Tono"</string>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index 3ef21be..c309294 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -524,7 +524,6 @@
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Kas peita <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"See kuvatakse uuesti järgmisel korral, kui selle seadetes sisse lülitate."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Peida"</string>
- <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Kasutate oma tööprofiili"</string>
<string name="stream_voice_call" msgid="4410002696470423714">"Kõne"</string>
<string name="stream_system" msgid="7493299064422163147">"Süsteem"</string>
<string name="stream_ring" msgid="8213049469184048338">"Helin"</string>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index 432bfc4..d2dbc79 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -524,7 +524,6 @@
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"<xliff:g id="TILE_LABEL">%1$s</xliff:g> ezkutatu nahi duzu?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Ezarpenetan aktibatzen duzun hurrengoan agertuko da berriro."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Ezkutatu"</string>
- <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Work profila erabiltzen ari zara"</string>
<string name="stream_voice_call" msgid="4410002696470423714">"Deia"</string>
<string name="stream_system" msgid="7493299064422163147">"Sistema"</string>
<string name="stream_ring" msgid="8213049469184048338">"Jo tonua"</string>
@@ -564,8 +563,8 @@
<string name="status_bar" msgid="4877645476959324760">"Egoera-barra"</string>
<string name="overview" msgid="4018602013895926956">"Ikuspegi orokorra"</string>
<string name="demo_mode" msgid="2532177350215638026">"Sistemaren erabiltzaile-interfazearen demo modua"</string>
- <string name="enable_demo_mode" msgid="4844205668718636518">"Gaitu proba modua"</string>
- <string name="show_demo_mode" msgid="2018336697782464029">"Erakutsi proba modua"</string>
+ <string name="enable_demo_mode" msgid="4844205668718636518">"Gaitu demo modua"</string>
+ <string name="show_demo_mode" msgid="2018336697782464029">"Erakutsi demo modua"</string>
<string name="status_bar_ethernet" msgid="5044290963549500128">"Ethernet"</string>
<string name="status_bar_alarm" msgid="8536256753575881818">"Alarma"</string>
<string name="status_bar_work" msgid="6022553324802866373">"Work profila"</string>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index 79dc007..34ab46f 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -522,7 +522,6 @@
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"<xliff:g id="TILE_LABEL">%1$s</xliff:g> مخفی شود؟"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"دفعه بعد که آن را روشن کنید، در تنظیمات نشان داده میشود."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"پنهان کردن"</string>
- <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"درحال استفاده از نمایه کاریتان هستید"</string>
<string name="stream_voice_call" msgid="4410002696470423714">"تماس"</string>
<string name="stream_system" msgid="7493299064422163147">"سیستم"</string>
<string name="stream_ring" msgid="8213049469184048338">"زنگ زدن"</string>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index fbccf86..baa7822 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -522,7 +522,6 @@
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Piilotetaanko <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Se tulee takaisin näkyviin, kun seuraavan kerran otat sen käyttöön asetuksissa."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Piilota"</string>
- <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Käytät työprofiilia."</string>
<string name="stream_voice_call" msgid="4410002696470423714">"Puhelu"</string>
<string name="stream_system" msgid="7493299064422163147">"Järjestelmä"</string>
<string name="stream_ring" msgid="8213049469184048338">"Soittoääni"</string>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index fb1e675..bb4cdfb 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -524,7 +524,6 @@
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Masquer <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Cet élément réapparaîtra la prochaine fois que vous l\'activerez dans les paramètres."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Masquer"</string>
- <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Vous utilisez votre profil professionnel."</string>
<string name="stream_voice_call" msgid="4410002696470423714">"Appeler"</string>
<string name="stream_system" msgid="7493299064422163147">"Système"</string>
<string name="stream_ring" msgid="8213049469184048338">"Sonnerie"</string>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index d13e9ff..e38e83c 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -524,7 +524,6 @@
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Masquer <xliff:g id="TILE_LABEL">%1$s</xliff:g> ?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Cet élément réapparaîtra la prochaine fois que vous l\'activerez dans les paramètres."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Masquer"</string>
- <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Vous utilisez votre profil professionnel."</string>
<string name="stream_voice_call" msgid="4410002696470423714">"Appel"</string>
<string name="stream_system" msgid="7493299064422163147">"Système"</string>
<string name="stream_ring" msgid="8213049469184048338">"Sonnerie"</string>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index 6b5e0c3..0e8b093 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -524,7 +524,6 @@
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Queres ocultar <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Volverá aparecer a próxima vez que se active na configuración."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Ocultar"</string>
- <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Estás usando o perfil de traballo"</string>
<string name="stream_voice_call" msgid="4410002696470423714">"Chamada"</string>
<string name="stream_system" msgid="7493299064422163147">"Sistema"</string>
<string name="stream_ring" msgid="8213049469184048338">"Ton"</string>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index a982341..a730f01 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -522,7 +522,6 @@
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"<xliff:g id="TILE_LABEL">%1$s</xliff:g> ને છુપાવીએ?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"તે સેટિંગ્સમાં તમે તેને ચાલુ કરશો ત્યારે આગલી વખતે ફરીથી દેખાશે."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"છુપાવો"</string>
- <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"તમે તમારી કાર્ય પ્રોફાઇલનો ઉપયોગ કરી રહ્યાં છો"</string>
<string name="stream_voice_call" msgid="4410002696470423714">"કૉલ કરો"</string>
<string name="stream_system" msgid="7493299064422163147">"સિસ્ટમ"</string>
<string name="stream_ring" msgid="8213049469184048338">"રિંગ વગાડો"</string>
diff --git a/packages/SystemUI/res/values-hi-ldrtl/strings.xml b/packages/SystemUI/res/values-hi-ldrtl/strings.xml
index 6200b8d..df86c57 100644
--- a/packages/SystemUI/res/values-hi-ldrtl/strings.xml
+++ b/packages/SystemUI/res/values-hi-ldrtl/strings.xml
@@ -19,5 +19,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="recents_quick_scrub_onboarding" msgid="3449337212132057178">"ऐप्लिकेशन को तेज़ी से स्विच करने के लिए उसे बाईं ओर खींचें और छोड़ें"</string>
+ <string name="recents_quick_scrub_onboarding" msgid="3449337212132057178">"ऐप्लिकेशन को तेज़ी से बदलने के लिए उसे बाईं ओर खींचें और छोड़ें"</string>
</resources>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index 3eb7e97..7049044 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -409,7 +409,7 @@
<string name="user_add_user" msgid="5110251524486079492">"उपयोगकर्ता जोड़ें"</string>
<string name="user_new_user_name" msgid="426540612051178753">"नया उपयोगकर्ता"</string>
<string name="guest_nickname" msgid="8059989128963789678">"मेहमान"</string>
- <string name="guest_new_guest" msgid="600537543078847803">"अतिथि जोड़ें"</string>
+ <string name="guest_new_guest" msgid="600537543078847803">"मेहमान जोड़ें"</string>
<string name="guest_exit_guest" msgid="7187359342030096885">"अतिथि को निकालें"</string>
<string name="guest_exit_guest_dialog_title" msgid="8480693520521766688">"अतिथि को निकालें?"</string>
<string name="guest_exit_guest_dialog_message" msgid="4155503224769676625">"इस सत्र के सभी ऐप्स और डेटा को हटा दिया जाएगा."</string>
@@ -522,7 +522,6 @@
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"<xliff:g id="TILE_LABEL">%1$s</xliff:g> को छिपाएं?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"जब आप उसे अगली बार सेटिंग में चालू करेंगे तो वह फिर से दिखाई देगी."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"छिपाएं"</string>
- <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"आप अपनी कार्य प्रोफ़ाइल का उपयोग कर रहे हैं"</string>
<string name="stream_voice_call" msgid="4410002696470423714">"कॉल करें"</string>
<string name="stream_system" msgid="7493299064422163147">"सिस्टम"</string>
<string name="stream_ring" msgid="8213049469184048338">"घंटी बजाएं"</string>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index d784ac4..a77b7ff 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -525,7 +525,6 @@
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Želite li sakriti pločicu <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Ponovo će se pojaviti kada je sljedeći put uključite u postavkama."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Sakrij"</string>
- <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Upotrebljavate radni profil"</string>
<string name="stream_voice_call" msgid="4410002696470423714">"Nazovi"</string>
<string name="stream_system" msgid="7493299064422163147">"Sustav"</string>
<string name="stream_ring" msgid="8213049469184048338">"Zvoni"</string>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index c9d83e6..d6e2cb6 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -522,7 +522,6 @@
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Elrejti ezt: <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Újból megjelenik majd, amikor ismét engedélyezi a beállítások között."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Elrejtés"</string>
- <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"A munkaprofilt használja"</string>
<string name="stream_voice_call" msgid="4410002696470423714">"Telefonhívás"</string>
<string name="stream_system" msgid="7493299064422163147">"Rendszer"</string>
<string name="stream_ring" msgid="8213049469184048338">"Csörgetés"</string>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index 96952f8..2e8c9be 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -69,14 +69,14 @@
<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_saved_title" msgid="5637073968117370753">"Էկրանի պատկերը պահվեց"</string>
- <string name="screenshot_saved_text" msgid="7574667448002050363">"Հպեք՝ էկրանի պատկերը տեսնելու համար"</string>
- <string name="screenshot_failed_title" msgid="7612509838919089748">"Չհաջողվեց պահել էկրանի պատկերը"</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_saved_title" msgid="5637073968117370753">"Սքրինշոթը պահվեց"</string>
+ <string name="screenshot_saved_text" msgid="7574667448002050363">"Հպեք՝ սքրինշոթը տեսնելու համար"</string>
+ <string name="screenshot_failed_title" msgid="7612509838919089748">"Չհաջողվեց պահել սքրինշոթը"</string>
<string name="screenshot_failed_to_save_unknown_text" msgid="3637758096565605541">"Փորձեք նորից"</string>
- <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"Չհաջողվեց պահել էկրանի պատկերը անբավարար հիշողության պատճառով"</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>
@@ -292,8 +292,8 @@
<string name="accessibility_quick_settings_rotation" msgid="4231661040698488779">"Ավտոմատ պտտել էկրանը"</string>
<string name="accessibility_quick_settings_rotation_value" msgid="8187398200140760213">"<xliff:g id="ID_1">%s</xliff:g> ռեժիմ"</string>
<string name="quick_settings_rotation_locked_label" msgid="6359205706154282377">"Պտտումը կողպված է"</string>
- <string name="quick_settings_rotation_locked_portrait_label" msgid="5102691921442135053">"Դիմանկար"</string>
- <string name="quick_settings_rotation_locked_landscape_label" msgid="8553157770061178719">"Լանդշաֆտ"</string>
+ <string name="quick_settings_rotation_locked_portrait_label" msgid="5102691921442135053">"Ուղղաձիգ"</string>
+ <string name="quick_settings_rotation_locked_landscape_label" msgid="8553157770061178719">"Հորիզոնական"</string>
<string name="quick_settings_ime_label" msgid="7073463064369468429">"Մուտքագրման եղանակը"</string>
<string name="quick_settings_location_label" msgid="5011327048748762257">"Տեղորոշում"</string>
<string name="quick_settings_location_off_label" msgid="7464544086507331459">"Անջատել տեղադրությունը"</string>
@@ -522,7 +522,6 @@
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Թաքցնե՞լ <xliff:g id="TILE_LABEL">%1$s</xliff:g>-ը:"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Այն դարձյալ կհայտնվի, երբ նորից միացնեք կարգավորումներում:"</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Թաքցնել"</string>
- <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Դուք օգտագործում եք ձեր աշխատանքային պրոֆիլը"</string>
<string name="stream_voice_call" msgid="4410002696470423714">"Զանգ"</string>
<string name="stream_system" msgid="7493299064422163147">"Համակարգ"</string>
<string name="stream_ring" msgid="8213049469184048338">"Զանգ"</string>
@@ -815,7 +814,7 @@
<string name="tuner_app" msgid="3507057938640108777">"<xliff:g id="APP">%1$s</xliff:g> հավելված"</string>
<string name="notification_channel_alerts" msgid="4496839309318519037">"Ծանուցումներ"</string>
<string name="notification_channel_battery" msgid="5786118169182888462">"Մարտկոց"</string>
- <string name="notification_channel_screenshot" msgid="6314080179230000938">"Էկրանի պատկերներ"</string>
+ <string name="notification_channel_screenshot" msgid="6314080179230000938">"Սքրինշոթներ"</string>
<string name="notification_channel_general" msgid="4525309436693914482">"Ընդհանուր հաղորդագրություններ"</string>
<string name="notification_channel_storage" msgid="3077205683020695313">"Տարածք"</string>
<string name="notification_channel_hints" msgid="7323870212489152689">"Հուշումներ"</string>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index 1a042fc..d75d121 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -522,7 +522,6 @@
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Sembunyikan <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Ini akan muncul kembali saat Anda mengaktifkannya dalam setelan."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Sembunyikan"</string>
- <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Anda menggunakan profil kerja"</string>
<string name="stream_voice_call" msgid="4410002696470423714">"Telepon"</string>
<string name="stream_system" msgid="7493299064422163147">"Sistem"</string>
<string name="stream_ring" msgid="8213049469184048338">"Dering"</string>
@@ -580,7 +579,7 @@
<string name="tuner_warning_title" msgid="7094689930793031682">"Tidak semua orang menganggapnya baik"</string>
<string name="tuner_warning" msgid="8730648121973575701">"Penyetel Antarmuka Pengguna Sistem memberikan cara tambahan untuk mengubah dan menyesuaikan antarmuka pengguna Android. Fitur eksperimental ini dapat berubah, rusak, atau menghilang dalam rilis di masa mendatang. Lanjutkan dengan hati-hati."</string>
<string name="tuner_persistent_warning" msgid="8597333795565621795">"Fitur eksperimental ini dapat berubah, rusak, atau menghilang dalam rilis di masa mendatang. Lanjutkan dengan hati-hati."</string>
- <string name="got_it" msgid="2239653834387972602">"Mengerti"</string>
+ <string name="got_it" msgid="2239653834387972602">"Oke"</string>
<string name="tuner_toast" msgid="603429811084428439">"Selamat! Penyetel Antarmuka Pengguna Sistem telah ditambahkan ke Setelan"</string>
<string name="remove_from_settings" msgid="8389591916603406378">"Hapus dari Setelan"</string>
<string name="remove_from_settings_prompt" msgid="6069085993355887748">"Hapus Penyetel Antarmuka Pengguna Sistem dari Setelan dan berhenti menggunakan semua fiturnya?"</string>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index 61596f5..eaad622 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -522,7 +522,6 @@
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Fela <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Þetta birtist aftur næst þegar þú kveikir á því í stillingunum."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Fela"</string>
- <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Þú ert að nota vinnusniðið"</string>
<string name="stream_voice_call" msgid="4410002696470423714">"Símtal"</string>
<string name="stream_system" msgid="7493299064422163147">"Kerfi"</string>
<string name="stream_ring" msgid="8213049469184048338">"Hringing"</string>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 65add2a..7bd52d4 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -524,7 +524,6 @@
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Nascondere <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Verranno visualizzate di nuovo quando le riattiverai nelle impostazioni."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Nascondi"</string>
- <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Stai utilizzando il profilo di lavoro"</string>
<string name="stream_voice_call" msgid="4410002696470423714">"Chiamata"</string>
<string name="stream_system" msgid="7493299064422163147">"Sistema"</string>
<string name="stream_ring" msgid="8213049469184048338">"Suoneria"</string>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index 06b20a7..cac6d31 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -528,7 +528,6 @@
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"להסתיר<xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"יופיע מחדש בפעם הבאה שתפעיל את האפשרות בהגדרות."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"הסתר"</string>
- <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"אתה משתמש בפרופיל העבודה שלך"</string>
<string name="stream_voice_call" msgid="4410002696470423714">"שיחה"</string>
<string name="stream_system" msgid="7493299064422163147">"מערכת"</string>
<string name="stream_ring" msgid="8213049469184048338">"צלצול"</string>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index 4b3621a..10e9568 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -524,7 +524,6 @@
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"<xliff:g id="TILE_LABEL">%1$s</xliff:g>を非表示にしますか?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"次回、設定でONにすると再表示されます。"</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"非表示"</string>
- <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"仕事用プロファイルを使用しています"</string>
<string name="stream_voice_call" msgid="4410002696470423714">"発信"</string>
<string name="stream_system" msgid="7493299064422163147">"システム"</string>
<string name="stream_ring" msgid="8213049469184048338">"着信音"</string>
diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml
index 8a57d62..c28fab7 100644
--- a/packages/SystemUI/res/values-ka/strings.xml
+++ b/packages/SystemUI/res/values-ka/strings.xml
@@ -522,7 +522,6 @@
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"დაიმალოს <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"ის კვლავ გამოჩნდება, როდესაც პარამეტრებში ჩართავთ"</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"დამალვა"</string>
- <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"თქვენ სამსახურის პროფილს იყენებთ"</string>
<string name="stream_voice_call" msgid="4410002696470423714">"დარეკვა"</string>
<string name="stream_system" msgid="7493299064422163147">"სისტემა"</string>
<string name="stream_ring" msgid="8213049469184048338">"დარეკვა"</string>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index e63a9d3..f256d33 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -522,7 +522,6 @@
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"<xliff:g id="TILE_LABEL">%1$s</xliff:g> жасыру керек пе?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Ол сіз оны параметрлерде келесі қосқанда қайта пайда болады."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Жасыру"</string>
- <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Сіз жұмыс профиліңізді пайдаланып жатырсыз"</string>
<string name="stream_voice_call" msgid="4410002696470423714">"Қоңырау шалу"</string>
<string name="stream_system" msgid="7493299064422163147">"Жүйе"</string>
<string name="stream_ring" msgid="8213049469184048338">"Шылдырлау"</string>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index 922d9c2..2c9a554 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -522,7 +522,6 @@
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"លាក់ <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"វានឹងបង្ហាញពេលក្រោយ ពេលដែលអ្នកបើកក្នុងការកំណត់។"</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"លាក់"</string>
- <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"អ្នកកំពុងប្រើប្រវត្តិរូបការងាររបស់អ្នក"</string>
<string name="stream_voice_call" msgid="4410002696470423714">"ហៅ"</string>
<string name="stream_system" msgid="7493299064422163147">"ប្រព័ន្ធ"</string>
<string name="stream_ring" msgid="8213049469184048338">"រោទ៍"</string>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index ec4c7ce..8760d00 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -522,7 +522,6 @@
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"<xliff:g id="TILE_LABEL">%1$s</xliff:g> ಮರೆಮಾಡುವುದೇ?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"ನೀವು ಸೆಟ್ಟಿಂಗ್ಗಳಲ್ಲಿ ಅದನ್ನು ಆನ್ ಮಾಡಿದಾಗ ಅದು ಮರುಕಾಣಿಸಿಕೊಳ್ಳುತ್ತದೆ."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"ಮರೆಮಾಡಿ"</string>
- <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"ನಿಮ್ಮ ಕೆಲಸದ ಪ್ರೊಫೈಲ್ ಅನ್ನು ನೀವು ಬಳಸುತ್ತಿರುವಿರಿ"</string>
<string name="stream_voice_call" msgid="4410002696470423714">"ಕರೆಮಾಡಿ"</string>
<string name="stream_system" msgid="7493299064422163147">"ಸಿಸ್ಟಂ"</string>
<string name="stream_ring" msgid="8213049469184048338">"ರಿಂಗ್"</string>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index 3db09d5..7983966 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -524,7 +524,6 @@
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"<xliff:g id="TILE_LABEL">%1$s</xliff:g>을(를) 숨기시겠습니까?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"다음번에 설정에서 사용 설정하면 다시 표시됩니다."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"숨기기"</string>
- <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"직장 프로필을 사용하고 있습니다."</string>
<string name="stream_voice_call" msgid="4410002696470423714">"전화걸기"</string>
<string name="stream_system" msgid="7493299064422163147">"시스템"</string>
<string name="stream_ring" msgid="8213049469184048338">"벨소리"</string>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index 5f4b485..5ee60c9 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -522,7 +522,6 @@
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"<xliff:g id="TILE_LABEL">%1$s</xliff:g> жашырылсынбы?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Бул кийинки жолу жөндөөлөрдөн күйгүзүлгөндө кайра көрүнөт."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Жашыруу"</string>
- <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Жумуш профилиңизди колдонуп жатасыз"</string>
<string name="stream_voice_call" msgid="4410002696470423714">"Чалуу"</string>
<string name="stream_system" msgid="7493299064422163147">"Тутум"</string>
<string name="stream_ring" msgid="8213049469184048338">"Шыңгыратуу"</string>
diff --git a/packages/SystemUI/res/values-land/dimens.xml b/packages/SystemUI/res/values-land/dimens.xml
index c59dbdc..bb0c6f6 100644
--- a/packages/SystemUI/res/values-land/dimens.xml
+++ b/packages/SystemUI/res/values-land/dimens.xml
@@ -24,8 +24,6 @@
<dimen name="brightness_mirror_height">40dp</dimen>
- <!-- Width for the spacer, used between QS tiles. -->
- <dimen name="qs_quick_tile_space_width">38dp</dimen>
<dimen name="qs_tile_margin_top">2dp</dimen>
<dimen name="qs_header_tooltip_height">24dp</dimen>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index 877b512..7e2375e 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -522,7 +522,6 @@
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"ເຊື່ອງ <xliff:g id="TILE_LABEL">%1$s</xliff:g> ຫຼືບໍ່?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"ມັນຈະສະແດງຄືນໃໝ່ເມື່ອທ່ານເປີດນຳໃຊ້ມັນໃນການຕັ້ງຄ່າຄັ້ງຕໍ່ໄປ."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"ເຊື່ອງ"</string>
- <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"ທ່ານກຳລັງໃຊ້ໂປຣໄຟລ໌ບ່ອນເຮັດວຽກຂອງທ່ານ"</string>
<string name="stream_voice_call" msgid="4410002696470423714">"ໂທ"</string>
<string name="stream_system" msgid="7493299064422163147">"ລະບົບ"</string>
<string name="stream_ring" msgid="8213049469184048338">"ເຕືອນດ້ວຍສຽງ"</string>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index eb92531..d784ba6 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -528,7 +528,6 @@
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Slėpti „<xliff:g id="TILE_LABEL">%1$s</xliff:g>“?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Tai bus vėl parodyta, kai kitą kartą įjungsite tai nustatymuose."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Slėpti"</string>
- <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Naudojate darbo profilį"</string>
<string name="stream_voice_call" msgid="4410002696470423714">"Skambutis"</string>
<string name="stream_system" msgid="7493299064422163147">"Sistema"</string>
<string name="stream_ring" msgid="8213049469184048338">"Skambutis"</string>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index 28014e4..86e2e4c 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -525,7 +525,6 @@
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Vai paslēpt vienumu <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Tas tiks atkārtoti parādīts, kad nākamreiz ieslēgsiet to iestatījumos."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Paslēpt"</string>
- <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Jūs izmantojat darba profilu."</string>
<string name="stream_voice_call" msgid="4410002696470423714">"Zvans"</string>
<string name="stream_system" msgid="7493299064422163147">"Sistēma"</string>
<string name="stream_ring" msgid="8213049469184048338">"Zvans"</string>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index 5d4dd4d..88ea162 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -522,7 +522,6 @@
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Сокриј <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Ќе се појави повторно следниот пат кога ќе го вклучите во поставки."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Сокриј"</string>
- <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Го користите работниот профил"</string>
<string name="stream_voice_call" msgid="4410002696470423714">"Повик"</string>
<string name="stream_system" msgid="7493299064422163147">"Систем"</string>
<string name="stream_ring" msgid="8213049469184048338">"Ѕвони"</string>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index 9e6c239..9c18a22 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -522,7 +522,6 @@
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"<xliff:g id="TILE_LABEL">%1$s</xliff:g> എന്നത് മറയ്ക്കണോ?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"അടുത്ത തവണ നിങ്ങൾ അത് ക്രമീകരണങ്ങളിൽ ഓണാക്കുമ്പോൾ അത് വീണ്ടും ദൃശ്യമാകും."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"മറയ്ക്കുക"</string>
- <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"നിങ്ങൾ ഉപയോഗിക്കുന്നത് ഔദ്യോഗിക പ്രൊഫൈലാണ്"</string>
<string name="stream_voice_call" msgid="4410002696470423714">"കോള്"</string>
<string name="stream_system" msgid="7493299064422163147">"സിസ്റ്റം"</string>
<string name="stream_ring" msgid="8213049469184048338">"റിംഗുചെയ്യുക"</string>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index ba56386..fb26e81 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -520,7 +520,6 @@
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"<xliff:g id="TILE_LABEL">%1$s</xliff:g>-ийг нуух уу?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Тохируулгын хэсэгт үүнийг асаахад энэ дахин харагдана."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Нуух"</string>
- <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Та өөрийн ажлын профайлыг ашиглаж байна"</string>
<string name="stream_voice_call" msgid="4410002696470423714">"Залгах"</string>
<string name="stream_system" msgid="7493299064422163147">"Систем"</string>
<string name="stream_ring" msgid="8213049469184048338">"Хонх"</string>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index 99b13f6..958a877 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -522,7 +522,6 @@
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"<xliff:g id="TILE_LABEL">%1$s</xliff:g> लपवायचे?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"तुम्ही सेटिंग्जमध्ये ते पुढील वेळी चालू कराल तेव्हा ते पुन्हा दिसेल."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"लपवा"</string>
- <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"तुम्ही तुमचे कार्य प्रोफाईल वापरत आहात"</string>
<string name="stream_voice_call" msgid="4410002696470423714">"कॉल करा"</string>
<string name="stream_system" msgid="7493299064422163147">"सिस्टम"</string>
<string name="stream_ring" msgid="8213049469184048338">"रिंग करा"</string>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index a1e8267..18007c2 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -522,7 +522,6 @@
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Sembunyikan <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Mesej itu akan terpapar semula pada kali seterusnya anda menghidupkan apl dalam tetapan."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Sembunyikan"</string>
- <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Anda sedang menggunakan profil kerja"</string>
<string name="stream_voice_call" msgid="4410002696470423714">"Panggil"</string>
<string name="stream_system" msgid="7493299064422163147">"Sistem"</string>
<string name="stream_ring" msgid="8213049469184048338">"Dering"</string>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index 15df61c..f1d48b9 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -522,7 +522,6 @@
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"<xliff:g id="TILE_LABEL">%1$s</xliff:g> ဝှက်မည်လား?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"နောက်တစ်ကြိမ်သင် ချိန်ညှိချက်များဖွင့်လျှင် ၎င်းပေါ်လာပါမည်။"</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"ဖျောက်ထားမည်"</string>
- <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"သင်သည် အလုပ်ပရိုဖိုင်းအား သုံးနေသည်"</string>
<string name="stream_voice_call" msgid="4410002696470423714">"ဖုန်းခေါ်ရန်"</string>
<string name="stream_system" msgid="7493299064422163147">"စနစ်"</string>
<string name="stream_ring" msgid="8213049469184048338">"အသံမြည်စေသည်"</string>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index 5da9570..d8d6e47 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -522,7 +522,6 @@
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Vil du skjule <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Den vises igjen neste gang du slår den på i innstillingene."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Skjul"</string>
- <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Du bruker jobbprofilen din"</string>
<string name="stream_voice_call" msgid="4410002696470423714">"Anrop"</string>
<string name="stream_system" msgid="7493299064422163147">"System"</string>
<string name="stream_ring" msgid="8213049469184048338">"Ring"</string>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index 6f37055..5cd4325 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -522,7 +522,6 @@
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"लुकाउनुहुन्छ <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"यो तपाईं सेटिङ् मा यो बारी अर्को समय देखापर्नेछ।"</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"लुकाउनुहोस्"</string>
- <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"तपाईँले कार्य प्रोफाइल प्रयोग गर्दै हुनुहुन्छ"</string>
<string name="stream_voice_call" msgid="4410002696470423714">"कल"</string>
<string name="stream_system" msgid="7493299064422163147">"प्रणाली"</string>
<string name="stream_ring" msgid="8213049469184048338">"घन्टी"</string>
diff --git a/packages/SystemUI/res/values-night/colors.xml b/packages/SystemUI/res/values-night/colors.xml
new file mode 100644
index 0000000..45d2185
--- /dev/null
+++ b/packages/SystemUI/res/values-night/colors.xml
@@ -0,0 +1,44 @@
+<?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
+
+ NOTE: You might also want to edit: core/res/res/values-night/*.xml
+ -->
+<resources>
+ <!-- The color of the material notification background -->
+ <color name="notification_material_background_color">@*android:color/notification_material_background_color</color>
+
+ <!-- The color of the legacy notifications with customs backgrounds (gingerbread and lollipop.)
+ It's fine to override this color since at that point the shade was dark. -->
+ <color name="notification_legacy_background_color">@*android:color/notification_material_background_color</color>
+
+ <!-- The color of the material notification background when dimmed -->
+ <color name="notification_material_background_dimmed_color">#aa212121</color>
+
+ <!-- The color of the dividing line between grouped notifications while . -->
+ <color name="notification_divider_color">#000</color>
+
+ <!-- The background color of the notification shade -->
+ <color name="notification_shade_background_color">#181818</color>
+
+ <!-- The color of the ripples on the untinted notifications -->
+ <color name="notification_ripple_untinted_color">#30ffffff</color>
+
+ <!-- The "inside" of a notification, reached via longpress -->
+ <color name="notification_guts_bg_color">@*android:color/notification_material_background_color</color>
+
+ <!-- The color of the text inside a notification -->
+ <color name="notification_primary_text_color">@*android:color/notification_primary_text_color_dark</color>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values-night/dimens.xml b/packages/SystemUI/res/values-night/dimens.xml
new file mode 100644
index 0000000..4814839
--- /dev/null
+++ b/packages/SystemUI/res/values-night/dimens.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2018 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<resources>
+ <!-- The height of the divider between the individual notifications. -->
+ <dimen name="notification_divider_height">1dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index def7b19b..ac5b3e1 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -522,7 +522,6 @@
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"<xliff:g id="TILE_LABEL">%1$s</xliff:g> verbergen?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Deze wordt opnieuw weergegeven zodra u de instelling weer inschakelt."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Verbergen"</string>
- <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Je gebruikt je werkprofiel"</string>
<string name="stream_voice_call" msgid="4410002696470423714">"Bellen"</string>
<string name="stream_system" msgid="7493299064422163147">"Systeem"</string>
<string name="stream_ring" msgid="8213049469184048338">"Bellen"</string>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index 7dbd5ad..8ac9f5f 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -95,8 +95,7 @@
<string name="accessibility_unlock_button" msgid="128158454631118828">"ଅନଲକ୍ କରନ୍ତୁ"</string>
<string name="accessibility_waiting_for_fingerprint" msgid="4808860050517462885">"ଆଙ୍ଗୁଠି ଚିହ୍ନ ପାଇଁ ଅପେକ୍ଷା କରିଛି"</string>
<string name="accessibility_unlock_without_fingerprint" msgid="7541705575183694446">"ଆଙ୍ଗୁଠିଚିହ୍ନ ବ୍ୟବହାର ନକରି ଅନଲକ୍ କରନ୍ତୁ"</string>
- <!-- no translation found for accessibility_scanning_face (769545173211758586) -->
- <skip />
+ <string name="accessibility_scanning_face" msgid="769545173211758586">"ଫେସ୍ ସ୍କାନିଙ୍ଗ କରାଯାଉଛି"</string>
<string name="accessibility_send_smart_reply" msgid="7766727839703044493">"ପଠାନ୍ତୁ"</string>
<string name="unlock_label" msgid="8779712358041029439">"ଅନଲକ୍"</string>
<string name="phone_label" msgid="2320074140205331708">"ଫୋନ୍ ଖୋଲନ୍ତୁ"</string>
@@ -151,16 +150,13 @@
<string name="data_connection_gprs" msgid="7652872568358508452">"GPRS"</string>
<string name="data_connection_hspa" msgid="1499615426569473562">"HSPA"</string>
<string name="data_connection_3g" msgid="503045449315378373">"3G"</string>
- <!-- no translation found for data_connection_3_5g (3164370985817123144) -->
- <skip />
- <!-- no translation found for data_connection_3_5g_plus (4464630787664529264) -->
- <skip />
+ <string name="data_connection_3_5g" msgid="3164370985817123144">"H"</string>
+ <string name="data_connection_3_5g_plus" msgid="4464630787664529264">"H+"</string>
<string name="data_connection_4g" msgid="9139963475267449144">"4G"</string>
<string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string>
<string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string>
<string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string>
- <!-- no translation found for data_connection_cdma (8176597308239086780) -->
- <skip />
+ <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string>
<string name="data_connection_roaming" msgid="6037232010953697354">"ରୋମିଙ୍ଗ"</string>
<string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string>
<string name="accessibility_data_connection_wifi" msgid="2324496756590645221">"ୱାଇ-ଫାଇ"</string>
@@ -526,7 +522,6 @@
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"<xliff:g id="TILE_LABEL">%1$s</xliff:g> ଲୁଚାନ୍ତୁ?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"ଆଗକୁ ଆପଣ ଯେତେବେଳେ ଏହି ସେଟିଙ୍ଗକୁ ଚାଲୁ କରିବେ, ଏହା ପୁଣି ଦେଖାଦେବ।"</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"ଲୁଚାନ୍ତୁ"</string>
- <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"ଆପଣ ନିଜ ୱର୍କ ପ୍ରୋଫାଇଲ୍ ବ୍ୟବହାର କରୁଛନ୍ତି"</string>
<string name="stream_voice_call" msgid="4410002696470423714">"କଲ୍ କରନ୍ତୁ"</string>
<string name="stream_system" msgid="7493299064422163147">"ସିଷ୍ଟମ୍"</string>
<string name="stream_ring" msgid="8213049469184048338">"ରିଙ୍ଗ"</string>
@@ -857,6 +852,5 @@
<string name="auto_saver_enabled_text" msgid="874711029884777579">"ଚାର୍ଜ <xliff:g id="PERCENTAGE">%d</xliff:g>%%ରୁ କମ୍ ହେଲେ ବ୍ୟାଟେରୀ ସେଭର୍ ଆପେ ଅନ୍ ହୋଇଯିବ।"</string>
<string name="open_saver_setting_action" msgid="8314624730997322529">"ସେଟିଙ୍ଗ"</string>
<string name="auto_saver_okay_action" msgid="2701221740227683650">"ବୁଝିଲି"</string>
- <!-- no translation found for heap_dump_tile_name (9141031328971226374) -->
- <skip />
+ <string name="heap_dump_tile_name" msgid="9141031328971226374">"SysUI ହିପ୍ ଡମ୍ପ୍ କରନ୍ତୁ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index a797fbc..f4be71c 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -522,7 +522,6 @@
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"ਕੀ <xliff:g id="TILE_LABEL">%1$s</xliff:g> ਨੂੰ ਲੁਕਾਉਣਾ ਹੈ?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"ਇਹ ਅਗਲੀ ਵਾਰ ਮੁੜ ਪ੍ਰਗਟ ਹੋਵੇਗਾ ਜਦੋਂ ਤੁਸੀਂ ਇਸਨੂੰ ਸੈਟਿੰਗਾਂ ਵਿੱਚ ਚਾਲੂ ਕਰਦੇ ਹੋ।"</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"ਲੁਕਾਓ"</string>
- <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"ਤੁਸੀਂ ਆਪਣੀ ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ ਵਰਤ ਰਹੇ ਹੋ"</string>
<string name="stream_voice_call" msgid="4410002696470423714">"ਕਾਲ ਕਰੋ"</string>
<string name="stream_system" msgid="7493299064422163147">"ਸਿਸਟਮ"</string>
<string name="stream_ring" msgid="8213049469184048338">"ਘੰਟੀ ਵਜਾਓ"</string>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 6f0c064..6e2f664 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -528,7 +528,6 @@
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Ukryć <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Pojawi się ponownie, gdy następnym włączysz go w ustawieniach."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Ukryj"</string>
- <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Używasz profilu do pracy"</string>
<string name="stream_voice_call" msgid="4410002696470423714">"Połączenie"</string>
<string name="stream_system" msgid="7493299064422163147">"System"</string>
<string name="stream_ring" msgid="8213049469184048338">"Dzwonek"</string>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index 895cab4..a4f46a0 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -524,7 +524,6 @@
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Esconder <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Ela reaparecerá na próxima vez que você ativá-la nas configurações."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Ocultar"</string>
- <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Você está usando seu perfil de trabalho"</string>
<string name="stream_voice_call" msgid="4410002696470423714">"Ligar"</string>
<string name="stream_system" msgid="7493299064422163147">"Sistema"</string>
<string name="stream_ring" msgid="8213049469184048338">"Tocar"</string>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index 8a6b170..58b0315 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -522,7 +522,6 @@
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Pretende ocultar <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Reaparecerá da próxima vez que a funcionalidade for ativada nas definições."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Ocultar"</string>
- <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Está a utilizar o seu perfil de trabalho"</string>
<string name="stream_voice_call" msgid="4410002696470423714">"Chamada"</string>
<string name="stream_system" msgid="7493299064422163147">"Sistema"</string>
<string name="stream_ring" msgid="8213049469184048338">"Toque"</string>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index 895cab4..a4f46a0 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -524,7 +524,6 @@
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Esconder <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Ela reaparecerá na próxima vez que você ativá-la nas configurações."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Ocultar"</string>
- <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Você está usando seu perfil de trabalho"</string>
<string name="stream_voice_call" msgid="4410002696470423714">"Ligar"</string>
<string name="stream_system" msgid="7493299064422163147">"Sistema"</string>
<string name="stream_ring" msgid="8213049469184048338">"Tocar"</string>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index 3d68c6f..34baa8a 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -527,7 +527,6 @@
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Ascundeți <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Va reapărea la următoarea activare în setări."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Ascundeți"</string>
- <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Acum folosiți profilul de serviciu"</string>
<string name="stream_voice_call" msgid="4410002696470423714">"Apel"</string>
<string name="stream_system" msgid="7493299064422163147">"Sistem"</string>
<string name="stream_ring" msgid="8213049469184048338">"Sonerie"</string>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index 18c6221..186baca 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -530,7 +530,6 @@
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Скрыть параметр \"<xliff:g id="TILE_LABEL">%1$s</xliff:g>\"?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Этот параметр появится в следующий раз, когда вы включите его."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Скрыть"</string>
- <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Вы перешли в рабочий профиль"</string>
<string name="stream_voice_call" msgid="4410002696470423714">"Вызов"</string>
<string name="stream_system" msgid="7493299064422163147">"Система"</string>
<string name="stream_ring" msgid="8213049469184048338">"Звонок"</string>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index b84082d..a34cd9d 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -522,7 +522,6 @@
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"<xliff:g id="TILE_LABEL">%1$s</xliff:g> සඟවන්නද?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"ඊළඟ අවස්ථාවේ සැකසීම් තුළ ඔබ එය සක්රිය කළ විට එය නැවත දිසිවේ."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"සඟවන්න"</string>
- <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"ඔබ ඔබේ කාර්යාල පැතිකඩ භාවිත කරමින් සිටී"</string>
<string name="stream_voice_call" msgid="4410002696470423714">"අමතන්න"</string>
<string name="stream_system" msgid="7493299064422163147">"පද්ධතිය"</string>
<string name="stream_ring" msgid="8213049469184048338">"නාද කරන්න"</string>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index e751dba..e2fa577 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -530,7 +530,6 @@
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Skryť <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Táto položka sa znova zobrazí, keď ju v nastaveniach opätovne zapnete."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Skryť"</string>
- <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Používate svoj pracovný profil."</string>
<string name="stream_voice_call" msgid="4410002696470423714">"Zavolať"</string>
<string name="stream_system" msgid="7493299064422163147">"Systém"</string>
<string name="stream_ring" msgid="8213049469184048338">"Zvonenie"</string>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index 2478309..3114f10 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -530,7 +530,6 @@
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Želite skriti <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Znova se bo pojavila, ko jo naslednjič vklopite v nastavitvah."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Skrij"</string>
- <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Uporabljate delovni profil"</string>
<string name="stream_voice_call" msgid="4410002696470423714">"Klic"</string>
<string name="stream_system" msgid="7493299064422163147">"Sistem"</string>
<string name="stream_ring" msgid="8213049469184048338">"Sprožitev zvonjenja"</string>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index 290e812..7ad3fa3 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -522,7 +522,6 @@
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Të fshihet <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Do të rishfaqet herën tjetër kur ta aktivizoni te cilësimet."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Fshih"</string>
- <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Po përdor profilin tënd të punës"</string>
<string name="stream_voice_call" msgid="4410002696470423714">"Telefono"</string>
<string name="stream_system" msgid="7493299064422163147">"Sistemi"</string>
<string name="stream_ring" msgid="8213049469184048338">"Bjeri ziles"</string>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index 07e0245..da30392 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -525,7 +525,6 @@
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Желите ли да сакријете <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Ово ће се поново појавити када га следећи пут будете укључили у подешавањима."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Сакриј"</string>
- <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Користите профил за Work"</string>
<string name="stream_voice_call" msgid="4410002696470423714">"Позив"</string>
<string name="stream_system" msgid="7493299064422163147">"Систем"</string>
<string name="stream_ring" msgid="8213049469184048338">"Звоно"</string>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index de5c82a..0deb4b4 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -522,7 +522,6 @@
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Vill du dölja <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Den visas på nytt nästa gång du aktiverar den i inställningarna."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Dölj"</string>
- <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Du använder din jobbprofil"</string>
<string name="stream_voice_call" msgid="4410002696470423714">"Samtal"</string>
<string name="stream_system" msgid="7493299064422163147">"System"</string>
<string name="stream_ring" msgid="8213049469184048338">"Ringsignal"</string>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index a60c9a4..ef90f3e 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -522,7 +522,6 @@
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Ungependa kuficha <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Itaonekana tena wakati mwingine utakapoiwasha katika mipangilio."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Ficha"</string>
- <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Unatumia wasifu wako wa kazini"</string>
<string name="stream_voice_call" msgid="4410002696470423714">"Piga simu"</string>
<string name="stream_system" msgid="7493299064422163147">"Mfumo"</string>
<string name="stream_ring" msgid="8213049469184048338">"Piga"</string>
@@ -706,7 +705,7 @@
<string name="nav_bar_layout" msgid="3664072994198772020">"Mpangilio"</string>
<string name="left_nav_bar_button_type" msgid="8555981238887546528">"Aina ya kitufe cha kushoto cha ziada"</string>
<string name="right_nav_bar_button_type" msgid="2481056627065649656">"Aina ya kitufe cha kulia cha ziada"</string>
- <string name="nav_bar_default" msgid="8587114043070993007">"(chaguo msingi)"</string>
+ <string name="nav_bar_default" msgid="8587114043070993007">"(chaguomsingi)"</string>
<string-array name="nav_bar_buttons">
<item msgid="1545641631806817203">"Ubao wa kunakili"</item>
<item msgid="5742013440802239414">"Msimbo wa ufunguo"</item>
@@ -736,12 +735,12 @@
<string name="tuner_time" msgid="6572217313285536011">"Wakati"</string>
<string-array name="clock_options">
<item msgid="5965318737560463480">"Onyesha saa, dakika na sekunde"</item>
- <item msgid="1427801730816895300">"Onyesha saa na dakika (chaguo msingi)"</item>
+ <item msgid="1427801730816895300">"Onyesha saa na dakika (chaguomsingi)"</item>
<item msgid="3830170141562534721">"Usionyeshe aikoni hii"</item>
</string-array>
<string-array name="battery_options">
<item msgid="3160236755818672034">"Onyesha asilimia kila wakati"</item>
- <item msgid="2139628951880142927">"Onyesha asilimia wakati inachaji (chaguo msingi)"</item>
+ <item msgid="2139628951880142927">"Onyesha asilimia wakati inachaji (chaguomsingi)"</item>
<item msgid="3327323682209964956">"Usionyeshe aikoni hii"</item>
</string-array>
<string name="other" msgid="4060683095962566764">"Nyingine"</string>
diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml
index 74acfa2..eb5c180 100644
--- a/packages/SystemUI/res/values-sw600dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp/dimens.xml
@@ -19,9 +19,6 @@
<!-- Standard notification width + gravity -->
<dimen name="notification_panel_width">416dp</dimen>
- <!-- Width for the spacer, used between QS tiles depend on notification_panel_width -->
- <dimen name="qs_quick_tile_space_width">0dp</dimen>
-
<!-- Diameter of outer shape drawable shown in navbar search-->
<dimen name="navbar_search_outerring_diameter">430dip</dimen>
diff --git a/packages/SystemUI/res/values-sw900dp-land/dimen.xml b/packages/SystemUI/res/values-sw900dp-land/dimen.xml
index 55f23dd..ac7e6b8 100644
--- a/packages/SystemUI/res/values-sw900dp-land/dimen.xml
+++ b/packages/SystemUI/res/values-sw900dp-land/dimen.xml
@@ -18,4 +18,9 @@
<resources>
<!-- Standard notification width + gravity for tablet large screen device -->
<dimen name="notification_panel_width">544dp</dimen>
-</resources>
\ No newline at end of file
+
+ <!-- Maximum width of quick quick settings panel. -->
+ <dimen name="qs_quick_layout_width">478dp</dimen>
+
+</resources>
+
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index 31eae85..b1eeb41 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -522,7 +522,6 @@
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"<xliff:g id="TILE_LABEL">%1$s</xliff:g>ஐ மறைக்கவா?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"அடுத்த முறை அமைப்புகளில் மீண்டும் இயக்கும்போது, இது மீண்டும் தோன்றும்."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"மறை"</string>
- <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"பணி சுயவிவரத்தைப் பயன்படுத்துகிறீர்கள்"</string>
<string name="stream_voice_call" msgid="4410002696470423714">"அழைப்பு"</string>
<string name="stream_system" msgid="7493299064422163147">"சிஸ்டம்"</string>
<string name="stream_ring" msgid="8213049469184048338">"ரிங் செய்"</string>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index b1ed350..20a146e 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -522,7 +522,6 @@
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"<xliff:g id="TILE_LABEL">%1$s</xliff:g>ని దాచాలా?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"మీరు సెట్టింగ్ల్లో దీన్ని ఆన్ చేసిన తదుపరిసారి ఇది కనిపిస్తుంది."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"దాచు"</string>
- <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"మీరు మీ కార్యాలయ ప్రొఫైల్ను ఉపయోగిస్తున్నారు"</string>
<string name="stream_voice_call" msgid="4410002696470423714">"కాల్"</string>
<string name="stream_system" msgid="7493299064422163147">"సిస్టమ్"</string>
<string name="stream_ring" msgid="8213049469184048338">"రింగ్"</string>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index e7a98b6..76fae4e 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -522,7 +522,6 @@
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"ซ่อน <xliff:g id="TILE_LABEL">%1$s</xliff:g> ไหม"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"จะปรากฏอีกครั้งเมื่อคุณเปิดใช้ในการตั้งค่าครั้งถัดไป"</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"ซ่อน"</string>
- <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"คุณกำลังใช้โปรไฟล์งานของคุณ"</string>
<string name="stream_voice_call" msgid="4410002696470423714">"การโทร"</string>
<string name="stream_system" msgid="7493299064422163147">"ระบบ"</string>
<string name="stream_ring" msgid="8213049469184048338">"เสียงเรียกเข้า"</string>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index 7fdee62..0c1b8a5 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -522,7 +522,6 @@
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Itago ang <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Lalabas itong muli sa susunod na pagkakataon na i-on mo ito sa mga setting."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Itago"</string>
- <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Ginagamit mo ang iyong profile sa trabaho"</string>
<string name="stream_voice_call" msgid="4410002696470423714">"Tumawag"</string>
<string name="stream_system" msgid="7493299064422163147">"System"</string>
<string name="stream_ring" msgid="8213049469184048338">"Ipa-ring"</string>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index d537a09..7abf64f 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -522,7 +522,6 @@
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"<xliff:g id="TILE_LABEL">%1$s</xliff:g> gizlensin mi?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Ayarlardan etkinleştirdiğiniz bir sonraki sefer tekrar görünür."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Gizle"</string>
- <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"İş profilinizi kullanıyorsunuz"</string>
<string name="stream_voice_call" msgid="4410002696470423714">"Çağrı"</string>
<string name="stream_system" msgid="7493299064422163147">"Sistem"</string>
<string name="stream_ring" msgid="8213049469184048338">"Zili çaldır"</string>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index 14ff120..f9f5ce8 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -530,7 +530,6 @@
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Сховати <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"З’явиться знову, коли ви ввімкнете його в налаштуваннях."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Сховати"</string>
- <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Ви в робочому профілі"</string>
<string name="stream_voice_call" msgid="4410002696470423714">"Виклик"</string>
<string name="stream_system" msgid="7493299064422163147">"Система"</string>
<string name="stream_ring" msgid="8213049469184048338">"Дзвінок"</string>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index b4aa388..0566022 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -522,7 +522,6 @@
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"<xliff:g id="TILE_LABEL">%1$s</xliff:g> کو چھپائیں؟"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"اگلی بار جب آپ اسے ترتیبات میں آن کریں گے تو یہ ظاہر ہوگی۔"</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"چھپائیں"</string>
- <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"آپ اپنا دفتری پروفائل استعمال کر رہے ہیں۔"</string>
<string name="stream_voice_call" msgid="4410002696470423714">"کال"</string>
<string name="stream_system" msgid="7493299064422163147">"سسٹم"</string>
<string name="stream_ring" msgid="8213049469184048338">"رِنگ"</string>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index 328faa5..5ce50a3 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -524,7 +524,6 @@
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"<xliff:g id="TILE_LABEL">%1$s</xliff:g> berkitilsinmi?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Keyingi safar sozlamalardan yoqilgan paydo bo‘ladi."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Berkitish"</string>
- <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Siz ishchi profildan foydalanmoqdasiz"</string>
<string name="stream_voice_call" msgid="4410002696470423714">"Chaqiruv"</string>
<string name="stream_system" msgid="7493299064422163147">"Tizim"</string>
<string name="stream_ring" msgid="8213049469184048338">"Jiringlatish"</string>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index d6e9b0a..3f0091f 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -522,7 +522,6 @@
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Ẩn <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Thông báo này sẽ xuất hiện lại vào lần tiếp theo bạn bật thông báo trong cài đặt."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Ẩn"</string>
- <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Bạn đang sử dụng hồ sơ công việc của mình"</string>
<string name="stream_voice_call" msgid="4410002696470423714">"Gọi"</string>
<string name="stream_system" msgid="7493299064422163147">"Hệ thống"</string>
<string name="stream_ring" msgid="8213049469184048338">"Chuông"</string>
diff --git a/packages/SystemUI/res/values-w550dp-land/dimens.xml b/packages/SystemUI/res/values-w550dp-land/dimens.xml
index eaca9d7..2c66454 100644
--- a/packages/SystemUI/res/values-w550dp-land/dimens.xml
+++ b/packages/SystemUI/res/values-w550dp-land/dimens.xml
@@ -18,4 +18,7 @@
<resources>
<!-- Standard notification width + gravity -->
<dimen name="notification_panel_width">544dp</dimen>
+
+ <!-- Maximum width of quick quick settings panel. -->
+ <dimen name="qs_quick_layout_width">478dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index 7d40962..12675b0 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -522,7 +522,6 @@
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"要隐藏“<xliff:g id="TILE_LABEL">%1$s</xliff:g>”吗?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"下次在设置中将其开启后,此快捷设置条目将会重新显示。"</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"隐藏"</string>
- <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"您当前正在使用工作资料"</string>
<string name="stream_voice_call" msgid="4410002696470423714">"通话"</string>
<string name="stream_system" msgid="7493299064422163147">"系统"</string>
<string name="stream_ring" msgid="8213049469184048338">"铃声"</string>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index 3ea0272..8f9b33f 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -524,7 +524,6 @@
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"隱藏 <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"下一次您在設定開啟它時,它將再次出現。"</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"隱藏"</string>
- <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"您正在使用工作設定檔"</string>
<string name="stream_voice_call" msgid="4410002696470423714">"通話"</string>
<string name="stream_system" msgid="7493299064422163147">"系統"</string>
<string name="stream_ring" msgid="8213049469184048338">"鈴聲"</string>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index 6b74a2a..2672138 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -522,7 +522,6 @@
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"隱藏<xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"只要在設定頁面中重新啟用,就能再次看到快捷設定選項。"</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"隱藏"</string>
- <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"你正在使用工作資料夾"</string>
<string name="stream_voice_call" msgid="4410002696470423714">"通話"</string>
<string name="stream_system" msgid="7493299064422163147">"系統"</string>
<string name="stream_ring" msgid="8213049469184048338">"鈴響"</string>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index 1835d82..099cbfe 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -522,7 +522,6 @@
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Fihla i-<xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Izovela ngesikhathi esilandelayo uma uvule lesi silungiselelo."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Fihla"</string>
- <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Usebenzisa iphrofayela yakho yomsebenzi"</string>
<string name="stream_voice_call" msgid="4410002696470423714">"Shaya"</string>
<string name="stream_system" msgid="7493299064422163147">"Isistimu"</string>
<string name="stream_ring" msgid="8213049469184048338">"Khalisa"</string>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 975055c..4920fb2 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -94,6 +94,9 @@
<!-- The color of the gear shown behind a notification -->
<color name="notification_gear_color">#ff757575</color>
+ <!-- The color of the text inside a notification -->
+ <color name="notification_primary_text_color">@*android:color/notification_primary_text_color_light</color>
+
<!-- The "inside" of a notification, reached via longpress -->
<color name="notification_guts_bg_color">#f8f9fa</color>
@@ -137,8 +140,10 @@
<color name="remote_input_accent">#eeeeee</color>
- <color name="quick_step_track_background_dark">#61000000</color>
- <color name="quick_step_track_background_light">#33FFFFFF</color>
+ <color name="quick_step_track_background_background_dark">#1F000000</color>
+ <color name="quick_step_track_background_background_light">#33FFFFFF</color>
+ <color name="quick_step_track_background_foreground_dark">#38000000</color>
+ <color name="quick_step_track_background_foreground_light">#59FFFFFF</color>
<!-- Keyboard shortcuts colors -->
<color name="ksh_application_group_color">#fff44336</color>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index eed1df0..a9d995c 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -252,6 +252,9 @@
-->
<dimen name="qs_header_system_icons_area_height">48dp</dimen>
+ <!-- How far the quick-quick settings panel extends below the status bar -->
+ <dimen name="qs_quick_header_panel_height">128dp</dimen>
+
<!-- The height of the container that holds the system icons in the quick settings header in the
car setting. -->
<dimen name="car_qs_header_system_icons_area_height">54dp</dimen>
@@ -348,8 +351,8 @@
<dimen name="qs_tile_margin_top_bottom">12dp</dimen>
<dimen name="qs_tile_margin_top">18dp</dimen>
<dimen name="qs_quick_tile_size">48dp</dimen>
- <!-- Width for the spacer, used between QS tiles. -->
- <dimen name="qs_quick_tile_space_width">0dp</dimen>
+ <!-- Maximum width of quick quick settings panel. Defaults to MATCH_PARENT-->
+ <dimen name="qs_quick_layout_width">-1px</dimen>
<dimen name="qs_quick_tile_padding">12dp</dimen>
<dimen name="qs_header_gear_translation">16dp</dimen>
<dimen name="qs_header_tile_margin_horizontal">0dp</dimen>
@@ -763,9 +766,6 @@
<dimen name="volume_expander_margin_end">2dp</dimen>
<dimen name="volume_expander_margin_top">6dp</dimen>
- <!-- Padding between icon and text for managed profile toast -->
- <dimen name="managed_profile_toast_padding">4dp</dimen>
-
<!-- Thickness of the assist disclosure beams -->
<dimen name="assist_disclosure_thickness">2.5dp</dimen>
@@ -921,14 +921,22 @@
<dimen name="global_actions_top_padding">120dp</dimen>
- <!-- the maximum offset in either direction that elements are moved horizontally to prevent
- burn-in on AOD -->
+ <dimen name="global_actions_padding">12dp</dimen>
+
+ <dimen name="global_actions_translate">9dp</dimen>
+
+ <!-- The maximum offset in either direction that elements are moved horizontally to prevent
+ burn-in on AOD. -->
<dimen name="burn_in_prevention_offset_x">8dp</dimen>
- <!-- the maximum offset in either direction that elements are moved vertically to prevent
- burn-in on AOD -->
+ <!-- The maximum offset in either direction that elements are moved vertically to prevent
+ burn-in on AOD. -->
<dimen name="burn_in_prevention_offset_y">50dp</dimen>
+ <!-- The maximum offset in either direction that the charging indication moves vertically
+ to prevent burn-in on AOD. -->
+ <dimen name="charging_indication_burn_in_prevention_offset_y">5dp</dimen>
+
<dimen name="corner_size">8dp</dimen>
<dimen name="top_padding">0dp</dimen>
<dimen name="bottom_padding">48dp</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 84a76bc..915fc6e 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1326,9 +1326,6 @@
<!-- Hide quick settings tile confirmation button -->
<string name="quick_settings_reset_confirmation_button">Hide</string>
- <!-- Toast shown when user unlocks screen and managed profile activity is in the foreground -->
- <string name="managed_profile_foreground_toast">You\'re using your work profile</string>
-
<!-- volume stream names. All nouns. -->
<string name="stream_voice_call">Call</string> <!-- STREAM_VOICE_CALL -->
<string name="stream_system">System</string> <!-- STREAM_SYSTEM -->
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index e4f5989..5a03c4a 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -335,10 +335,7 @@
<item name="*android:errorColor">?android:attr/colorError</item>
</style>
- <!-- Overlay manager may replace this theme -->
- <style name="qs_base" parent="@*android:style/Theme.DeviceDefault.QuickSettings" />
-
- <style name="qs_theme" parent="qs_base">
+ <style name="qs_theme" parent="@*android:style/Theme.DeviceDefault.QuickSettings">
<item name="lightIconTheme">@style/QSIconTheme</item>
<item name="darkIconTheme">@style/QSIconTheme</item>
<item name="android:windowIsFloating">true</item>
@@ -403,6 +400,9 @@
<item name="fillColor">?android:attr/textColorPrimary</item>
<item name="singleToneColor">?android:attr/textColorPrimary</item>
</style>
+ <style name="ScreenPinningRequestTheme" parent="@*android:style/ThemeOverlay.DeviceDefault.Accent">
+ <item name="singleToneColor">@color/light_mode_icon_color_single_tone</item>
+ </style>
<style name="TextAppearance.Volume">
<item name="android:textStyle">normal</item>
@@ -452,23 +452,20 @@
<style name="TextAppearance.NotificationInfo">
<item name="android:fontFamily">sans-serif</item>
- <item name="android:textColor">@android:color/black</item>
+ <item name="android:textColor">@color/notification_primary_text_color</item>
</style>
<style name="TextAppearance.NotificationInfo.Primary">
- <item name="android:textColor">?android:attr/textColorPrimary</item>
<item name="android:textSize">16sp</item>
<item name="android:alpha">0.87</item>
</style>
<style name="TextAppearance.NotificationInfo.Confirmation">
- <item name="android:textColor">?android:attr/textColorPrimary</item>
<item name="android:textSize">14sp</item>
<item name="android:alpha">0.87</item>
</style>
<style name="TextAppearance.NotificationInfo.Secondary">
- <item name="android:textColor">?android:attr/textColorPrimary</item>
<item name="android:textSize">14sp</item>
<item name="android:alpha">0.54</item>
</style>
@@ -498,7 +495,7 @@
parent="@*android:style/TextAppearance.Material.Notification.Info">
</style>
- <style name="edit_theme" parent="qs_base">
+ <style name="edit_theme" parent="qs_theme">
<item name="android:colorBackground">?android:attr/colorSecondary</item>
</style>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/NavigationBarCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/NavigationBarCompat.java
index cd831d1..d38cc0f 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/NavigationBarCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/NavigationBarCompat.java
@@ -54,6 +54,7 @@
public static final int HIT_TARGET_HOME = 2;
public static final int HIT_TARGET_OVERVIEW = 3;
public static final int HIT_TARGET_ROTATION = 4;
+ public static final int HIT_TARGET_DEAD_ZONE = 5;
@Retention(RetentionPolicy.SOURCE)
@IntDef({FLAG_DISABLE_SWIPE_UP,
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardConstants.java b/packages/SystemUI/src/com/android/keyguard/KeyguardConstants.java
index 62b8e7c..0340904 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardConstants.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardConstants.java
@@ -27,5 +27,5 @@
*/
public static final boolean DEBUG = false;
public static final boolean DEBUG_SIM_STATES = true;
- public static final boolean DEBUG_FP_WAKELOCK = true;
+ public static final boolean DEBUG_BIOMETRIC_WAKELOCK = true;
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index 1942fc1..745f81d 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -143,6 +143,7 @@
mSecurityViewFlipper.addView(v);
updateSecurityView(v);
view = (KeyguardSecurityView)v;
+ view.reset();
}
return view;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
index d5d96fe..6da143c 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
@@ -199,6 +199,9 @@
mClockView.setElegantTextHeight(false);
}
+ /**
+ * Moves clock and separator, adjusting margins when slice content changes.
+ */
private void onSliceContentChanged() {
boolean smallClock = mKeyguardSlice.hasHeader() || mPulsing;
float clockScale = smallClock ? mSmallClockScale : 1;
@@ -221,7 +224,7 @@
@Override
public void onLayoutChange(View view, int left, int top, int right, int bottom,
int oldLeft, int oldTop, int oldRight, int oldBottom) {
- int heightOffset = mPulsing ? 0 : getHeight() - mLastLayoutHeight;
+ int heightOffset = mPulsing || mWasPulsing ? 0 : getHeight() - mLastLayoutHeight;
boolean hasHeader = mKeyguardSlice.hasHeader();
boolean smallClock = hasHeader || mPulsing;
long duration = KeyguardSliceView.DEFAULT_ANIM_DURATION;
@@ -457,6 +460,11 @@
mWasPulsing = true;
}
mPulsing = pulsing;
+ // Animation can look really weird when the slice has a header, let's hide the views
+ // immediately instead of fading them away.
+ if (mKeyguardSlice.hasHeader()) {
+ animate = false;
+ }
mKeyguardSlice.setPulsing(pulsing, animate);
updateDozeVisibleViews();
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 075faa1..fa7e814 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -39,6 +39,7 @@
import android.app.UserSwitchObserver;
import android.app.admin.DevicePolicyManager;
import android.app.trust.TrustManager;
+import android.hardware.biometrics.BiometricSourceType;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
@@ -48,6 +49,7 @@
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.database.ContentObserver;
+import android.hardware.face.FaceManager;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintManager.AuthenticationCallback;
import android.hardware.fingerprint.FingerprintManager.AuthenticationResult;
@@ -72,7 +74,6 @@
import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
import android.telephony.TelephonyManager;
import android.util.Log;
-import android.util.Slog;
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
@@ -145,26 +146,26 @@
private static final int MSG_DREAMING_STATE_CHANGED = 333;
private static final int MSG_USER_UNLOCKED = 334;
private static final int MSG_ASSISTANT_STACK_CHANGED = 335;
- private static final int MSG_FINGERPRINT_AUTHENTICATION_CONTINUE = 336;
+ private static final int MSG_BIOMETRIC_AUTHENTICATION_CONTINUE = 336;
private static final int MSG_DEVICE_POLICY_MANAGER_STATE_CHANGED = 337;
- /** Fingerprint state: Not listening to fingerprint. */
- private static final int FINGERPRINT_STATE_STOPPED = 0;
+ /** Biometric authentication state: Not listening. */
+ private static final int BIOMETRIC_STATE_STOPPED = 0;
- /** Fingerprint state: Listening. */
- private static final int FINGERPRINT_STATE_RUNNING = 1;
+ /** Biometric authentication state: Listening. */
+ private static final int BIOMETRIC_STATE_RUNNING = 1;
/**
- * Fingerprint state: Cancelling and waiting for the confirmation from FingerprintService to
+ * Biometric authentication: Cancelling and waiting for the relevant biometric service to
* send us the confirmation that cancellation has happened.
*/
- private static final int FINGERPRINT_STATE_CANCELLING = 2;
+ private static final int BIOMETRIC_STATE_CANCELLING = 2;
/**
- * Fingerprint state: During cancelling we got another request to start listening, so when we
+ * Biometric state: During cancelling we got another request to start listening, so when we
* receive the cancellation done signal, we should start listening again.
*/
- private static final int FINGERPRINT_STATE_CANCELLING_RESTARTING = 3;
+ private static final int BIOMETRIC_STATE_CANCELLING_RESTARTING = 3;
private static final int DEFAULT_CHARGING_VOLTAGE_MICRO_VOLT = 5000000;
@@ -227,7 +228,8 @@
private List<SubscriptionInfo> mSubscriptionInfo;
private TrustManager mTrustManager;
private UserManager mUserManager;
- private int mFingerprintRunningState = FINGERPRINT_STATE_STOPPED;
+ private int mFingerprintRunningState = BIOMETRIC_STATE_STOPPED;
+ private int mFaceRunningState = BIOMETRIC_STATE_STOPPED;
private LockPatternUtils mLockPatternUtils;
private final IDreamManager mDreamManager;
private boolean mIsDreaming;
@@ -235,14 +237,15 @@
private boolean mLogoutEnabled;
/**
- * Short delay before restarting fingerprint authentication after a successful try
- * This should be slightly longer than the time between onFingerprintAuthenticated and
- * setKeyguardGoingAway(true).
+ * Short delay before restarting biometric authentication after a successful try
+ * This should be slightly longer than the time between on<biometric>Authenticated
+ * (e.g. onFingerprintAuthenticated) and setKeyguardGoingAway(true).
*/
- private static final int FINGERPRINT_CONTINUE_DELAY_MS = 500;
+ private static final int BIOMETRIC_CONTINUE_DELAY_MS = 500;
// If FP daemon dies, keyguard should retry after a short delay
- private int mHardwareUnavailableRetryCount = 0;
+ private int mHardwareFingerprintUnavailableRetryCount = 0;
+ private int mHardwareFaceUnavailableRetryCount = 0;
private static final int HW_UNAVAILABLE_TIMEOUT = 3000; // ms
private static final int HW_UNAVAILABLE_RETRY_MAX = 3;
@@ -333,10 +336,10 @@
break;
case MSG_ASSISTANT_STACK_CHANGED:
mAssistantVisible = (boolean)msg.obj;
- updateFingerprintListeningState();
+ updateBiometricListeningState();
break;
- case MSG_FINGERPRINT_AUTHENTICATION_CONTINUE:
- updateFingerprintListeningState();
+ case MSG_BIOMETRIC_AUTHENTICATION_CONTINUE:
+ updateBiometricListeningState();
break;
case MSG_DEVICE_POLICY_MANAGER_STATE_CHANGED:
updateLogoutEnabled();
@@ -359,10 +362,11 @@
private SparseBooleanArray mUserHasTrust = new SparseBooleanArray();
private SparseBooleanArray mUserTrustIsManaged = new SparseBooleanArray();
private SparseBooleanArray mUserFingerprintAuthenticated = new SparseBooleanArray();
+ private SparseBooleanArray mUserFaceAuthenticated = new SparseBooleanArray();
private SparseBooleanArray mUserFaceUnlockRunning = new SparseBooleanArray();
private static int sCurrentUser;
- private Runnable mUpdateFingerprintListeningState = this::updateFingerprintListeningState;
+ private Runnable mUpdateBiometricListeningState = this::updateBiometricListeningState;
private static boolean sDisableHandlerCheckForTesting;
public synchronized static void setCurrentUser(int currentUser) {
@@ -487,7 +491,7 @@
*/
public void setKeyguardOccluded(boolean occluded) {
mKeyguardOccluded = occluded;
- updateFingerprintListeningState();
+ updateBiometricListeningState();
}
/**
@@ -515,19 +519,19 @@
mUserFingerprintAuthenticated.put(userId, true);
// Update/refresh trust state only if user can skip bouncer
if (getUserCanSkipBouncer(userId)) {
- mTrustManager.unlockedByFingerprintForUser(userId);
+ mTrustManager.unlockedByBiometricForUser(userId, BiometricSourceType.FINGERPRINT);
}
// Don't send cancel if authentication succeeds
mFingerprintCancelSignal = null;
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
- cb.onFingerprintAuthenticated(userId);
+ cb.onBiometricAuthenticated(userId, BiometricSourceType.FINGERPRINT);
}
}
- mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_FINGERPRINT_AUTHENTICATION_CONTINUE),
- FINGERPRINT_CONTINUE_DELAY_MS);
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_BIOMETRIC_AUTHENTICATION_CONTINUE),
+ BIOMETRIC_CONTINUE_DELAY_MS);
// Only authenticate fingerprint once when assistant is visible
mAssistantVisible = false;
@@ -539,10 +543,10 @@
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
- cb.onFingerprintAuthFailed();
+ cb.onBiometricAuthFailed(BiometricSourceType.FINGERPRINT);
}
}
- handleFingerprintHelp(-1, mContext.getString(R.string.fingerprint_not_recognized));
+ handleFingerprintHelp(-1, mContext.getString(R.string.kg_fingerprint_not_recognized));
}
private void handleFingerprintAcquired(int acquireInfo) {
@@ -552,7 +556,7 @@
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
- cb.onFingerprintAcquired();
+ cb.onBiometricAcquired(BiometricSourceType.FINGERPRINT);
}
}
}
@@ -577,7 +581,7 @@
}
onFingerprintAuthenticated(userId);
} finally {
- setFingerprintRunningState(FINGERPRINT_STATE_STOPPED);
+ setFingerprintRunningState(BIOMETRIC_STATE_STOPPED);
}
Trace.endSection();
}
@@ -586,7 +590,7 @@
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
- cb.onFingerprintHelp(msgId, helpString);
+ cb.onBiometricHelp(msgId, helpString, BiometricSourceType.FINGERPRINT);
}
}
}
@@ -595,23 +599,23 @@
@Override
public void run() {
Log.w(TAG, "Retrying fingerprint after HW unavailable, attempt " +
- mHardwareUnavailableRetryCount);
+ mHardwareFingerprintUnavailableRetryCount);
updateFingerprintListeningState();
}
};
private void handleFingerprintError(int msgId, String errString) {
if (msgId == FingerprintManager.FINGERPRINT_ERROR_CANCELED
- && mFingerprintRunningState == FINGERPRINT_STATE_CANCELLING_RESTARTING) {
- setFingerprintRunningState(FINGERPRINT_STATE_STOPPED);
+ && mFingerprintRunningState == BIOMETRIC_STATE_CANCELLING_RESTARTING) {
+ setFingerprintRunningState(BIOMETRIC_STATE_STOPPED);
startListeningForFingerprint();
} else {
- setFingerprintRunningState(FINGERPRINT_STATE_STOPPED);
+ setFingerprintRunningState(BIOMETRIC_STATE_STOPPED);
}
if (msgId == FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE) {
- if (mHardwareUnavailableRetryCount < HW_UNAVAILABLE_RETRY_MAX) {
- mHardwareUnavailableRetryCount++;
+ if (mHardwareFingerprintUnavailableRetryCount < HW_UNAVAILABLE_RETRY_MAX) {
+ mHardwareFingerprintUnavailableRetryCount++;
mHandler.removeCallbacks(mRetryFingerprintAuthentication);
mHandler.postDelayed(mRetryFingerprintAuthentication, HW_UNAVAILABLE_TIMEOUT);
}
@@ -626,7 +630,7 @@
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
- cb.onFingerprintError(msgId, errString);
+ cb.onBiometricError(msgId, errString, BiometricSourceType.FINGERPRINT);
}
}
}
@@ -636,8 +640,8 @@
}
private void setFingerprintRunningState(int fingerprintRunningState) {
- boolean wasRunning = mFingerprintRunningState == FINGERPRINT_STATE_RUNNING;
- boolean isRunning = fingerprintRunningState == FINGERPRINT_STATE_RUNNING;
+ boolean wasRunning = mFingerprintRunningState == BIOMETRIC_STATE_RUNNING;
+ boolean isRunning = fingerprintRunningState == BIOMETRIC_STATE_RUNNING;
mFingerprintRunningState = fingerprintRunningState;
// Clients of KeyguardUpdateMonitor don't care about the internal state about the
@@ -653,10 +657,162 @@
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
- cb.onFingerprintRunningStateChanged(isFingerprintDetectionRunning());
+ cb.onBiometricRunningStateChanged(isFingerprintDetectionRunning(),
+ BiometricSourceType.FINGERPRINT);
}
}
}
+
+ private void onFaceAuthenticated(int userId) {
+ Trace.beginSection("KeyGuardUpdateMonitor#onFaceAuthenticated");
+ mUserFaceAuthenticated.put(userId, true);
+ // Update/refresh trust state only if user can skip bouncer
+ if (getUserCanSkipBouncer(userId)) {
+ mTrustManager.unlockedByBiometricForUser(userId, BiometricSourceType.FACE);
+ }
+ // Don't send cancel if authentication succeeds
+ mFaceCancelSignal = null;
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+ if (cb != null) {
+ cb.onBiometricAuthenticated(userId,
+ BiometricSourceType.FACE);
+ }
+ }
+
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_BIOMETRIC_AUTHENTICATION_CONTINUE),
+ BIOMETRIC_CONTINUE_DELAY_MS);
+
+ // Only authenticate face once when assistant is visible
+ mAssistantVisible = false;
+
+ Trace.endSection();
+ }
+
+ private void handleFaceAuthFailed() {
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+ if (cb != null) {
+ cb.onBiometricAuthFailed(BiometricSourceType.FACE);
+ }
+ }
+ handleFaceHelp(-1, mContext.getString(R.string.kg_face_not_recognized));
+ }
+
+ private void handleFaceAcquired(int acquireInfo) {
+ if (acquireInfo != FaceManager.FACE_ACQUIRED_GOOD) {
+ return;
+ }
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+ if (cb != null) {
+ cb.onBiometricAcquired(BiometricSourceType.FACE);
+ }
+ }
+ }
+
+ private void handleFaceAuthenticated(int authUserId) {
+ Trace.beginSection("KeyGuardUpdateMonitor#handlerFaceAuthenticated");
+ try {
+ final int userId;
+ try {
+ userId = ActivityManager.getService().getCurrentUser().id;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to get current user id: ", e);
+ return;
+ }
+ if (userId != authUserId) {
+ Log.d(TAG, "Face authenticated for wrong user: " + authUserId);
+ return;
+ }
+ if (isFaceDisabled(userId)) {
+ Log.d(TAG, "Face authentication disabled by DPM for userId: " + userId);
+ return;
+ }
+ onFaceAuthenticated(userId);
+ } finally {
+ setFaceRunningState(BIOMETRIC_STATE_STOPPED);
+ }
+ Trace.endSection();
+ }
+
+ private void handleFaceHelp(int msgId, String helpString) {
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+ if (cb != null) {
+ cb.onBiometricHelp(msgId, helpString, BiometricSourceType.FACE);
+ }
+ }
+ }
+
+ private Runnable mRetryFaceAuthentication = new Runnable() {
+ @Override
+ public void run() {
+ Log.w(TAG, "Retrying face after HW unavailable, attempt " +
+ mHardwareFaceUnavailableRetryCount);
+ updateFaceListeningState();
+ }
+ };
+
+ private void handleFaceError(int msgId, String errString) {
+ if (msgId == FaceManager.FACE_ERROR_CANCELED
+ && mFaceRunningState == BIOMETRIC_STATE_CANCELLING_RESTARTING) {
+ setFaceRunningState(BIOMETRIC_STATE_STOPPED);
+ startListeningForFace();
+ } else {
+ setFaceRunningState(BIOMETRIC_STATE_STOPPED);
+ }
+
+ if (msgId == FaceManager.FACE_ERROR_HW_UNAVAILABLE) {
+ if (mHardwareFaceUnavailableRetryCount < HW_UNAVAILABLE_RETRY_MAX) {
+ mHardwareFaceUnavailableRetryCount++;
+ mHandler.removeCallbacks(mRetryFaceAuthentication);
+ mHandler.postDelayed(mRetryFaceAuthentication, HW_UNAVAILABLE_TIMEOUT);
+ }
+ }
+
+ if (msgId == FaceManager.FACE_ERROR_LOCKOUT_PERMANENT) {
+ mLockPatternUtils.requireStrongAuth(
+ LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT,
+ getCurrentUser());
+ }
+
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+ if (cb != null) {
+ cb.onBiometricError(msgId, errString,
+ BiometricSourceType.FACE);
+ }
+ }
+ }
+
+ private void handleFaceLockoutReset() {
+ updateFaceListeningState();
+ }
+
+ private void setFaceRunningState(int faceRunningState) {
+ boolean wasRunning = mFaceRunningState == BIOMETRIC_STATE_RUNNING;
+ boolean isRunning = faceRunningState == BIOMETRIC_STATE_RUNNING;
+ mFaceRunningState = faceRunningState;
+
+ // Clients of KeyguardUpdateMonitor don't care about the internal state or about the
+ // asynchronousness of the cancel cycle. So only notify them if the actualy running state
+ // has changed.
+ if (wasRunning != isRunning) {
+ notifyFaceRunningStateChanged();
+ }
+ }
+
+ private void notifyFaceRunningStateChanged() {
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+ if (cb != null) {
+ cb.onBiometricRunningStateChanged(isFaceDetectionRunning(),
+ BiometricSourceType.FACE);
+ }
+ }
+ }
+
private void handleFaceUnlockStateChanged(boolean running, int userId) {
checkIsHandlerThread();
mUserFaceUnlockRunning.put(userId, running);
@@ -673,7 +829,11 @@
}
public boolean isFingerprintDetectionRunning() {
- return mFingerprintRunningState == FINGERPRINT_STATE_RUNNING;
+ return mFingerprintRunningState == BIOMETRIC_STATE_RUNNING;
+ }
+
+ public boolean isFaceDetectionRunning() {
+ return mFaceRunningState == BIOMETRIC_STATE_RUNNING;
}
private boolean isTrustDisabled(int userId) {
@@ -692,9 +852,18 @@
|| isSimPinSecure();
}
+ private boolean isFaceDisabled(int userId) {
+ final DevicePolicyManager dpm =
+ (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
+ return dpm != null && (dpm.getKeyguardDisabledFeatures(null, userId)
+ & DevicePolicyManager.KEYGUARD_DISABLE_FACE) != 0
+ || isSimPinSecure();
+ }
+
+
public boolean getUserCanSkipBouncer(int userId) {
return getUserHasTrust(userId) || (mUserFingerprintAuthenticated.get(userId)
- && isUnlockingWithFingerprintAllowed());
+ && isUnlockingWithBiometricAllowed());
}
public boolean getUserHasTrust(int userId) {
@@ -705,8 +874,8 @@
return mUserTrustIsManaged.get(userId) && !isTrustDisabled(userId);
}
- public boolean isUnlockingWithFingerprintAllowed() {
- return mStrongAuthTracker.isUnlockingWithFingerprintAllowed();
+ public boolean isUnlockingWithBiometricAllowed() {
+ return mStrongAuthTracker.isUnlockingWithBiometricAllowed();
}
public boolean isUserInLockdown(int userId) {
@@ -862,7 +1031,7 @@
}
};
- private final FingerprintManager.LockoutResetCallback mLockoutResetCallback
+ private final FingerprintManager.LockoutResetCallback mFingerprintLockoutResetCallback
= new FingerprintManager.LockoutResetCallback() {
@Override
public void onLockoutReset() {
@@ -870,13 +1039,21 @@
}
};
- private FingerprintManager.AuthenticationCallback mAuthenticationCallback
+ private final FaceManager.LockoutResetCallback mFaceLockoutResetCallback
+ = new FaceManager.LockoutResetCallback() {
+ @Override
+ public void onLockoutReset() {
+ handleFaceLockoutReset();
+ }
+ };
+
+ private FingerprintManager.AuthenticationCallback mFingerprintAuthenticationCallback
= new AuthenticationCallback() {
@Override
public void onAuthenticationFailed() {
handleFingerprintAuthFailed();
- };
+ }
@Override
public void onAuthenticationSucceeded(AuthenticationResult result) {
@@ -900,8 +1077,42 @@
handleFingerprintAcquired(acquireInfo);
}
};
+
+ private FaceManager.AuthenticationCallback mFaceAuthenticationCallback
+ = new FaceManager.AuthenticationCallback() {
+
+ @Override
+ public void onAuthenticationFailed() {
+ handleFaceAuthFailed();
+ }
+
+ @Override
+ public void onAuthenticationSucceeded(FaceManager.AuthenticationResult result) {
+ Trace.beginSection("KeyguardUpdateMonitor#onAuthenticationSucceeded");
+ handleFaceAuthenticated(result.getUserId());
+ Trace.endSection();
+ }
+
+ @Override
+ public void onAuthenticationHelp(int helpMsgId, CharSequence helpString) {
+ handleFaceHelp(helpMsgId, helpString.toString());
+ }
+
+ @Override
+ public void onAuthenticationError(int errMsgId, CharSequence errString) {
+ handleFaceError(errMsgId, errString.toString());
+ }
+
+ @Override
+ public void onAuthenticationAcquired(int acquireInfo) {
+ handleFaceAcquired(acquireInfo);
+ }
+ };
+
private CancellationSignal mFingerprintCancelSignal;
+ private CancellationSignal mFaceCancelSignal;
private FingerprintManager mFpm;
+ private FaceManager mFaceAuthenticationManager;
/**
* When we receive a
@@ -1049,9 +1260,9 @@
super(context);
}
- public boolean isUnlockingWithFingerprintAllowed() {
+ public boolean isUnlockingWithBiometricAllowed() {
int userId = getCurrentUser();
- return isFingerprintAllowedForUser(userId);
+ return isBiometricAllowedForUser(userId);
}
public boolean hasUserAuthenticatedSinceBoot() {
@@ -1075,7 +1286,7 @@
protected void handleStartedWakingUp() {
Trace.beginSection("KeyguardUpdateMonitor#handleStartedWakingUp");
- updateFingerprintListeningState();
+ updateBiometricListeningState();
final int count = mCallbacks.size();
for (int i = 0; i < count; i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
@@ -1087,7 +1298,7 @@
}
protected void handleStartedGoingToSleep(int arg1) {
- clearFingerprintRecognized();
+ clearBiometricRecognized();
final int count = mCallbacks.size();
for (int i = 0; i < count; i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
@@ -1096,7 +1307,7 @@
}
}
mGoingToSleep = true;
- updateFingerprintListeningState();
+ updateBiometricListeningState();
}
protected void handleFinishedGoingToSleep(int arg1) {
@@ -1108,7 +1319,7 @@
cb.onFinishedGoingToSleep(arg1);
}
}
- updateFingerprintListeningState();
+ updateBiometricListeningState();
}
private void handleScreenTurnedOn() {
@@ -1122,7 +1333,8 @@
}
private void handleScreenTurnedOff() {
- mHardwareUnavailableRetryCount = 0;
+ mHardwareFingerprintUnavailableRetryCount = 0;
+ mHardwareFaceUnavailableRetryCount = 0;
final int count = mCallbacks.size();
for (int i = 0; i < count; i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
@@ -1141,7 +1353,7 @@
cb.onDreamingStateChanged(mIsDreaming);
}
}
- updateFingerprintListeningState();
+ updateBiometricListeningState();
}
private void handleUserInfoChanged(int userId) {
@@ -1238,9 +1450,17 @@
if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
mFpm = (FingerprintManager) context.getSystemService(Context.FINGERPRINT_SERVICE);
}
- updateFingerprintListeningState();
+
+ if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FACE)) {
+ mFaceAuthenticationManager =
+ (FaceManager) context.getSystemService(Context.FACE_SERVICE);
+ }
+ updateBiometricListeningState();
if (mFpm != null) {
- mFpm.addLockoutResetCallback(mLockoutResetCallback);
+ mFpm.addLockoutResetCallback(mFingerprintLockoutResetCallback);
+ }
+ if (mFaceAuthenticationManager != null) {
+ mFaceAuthenticationManager.addLockoutResetCallback(mFaceLockoutResetCallback);
}
ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
@@ -1249,28 +1469,55 @@
mLogoutEnabled = mDevicePolicyManager.isLogoutEnabled();
}
+ private void updateBiometricListeningState() {
+ updateFingerprintListeningState();
+ updateFaceListeningState();
+ }
+
private void updateFingerprintListeningState() {
// If this message exists, we should not authenticate again until this message is
// consumed by the handler
- if (mHandler.hasMessages(MSG_FINGERPRINT_AUTHENTICATION_CONTINUE)) {
+ if (mHandler.hasMessages(MSG_BIOMETRIC_AUTHENTICATION_CONTINUE)) {
return;
}
mHandler.removeCallbacks(mRetryFingerprintAuthentication);
boolean shouldListenForFingerprint = shouldListenForFingerprint();
- if (mFingerprintRunningState == FINGERPRINT_STATE_RUNNING && !shouldListenForFingerprint) {
+ if (mFingerprintRunningState == BIOMETRIC_STATE_RUNNING && !shouldListenForFingerprint) {
stopListeningForFingerprint();
- } else if (mFingerprintRunningState != FINGERPRINT_STATE_RUNNING
+ } else if (mFingerprintRunningState != BIOMETRIC_STATE_RUNNING
&& shouldListenForFingerprint) {
startListeningForFingerprint();
}
}
+ private void updateFaceListeningState() {
+ // If this message exists, we should not authenticate again until this message is
+ // consumed by the handler
+ if (mHandler.hasMessages(MSG_BIOMETRIC_AUTHENTICATION_CONTINUE)) {
+ return;
+ }
+ mHandler.removeCallbacks(mRetryFaceAuthentication);
+ boolean shouldListenForFace = shouldListenForFace();
+ if (mFaceRunningState == BIOMETRIC_STATE_RUNNING && !shouldListenForFace) {
+ stopListeningForFace();
+ } else if (mFaceRunningState != BIOMETRIC_STATE_RUNNING
+ && shouldListenForFace) {
+ startListeningForFace();
+ }
+ }
+
private boolean shouldListenForFingerprintAssistant() {
return mAssistantVisible && mKeyguardOccluded
&& !mUserFingerprintAuthenticated.get(getCurrentUser(), false)
&& !mUserHasTrust.get(getCurrentUser(), false);
}
+ private boolean shouldListenForFaceAssistant() {
+ return mAssistantVisible && mKeyguardOccluded
+ && !mUserFaceAuthenticated.get(getCurrentUser(), false)
+ && !mUserHasTrust.get(getCurrentUser(), false);
+ }
+
private boolean shouldListenForFingerprint() {
return (mKeyguardIsVisible || !mDeviceInteractive ||
(mBouncer && !mKeyguardGoingAway) || mGoingToSleep ||
@@ -1279,9 +1526,18 @@
&& !mKeyguardGoingAway;
}
+ private boolean shouldListenForFace() {
+ return (mKeyguardIsVisible || !mDeviceInteractive ||
+ (mBouncer && !mKeyguardGoingAway) || mGoingToSleep ||
+ shouldListenForFaceAssistant() || (mKeyguardOccluded && mIsDreaming))
+ && !mSwitchingUser && !isFaceDisabled(getCurrentUser())
+ && !mKeyguardGoingAway;
+ }
+
+
private void startListeningForFingerprint() {
- if (mFingerprintRunningState == FINGERPRINT_STATE_CANCELLING) {
- setFingerprintRunningState(FINGERPRINT_STATE_CANCELLING_RESTARTING);
+ if (mFingerprintRunningState == BIOMETRIC_STATE_CANCELLING) {
+ setFingerprintRunningState(BIOMETRIC_STATE_CANCELLING_RESTARTING);
return;
}
if (DEBUG) Log.v(TAG, "startListeningForFingerprint()");
@@ -1291,9 +1547,27 @@
mFingerprintCancelSignal.cancel();
}
mFingerprintCancelSignal = new CancellationSignal();
- mFpm.authenticate(null, mFingerprintCancelSignal, 0, mAuthenticationCallback, null,
- userId);
- setFingerprintRunningState(FINGERPRINT_STATE_RUNNING);
+ mFpm.authenticate(null, mFingerprintCancelSignal, 0, mFingerprintAuthenticationCallback,
+ null, userId);
+ setFingerprintRunningState(BIOMETRIC_STATE_RUNNING);
+ }
+ }
+
+ private void startListeningForFace() {
+ if (mFaceRunningState == BIOMETRIC_STATE_CANCELLING) {
+ setFaceRunningState(BIOMETRIC_STATE_CANCELLING_RESTARTING);
+ return;
+ }
+ if (DEBUG) Log.v(TAG, "startListeningForFace()");
+ int userId = ActivityManager.getCurrentUser();
+ if (isUnlockWithFacePossible(userId)) {
+ if (mFaceCancelSignal != null) {
+ mFaceCancelSignal.cancel();
+ }
+ mFaceCancelSignal = new CancellationSignal();
+ mFaceAuthenticationManager.authenticate(null, mFaceCancelSignal, 0,
+ mFaceAuthenticationCallback, null);
+ setFaceRunningState(BIOMETRIC_STATE_RUNNING);
}
}
@@ -1302,17 +1576,37 @@
&& mFpm.getEnrolledFingerprints(userId).size() > 0;
}
+ public boolean isUnlockWithFacePossible(int userId) {
+ return mFaceAuthenticationManager != null && mFaceAuthenticationManager.isHardwareDetected()
+ && !isFaceDisabled(userId)
+ && mFaceAuthenticationManager.hasEnrolledFaces(userId);
+ }
+
private void stopListeningForFingerprint() {
if (DEBUG) Log.v(TAG, "stopListeningForFingerprint()");
- if (mFingerprintRunningState == FINGERPRINT_STATE_RUNNING) {
+ if (mFingerprintRunningState == BIOMETRIC_STATE_RUNNING) {
if (mFingerprintCancelSignal != null) {
mFingerprintCancelSignal.cancel();
mFingerprintCancelSignal = null;
}
- setFingerprintRunningState(FINGERPRINT_STATE_CANCELLING);
+ setFingerprintRunningState(BIOMETRIC_STATE_CANCELLING);
}
- if (mFingerprintRunningState == FINGERPRINT_STATE_CANCELLING_RESTARTING) {
- setFingerprintRunningState(FINGERPRINT_STATE_CANCELLING);
+ if (mFingerprintRunningState == BIOMETRIC_STATE_CANCELLING_RESTARTING) {
+ setFingerprintRunningState(BIOMETRIC_STATE_CANCELLING);
+ }
+ }
+
+ private void stopListeningForFace() {
+ if (DEBUG) Log.v(TAG, "stopListeningForFace()");
+ if (mFaceRunningState == BIOMETRIC_STATE_RUNNING) {
+ if (mFaceCancelSignal != null) {
+ mFaceCancelSignal.cancel();
+ mFaceCancelSignal = null;
+ }
+ setFaceRunningState(BIOMETRIC_STATE_CANCELLING);
+ }
+ if (mFaceRunningState == BIOMETRIC_STATE_CANCELLING_RESTARTING) {
+ setFaceRunningState(BIOMETRIC_STATE_CANCELLING);
}
}
@@ -1609,7 +1903,7 @@
cb.onKeyguardVisibilityChangedRaw(showing);
}
}
- updateFingerprintListeningState();
+ updateBiometricListeningState();
}
/**
@@ -1617,7 +1911,7 @@
*/
private void handleKeyguardReset() {
if (DEBUG) Log.d(TAG, "handleKeyguardReset");
- updateFingerprintListeningState();
+ updateBiometricListeningState();
mNeedsSlowUnlockTransition = resolveNeedsSlowUnlockTransition();
}
@@ -1646,7 +1940,7 @@
cb.onKeyguardBouncerChanged(isBouncer);
}
}
- updateFingerprintListeningState();
+ updateBiometricListeningState();
}
/**
@@ -1729,7 +2023,7 @@
public void setSwitchingUser(boolean switching) {
mSwitchingUser = switching;
// Since this comes in on a binder thread, we need to post if first
- mHandler.post(mUpdateFingerprintListeningState);
+ mHandler.post(mUpdateBiometricListeningState);
}
private void sendUpdates(KeyguardUpdateMonitorCallback callback) {
@@ -1817,9 +2111,11 @@
mFailedAttempts.put(userId, getFailedUnlockAttempts(userId) + 1);
}
- public void clearFingerprintRecognized() {
+ public void clearBiometricRecognized() {
mUserFingerprintAuthenticated.clear();
- mTrustManager.clearAllFingerprints();
+ mUserFaceAuthenticated.clear();
+ mTrustManager.clearAllBiometricRecognized(BiometricSourceType.FINGERPRINT);
+ mTrustManager.clearAllBiometricRecognized(BiometricSourceType.FACE);
}
public boolean isSimPinVoiceSecure() {
@@ -2054,7 +2350,7 @@
final int userId = ActivityManager.getCurrentUser();
final int strongAuthFlags = mStrongAuthTracker.getStrongAuthForUser(userId);
pw.println(" Fingerprint state (user=" + userId + ")");
- pw.println(" allowed=" + isUnlockingWithFingerprintAllowed());
+ pw.println(" allowed=" + isUnlockingWithBiometricAllowed());
pw.println(" auth'd=" + mUserFingerprintAuthenticated.get(userId));
pw.println(" authSinceBoot="
+ getStrongAuthTracker().hasUserAuthenticatedSinceBoot());
@@ -2063,5 +2359,18 @@
pw.println(" strongAuthFlags=" + Integer.toHexString(strongAuthFlags));
pw.println(" trustManaged=" + getUserTrustIsManaged(userId));
}
+ if (mFaceAuthenticationManager != null && mFaceAuthenticationManager.isHardwareDetected()) {
+ final int userId = ActivityManager.getCurrentUser();
+ final int strongAuthFlags = mStrongAuthTracker.getStrongAuthForUser(userId);
+ pw.println(" Face authentication state (user=" + userId + ")");
+ pw.println(" allowed=" + isUnlockingWithBiometricAllowed());
+ pw.println(" auth'd=" + mUserFaceAuthenticated.get(userId));
+ pw.println(" authSinceBoot="
+ + getStrongAuthTracker().hasUserAuthenticatedSinceBoot());
+ pw.println(" disabled(DPM)=" + isFaceDisabled(userId));
+ pw.println(" possible=" + isUnlockWithFacePossible(userId));
+ pw.println(" strongAuthFlags=" + Integer.toHexString(strongAuthFlags));
+ pw.println(" trustManaged=" + getUserTrustIsManaged(userId));
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
index 67571bb..8135ac5 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
@@ -17,7 +17,7 @@
import android.app.admin.DevicePolicyManager;
import android.graphics.Bitmap;
-import android.hardware.fingerprint.FingerprintManager;
+import android.hardware.biometrics.BiometricSourceType;
import android.media.AudioManager;
import android.os.SystemClock;
import android.telephony.TelephonyManager;
@@ -213,38 +213,46 @@
public void onTrustGrantedWithFlags(int flags, int userId) { }
/**
- * Called when a finger has been acquired.
+ * Called when a biometric has been acquired.
* <p>
- * It is guaranteed that either {@link #onFingerprintAuthenticated} or
- * {@link #onFingerprintAuthFailed()} is called after this method eventually.
+ * It is guaranteed that either {@link #onBiometricAuthenticated} or
+ * {@link #onBiometricAuthFailed(BiometricSourceType)} is called after this method eventually.
+ * @param biometricSourceType
*/
- public void onFingerprintAcquired() { }
+ public void onBiometricAcquired(BiometricSourceType biometricSourceType) { }
/**
- * Called when a fingerprint couldn't be authenticated.
+ * Called when a biometric couldn't be authenticated.
+ * @param biometricSourceType
*/
- public void onFingerprintAuthFailed() { }
+ public void onBiometricAuthFailed(BiometricSourceType biometricSourceType) { }
/**
- * Called when a fingerprint is recognized.
- * @param userId the user id for which the fingerprint was authenticated
+ * Called when a biometric is recognized.
+ * @param userId the user id for which the biometric sample was authenticated
+ * @param biometricSourceType
*/
- public void onFingerprintAuthenticated(int userId) { }
+ public void onBiometricAuthenticated(int userId, BiometricSourceType biometricSourceType) { }
/**
- * Called when fingerprint provides help string (e.g. "Try again")
+ * Called when biometric authentication provides help string (e.g. "Try again")
* @param msgId
* @param helpString
+ * @param biometricSourceType
*/
- public void onFingerprintHelp(int msgId, String helpString) { }
+ public void onBiometricHelp(int msgId, String helpString,
+ BiometricSourceType biometricSourceType) { }
/**
- * Called when fingerprint provides an semi-permanent error message
- * (e.g. "Hardware not available").
- * @param msgId one of the error messages listed in {@link FingerprintManager}
+ * Called when biometric authentication method provides a semi-permanent
+ * error message (e.g. "Hardware not available").
+ * @param msgId one of the error messages listed in
+ * {@link android.hardware.biometrics.BiometricConstants}
* @param errString
+ * @param biometricSourceType
*/
- public void onFingerprintError(int msgId, String errString) { }
+ public void onBiometricError(int msgId, String errString,
+ BiometricSourceType biometricSourceType) { }
/**
* Called when the state of face unlock changed.
@@ -252,9 +260,10 @@
public void onFaceUnlockStateChanged(boolean running, int userId) { }
/**
- * Called when the fingerprint running state changed.
+ * Called when biometric running state changed.
*/
- public void onFingerprintRunningStateChanged(boolean running) { }
+ public void onBiometricRunningStateChanged(boolean running,
+ BiometricSourceType biometricSourceType) { }
/**
* Called when the state that the user hasn't used strong authentication since quite some time
diff --git a/packages/SystemUI/src/com/android/systemui/AutoReinflateContainer.java b/packages/SystemUI/src/com/android/systemui/AutoReinflateContainer.java
index 01b4254..bbc8ecd 100644
--- a/packages/SystemUI/src/com/android/systemui/AutoReinflateContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/AutoReinflateContainer.java
@@ -92,6 +92,11 @@
}
@Override
+ public void onUiModeChanged() {
+ inflateLayout();
+ }
+
+ @Override
public void onLocaleListChanged() {
inflateLayout();
}
diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
index 61784fa..3ac6705 100644
--- a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
+++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
@@ -55,6 +55,7 @@
import com.android.systemui.tuner.TunerService;
import com.android.systemui.tuner.TunerService.Tunable;
import com.android.systemui.util.Utils.DisableStateTracker;
+import com.android.systemui.R;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -74,6 +75,7 @@
private int mTextColor;
private int mLevel;
private boolean mForceShowPercent;
+ private boolean mShowPercentAvailable;
private int mDarkModeBackgroundColor;
private int mDarkModeFillColor;
@@ -113,6 +115,9 @@
atts.recycle();
mSettingObserver = new SettingObserver(new Handler(context.getMainLooper()));
+ mShowPercentAvailable = context.getResources().getBoolean(
+ com.android.internal.R.bool.config_battery_percentage_setting_available);
+
addOnAttachStateChangeListener(
new DisableStateTracker(DISABLE_NONE, DISABLE2_SYSTEM_ICONS));
@@ -264,8 +269,11 @@
private void updateShowPercent() {
final boolean showing = mBatteryPercentView != null;
- if (0 != Settings.System.getIntForUser(getContext().getContentResolver(),
- SHOW_BATTERY_PERCENT, 0, mUser) || mForceShowPercent) {
+ final boolean systemSetting = 0 != Settings.System
+ .getIntForUser(getContext().getContentResolver(),
+ SHOW_BATTERY_PERCENT, 0, mUser);
+
+ if ((mShowPercentAvailable && systemSetting) || mForceShowPercent) {
if (!showing) {
mBatteryPercentView = loadPercentView();
if (mTextColor != 0) mBatteryPercentView.setTextColor(mTextColor);
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index 9c9f021..86e0e1f 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -46,7 +46,7 @@
import com.android.systemui.power.EnhancedEstimatesImpl;
import com.android.systemui.power.PowerNotificationWarnings;
import com.android.systemui.power.PowerUI;
-import com.android.systemui.statusbar.AppOpsListener;
+import com.android.systemui.statusbar.notification.AppOpsListener;
import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.phone.ConfigurationControllerImpl;
import com.android.systemui.statusbar.phone.DarkIconDispatcherImpl;
diff --git a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
index 377fab5..f5ad747 100644
--- a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
@@ -32,8 +32,8 @@
import android.view.ViewConfiguration;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
-import com.android.systemui.statusbar.ExpandableView;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.ExpandableView;
import com.android.systemui.statusbar.FlingAnimationUtils;
import com.android.systemui.statusbar.policy.ScrollAdapter;
diff --git a/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java b/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java
index 98dc321..198a4e6 100644
--- a/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java
@@ -27,32 +27,33 @@
import android.view.ViewGroup;
import android.view.ViewOutlineProvider;
import android.view.ViewTreeObserver;
-import android.widget.FrameLayout;
import android.widget.LinearLayout;
+
import com.android.systemui.tuner.TunerService;
import com.android.systemui.tuner.TunerService.Tunable;
import com.android.systemui.util.leak.RotationUtils;
-import java.util.ArrayList;
-
import static com.android.systemui.util.leak.RotationUtils.ROTATION_LANDSCAPE;
import static com.android.systemui.util.leak.RotationUtils.ROTATION_NONE;
import static com.android.systemui.util.leak.RotationUtils.ROTATION_SEASCAPE;
-public class HardwareUiLayout extends FrameLayout implements Tunable {
+public class HardwareUiLayout extends LinearLayout implements Tunable {
private static final String EDGE_BLEED = "sysui_hwui_edge_bleed";
private static final String ROUNDED_DIVIDER = "sysui_hwui_rounded_divider";
private final int[] mTmp2 = new int[2];
- private View mChild;
+ private View mList;
+ private View mSeparatedView;
private int mOldHeight;
private boolean mAnimating;
private AnimatorSet mAnimation;
private View mDivision;
private boolean mHasOutsideTouch;
- private HardwareBgDrawable mBackground;
+ private HardwareBgDrawable mListBackground;
+ private HardwareBgDrawable mSeparatedViewBackground;
private Animator mAnimator;
private boolean mCollapse;
+ private boolean mHasSeparatedButton;
private int mEndPoint;
private boolean mEdgeBleed;
private boolean mRoundedDivider;
@@ -91,16 +92,19 @@
mRoundedDivider = Settings.Secure.getInt(getContext().getContentResolver(),
ROUNDED_DIVIDER, 0) != 0;
updateEdgeMargin(mEdgeBleed ? 0 : getEdgePadding());
- mBackground = new HardwareBgDrawable(mRoundedDivider, !mEdgeBleed, getContext());
- if (mChild != null) {
- mChild.setBackground(mBackground);
+ mListBackground = new HardwareBgDrawable(mRoundedDivider, !mEdgeBleed, getContext());
+ mSeparatedViewBackground = new HardwareBgDrawable(mRoundedDivider, !mEdgeBleed,
+ getContext());
+ if (mList != null) {
+ mList.setBackground(mListBackground);
+ mSeparatedView.setBackground(mSeparatedViewBackground);
requestLayout();
}
}
private void updateEdgeMargin(int edge) {
- if (mChild != null) {
- MarginLayoutParams params = (MarginLayoutParams) mChild.getLayoutParams();
+ if (mList != null) {
+ MarginLayoutParams params = (MarginLayoutParams) mList.getLayoutParams();
if (mRotation == ROTATION_LANDSCAPE) {
params.topMargin = edge;
} else if (mRotation == ROTATION_SEASCAPE) {
@@ -108,7 +112,19 @@
} else {
params.rightMargin = edge;
}
- mChild.setLayoutParams(params);
+ mList.setLayoutParams(params);
+ }
+
+ if (mSeparatedView != null) {
+ MarginLayoutParams params = (MarginLayoutParams) mSeparatedView.getLayoutParams();
+ if (mRotation == ROTATION_LANDSCAPE) {
+ params.topMargin = edge;
+ } else if (mRotation == ROTATION_SEASCAPE) {
+ params.bottomMargin = edge;
+ } else {
+ params.rightMargin = edge;
+ }
+ mSeparatedView.setLayoutParams(params);
}
}
@@ -119,13 +135,15 @@
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- if (mChild == null) {
+ if (mList == null) {
if (getChildCount() != 0) {
- mChild = getChildAt(0);
- mChild.setBackground(mBackground);
+ mList = getChildAt(0);
+ mList.setBackground(mListBackground);
+ mSeparatedView = getChildAt(1);
+ mSeparatedView.setBackground(mSeparatedViewBackground);
updateEdgeMargin(mEdgeBleed ? 0 : getEdgePadding());
- mOldHeight = mChild.getMeasuredHeight();
- mChild.addOnLayoutChangeListener(
+ mOldHeight = mList.getMeasuredHeight();
+ mList.addOnLayoutChangeListener(
(v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) ->
updatePosition());
updateRotation();
@@ -133,7 +151,7 @@
return;
}
}
- int newHeight = mChild.getMeasuredHeight();
+ int newHeight = mList.getMeasuredHeight();
if (newHeight != mOldHeight) {
animateChild(mOldHeight, newHeight);
}
@@ -170,37 +188,60 @@
} else {
rotateLeft();
}
+ if (mHasSeparatedButton) {
+ if (from == ROTATION_SEASCAPE || to == ROTATION_SEASCAPE) {
+ // Separated view has top margin, so seascape separated view need special rotation,
+ // not a full left or right rotation.
+ swapLeftAndTop(mSeparatedView);
+ } else if (from == ROTATION_LANDSCAPE) {
+ rotateRight(mSeparatedView);
+ } else {
+ rotateLeft(mSeparatedView);
+ }
+ }
if (to != ROTATION_NONE) {
- if (mChild instanceof LinearLayout) {
+ if (mList instanceof LinearLayout) {
mRotatedBackground = true;
- mBackground.setRotatedBackground(true);
- LinearLayout linearLayout = (LinearLayout) mChild;
+ mListBackground.setRotatedBackground(true);
+ mSeparatedViewBackground.setRotatedBackground(true);
+ LinearLayout linearLayout = (LinearLayout) mList;
if (mSwapOrientation) {
linearLayout.setOrientation(LinearLayout.HORIZONTAL);
+ setOrientation(LinearLayout.HORIZONTAL);
}
- swapDimens(this.mChild);
+ swapDimens(mList);
+ swapDimens(mSeparatedView);
}
} else {
- if (mChild instanceof LinearLayout) {
+ if (mList instanceof LinearLayout) {
mRotatedBackground = false;
- mBackground.setRotatedBackground(false);
- LinearLayout linearLayout = (LinearLayout) mChild;
+ mListBackground.setRotatedBackground(false);
+ mSeparatedViewBackground.setRotatedBackground(false);
+ LinearLayout linearLayout = (LinearLayout) mList;
if (mSwapOrientation) {
linearLayout.setOrientation(LinearLayout.VERTICAL);
+ setOrientation(LinearLayout.VERTICAL);
}
- swapDimens(mChild);
+ swapDimens(mList);
+ swapDimens(mSeparatedView);
}
}
}
private void rotateRight() {
rotateRight(this);
- rotateRight(mChild);
+ rotateRight(mList);
swapDimens(this);
- LayoutParams p = (LayoutParams) mChild.getLayoutParams();
+ LayoutParams p = (LayoutParams) mList.getLayoutParams();
p.gravity = rotateGravityRight(p.gravity);
- mChild.setLayoutParams(p);
+ mList.setLayoutParams(p);
+
+ LayoutParams separatedViewLayoutParams = (LayoutParams) mSeparatedView.getLayoutParams();
+ separatedViewLayoutParams.gravity = rotateGravityRight(separatedViewLayoutParams.gravity);
+ mSeparatedView.setLayoutParams(separatedViewLayoutParams);
+
+ setGravity(p.gravity);
}
private void swapDimens(View v) {
@@ -247,12 +288,18 @@
private void rotateLeft() {
rotateLeft(this);
- rotateLeft(mChild);
+ rotateLeft(mList);
swapDimens(this);
- LayoutParams p = (LayoutParams) mChild.getLayoutParams();
+ LayoutParams p = (LayoutParams) mList.getLayoutParams();
p.gravity = rotateGravityLeft(p.gravity);
- mChild.setLayoutParams(p);
+ mList.setLayoutParams(p);
+
+ LayoutParams separatedViewLayoutParams = (LayoutParams) mSeparatedView.getLayoutParams();
+ separatedViewLayoutParams.gravity = rotateGravityLeft(separatedViewLayoutParams.gravity);
+ mSeparatedView.setLayoutParams(separatedViewLayoutParams);
+
+ setGravity(p.gravity);
}
private int rotateGravityLeft(int gravity) {
@@ -310,6 +357,15 @@
v.setLayoutParams(params);
}
+ private void swapLeftAndTop(View v) {
+ v.setPadding(v.getPaddingTop(), v.getPaddingLeft(), v.getPaddingBottom(),
+ v.getPaddingRight());
+ MarginLayoutParams params = (MarginLayoutParams) v.getLayoutParams();
+ params.setMargins(params.topMargin, params.leftMargin, params.bottomMargin,
+ params.rightMargin);
+ v.setLayoutParams(params);
+ }
+
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
@@ -329,14 +385,14 @@
mAnimating = false;
}
});
- int fromTop = mChild.getTop();
- int fromBottom = mChild.getBottom();
+ int fromTop = mList.getTop();
+ int fromBottom = mList.getBottom();
int toTop = fromTop - ((newHeight - oldHeight) / 2);
int toBottom = fromBottom + ((newHeight - oldHeight) / 2);
- ObjectAnimator top = ObjectAnimator.ofInt(mChild, "top", fromTop, toTop);
- top.addUpdateListener(animation -> mBackground.invalidateSelf());
+ ObjectAnimator top = ObjectAnimator.ofInt(mList, "top", fromTop, toTop);
+ top.addUpdateListener(animation -> mListBackground.invalidateSelf());
mAnimation.playTogether(top,
- ObjectAnimator.ofInt(mChild, "bottom", fromBottom, toBottom));
+ ObjectAnimator.ofInt(mList, "bottom", fromBottom, toBottom));
}
public void setDivisionView(View v) {
@@ -350,26 +406,30 @@
}
private void updatePosition() {
- if (mChild == null) return;
+ if (mList == null) return;
+ // If got separated button, setRotatedBackground to false,
+ // all items won't get white background.
+ mListBackground.setRotatedBackground(mHasSeparatedButton);
+ mSeparatedViewBackground.setRotatedBackground(mHasSeparatedButton);
if (mDivision != null && mDivision.getVisibility() == VISIBLE) {
int index = mRotatedBackground ? 0 : 1;
mDivision.getLocationOnScreen(mTmp2);
float trans = mRotatedBackground ? mDivision.getTranslationX()
: mDivision.getTranslationY();
int viewTop = (int) (mTmp2[index] + trans);
- mChild.getLocationOnScreen(mTmp2);
+ mList.getLocationOnScreen(mTmp2);
viewTop -= mTmp2[index];
setCutPoint(viewTop);
} else {
- setCutPoint(mChild.getMeasuredHeight());
+ setCutPoint(mList.getMeasuredHeight());
}
}
private void setCutPoint(int point) {
- int curPoint = mBackground.getCutPoint();
+ int curPoint = mListBackground.getCutPoint();
if (curPoint == point) return;
if (getAlpha() == 0 || curPoint == 0) {
- mBackground.setCutPoint(point);
+ mListBackground.setCutPoint(point);
return;
}
if (mAnimator != null) {
@@ -379,7 +439,7 @@
mAnimator.cancel();
}
mEndPoint = point;
- mAnimator = ObjectAnimator.ofInt(mBackground, "cutPoint", curPoint, point);
+ mAnimator = ObjectAnimator.ofInt(mListBackground, "cutPoint", curPoint, point);
if (mCollapse) {
mAnimator.setStartDelay(300);
mCollapse = false;
@@ -404,6 +464,10 @@
mCollapse = true;
}
+ public void setHasSeparatedButton(boolean hasSeparatedButton) {
+ mHasSeparatedButton = hasSeparatedButton;
+ }
+
public static HardwareUiLayout get(View v) {
if (v instanceof HardwareUiLayout) return (HardwareUiLayout) v;
if (v.getParent() instanceof View) {
@@ -413,14 +477,14 @@
}
private final ViewTreeObserver.OnComputeInternalInsetsListener mInsetsListener = inoutInfo -> {
- if (mHasOutsideTouch || (mChild == null)) {
+ if (mHasOutsideTouch || (mList == null)) {
inoutInfo.setTouchableInsets(
ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME);
return;
}
inoutInfo.setTouchableInsets(
ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT);
- inoutInfo.contentInsets.set(mChild.getLeft(), mChild.getTop(),
- 0, getBottom() - mChild.getBottom());
+ inoutInfo.contentInsets.set(mList.getLeft(), mList.getTop(),
+ 0, getBottom() - mList.getBottom());
};
}
diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
index b8a57bf..50d862d 100644
--- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
@@ -290,7 +290,7 @@
|| dh != mLastSurfaceHeight;
boolean redrawNeeded = surfaceDimensionsChanged || newRotation != mLastRotation
- || mSurfaceRedrawNeeded;
+ || mSurfaceRedrawNeeded || mNeedsDrawAfterLoadingWallpaper;
if (!redrawNeeded && !mOffsetsChanged) {
if (DEBUG) {
Log.d(TAG, "Suppressed drawFrame since redraw is not needed "
diff --git a/packages/SystemUI/src/com/android/systemui/Interpolators.java b/packages/SystemUI/src/com/android/systemui/Interpolators.java
index aeef496..2b3ea3a 100644
--- a/packages/SystemUI/src/com/android/systemui/Interpolators.java
+++ b/packages/SystemUI/src/com/android/systemui/Interpolators.java
@@ -24,13 +24,20 @@
import android.view.animation.LinearInterpolator;
import android.view.animation.PathInterpolator;
-import com.android.systemui.statusbar.stack.HeadsUpAppearInterpolator;
+import com.android.systemui.statusbar.notification.stack.HeadsUpAppearInterpolator;
/**
* Utility class to receive interpolators from
*/
public class Interpolators {
public static final Interpolator FAST_OUT_SLOW_IN = new PathInterpolator(0.4f, 0f, 0.2f, 1f);
+
+ /**
+ * Like {@link #FAST_OUT_SLOW_IN}, but used in case the animation is played in reverse (i.e. t
+ * goes from 1 to 0 instead of 0 to 1).
+ */
+ public static final Interpolator FAST_OUT_SLOW_IN_REVERSE =
+ new PathInterpolator(0.8f, 0f, 0.6f, 1f);
public static final Interpolator FAST_OUT_LINEAR_IN = new PathInterpolator(0.4f, 0f, 1f, 1f);
public static final Interpolator LINEAR_OUT_SLOW_IN = new PathInterpolator(0f, 0f, 0.2f, 1f);
public static final Interpolator ALPHA_IN = new PathInterpolator(0.4f, 0f, 1f, 1f);
@@ -51,4 +58,11 @@
*/
public static final Interpolator TOUCH_RESPONSE =
new PathInterpolator(0.3f, 0f, 0.1f, 1f);
+
+ /**
+ * Like {@link #TOUCH_RESPONSE}, but used in case the animation is played in reverse (i.e. t
+ * goes from 1 to 0 instead of 0 to 1).
+ */
+ public static final Interpolator TOUCH_RESPONSE_REVERSE =
+ new PathInterpolator(0.9f, 0f, 0.7f, 1f);
}
diff --git a/packages/SystemUI/src/com/android/systemui/LatencyTester.java b/packages/SystemUI/src/com/android/systemui/LatencyTester.java
index cbb69ee..1e458fa 100644
--- a/packages/SystemUI/src/com/android/systemui/LatencyTester.java
+++ b/packages/SystemUI/src/com/android/systemui/LatencyTester.java
@@ -16,6 +16,7 @@
package com.android.systemui;
+import android.hardware.biometrics.BiometricSourceType;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -26,7 +27,7 @@
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.internal.util.LatencyTracker;
-import com.android.systemui.statusbar.phone.FingerprintUnlockController;
+import com.android.systemui.statusbar.phone.BiometricUnlockController;
import com.android.systemui.statusbar.phone.StatusBar;
/**
@@ -72,10 +73,10 @@
}
private void fakeWakeAndUnlock() {
- FingerprintUnlockController fingerprintUnlockController = getComponent(StatusBar.class)
- .getFingerprintUnlockController();
- fingerprintUnlockController.onFingerprintAcquired();
- fingerprintUnlockController.onFingerprintAuthenticated(
- KeyguardUpdateMonitor.getCurrentUser());
+ BiometricUnlockController biometricUnlockController = getComponent(StatusBar.class)
+ .getBiometricUnlockController();
+ biometricUnlockController.onBiometricAcquired(BiometricSourceType.FINGERPRINT);
+ biometricUnlockController.onBiometricAuthenticated(
+ KeyguardUpdateMonitor.getCurrentUser(), BiometricSourceType.FINGERPRINT);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
index 9fc71c8..4bb4c24 100644
--- a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
@@ -30,6 +30,7 @@
import android.os.PatternMatcher;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.provider.Settings;
import android.util.Log;
import android.view.SurfaceControl;
@@ -65,7 +66,7 @@
public static final String TAG_OPS = "OverviewProxyService";
public static final boolean DEBUG_OVERVIEW_PROXY = false;
- private static final long BACKOFF_MILLIS = 5000;
+ private static final long BACKOFF_MILLIS = 1000;
private static final long DEFERRED_CALLBACK_MILLIS = 5000;
private final Context mContext;
@@ -81,11 +82,15 @@
private int mConnectionBackoffAttempts;
private @InteractionType int mInteractionFlags;
private boolean mIsEnabled;
+ private int mCurrentBoundedUserId = -1;
private ISystemUiProxy mSysUiProxy = new ISystemUiProxy.Stub() {
public GraphicBufferCompat screenshot(Rect sourceCrop, int width, int height, int minLayer,
int maxLayer, boolean useIdentityTransform, int rotation) {
+ if (!verifyCaller("screenshot")) {
+ return null;
+ }
long token = Binder.clearCallingIdentity();
try {
return new GraphicBufferCompat(SurfaceControl.screenshotToBuffer(sourceCrop, width,
@@ -96,10 +101,13 @@
}
public void startScreenPinning(int taskId) {
+ if (!verifyCaller("startScreenPinning")) {
+ return;
+ }
long token = Binder.clearCallingIdentity();
try {
mHandler.post(() -> {
- StatusBar statusBar = ((SystemUIApplication) mContext).getComponent(
+ StatusBar statusBar = SysUiServiceProvider.getComponent(mContext,
StatusBar.class);
if (statusBar != null) {
statusBar.showScreenPinningRequest(taskId, false /* allowCancel */);
@@ -111,6 +119,9 @@
}
public void onSplitScreenInvoked() {
+ if (!verifyCaller("onSplitScreenInvoked")) {
+ return;
+ }
long token = Binder.clearCallingIdentity();
try {
EventBus.getDefault().post(new DockedFirstAnimationFrameEvent());
@@ -120,6 +131,9 @@
}
public void onOverviewShown(boolean fromHome) {
+ if (!verifyCaller("onOverviewShown")) {
+ return;
+ }
long token = Binder.clearCallingIdentity();
try {
mHandler.post(() -> {
@@ -133,6 +147,9 @@
}
public void setInteractionState(@InteractionType int flags) {
+ if (!verifyCaller("setInteractionState")) {
+ return;
+ }
long token = Binder.clearCallingIdentity();
try {
if (mInteractionFlags != flags) {
@@ -150,9 +167,12 @@
}
public Rect getNonMinimizedSplitScreenSecondaryBounds() {
+ if (!verifyCaller("getNonMinimizedSplitScreenSecondaryBounds")) {
+ return null;
+ }
long token = Binder.clearCallingIdentity();
try {
- Divider divider = ((SystemUIApplication) mContext).getComponent(Divider.class);
+ Divider divider = SysUiServiceProvider.getComponent(mContext, Divider.class);
if (divider != null) {
return divider.getView().getNonMinimizedSplitScreenSecondaryBounds();
}
@@ -163,6 +183,9 @@
}
public void setBackButtonAlpha(float alpha, boolean animate) {
+ if (!verifyCaller("setBackButtonAlpha")) {
+ return;
+ }
long token = Binder.clearCallingIdentity();
try {
mHandler.post(() -> {
@@ -172,6 +195,16 @@
Binder.restoreCallingIdentity(token);
}
}
+
+ private boolean verifyCaller(String reason) {
+ final int callerId = Binder.getCallingUserHandle().getIdentifier();
+ if (callerId != mCurrentBoundedUserId) {
+ Log.w(TAG_OPS, "Launcher called sysui with invalid user: " + callerId + ", reason: "
+ + reason);
+ return false;
+ }
+ return true;
+ }
};
private final Runnable mDeferredConnectionCallback = () -> {
@@ -210,7 +243,9 @@
}
try {
mOverviewProxy.onBind(mSysUiProxy);
+ mCurrentBoundedUserId = mDeviceProvisionedController.getCurrentUser();
} catch (RemoteException e) {
+ mCurrentBoundedUserId = -1;
Log.e(TAG_OPS, "Failed to call onBind()", e);
}
notifyConnectionChanged();
@@ -219,18 +254,21 @@
@Override
public void onNullBinding(ComponentName name) {
Log.w(TAG_OPS, "Null binding of '" + name + "', try reconnecting");
+ mCurrentBoundedUserId = -1;
internalConnectToCurrentUser();
}
@Override
public void onBindingDied(ComponentName name) {
Log.w(TAG_OPS, "Binding died of '" + name + "', try reconnecting");
+ mCurrentBoundedUserId = -1;
internalConnectToCurrentUser();
}
@Override
public void onServiceDisconnected(ComponentName name) {
// Do nothing
+ mCurrentBoundedUserId = -1;
}
};
@@ -391,14 +429,22 @@
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println(TAG_OPS + " state:");
- pw.print(" mConnectionBackoffAttempts="); pw.println(mConnectionBackoffAttempts);
+ pw.print(" recentsComponentName="); pw.println(mRecentsComponentName);
+ pw.print(" isConnected="); pw.println(mOverviewProxy != null);
pw.print(" isCurrentUserSetup="); pw.println(mDeviceProvisionedController
.isCurrentUserSetup());
- pw.print(" isConnected="); pw.println(mOverviewProxy != null);
- pw.print(" mRecentsComponentName="); pw.println(mRecentsComponentName);
- pw.print(" mIsEnabled="); pw.println(isEnabled());
- pw.print(" mInteractionFlags="); pw.println(mInteractionFlags);
- pw.print(" mQuickStepIntent="); pw.println(mQuickStepIntent);
+ pw.print(" connectionBackoffAttempts="); pw.println(mConnectionBackoffAttempts);
+ pw.print(" interactionFlags="); pw.println(mInteractionFlags);
+
+ pw.print(" quickStepIntent="); pw.println(mQuickStepIntent);
+ pw.print(" quickStepIntentResolved="); pw.println(isEnabled());
+
+ final int swipeUpDefaultValue = mContext.getResources()
+ .getBoolean(com.android.internal.R.bool.config_swipe_up_gesture_default) ? 1 : 0;
+ final int swipeUpEnabled = Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.SWIPE_UP_TO_SWITCH_APPS_ENABLED, swipeUpDefaultValue);
+ pw.print(" swipeUpSetting="); pw.println(swipeUpEnabled != 0);
+ pw.print(" swipeUpSettingDefault="); pw.println(swipeUpDefaultValue != 0);
}
public interface OverviewProxyListener {
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index bbbc71f..520e40a 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -55,6 +55,7 @@
import android.view.View.OnLayoutChangeListener;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
+import android.view.ViewTreeObserver;
import android.view.WindowManager;
import android.widget.FrameLayout;
import android.widget.ImageView;
@@ -97,6 +98,7 @@
private DisplayCutoutView mCutoutTop;
private DisplayCutoutView mCutoutBottom;
private SecureSetting mColorInversionSetting;
+ private boolean mPendingRotationChange;
@Override
public void start() {
@@ -130,6 +132,21 @@
@Override
public void onDisplayChanged(int displayId) {
+ if ((hasRoundedCorners() || shouldDrawCutout()) &&
+ mRotation != RotationUtils.getExactRotation(mContext)) {
+ // We cannot immediately update the orientation. Otherwise
+ // WindowManager is still deferring layout until it has finished dispatching
+ // the config changes, which may cause divergence between what we draw
+ // (new orientation), and where we are placed on the screen (old orientation).
+ // Instead we wait until either:
+ // - we are trying to redraw. This because WM resized our window and told us to.
+ // - the config change has been dispatched, so WM is no longer deferring layout.
+ mPendingRotationChange = true;
+ mOverlay.getViewTreeObserver().addOnPreDrawListener(
+ new RestartingPreDrawListener(mOverlay));
+ mBottomOverlay.getViewTreeObserver().addOnPreDrawListener(
+ new RestartingPreDrawListener(mBottomOverlay));
+ }
updateOrientation();
}
};
@@ -144,12 +161,12 @@
mOverlay = LayoutInflater.from(mContext)
.inflate(R.layout.rounded_corners, null);
mCutoutTop = new DisplayCutoutView(mContext, true,
- this::updateWindowVisibilities);
+ this::updateWindowVisibilities, this);
((ViewGroup)mOverlay).addView(mCutoutTop);
mBottomOverlay = LayoutInflater.from(mContext)
.inflate(R.layout.rounded_corners, null);
mCutoutBottom = new DisplayCutoutView(mContext, false,
- this::updateWindowVisibilities);
+ this::updateWindowVisibilities, this);
((ViewGroup)mBottomOverlay).addView(mCutoutBottom);
mOverlay.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
@@ -229,6 +246,7 @@
@Override
protected void onConfigurationChanged(Configuration newConfig) {
+ mPendingRotationChange = false;
updateOrientation();
if (shouldDrawCutout() && mOverlay == null) {
setupDecorations();
@@ -236,6 +254,9 @@
}
protected void updateOrientation() {
+ if (mPendingRotationChange) {
+ return;
+ }
int newRotation = RotationUtils.getExactRotation(mContext);
if (newRotation != mRotation) {
mRotation = newRotation;
@@ -451,15 +472,17 @@
private final int[] mLocation = new int[2];
private final boolean mInitialStart;
private final Runnable mVisibilityChangedListener;
+ private final ScreenDecorations mDecorations;
private int mColor = Color.BLACK;
private boolean mStart;
private int mRotation;
public DisplayCutoutView(Context context, boolean start,
- Runnable visibilityChangedListener) {
+ Runnable visibilityChangedListener, ScreenDecorations decorations) {
super(context);
mInitialStart = start;
mVisibilityChangedListener = visibilityChangedListener;
+ mDecorations = decorations;
setId(R.id.display_cutout);
}
@@ -522,10 +545,10 @@
}
private void update() {
- mStart = isStart();
- if (!isAttachedToWindow()) {
+ if (!isAttachedToWindow() || mDecorations.mPendingRotationChange) {
return;
}
+ mStart = isStart();
requestLayout();
getDisplay().getDisplayInfo(mInfo);
mBounds.setEmpty();
@@ -688,4 +711,28 @@
return rotation == RotationUtils.ROTATION_LANDSCAPE || rotation ==
RotationUtils.ROTATION_SEASCAPE;
}
+
+ /**
+ * A pre-draw listener, that cancels the draw and restarts the traversal with the updated
+ * window attributes.
+ */
+ private class RestartingPreDrawListener implements ViewTreeObserver.OnPreDrawListener {
+
+ private final View mView;
+
+ private RestartingPreDrawListener(View view) {
+ mView = view;
+ }
+
+ @Override
+ public boolean onPreDraw() {
+ mPendingRotationChange = false;
+ mView.getViewTreeObserver().removeOnPreDrawListener(this);
+ // This changes the window attributes - we need to restart the traversal for them to
+ // take effect.
+ updateOrientation();
+ mView.invalidate();
+ return false;
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
index a64ce29..a3b5395 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -35,8 +35,7 @@
import android.view.accessibility.AccessibilityEvent;
import com.android.systemui.classifier.FalsingManager;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
-import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.FlingAnimationUtils;
public class SwipeHelper implements Gefingerpoken {
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index 03a6398..311a2f2 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -22,7 +22,6 @@
import android.util.Log;
import android.view.ViewGroup;
-import com.android.internal.logging.MetricsLogger;
import com.android.internal.util.function.TriConsumer;
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.colorextraction.ColorExtractor.GradientColors;
@@ -32,12 +31,12 @@
import com.android.systemui.keyguard.DismissCallbackRegistry;
import com.android.systemui.qs.QSTileHost;
import com.android.systemui.statusbar.KeyguardIndicationController;
-import com.android.systemui.statusbar.NotificationBlockingHelperManager;
-import com.android.systemui.statusbar.NotificationEntryManager;
-import com.android.systemui.statusbar.NotificationGutsManager;
+import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
-import com.android.systemui.statusbar.NotificationLogger;
+import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.NotificationViewHierarchyManager;
diff --git a/packages/SystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java
index a89a8ef..d6a1cf0 100644
--- a/packages/SystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java
@@ -18,9 +18,9 @@
import android.content.Context;
import android.service.notification.StatusBarNotification;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
-import com.android.systemui.statusbar.NotificationData;
-import com.android.systemui.statusbar.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
public class CarNotificationEntryManager extends NotificationEntryManager {
public CarNotificationEntryManager(Context context) {
@@ -29,7 +29,7 @@
/**
* Returns the
- * {@link com.android.systemui.statusbar.ExpandableNotificationRow.LongPressListener} that will
+ * {@link ExpandableNotificationRow.LongPressListener} that will
* be triggered when a notification card is long-pressed.
*/
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/car/CarSystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/car/CarSystemUIFactory.java
index cc9fec0..a015a18 100644
--- a/packages/SystemUI/src/com/android/systemui/car/CarSystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/car/CarSystemUIFactory.java
@@ -17,15 +17,13 @@
import android.content.Context;
import android.util.ArrayMap;
-import android.view.View;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.ViewMediatorCallback;
import com.android.systemui.Dependency.DependencyProvider;
import com.android.systemui.SystemUIFactory;
-import com.android.systemui.statusbar.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.car.CarFacetButtonController;
-import com.android.systemui.statusbar.car.CarStatusBar;
import com.android.systemui.statusbar.car.CarStatusBarKeyguardViewManager;
import com.android.systemui.statusbar.car.hvac.HvacController;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
index b7ff984..7863245 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
@@ -33,7 +33,6 @@
import com.android.systemui.util.wakelock.WakeLock;
import java.util.Calendar;
-import java.util.GregorianCalendar;
/**
* The policy controlling doze.
@@ -113,9 +112,7 @@
// The display buffers will be empty and need to be filled.
mHost.dozeTimeTick();
// The first frame may arrive when the display isn't ready yet.
- mHandler.postDelayed(mWakeLock.wrap(mHost::dozeTimeTick), 100);
- // The the delayed frame may arrive when the display isn't ready yet either.
- mHandler.postDelayed(mWakeLock.wrap(mHost::dozeTimeTick), 1000);
+ mHandler.postDelayed(mWakeLock.wrap(mHost::dozeTimeTick), 500);
}
scheduleTimeTick();
break;
@@ -184,7 +181,7 @@
}
private long roundToNextMinute(long timeInMillis) {
- Calendar calendar = GregorianCalendar.getInstance();
+ Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(timeInMillis);
calendar.set(Calendar.MILLISECOND, 0);
calendar.set(Calendar.SECOND, 0);
diff --git a/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java b/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java
index f422737..0ed1cd1 100644
--- a/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java
+++ b/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java
@@ -51,7 +51,8 @@
private final View mRootView;
private final InterestingConfigChanges mConfigChanges = new InterestingConfigChanges(
ActivityInfo.CONFIG_FONT_SCALE | ActivityInfo.CONFIG_LOCALE
- | ActivityInfo.CONFIG_SCREEN_LAYOUT | ActivityInfo.CONFIG_ASSETS_PATHS);
+ | ActivityInfo.CONFIG_SCREEN_LAYOUT | ActivityInfo.CONFIG_ASSETS_PATHS
+ | ActivityInfo.CONFIG_UI_MODE);
private final FragmentService mManager;
private final ExtensionFragmentManager mPlugins = new ExtensionFragmentManager();
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index 92674d4..201f40e 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -28,12 +28,10 @@
import android.app.admin.DevicePolicyManager;
import android.app.trust.TrustManager;
import android.content.BroadcastReceiver;
-import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.ServiceConnection;
import android.content.pm.UserInfo;
import android.database.ContentObserver;
import android.graphics.Point;
@@ -42,9 +40,7 @@
import android.net.ConnectivityManager;
import android.os.Build;
import android.os.Handler;
-import android.os.IBinder;
import android.os.Message;
-import android.os.Messenger;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemProperties;
@@ -68,9 +64,9 @@
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
import android.view.accessibility.AccessibilityEvent;
-import android.widget.AdapterView;
import android.widget.AdapterView.OnItemLongClickListener;
import android.widget.BaseAdapter;
+import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.ImageView.ScaleType;
import android.widget.LinearLayout;
@@ -150,6 +146,7 @@
private boolean mDeviceProvisioned = false;
private ToggleAction.State mAirplaneState = ToggleAction.State.Off;
private boolean mIsWaitingForEcmExit = false;
+ private boolean mHasFasterEmergencyButton;
private boolean mHasTelephony;
private boolean mHasVibrator;
private boolean mHasLogoutButton;
@@ -268,7 +265,7 @@
if (!mHasVibrator) {
mSilentModeAction = new SilentModeToggleAction();
} else {
- mSilentModeAction = new SilentModeTriStateAction(mContext, mAudioManager, mHandler);
+ mSilentModeAction = new SilentModeTriStateAction(mAudioManager, mHandler);
}
mAirplaneModeOn = new ToggleAction(
R.drawable.ic_lock_airplane_mode,
@@ -320,6 +317,7 @@
ArraySet<String> addedKeys = new ArraySet<String>();
mHasLogoutButton = false;
mHasLockdownButton = false;
+ mHasFasterEmergencyButton = false;
for (int i = 0; i < defaultActions.length; i++) {
String actionKey = defaultActions[i];
if (addedKeys.contains(actionKey)) {
@@ -363,6 +361,7 @@
Settings.Global.FASTER_EMERGENCY_PHONE_CALL_ENABLED, 0) != 0
&& !mEmergencyAffordanceManager.needsEmergencyAffordance()) {
mItems.add(new EmergencyAction());
+ mHasFasterEmergencyButton = true;
}
} else if (GLOBAL_ACTION_KEY_SCREENSHOT.equals(actionKey)) {
mItems.add(new ScreenshotAction());
@@ -393,7 +392,8 @@
}
return false;
};
- ActionsDialog dialog = new ActionsDialog(mContext, this, mAdapter, onItemLongClickListener);
+ ActionsDialog dialog = new ActionsDialog(mContext, this, mAdapter, onItemLongClickListener,
+ mHasFasterEmergencyButton);
dialog.setCanceledOnTouchOutside(false); // Handled by the custom class.
dialog.setKeyguardShowing(mKeyguardShowing);
@@ -453,7 +453,8 @@
"com.android.phone.EmergencyDialer.DIAL";
private EmergencyAction() {
- super(R.drawable.emergency_icon, R.string.global_action_emergency);
+ super(com.android.systemui.R.drawable.faster_emergency_icon,
+ R.string.global_action_emergency);
}
@Override
@@ -1210,12 +1211,10 @@
private final AudioManager mAudioManager;
private final Handler mHandler;
- private final Context mContext;
- SilentModeTriStateAction(Context context, AudioManager audioManager, Handler handler) {
+ SilentModeTriStateAction(AudioManager audioManager, Handler handler) {
mAudioManager = audioManager;
mHandler = handler;
- mContext = context;
}
private int ringerModeToIndex(int ringerMode) {
@@ -1387,15 +1386,17 @@
private final Context mContext;
private final MyAdapter mAdapter;
private final LinearLayout mListView;
+ private final FrameLayout mSeparatedView;
private final HardwareUiLayout mHardwareLayout;
private final OnClickListener mClickListener;
private final OnItemLongClickListener mLongClickListener;
private final GradientDrawable mGradientDrawable;
private final ColorExtractor mColorExtractor;
private boolean mKeyguardShowing;
+ private boolean mShouldDisplaySeparatedButton;
public ActionsDialog(Context context, OnClickListener clickListener, MyAdapter adapter,
- OnItemLongClickListener longClickListener) {
+ OnItemLongClickListener longClickListener, boolean shouldDisplaySeparatedButton) {
super(context, com.android.systemui.R.style.Theme_SystemUI_Dialog_GlobalActions);
mContext = context;
mAdapter = adapter;
@@ -1403,6 +1404,7 @@
mLongClickListener = longClickListener;
mGradientDrawable = new GradientDrawable(mContext);
mColorExtractor = Dependency.get(SysuiColorExtractor.class);
+ mShouldDisplaySeparatedButton = shouldDisplaySeparatedButton;
// Window initialization
Window window = getWindow();
@@ -1426,8 +1428,13 @@
setContentView(com.android.systemui.R.layout.global_actions_wrapped);
mListView = findViewById(android.R.id.list);
+ mSeparatedView = findViewById(com.android.systemui.R.id.separated_button);
+ if (!mShouldDisplaySeparatedButton) {
+ mSeparatedView.setVisibility(View.GONE);
+ }
mHardwareLayout = HardwareUiLayout.get(mListView);
mHardwareLayout.setOutsideTouchListener(view -> dismiss());
+ mHardwareLayout.setHasSeparatedButton(mShouldDisplaySeparatedButton);
setTitle(R.string.global_actions);
mListView.setAccessibilityDelegate(new View.AccessibilityDelegate() {
@Override
@@ -1442,13 +1449,16 @@
private void updateList() {
mListView.removeAllViews();
+ mSeparatedView.removeAllViews();
for (int i = 0; i < mAdapter.getCount(); i++) {
- View v = mAdapter.getView(i, null, mListView);
+ ViewGroup parentView = mShouldDisplaySeparatedButton && i == mAdapter.getCount() - 1
+ ? mSeparatedView : mListView;
+ View v = mAdapter.getView(i, null, parentView);
final int pos = i;
v.setOnClickListener(view -> mClickListener.onClick(this, pos));
v.setOnLongClickListener(view ->
mLongClickListener.onItemLongClick(null, v, pos, 0));
- mListView.addView(v);
+ parentView.addView(v);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index cf6f16a..8d7eb6c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -30,6 +30,7 @@
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.app.StatusBarManager;
+import android.hardware.biometrics.BiometricSourceType;
import android.app.trust.TrustManager;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
@@ -82,8 +83,8 @@
import com.android.systemui.SystemUIFactory;
import com.android.systemui.UiOffloadThread;
import com.android.systemui.classifier.FalsingManager;
-import com.android.systemui.statusbar.phone.FingerprintUnlockController;
import com.android.systemui.statusbar.phone.NotificationPanelView;
+import com.android.systemui.statusbar.phone.BiometricUnlockController;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
@@ -345,6 +346,11 @@
*/
private WorkLockActivityController mWorkLockController;
+ /**
+ * @see #setPulsing(boolean)
+ */
+ private boolean mPulsing;
+
private boolean mLockLater;
private boolean mWakeAndUnlocking;
@@ -522,18 +528,18 @@
}
@Override
- public void onFingerprintAuthFailed() {
+ public void onBiometricAuthFailed(BiometricSourceType biometricSourceType) {
final int currentUser = KeyguardUpdateMonitor.getCurrentUser();
if (mLockPatternUtils.isSecure(currentUser)) {
- mLockPatternUtils.getDevicePolicyManager().reportFailedFingerprintAttempt(
+ mLockPatternUtils.getDevicePolicyManager().reportFailedBiometricAttempt(
currentUser);
}
}
@Override
- public void onFingerprintAuthenticated(int userId) {
+ public void onBiometricAuthenticated(int userId, BiometricSourceType biometricSourceType) {
if (mLockPatternUtils.isSecure(userId)) {
- mLockPatternUtils.getDevicePolicyManager().reportSuccessfulFingerprintAttempt(
+ mLockPatternUtils.getDevicePolicyManager().reportSuccessfulBiometricAttempt(
userId);
}
}
@@ -1645,7 +1651,7 @@
}
mUpdateMonitor.clearFailedUnlockAttempts();
- mUpdateMonitor.clearFingerprintRecognized();
+ mUpdateMonitor.clearBiometricRecognized();
if (mGoingToSleep) {
Log.i(TAG, "Device is going to sleep, aborting keyguardDone");
@@ -1800,10 +1806,12 @@
int flags = 0;
if (mStatusBarKeyguardViewManager.shouldDisableWindowAnimationsForUnlock()
- || mWakeAndUnlocking) {
- flags |= WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_NO_WINDOW_ANIMATIONS;
+ || (mWakeAndUnlocking && !mPulsing)) {
+ flags |= WindowManagerPolicyConstants
+ .KEYGUARD_GOING_AWAY_FLAG_NO_WINDOW_ANIMATIONS;
}
- if (mStatusBarKeyguardViewManager.isGoingToNotificationShade()) {
+ if (mStatusBarKeyguardViewManager.isGoingToNotificationShade()
+ || (mWakeAndUnlocking && mPulsing)) {
flags |= WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_TO_SHADE;
}
if (mStatusBarKeyguardViewManager.isUnlockWithWallpaper()) {
@@ -2051,9 +2059,9 @@
public StatusBarKeyguardViewManager registerStatusBar(StatusBar statusBar,
ViewGroup container, NotificationPanelView panelView,
- FingerprintUnlockController fingerprintUnlockController) {
+ BiometricUnlockController biometricUnlockController) {
mStatusBarKeyguardViewManager.registerStatusBar(statusBar, container, panelView,
- fingerprintUnlockController, mDismissCallbackRegistry);
+ biometricUnlockController, mDismissCallbackRegistry);
return mStatusBarKeyguardViewManager;
}
@@ -2102,10 +2110,20 @@
pw.print(" mDrawnCallback: "); pw.println(mDrawnCallback);
}
+ /**
+ * @param aodShowing true when AOD - or ambient mode - is showing.
+ */
public void setAodShowing(boolean aodShowing) {
setShowingLocked(mShowing, aodShowing);
}
+ /**
+ * @param pulsing true when device temporarily wakes up to display an incoming notification.
+ */
+ public void setPulsing(boolean pulsing) {
+ mPulsing = pulsing;
+ }
+
private static class StartKeyguardExitAnimParams {
long startTime;
diff --git a/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java b/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java
index f5f06db..89786ee 100644
--- a/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java
+++ b/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java
@@ -86,11 +86,12 @@
// synchronized on mCompletionHandlingLock due to the Object.wait() in startSound(cmd)
mLooper = Looper.myLooper();
if (DEBUG) Log.d(mTag, "in run: new looper " + mLooper);
+ MediaPlayer player = null;
synchronized(this) {
AudioManager audioManager =
(AudioManager) mCmd.context.getSystemService(Context.AUDIO_SERVICE);
try {
- MediaPlayer player = new MediaPlayer();
+ player = new MediaPlayer();
if (mCmd.attributes == null) {
mCmd.attributes = new AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_NOTIFICATION)
@@ -137,26 +138,26 @@
Log.e(mTag, "Exception while sleeping to sync notification playback"
+ " with ducking", e);
}
- try {
- player.start();
- if (DEBUG) { Log.d(mTag, "player.start"); }
- } catch (Exception e) {
+ player.start();
+ if (DEBUG) { Log.d(mTag, "player.start"); }
+ } catch (Exception e) {
+ if (player != null) {
player.release();
player = null;
- // playing the notification didn't work, revert the focus request
- abandonAudioFocusAfterError();
}
- if (mPlayer != null) {
- if (DEBUG) { Log.d(mTag, "mPlayer.release"); }
- mPlayer.release();
- }
- mPlayer = player;
- }
- catch (Exception e) {
Log.w(mTag, "error loading sound for " + mCmd.uri, e);
// playing the notification didn't work, revert the focus request
abandonAudioFocusAfterError();
}
+ final MediaPlayer mp;
+ synchronized (mPlayerLock) {
+ mp = mPlayer;
+ mPlayer = player;
+ }
+ if (mp != null) {
+ if (DEBUG) { Log.d(mTag, "mPlayer.release"); }
+ mp.release();
+ }
this.notify();
}
Looper.loop();
@@ -230,14 +231,20 @@
break;
case STOP:
if (DEBUG) Log.d(mTag, "STOP");
- if (mPlayer != null) {
+ final MediaPlayer mp;
+ synchronized (mPlayerLock) {
+ mp = mPlayer;
+ mPlayer = null;
+ }
+ if (mp != null) {
long delay = SystemClock.uptimeMillis() - cmd.requestTime;
if (delay > 1000) {
Log.w(mTag, "Notification stop delayed by " + delay + "msecs");
}
- mPlayer.stop();
- mPlayer.release();
- mPlayer = null;
+ try {
+ mp.stop();
+ } catch (Exception e) { }
+ mp.release();
synchronized(mQueueAudioFocusLock) {
if (mAudioManagerWithAudioFocus != null) {
if (DEBUG) { Log.d(mTag, "in STOP: abandonning AudioFocus"); }
@@ -297,6 +304,14 @@
}
}
}
+ synchronized (mPlayerLock) {
+ if (mp == mPlayer) {
+ mPlayer = null;
+ }
+ }
+ if (mp != null) {
+ mp.release();
+ }
}
public boolean onError(MediaPlayer mp, int what, int extra) {
@@ -311,6 +326,8 @@
@GuardedBy("mCmdQueue")
private CmdThread mThread;
+ private final Object mPlayerLock = new Object();
+ @GuardedBy("mPlayerLock")
private MediaPlayer mPlayer;
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
index 615b29f..2dc531a 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
@@ -229,7 +229,9 @@
mSettingsButton = findViewById(R.id.settings);
mSettingsButton.setAlpha(0);
mSettingsButton.setOnClickListener((v) -> {
- showSettings();
+ if (v.getAlpha() != 0) {
+ showSettings();
+ }
});
mDismissButton = findViewById(R.id.dismiss);
mDismissButton.setAlpha(0);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
old mode 100644
new mode 100755
index d9f923f..020c550
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
@@ -262,12 +262,13 @@
entry = Pair.<String, String>create(packageAndClassName[0], null);
break;
case 2:
- if (packageAndClassName[1] != null
- && packageAndClassName[1].startsWith(".")) {
- entry = Pair.<String, String>create(
- packageAndClassName[0],
- packageAndClassName[0] + packageAndClassName[1]);
+ if (packageAndClassName[1] != null) {
+ entry = Pair.<String, String>create(packageAndClassName[0],
+ packageAndClassName[1].startsWith(".")
+ ? packageAndClassName[0] + packageAndClassName[1]
+ : packageAndClassName[1]);
}
+ break;
}
if (entry != null) {
sSettingsPackageAndClassNamePairList.add(entry);
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
index 6801e69..9a648d1 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
@@ -77,6 +77,7 @@
private int mPlugType = 0;
private int mInvalidCharger = 0;
private EnhancedEstimates mEnhancedEstimates;
+ private Estimate mLastEstimate;
private boolean mLowWarningShownThisChargeCycle;
private boolean mSevereWarningShownThisChargeCycle;
@@ -247,7 +248,8 @@
// Show the correct version of low battery warning if needed
ThreadUtils.postOnBackgroundThread(() -> {
- maybeShowBatteryWarning(plugged, oldPlugged, oldBucket, bucket);
+ maybeShowBatteryWarning(
+ oldBatteryLevel, plugged, oldPlugged, oldBucket, bucket);
});
} else if (Intent.ACTION_SCREEN_OFF.equals(action)) {
@@ -262,14 +264,18 @@
}
}
- protected void maybeShowBatteryWarning(boolean plugged, boolean oldPlugged, int oldBucket,
- int bucket) {
+ protected void maybeShowBatteryWarning(int oldBatteryLevel, boolean plugged, boolean oldPlugged,
+ int oldBucket, int bucket) {
boolean isPowerSaver = mPowerManager.isPowerSaveMode();
// only play SFX when the dialog comes up or the bucket changes
final boolean playSound = bucket != oldBucket || oldPlugged;
final boolean hybridEnabled = mEnhancedEstimates.isHybridNotificationEnabled();
if (hybridEnabled) {
- final Estimate estimate = mEnhancedEstimates.getEstimate();
+ Estimate estimate = mLastEstimate;
+ if (estimate == null || mBatteryLevel != oldBatteryLevel) {
+ estimate = mEnhancedEstimates.getEstimate();
+ mLastEstimate = estimate;
+ }
// Turbo is not always booted once SysUI is running so we have ot make sure we actually
// get data back
if (estimate != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
index 100751c..f13f489 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
@@ -189,8 +189,9 @@
// marquee. This will ensure that accessibility doesn't announce the TYPE_VIEW_SELECTED
// event on any of the children.
setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
+ int currentItem = isLayoutRtl() ? mPages.size() - 1 - getCurrentItem() : getCurrentItem();
for (int i = 0; i < mPages.size(); i++) {
- mPages.get(i).setSelected(i == getCurrentItem() ? selected : false);
+ mPages.get(i).setSelected(i == currentItem ? selected : false);
}
setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_AUTO);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index bd89ad1..7cb22a3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -19,7 +19,6 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.app.Fragment;
-import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Bundle;
@@ -45,7 +44,7 @@
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer;
import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
-import com.android.systemui.statusbar.stack.StackStateAnimator;
+import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
public class QSFragment extends Fragment implements QS, CommandQueue.Callbacks {
private static final String TAG = "QS";
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
index 6dbe119..1c50f79 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
@@ -192,29 +192,21 @@
mTileDimensionSize = mContext.getResources().getDimensionPixelSize(
R.dimen.qs_quick_tile_size);
-
- setGravity(Gravity.CENTER);
- setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
+ updateLayoutParams();
}
@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
+ updateLayoutParams();
+ }
+ private void updateLayoutParams() {
setGravity(Gravity.CENTER);
- LayoutParams staticSpaceLayoutParams = generateSpaceLayoutParams(
- mContext.getResources().getDimensionPixelSize(
- R.dimen.qs_quick_tile_space_width));
-
- // Update space params since they fill any open space in portrait orientation and have
- // a static width in landscape orientation.
- final int childViewCount = getChildCount();
- for (int i = 0; i < childViewCount; i++) {
- View childView = getChildAt(i);
- if (childView instanceof Space) {
- childView.setLayoutParams(staticSpaceLayoutParams);
- }
- }
+ int width = getResources().getDimensionPixelSize(R.dimen.qs_quick_layout_width);
+ LayoutParams lp = new LayoutParams(width, LayoutParams.MATCH_PARENT);
+ lp.gravity = Gravity.CENTER_HORIZONTAL;
+ setLayoutParams(lp);
}
/**
@@ -222,11 +214,9 @@
* then we're going to have the space expand to take up as much space as possible. If the
* width is non-zero, we want the inter-tile spacers to be fixed.
*/
- private LayoutParams generateSpaceLayoutParams(int spaceWidth) {
- LayoutParams lp = new LayoutParams(spaceWidth, mTileDimensionSize);
- if (spaceWidth == 0) {
- lp.weight = 1;
- }
+ private LayoutParams generateSpaceLayoutParams() {
+ LayoutParams lp = new LayoutParams(0, mTileDimensionSize);
+ lp.weight = 1;
lp.gravity = Gravity.CENTER;
return lp;
}
@@ -243,13 +233,7 @@
@Override
public void addTile(TileRecord tile) {
if (getChildCount() != 0) {
- // Add a spacer between tiles. We want static-width spaces if we're in landscape to
- // keep the tiles close. For portrait, we stick with spaces that fill up any
- // available space.
- LayoutParams spaceLayoutParams = generateSpaceLayoutParams(
- mContext.getResources().getDimensionPixelSize(
- R.dimen.qs_quick_tile_space_width));
- addView(new Space(mContext), getChildCount(), spaceLayoutParams);
+ addView(new Space(mContext), getChildCount(), generateSpaceLayoutParams());
}
addView(tile.tileView, getChildCount(), generateTileLayoutParams());
@@ -305,6 +289,10 @@
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ if (hideOverflowingChildren(widthMeasureSpec)) {
+ return; // Rely on visibility change to trigger remeasure.
+ }
+
if (mRecords != null && mRecords.size() > 0) {
View previousView = this;
for (TileRecord record : mRecords) {
@@ -317,5 +305,32 @@
R.id.expand_indicator);
}
}
+
+ /**
+ * Hide child views that would otherwise be clipped.
+ * @return {@code true} if any child visibilities have changed.
+ */
+ private boolean hideOverflowingChildren(int widthMeasureSpec) {
+ if (getChildCount() == 0) {
+ return false;
+ }
+ boolean childVisibilityChanged = false;
+ int widthRemaining = MeasureSpec.getSize(widthMeasureSpec)
+ - getChildAt(0).getMeasuredWidth() - getPaddingStart() - getPaddingEnd();
+ for (int i = 2; i < getChildCount(); i += 2) {
+ View tileChild = getChildAt(i);
+ LayoutParams lp = (LayoutParams) tileChild.getLayoutParams();
+ // All Space views have 0 width; only tiles contribute to the total width.
+ widthRemaining = widthRemaining
+ - tileChild.getMeasuredWidth() - lp.getMarginEnd() - lp.getMarginStart();
+ int newVisibility = widthRemaining < 0 ? View.GONE : View.VISIBLE;
+ if (tileChild.getVisibility() != newVisibility) {
+ tileChild.setVisibility(newVisibility);
+ getChildAt(i - 1).setVisibility(newVisibility); // Hide spacer as well.
+ childVisibilityChanged = true;
+ }
+ }
+ return childVisibilityChanged;
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index a218868..ff5ac76 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -33,6 +33,7 @@
import android.os.Handler;
import android.provider.AlarmClock;
import android.service.notification.ZenModeConfig;
+import android.widget.FrameLayout;
import androidx.annotation.VisibleForTesting;
import android.text.format.DateUtils;
import android.util.AttributeSet;
@@ -270,8 +271,22 @@
updateResources();
}
+ /**
+ * The height of QQS should always be the status bar height + 128dp. This is normally easy, but
+ * when there is a notch involved the status bar can remain a fixed pixel size.
+ */
+ private void updateMinimumHeight() {
+ int sbHeight = mContext.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.status_bar_height);
+ int qqsHeight = mContext.getResources().getDimensionPixelSize(
+ R.dimen.qs_quick_header_panel_height);
+
+ setMinimumHeight(sbHeight + qqsHeight);
+ }
+
private void updateResources() {
Resources resources = mContext.getResources();
+ updateMinimumHeight();
// Update height for a few views, especially due to landscape mode restricting space.
mHeaderTextContainerView.getLayoutParams().height =
@@ -282,10 +297,17 @@
com.android.internal.R.dimen.quick_qs_offset_height);
mSystemIconsView.setLayoutParams(mSystemIconsView.getLayoutParams());
- getLayoutParams().height = resources.getDimensionPixelSize(mQsDisabled
- ? com.android.internal.R.dimen.quick_qs_offset_height
- : com.android.internal.R.dimen.quick_qs_total_height);
- setLayoutParams(getLayoutParams());
+ FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams();
+ if (mQsDisabled) {
+ lp.height = resources.getDimensionPixelSize(
+ com.android.internal.R.dimen.quick_qs_offset_height);
+ } else {
+ lp.height = Math.max(getMinimumHeight(),
+ resources.getDimensionPixelSize(
+ com.android.internal.R.dimen.quick_qs_total_height));
+ }
+
+ setLayoutParams(lp);
updateStatusIconAlphaAnimator();
updateHeaderTextContainerAlphaAnimator();
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 e0a9148..c41f087 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java
@@ -26,7 +26,6 @@
import android.service.quicksettings.Tile;
import androidx.annotation.StringRes;
import android.text.TextUtils;
-import android.text.format.DateFormat;
import android.util.Log;
import android.widget.Switch;
@@ -37,8 +36,11 @@
import com.android.systemui.plugins.qs.QSTile.BooleanState;
import com.android.systemui.qs.tileimpl.QSTileImpl;
+import java.text.DateFormat;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
+import java.util.Calendar;
+import java.util.TimeZone;
public class NightDisplayTile extends QSTileImpl<BooleanState>
implements ColorDisplayController.Callback {
@@ -144,13 +146,17 @@
toggleTimeStringRes = R.string.quick_settings_night_secondary_label_on_at;
}
- // Choose between just showing the hour or also showing the minutes (based on the
- // user-selected toggle time). This helps reduce how much space the label takes.
- toggleTimeFormat = DateTimeFormatter.ofPattern(
- DateFormat.is24HourFormat(mContext) ? PATTERN_HOUR_NINUTE_24 :
- toggleTime.getMinute() == 0 ? PATTERN_HOUR : PATTERN_HOUR_MINUTE);
-
- return mContext.getString(toggleTimeStringRes, toggleTime.format(toggleTimeFormat));
+ // TODO(b/111085930): Move this calendar snippet to a common code location that
+ // settings lib can also access.
+ final Calendar c = Calendar.getInstance();
+ DateFormat nightTileFormat = android.text.format.DateFormat.getTimeFormat(mContext);
+ nightTileFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+ c.setTimeZone(nightTileFormat.getTimeZone());
+ c.set(Calendar.HOUR_OF_DAY, toggleTime.getHour());
+ c.set(Calendar.MINUTE, toggleTime.getMinute());
+ c.set(Calendar.SECOND, 0);
+ c.set(Calendar.MILLISECOND, 0);
+ return mContext.getString(toggleTimeStringRes, nightTileFormat.format(c.getTime()));
default:
// No secondary label when auto mode is disabled.
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
index 7e4acc2..63a65d0 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
@@ -48,6 +48,7 @@
import com.android.systemui.Dependency;
import com.android.systemui.OverviewProxyService;
+import com.android.systemui.SysUiServiceProvider;
import com.google.android.collect.Lists;
import com.android.internal.logging.MetricsLogger;
@@ -1095,7 +1096,7 @@
}
private StatusBar getStatusBar() {
- return ((SystemUIApplication) mContext).getComponent(StatusBar.class);
+ return SysUiServiceProvider.getComponent(mContext, StatusBar.class);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsSystemUserService.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsSystemUserService.java
index 2c1158d..b4212d3 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsSystemUserService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsSystemUserService.java
@@ -21,6 +21,7 @@
import android.os.IBinder;
import android.util.Log;
+import com.android.systemui.SysUiServiceProvider;
import com.android.systemui.SystemUIApplication;
/**
@@ -40,8 +41,7 @@
@Override
public IBinder onBind(Intent intent) {
- SystemUIApplication app = (SystemUIApplication) getApplication();
- Recents recents = app.getComponent(Recents.class);
+ Recents recents = SysUiServiceProvider.getComponent(this, Recents.class);
if (DEBUG) {
Log.d(TAG, "onBind: " + recents);
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index 8a0d7e3..5eaee54 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -16,9 +16,11 @@
package com.android.systemui.screenshot;
+import static android.content.Context.NOTIFICATION_SERVICE;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
-
-import static com.android.systemui.screenshot.GlobalScreenshot.SHARING_INTENT;
+import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_ACTION_INTENT;
+import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_CANCEL_NOTIFICATION;
+import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_DISALLOW_ENTER_PIP;
import static com.android.systemui.statusbar.phone.StatusBar.SYSTEM_DIALOG_REASON_SCREENSHOT;
import android.animation.Animator;
@@ -26,7 +28,6 @@
import android.animation.AnimatorSet;
import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
-import android.app.ActivityManager;
import android.app.ActivityOptions;
import android.app.Notification;
import android.app.Notification.BigPictureStyle;
@@ -56,10 +57,11 @@
import android.os.Environment;
import android.os.PowerManager;
import android.os.Process;
-import android.os.RemoteException;
import android.os.UserHandle;
import android.provider.MediaStore;
+import android.text.TextUtils;
import android.util.DisplayMetrics;
+import android.util.Log;
import android.util.Slog;
import android.view.Display;
import android.view.LayoutInflater;
@@ -72,12 +74,13 @@
import android.view.animation.Interpolator;
import android.widget.ImageView;
import android.widget.Toast;
-
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.systemui.R;
+import com.android.systemui.SysUiServiceProvider;
import com.android.systemui.SystemUI;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.util.NotificationChannels;
-
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
@@ -246,7 +249,10 @@
try {
// Create screenshot directory if it doesn't exist
- mScreenshotDir.mkdirs();
+ boolean madeDirs = mScreenshotDir.mkdirs();
+ if (madeDirs == false) {
+ Log.e(TAG, "Couldn't create screenshot directory: " + mScreenshotDir);
+ }
// media provider uses seconds for DATE_MODIFIED and DATE_ADDED, but milliseconds
// for DATE_TAKEN
@@ -273,7 +279,12 @@
values.put(MediaStore.Images.ImageColumns.SIZE, new File(mImageFilePath).length());
Uri uri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
- // Create a share intent
+ // Note: Both the share and edit actions are proxied through ActionProxyReceiver in
+ // order to do some common work like dismissing the keyguard and sending
+ // closeSystemWindows
+
+ // Create a share intent, this will always go through the chooser activity first which
+ // should not trigger auto-enter PiP
String subjectDate = DateFormat.getDateTimeInstance().format(new Date(mImageTime));
String subject = String.format(SCREENSHOT_SHARE_SUBJECT_TEMPLATE, subjectDate);
Intent sharingIntent = new Intent(Intent.ACTION_SEND);
@@ -282,37 +293,47 @@
sharingIntent.putExtra(Intent.EXTRA_SUBJECT, subject);
sharingIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
- // 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.ScreenshotActionReceiver.class)
- .putExtra(SHARING_INTENT, sharingIntent),
- PendingIntent.FLAG_CANCEL_CURRENT);
+ PendingIntent chooserAction = PendingIntent.getBroadcast(context, 0,
+ new Intent(context, GlobalScreenshot.TargetChosenReceiver.class),
+ PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT);
+ Intent sharingChooserIntent = Intent.createChooser(sharingIntent, null,
+ chooserAction.getIntentSender())
+ .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
+
+ // Create a share action for the notification
+ PendingIntent shareAction = PendingIntent.getBroadcastAsUser(context, 0,
+ new Intent(context, GlobalScreenshot.ActionProxyReceiver.class)
+ .putExtra(EXTRA_ACTION_INTENT, sharingChooserIntent)
+ .putExtra(EXTRA_DISALLOW_ENTER_PIP, true),
+ PendingIntent.FLAG_CANCEL_CURRENT, UserHandle.SYSTEM);
Notification.Action.Builder shareActionBuilder = new Notification.Action.Builder(
R.drawable.ic_screenshot_share,
r.getString(com.android.internal.R.string.share), shareAction);
mNotificationBuilder.addAction(shareActionBuilder.build());
+ // Create an edit intent, if a specific package is provided as the editor, then launch
+ // that directly
+ String editorPackage = context.getString(R.string.config_screenshotEditor);
Intent editIntent = new Intent(Intent.ACTION_EDIT);
+ if (!TextUtils.isEmpty(editorPackage)) {
+ editIntent.setComponent(ComponentName.unflattenFromString(editorPackage));
+ }
editIntent.setType("image/png");
editIntent.setData(uri);
editIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
editIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
- // 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);
+ // Create a edit action
+ PendingIntent editAction = PendingIntent.getBroadcastAsUser(context, 1,
+ new Intent(context, GlobalScreenshot.ActionProxyReceiver.class)
+ .putExtra(EXTRA_ACTION_INTENT, editIntent)
+ .putExtra(EXTRA_CANCEL_NOTIFICATION, editIntent.getComponent() != null),
+ PendingIntent.FLAG_CANCEL_CURRENT, UserHandle.SYSTEM);
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());
-
// Create a delete action for the notification
PendingIntent deleteAction = PendingIntent.getBroadcast(context, 0,
new Intent(context, GlobalScreenshot.DeleteScreenshotReceiver.class)
@@ -425,7 +446,9 @@
class GlobalScreenshot {
static final String SCREENSHOT_URI_ID = "android:screenshot_uri_id";
- static final String SHARING_INTENT = "android:screenshot_sharing_intent";
+ static final String EXTRA_ACTION_INTENT = "android:screenshot_action_intent";
+ static final String EXTRA_CANCEL_NOTIFICATION = "android:screenshot_cancel_notification";
+ static final String EXTRA_DISALLOW_ENTER_PIP = "android:screenshot_disallow_enter_pip";
private static final int SCREENSHOT_FLASH_TO_PEAK_DURATION = 130;
private static final int SCREENSHOT_DROP_IN_DURATION = 430;
@@ -448,7 +471,6 @@
private NotificationManager mNotificationManager;
private Display mDisplay;
private DisplayMetrics mDisplayMetrics;
- private Matrix mDisplayMatrix;
private Bitmap mScreenBitmap;
private View mScreenshotLayout;
@@ -478,7 +500,6 @@
context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
// Inflate the screenshot layout
- mDisplayMatrix = new Matrix();
mScreenshotLayout = layoutInflater.inflate(R.layout.global_screenshot, null);
mBackgroundView = (ImageView) mScreenshotLayout.findViewById(R.id.global_screenshot_background);
mScreenshotView = (ImageView) mScreenshotLayout.findViewById(R.id.global_screenshot);
@@ -508,7 +529,7 @@
mWindowLayoutParams.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
mNotificationManager =
- (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
+ (NotificationManager) context.getSystemService(NOTIFICATION_SERVICE);
mDisplay = mWindowManager.getDefaultDisplay();
mDisplayMetrics = new DisplayMetrics();
mDisplay.getRealMetrics(mDisplayMetrics);
@@ -558,21 +579,6 @@
}
/**
- * @return the current display rotation in degrees
- */
- private float getDegreesForRotation(int value) {
- switch (value) {
- case Surface.ROTATION_90:
- return 360f - 90f;
- case Surface.ROTATION_180:
- return 360f - 180f;
- case Surface.ROTATION_270:
- return 360f - 270f;
- }
- return 0f;
- }
-
- /**
* Takes a screenshot of the current display and shows an animation.
*/
private void takeScreenshot(Runnable finisher, boolean statusBarVisible, boolean navBarVisible,
@@ -887,52 +893,39 @@
}
/**
- * Receiver to proxy the share or edit intent.
+ * Receiver to proxy the share or edit intent, used to clean up the notification and send
+ * appropriate signals to the system (ie. to dismiss the keyguard if necessary).
*/
- public static class ScreenshotActionReceiver extends BroadcastReceiver {
+ public static class ActionProxyReceiver extends BroadcastReceiver {
@Override
- public void onReceive(Context context, Intent intent) {
- try {
- ActivityManager.getService().closeSystemDialogs(SYSTEM_DIALOG_REASON_SCREENSHOT);
- } catch (RemoteException e) {
- }
+ public void onReceive(Context context, final Intent intent) {
+ Runnable startActivityRunnable = () -> {
+ ActivityManagerWrapper.getInstance().closeSystemWindows(
+ SYSTEM_DIALOG_REASON_SCREENSHOT);
- Intent actionIntent = intent.getParcelableExtra(SHARING_INTENT);
-
- // If this is an edit & default editor exists, route straight there.
- String editorPackage = context.getResources().getString(R.string.config_screenshotEditor);
- if (actionIntent.getAction() == Intent.ACTION_EDIT &&
- editorPackage != null && editorPackage.length() > 0) {
- actionIntent.setComponent(ComponentName.unflattenFromString(editorPackage));
- final NotificationManager nm =
- (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
- nm.cancel(SystemMessage.NOTE_GLOBAL_SCREENSHOT);
- } else {
- PendingIntent chooseAction = PendingIntent.getBroadcast(context, 0,
- new Intent(context, GlobalScreenshot.TargetChosenReceiver.class),
- PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT);
- actionIntent = Intent.createChooser(actionIntent, null,
- chooseAction.getIntentSender())
- .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
- }
-
- ActivityOptions opts = ActivityOptions.makeBasic();
- opts.setDisallowEnterPictureInPictureWhileLaunching(true);
-
- context.startActivityAsUser(actionIntent, opts.toBundle(), UserHandle.CURRENT);
+ Intent actionIntent = intent.getParcelableExtra(EXTRA_ACTION_INTENT);
+ if (intent.getBooleanExtra(EXTRA_CANCEL_NOTIFICATION, false)) {
+ cancelScreenshotNotification(context);
+ }
+ ActivityOptions opts = ActivityOptions.makeBasic();
+ opts.setDisallowEnterPictureInPictureWhileLaunching(
+ intent.getBooleanExtra(EXTRA_DISALLOW_ENTER_PIP, false));
+ context.startActivityAsUser(actionIntent, opts.toBundle(), UserHandle.CURRENT);
+ };
+ StatusBar statusBar = SysUiServiceProvider.getComponent(context, StatusBar.class);
+ statusBar.executeRunnableDismissingKeyguard(startActivityRunnable, null,
+ true /* dismissShade */, true /* afterKeyguardGone */, true /* deferred */);
}
}
/**
- * Removes the notification for a screenshot after a share or edit target is chosen.
+ * Removes the notification for a screenshot after a share target is chosen.
*/
public static class TargetChosenReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
- // Clear the notification
- final NotificationManager nm =
- (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
- nm.cancel(SystemMessage.NOTE_GLOBAL_SCREENSHOT);
+ // Clear the notification only after the user has chosen a share action
+ cancelScreenshotNotification(context);
}
}
@@ -946,14 +939,18 @@
return;
}
- // Clear the notification
- final NotificationManager nm =
- (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
- final Uri uri = Uri.parse(intent.getStringExtra(SCREENSHOT_URI_ID));
- nm.cancel(SystemMessage.NOTE_GLOBAL_SCREENSHOT);
+ // Clear the notification when the image is deleted
+ cancelScreenshotNotification(context);
// And delete the image from the media store
+ final Uri uri = Uri.parse(intent.getStringExtra(SCREENSHOT_URI_ID));
new DeleteImageInBackgroundTask(context).execute(uri);
}
}
+
+ private static void cancelScreenshotNotification(Context context) {
+ final NotificationManager nm =
+ (NotificationManager) context.getSystemService(NOTIFICATION_SERVICE);
+ nm.cancel(SystemMessage.NOTE_GLOBAL_SCREENSHOT);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AlphaImageView.java b/packages/SystemUI/src/com/android/systemui/statusbar/AlphaImageView.java
deleted file mode 100644
index 06dc4e6..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/AlphaImageView.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * 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
- */
-
-package com.android.systemui.statusbar;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.widget.ImageView;
-
-/**
- * An ImageView which does not have overlapping renderings commands and therefore does not need a
- * layer when alpha is changed.
- */
-public class AlphaImageView extends ImageView {
- public AlphaImageView(Context context) {
- super(context);
- }
-
- public AlphaImageView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- public AlphaImageView(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- }
-
- public AlphaImageView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
- super(context, attrs, defStyleAttr, defStyleRes);
- }
-
- @Override
- public boolean hasOverlappingRendering() {
- return false;
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java
index 4728a1d..6c3e504 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java
@@ -19,7 +19,7 @@
import android.view.View;
import com.android.systemui.Interpolators;
-import com.android.systemui.statusbar.stack.StackStateAnimator;
+import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
/**
* A helper to fade views in and out.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java
index 1bbf848..30d9ef7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java
@@ -30,7 +30,7 @@
import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.classifier.FalsingManager;
-import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.notification.row.ExpandableView;
/**
* A utility class to enable the downward swipe on the lockscreen to go to the full shade and expand
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/EmptyShadeView.java b/packages/SystemUI/src/com/android/systemui/statusbar/EmptyShadeView.java
index 4da1558..745b2f9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/EmptyShadeView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/EmptyShadeView.java
@@ -25,8 +25,9 @@
import android.widget.TextView;
import com.android.systemui.R;
-import com.android.systemui.statusbar.stack.ExpandableViewState;
-import com.android.systemui.statusbar.stack.StackScrollState;
+import com.android.systemui.statusbar.notification.row.StackScrollerDecorView;
+import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
+import com.android.systemui.statusbar.notification.stack.StackScrollState;
public class EmptyShadeView extends StackScrollerDecorView {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java
index ac289d7..b39a96d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java
@@ -24,7 +24,6 @@
import android.os.Bundle;
import android.os.Parcelable;
import android.util.AttributeSet;
-import android.view.Display;
import android.view.DisplayCutout;
import android.view.View;
import android.widget.TextView;
@@ -32,6 +31,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.keyguard.AlphaOptimizedLinearLayout;
import com.android.systemui.R;
+import com.android.systemui.statusbar.notification.NotificationData;
import com.android.systemui.statusbar.policy.DarkIconDispatcher;
import java.util.List;
@@ -214,7 +214,7 @@
}
/** In order to do UI alignment, this view will be notified by
- * {@link com.android.systemui.statusbar.stack.NotificationStackScrollLayout}.
+ * {@link com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout}.
* After scroller laid out, the scroller will tell this view about scroller's getX()
* @param translationX how to translate the horizontal position
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 35e1511..551e8a9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -19,6 +19,7 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.app.admin.DevicePolicyManager;
+import android.hardware.biometrics.BiometricSourceType;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -26,6 +27,7 @@
import android.content.res.Resources;
import android.content.res.ColorStateList;
import android.graphics.Color;
+import android.hardware.face.FaceManager;
import android.hardware.fingerprint.FingerprintManager;
import android.os.BatteryManager;
import android.os.BatteryStats;
@@ -70,8 +72,8 @@
private static final boolean DEBUG_CHARGING_SPEED = false;
private static final int MSG_HIDE_TRANSIENT = 1;
- private static final int MSG_CLEAR_FP_MSG = 2;
- private static final long TRANSIENT_FP_ERROR_TIMEOUT = 1300;
+ private static final int MSG_CLEAR_BIOMETRIC_MSG = 2;
+ private static final long TRANSIENT_BIOMETRIC_ERROR_TIMEOUT = 1300;
private final Context mContext;
private ViewGroup mIndicationArea;
@@ -362,22 +364,41 @@
R.integer.wired_charging_keyguard_text_animation_duration_up);
int animateDownDuration = mContext.getResources().getInteger(
R.integer.wired_charging_keyguard_text_animation_duration_down);
+ textView.animate().cancel();
+ float translation = textView.getTranslationY();
textView.animate()
.translationYBy(yTranslation)
.setInterpolator(Interpolators.LINEAR)
.setDuration(animateUpDuration)
.setListener(new AnimatorListenerAdapter() {
+ private boolean mCancelled;
+
@Override
public void onAnimationStart(Animator animation) {
textView.switchIndication(indication);
}
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ textView.setTranslationY(translation);
+ mCancelled = true;
+ }
+
@Override
public void onAnimationEnd(Animator animation) {
+ if (mCancelled) {
+ return;
+ }
textView.animate()
.setDuration(animateDownDuration)
.setInterpolator(Interpolators.BOUNCE)
- .translationYBy(-1 * yTranslation)
- .setListener(null);
+ .translationY(translation)
+ .setListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ textView.setTranslationY(translation);
+ }
+ });
}
});
}
@@ -461,7 +482,7 @@
public void handleMessage(Message msg) {
if (msg.what == MSG_HIDE_TRANSIENT) {
hideTransientIndication();
- } else if (msg.what == MSG_CLEAR_FP_MSG) {
+ } else if (msg.what == MSG_CLEAR_BIOMETRIC_MSG) {
mLockIcon.setTransientFpError(false);
}
}
@@ -526,9 +547,10 @@
}
@Override
- public void onFingerprintHelp(int msgId, String helpString) {
+ public void onBiometricHelp(int msgId, String helpString,
+ BiometricSourceType biometricSourceType) {
KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
- if (!updateMonitor.isUnlockingWithFingerprintAllowed()) {
+ if (!updateMonitor.isUnlockingWithBiometricAllowed()) {
return;
}
ColorStateList errorColorState = Utils.getColorError(mContext);
@@ -538,10 +560,10 @@
} else if (updateMonitor.isScreenOn()) {
mLockIcon.setTransientFpError(true);
showTransientIndication(helpString, errorColorState);
- hideTransientIndicationDelayed(TRANSIENT_FP_ERROR_TIMEOUT);
- mHandler.removeMessages(MSG_CLEAR_FP_MSG);
- mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_CLEAR_FP_MSG),
- TRANSIENT_FP_ERROR_TIMEOUT);
+ hideTransientIndicationDelayed(TRANSIENT_BIOMETRIC_ERROR_TIMEOUT);
+ mHandler.removeMessages(MSG_CLEAR_BIOMETRIC_MSG);
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_CLEAR_BIOMETRIC_MSG),
+ TRANSIENT_BIOMETRIC_ERROR_TIMEOUT);
}
// Help messages indicate that there was actually a try since the last error, so those
// are not two successive error messages anymore.
@@ -549,16 +571,15 @@
}
@Override
- public void onFingerprintError(int msgId, String errString) {
+ public void onBiometricError(int msgId, String errString,
+ BiometricSourceType biometricSourceType) {
KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
- if ((!updateMonitor.isUnlockingWithFingerprintAllowed()
- && msgId != FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT)
- || msgId == FingerprintManager.FINGERPRINT_ERROR_CANCELED) {
+ if (shouldSuppressBiometricError(msgId, biometricSourceType, updateMonitor)) {
return;
}
ColorStateList errorColorState = Utils.getColorError(mContext);
if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
- // When swiping up right after receiving a fingerprint error, the bouncer calls
+ // When swiping up right after receiving a biometric error, the bouncer calls
// authenticate leading to the same message being shown again on the bouncer.
// We want to avoid this, as it may confuse the user when the message is too
// generic.
@@ -576,6 +597,28 @@
mLastSuccessiveErrorMessage = msgId;
}
+ private boolean shouldSuppressBiometricError(int msgId,
+ BiometricSourceType biometricSourceType, KeyguardUpdateMonitor updateMonitor) {
+ if (biometricSourceType == BiometricSourceType.FINGERPRINT)
+ return shouldSuppressFingerprintError(msgId, updateMonitor);
+ if (biometricSourceType == BiometricSourceType.FACE)
+ return shouldSuppressFaceError(msgId, updateMonitor);
+ return false;
+ }
+
+ private boolean shouldSuppressFingerprintError(int msgId,
+ KeyguardUpdateMonitor updateMonitor) {
+ return ((!updateMonitor.isUnlockingWithBiometricAllowed()
+ && msgId != FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT)
+ || msgId == FingerprintManager.FINGERPRINT_ERROR_CANCELED);
+ }
+
+ private boolean shouldSuppressFaceError(int msgId, KeyguardUpdateMonitor updateMonitor) {
+ return ((!updateMonitor.isUnlockingWithBiometricAllowed()
+ && msgId != FaceManager.FACE_ERROR_LOCKOUT_PERMANENT)
+ || msgId == FaceManager.FACE_ERROR_CANCELED);
+ }
+
@Override
public void onTrustAgentErrorMessage(CharSequence message) {
showTransientIndication(message, Utils.getColorError(mContext));
@@ -592,21 +635,22 @@
}
@Override
- public void onFingerprintRunningStateChanged(boolean running) {
+ public void onBiometricRunningStateChanged(boolean running,
+ BiometricSourceType biometricSourceType) {
if (running) {
mMessageToShowOnScreenOn = null;
}
}
@Override
- public void onFingerprintAuthenticated(int userId) {
- super.onFingerprintAuthenticated(userId);
+ public void onBiometricAuthenticated(int userId, BiometricSourceType biometricSourceType) {
+ super.onBiometricAuthenticated(userId, biometricSourceType);
mLastSuccessiveErrorMessage = -1;
}
@Override
- public void onFingerprintAuthFailed() {
- super.onFingerprintAuthFailed();
+ public void onBiometricAuthFailed(BiometricSourceType biometricSourceType) {
+ super.onBiometricAuthFailed(biometricSourceType);
mLastSuccessiveErrorMessage = -1;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java
index 886d6f1..ef40d98 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar;
import android.app.Notification;
+import android.content.res.Configuration;
import android.graphics.PorterDuff;
import android.graphics.drawable.Icon;
import android.text.TextUtils;
@@ -25,6 +26,10 @@
import android.widget.ImageView;
import android.widget.TextView;
+import com.android.internal.util.ContrastColorUtil;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.NotificationContentView;
+
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
@@ -74,8 +79,11 @@
imageView.getDrawable().mutate();
if (shouldApply) {
// lets gray it out
- int grey = view.getContext().getColor(
- com.android.internal.R.color.notification_default_color_light);
+ Configuration config = view.getContext().getResources().getConfiguration();
+ boolean inNightMode = (config.uiMode & Configuration.UI_MODE_NIGHT_MASK)
+ == Configuration.UI_MODE_NIGHT_YES;
+ int grey = ContrastColorUtil.resolveColor(view.getContext(),
+ Notification.COLOR_DEFAULT, inNightMode);
imageView.getDrawable().setColorFilter(grey, PorterDuff.Mode.SRC_ATOP);
} else {
// lets reset it
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
index a2d0c2b..9b375df 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
@@ -28,6 +28,7 @@
import android.util.Log;
import com.android.systemui.Dependency;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.phone.NotificationListenerWithPlugins;
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
index 7e397e8..d6886f5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
@@ -16,7 +16,6 @@
package com.android.systemui.statusbar;
import android.app.ActivityManager;
-import android.app.ActivityTaskManager;
import android.app.Notification;
import android.app.admin.DevicePolicyManager;
import android.content.BroadcastReceiver;
@@ -35,8 +34,6 @@
import android.util.Log;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
-import android.widget.TextView;
-import android.widget.Toast;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.NotificationVisibility;
@@ -44,7 +41,8 @@
import com.android.systemui.Dependency;
import com.android.systemui.Dumpable;
import com.android.systemui.OverviewProxyService;
-import com.android.systemui.R;
+import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import java.io.FileDescriptor;
@@ -109,16 +107,6 @@
} else if (Intent.ACTION_USER_UNLOCKED.equals(action)) {
// Start the overview connection to the launcher service
Dependency.get(OverviewProxyService.class).startConnectionToCurrentUser();
- } else if (Intent.ACTION_USER_PRESENT.equals(action)) {
- try {
- final int lastResumedActivityUserId =
- ActivityTaskManager.getService().getLastResumedActivityUserId();
- if (mUserManager.isManagedProfile(lastResumedActivityUserId)) {
- showForegroundManagedProfileActivityToast();
- }
- } catch (RemoteException e) {
- // Abandon hope activity manager not running.
- }
} else if (NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION.equals(action)) {
final IntentSender intentSender = intent.getParcelableExtra(Intent.EXTRA_INTENT);
final String notificationKey = intent.getStringExtra(Intent.EXTRA_INDEX);
@@ -225,7 +213,6 @@
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_USER_SWITCHED);
filter.addAction(Intent.ACTION_USER_ADDED);
- filter.addAction(Intent.ACTION_USER_PRESENT);
filter.addAction(Intent.ACTION_USER_UNLOCKED);
mContext.registerReceiver(mBaseBroadcastReceiver, filter);
@@ -238,19 +225,6 @@
mSettingsObserver.onChange(false); // set up
}
- private void showForegroundManagedProfileActivityToast() {
- Toast toast = Toast.makeText(mContext,
- R.string.managed_profile_foreground_toast,
- Toast.LENGTH_SHORT);
- TextView text = toast.getView().findViewById(android.R.id.message);
- text.setCompoundDrawablesRelativeWithIntrinsicBounds(
- R.drawable.stat_sys_managed_profile_status, 0, 0, 0);
- int paddingPx = mContext.getResources().getDimensionPixelSize(
- R.dimen.managed_profile_toast_padding);
- text.setCompoundDrawablePadding(paddingPx);
- toast.show();
- }
-
public boolean shouldShowLockscreenNotifications() {
return mShowLockscreenNotifications;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index f737a8c..e89e6e8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -26,6 +26,8 @@
import android.util.Log;
import com.android.systemui.Dumpable;
+import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
import java.io.FileDescriptor;
import java.io.PrintWriter;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java
index 21d3d81..c58eb80 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java
@@ -19,6 +19,11 @@
import android.os.Handler;
import android.view.View;
+import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+
/**
* An abstraction of something that presents notifications, e.g. StatusBar. Contains methods
* for both querying the state of the system (some modularised piece of functionality may
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
index 9e87a0b..6de0543 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
@@ -42,6 +42,9 @@
import com.android.internal.statusbar.NotificationVisibility;
import com.android.systemui.Dependency;
import com.android.systemui.Dumpable;
+import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.policy.RemoteInputView;
import java.io.FileDescriptor;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index 3063199..2a22bf2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -34,13 +34,16 @@
import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.statusbar.notification.NotificationUtils;
+import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.ExpandableView;
import com.android.systemui.statusbar.phone.NotificationIconContainer;
-import com.android.systemui.statusbar.stack.AmbientState;
-import com.android.systemui.statusbar.stack.AnimationProperties;
-import com.android.systemui.statusbar.stack.ExpandableViewState;
-import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
-import com.android.systemui.statusbar.stack.StackScrollState;
-import com.android.systemui.statusbar.stack.ViewState;
+import com.android.systemui.statusbar.notification.stack.AmbientState;
+import com.android.systemui.statusbar.notification.stack.AnimationProperties;
+import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
+import com.android.systemui.statusbar.notification.stack.StackScrollState;
+import com.android.systemui.statusbar.notification.stack.ViewState;
/**
* A notification shelf view that is placed inside the notification scroller. It manages the
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationUiAdjustment.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationUiAdjustment.java
new file mode 100644
index 0000000..92bf821
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationUiAdjustment.java
@@ -0,0 +1,153 @@
+/*
+ * 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.systemui.statusbar;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.Notification;
+import android.app.RemoteInput;
+import android.graphics.drawable.Icon;
+import android.text.TextUtils;
+
+import com.android.systemui.statusbar.notification.NotificationData;
+
+import androidx.annotation.VisibleForTesting;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * By diffing two entries, determines is view reinflation needed.
+ */
+public class NotificationUiAdjustment {
+
+ public final String key;
+ public final List<Notification.Action> smartActions;
+
+ @VisibleForTesting
+ NotificationUiAdjustment(String key, List<Notification.Action> smartActions) {
+ this.key = key;
+ this.smartActions = smartActions == null
+ ? Collections.emptyList()
+ : new ArrayList<>(smartActions);
+ }
+
+ public static NotificationUiAdjustment extractFromNotificationEntry(
+ NotificationData.Entry entry) {
+ return new NotificationUiAdjustment(entry.key, entry.smartActions);
+ }
+
+ public static boolean needReinflate(
+ @NonNull NotificationUiAdjustment oldAdjustment,
+ @NonNull NotificationUiAdjustment newAdjustment) {
+ if (oldAdjustment == newAdjustment) {
+ return false;
+ }
+ return areDifferent(oldAdjustment.smartActions, newAdjustment.smartActions);
+ }
+
+ public static boolean areDifferent(
+ @NonNull List<Notification.Action> first, @NonNull List<Notification.Action> second) {
+ if (first == second) {
+ return false;
+ }
+ if (first == null || second == null) {
+ return true;
+ }
+ if (first.size() != second.size()) {
+ return true;
+ }
+ for (int i = 0; i < first.size(); i++) {
+ Notification.Action firstAction = first.get(i);
+ Notification.Action secondAction = second.get(i);
+
+ if (!TextUtils.equals(firstAction.title, secondAction.title)) {
+ return true;
+ }
+
+ if (areDifferent(firstAction.getIcon(), secondAction.getIcon())) {
+ return true;
+ }
+
+ if (!Objects.equals(firstAction.actionIntent, secondAction.actionIntent)) {
+ return true;
+ }
+
+ if (areDifferent(firstAction.getRemoteInputs(), secondAction.getRemoteInputs())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static boolean areDifferent(@Nullable Icon first, @Nullable Icon second) {
+ if (first == second) {
+ return false;
+ }
+ if (first == null || second == null) {
+ return true;
+ }
+ return !first.sameAs(second);
+ }
+
+ private static boolean areDifferent(
+ @Nullable RemoteInput[] first, @Nullable RemoteInput[] second) {
+ if (first == second) {
+ return false;
+ }
+ if (first == null || second == null) {
+ return true;
+ }
+ if (first.length != second.length) {
+ return true;
+ }
+ for (int i = 0; i < first.length; i++) {
+ RemoteInput firstRemoteInput = first[i];
+ RemoteInput secondRemoteInput = second[i];
+
+ if (!TextUtils.equals(firstRemoteInput.getLabel(), secondRemoteInput.getLabel())) {
+ return true;
+ }
+ if (areDifferent(firstRemoteInput.getChoices(), secondRemoteInput.getChoices())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static boolean areDifferent(
+ @Nullable CharSequence[] first, @Nullable CharSequence[] second) {
+ if (first == second) {
+ return false;
+ }
+ if (first == null || second == null) {
+ return true;
+ }
+ if (first.length != second.length) {
+ return true;
+ }
+ for (int i = 0; i < first.length; i++) {
+ CharSequence firstCharSequence = first[i];
+ CharSequence secondCharSequence = second[i];
+ if (!TextUtils.equals(firstCharSequence, secondCharSequence)) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
index 341c692..d479838 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
@@ -19,14 +19,17 @@
import android.content.Context;
import android.content.res.Resources;
import android.os.Trace;
-import android.service.notification.NotificationListenerService;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import com.android.systemui.Dependency;
import com.android.systemui.R;
+import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import java.util.ArrayList;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java b/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java
index b0d5536..7f63191 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java
@@ -17,8 +17,7 @@
package com.android.systemui.statusbar;
import com.android.internal.util.Preconditions;
-import com.android.systemui.Dependency;
-import com.android.systemui.statusbar.phone.StatusBarWindowManager;
+import com.android.systemui.statusbar.notification.NotificationData;
import com.android.systemui.statusbar.policy.RemoteInputView;
import android.app.Notification;
@@ -26,7 +25,6 @@
import android.content.Context;
import android.os.SystemProperties;
import android.util.ArrayMap;
-import android.util.ArraySet;
import android.util.Pair;
import java.lang.ref.WeakReference;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java b/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java
index 5ba75de..e43c9e5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java
@@ -21,6 +21,8 @@
import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.Dependency;
+import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
import java.util.Set;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
index c820e2b..cc5fbe5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
@@ -661,8 +661,9 @@
if (hsl[1] < 0.2f) {
contrastedColor = Notification.COLOR_DEFAULT;
}
+ boolean isDark = !ContrastColorUtil.isColorLight(mCachedContrastBackgroundColor);
contrastedColor = ContrastColorUtil.resolveContrastColor(mContext,
- contrastedColor, mCachedContrastBackgroundColor);
+ contrastedColor, mCachedContrastBackgroundColor, isDark);
}
mContrastedDrawableColor = contrastedColor;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java
index ccbf483..3c52e8c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java
@@ -126,20 +126,21 @@
}
public void applyMobileState(MobileIconState state) {
+ boolean requestLayout = false;
if (state == null) {
+ requestLayout = getVisibility() != View.GONE;
setVisibility(View.GONE);
mState = null;
- return;
- }
-
- if (mState == null) {
+ } else if (mState == null) {
+ requestLayout = true;
mState = state.copy();
initViewState();
- return;
+ } else if (!mState.equals(state)) {
+ requestLayout = updateState(state.copy());
}
- if (!mState.equals(state)) {
- updateState(state.copy());
+ if (requestLayout) {
+ requestLayout();
}
}
@@ -162,20 +163,24 @@
mMobileRoaming.setVisibility(mState.roaming ? View.VISIBLE : View.GONE);
mMobileRoamingSpace.setVisibility(mState.roaming ? View.VISIBLE : View.GONE);
mIn.setVisibility(mState.activityIn ? View.VISIBLE : View.GONE);
- mOut.setVisibility(mState.activityIn ? View.VISIBLE : View.GONE);
+ mOut.setVisibility(mState.activityOut ? View.VISIBLE : View.GONE);
mInoutContainer.setVisibility((mState.activityIn || mState.activityOut)
? View.VISIBLE : View.GONE);
}
- private void updateState(MobileIconState state) {
+ private boolean updateState(MobileIconState state) {
+ boolean needsLayout = false;
+
setContentDescription(state.contentDescription);
if (mState.visible != state.visible) {
mMobileGroup.setVisibility(state.visible ? View.VISIBLE : View.GONE);
+ needsLayout = true;
}
if (mState.strengthId != state.strengthId) {
mMobileDrawable.setLevel(state.strengthId);
}
if (mState.typeId != state.typeId) {
+ needsLayout |= state.typeId == 0 || mState.typeId == 0;
if (state.typeId != 0) {
mMobileType.setContentDescription(state.typeContentDescription);
mMobileType.setImageResource(state.typeId);
@@ -188,11 +193,16 @@
mMobileRoaming.setVisibility(state.roaming ? View.VISIBLE : View.GONE);
mMobileRoamingSpace.setVisibility(state.roaming ? View.VISIBLE : View.GONE);
mIn.setVisibility(state.activityIn ? View.VISIBLE : View.GONE);
- mOut.setVisibility(state.activityIn ? View.VISIBLE : View.GONE);
+ mOut.setVisibility(state.activityOut ? View.VISIBLE : View.GONE);
mInoutContainer.setVisibility((state.activityIn || state.activityOut)
? View.VISIBLE : View.GONE);
+ needsLayout |= state.roaming != mState.roaming
+ || state.activityIn != mState.activityIn
+ || state.activityOut != mState.activityOut;
+
mState = state;
+ return needsLayout;
}
@Override
@@ -243,7 +253,7 @@
}
@Override
- public void setVisibleState(int state) {
+ public void setVisibleState(int state, boolean animate) {
if (state == mVisibleState) {
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarWifiView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarWifiView.java
index 0ed6b77..f3fc99e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarWifiView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarWifiView.java
@@ -124,7 +124,7 @@
}
@Override
- public void setVisibleState(int state) {
+ public void setVisibleState(int state, boolean animate) {
if (state == mVisibleState) {
return;
}
@@ -191,23 +191,26 @@
}
public void applyWifiState(WifiIconState state) {
+ boolean requestLayout = false;
+
if (state == null) {
+ requestLayout = getVisibility() != View.GONE;
setVisibility(View.GONE);
mState = null;
- return;
- }
-
- if (mState == null) {
+ } else if (mState == null) {
+ requestLayout = true;
mState = state.copy();
initViewState();
+ } else if (!mState.equals(state)) {
+ requestLayout = updateState(state.copy());
}
- if (!mState.equals(state)) {
- updateState(state.copy());
+ if (requestLayout) {
+ requestLayout();
}
}
- private void updateState(WifiIconState state) {
+ private boolean updateState(WifiIconState state) {
setContentDescription(state.contentDescription);
if (mState.resId != state.resId && state.resId >= 0) {
NeutralGoodDrawable drawable = NeutralGoodDrawable
@@ -222,11 +225,17 @@
(state.activityIn || state.activityOut) ? View.VISIBLE : View.GONE);
mAirplaneSpacer.setVisibility(state.airplaneSpacerVisible ? View.VISIBLE : View.GONE);
mSignalSpacer.setVisibility(state.signalSpacerVisible ? View.VISIBLE : View.GONE);
+
+ boolean needsLayout = state.activityIn != mState.activityIn
+ ||state.activityOut != mState.activityOut;
+
if (mState.visible != state.visible) {
+ needsLayout |= true;
setVisibility(state.visible ? View.VISIBLE : View.GONE);
}
mState = state;
+ return needsLayout;
}
private void initViewState() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusIconDisplayable.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusIconDisplayable.java
index b831b86..beb90b8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusIconDisplayable.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusIconDisplayable.java
@@ -22,7 +22,10 @@
String getSlot();
void setStaticDrawableColor(int color);
void setDecorColor(int color);
- void setVisibleState(int state);
+ default void setVisibleState(int state) {
+ setVisibleState(state, false);
+ }
+ void setVisibleState(int state, boolean animate);
int getVisibleState();
boolean isIconVisible();
default boolean isIconBlocked() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ViewTransformationHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/ViewTransformationHelper.java
index 09b11c2..7b5a70e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ViewTransformationHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ViewTransformationHelper.java
@@ -28,7 +28,7 @@
import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.statusbar.notification.TransformState;
-import com.android.systemui.statusbar.stack.StackStateAnimator;
+import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
import java.util.Stack;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/AboveShelfObserver.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/AboveShelfObserver.java
index f10d2d7..24eb5be 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/AboveShelfObserver.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/AboveShelfObserver.java
@@ -20,7 +20,7 @@
import android.view.ViewGroup;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
/**
* An observer that listens to the above shelf state and can notify listeners
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
index a68d75a..1a1941e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
@@ -33,16 +33,14 @@
import com.android.systemui.shared.system.SurfaceControlCompat;
import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplier;
import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplier.SurfaceParams;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
-import com.android.systemui.statusbar.NotificationListContainer;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.phone.CollapsedStatusBarFragment;
import com.android.systemui.statusbar.phone.NotificationPanelView;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.StatusBarWindowView;
-import java.util.ArrayList;
-
/**
* A class that allows activities to be launched in a seamless way where the notification
* transforms nicely into the starting window.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AppOpsListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/AppOpsListener.java
similarity index 95%
rename from packages/SystemUI/src/com/android/systemui/statusbar/AppOpsListener.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/AppOpsListener.java
index 019c680..8cae806 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/AppOpsListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/AppOpsListener.java
@@ -14,13 +14,14 @@
* limitations under the License
*/
-package com.android.systemui.statusbar;
+package com.android.systemui.statusbar.notification;
import android.app.AppOpsManager;
import android.content.Context;
import com.android.systemui.Dependency;
import com.android.systemui.ForegroundServiceController;
+import com.android.systemui.statusbar.NotificationPresenter;
/**
* This class handles listening to notification updates and passing them along to
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ImageTransformState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ImageTransformState.java
index d3a325d..4882a23 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ImageTransformState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ImageTransformState.java
@@ -21,13 +21,12 @@
import android.view.View;
import android.widget.ImageView;
-import com.android.internal.widget.MessagingImageMessage;
-import com.android.internal.widget.MessagingMessage;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.statusbar.CrossFadeHelper;
import com.android.systemui.statusbar.TransformableView;
-import com.android.systemui.statusbar.stack.StackStateAnimator;
+import com.android.systemui.statusbar.notification.row.HybridNotificationView;
+import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
/**
* A transform state of a image view.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
similarity index 96%
rename from packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
index a58752c..ce8f224 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
@@ -11,10 +11,10 @@
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
- * limitations under the License.
+ * limitations under the License
*/
-package com.android.systemui.statusbar;
+package com.android.systemui.statusbar.notification;
import static android.app.Notification.CATEGORY_ALARM;
import static android.app.Notification.CATEGORY_CALL;
@@ -28,6 +28,7 @@
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_STATUS_BAR;
import android.Manifest;
+import android.annotation.NonNull;
import android.app.AppGlobals;
import android.app.Notification;
import android.app.NotificationChannel;
@@ -51,13 +52,17 @@
import android.widget.ImageView;
import android.widget.RemoteViews;
+import androidx.annotation.Nullable;
+
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.statusbar.StatusBarIcon;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.ContrastColorUtil;
import com.android.systemui.Dependency;
import com.android.systemui.ForegroundServiceController;
-import com.android.systemui.statusbar.notification.InflationException;
+import com.android.systemui.statusbar.InflationTask;
+import com.android.systemui.statusbar.StatusBarIconView;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.policy.HeadsUpManager;
@@ -105,6 +110,8 @@
public CharSequence remoteInputText;
public List<SnoozeCriterion> snoozeCriteria;
public int userSentiment = Ranking.USER_SENTIMENT_NEUTRAL;
+ @NonNull
+ public List<Notification.Action> smartActions = Collections.emptyList();
private int mCachedContrastColor = COLOR_INVALID;
private int mCachedContrastColorIsFor = COLOR_INVALID;
@@ -131,8 +138,23 @@
private boolean hasSentReply;
public Entry(StatusBarNotification n) {
+ this(n, null);
+ }
+
+ public Entry(StatusBarNotification n, @Nullable Ranking ranking) {
this.key = n.getKey();
this.notification = n;
+ if (ranking != null) {
+ populateFromRanking(ranking);
+ }
+ }
+
+ public void populateFromRanking(@NonNull Ranking ranking) {
+ channel = ranking.getChannel();
+ snoozeCriteria = ranking.getSnoozeCriteria();
+ userSentiment = ranking.getUserSentiment();
+ smartActions = ranking.getSmartActions() == null
+ ? Collections.emptyList() : ranking.getSmartActions();
}
public void setInterruption() {
@@ -232,6 +254,7 @@
/**
* Update the notification icons.
+ *
* @param context the context to create the icons with.
* @param sbn the notification to read the icon from.
* @throws InflationException
@@ -291,7 +314,7 @@
}
public void onInflationTaskFinished() {
- mRunningTask = null;
+ mRunningTask = null;
}
@VisibleForTesting
@@ -607,7 +630,7 @@
getRanking(key, mTmpRanking);
return mTmpRanking.getOverrideGroupKey();
}
- return null;
+ return null;
}
public List<SnoozeCriterion> getSnoozeCriteria(String key) {
@@ -658,9 +681,7 @@
entry.notification.setOverrideGroupKey(overrideGroupKey);
mGroupManager.onEntryUpdated(entry, oldSbn);
}
- entry.channel = getChannel(entry.key);
- entry.snoozeCriteria = getSnoozeCriteria(entry.key);
- entry.userSentiment = mTmpRanking.getUserSentiment();
+ entry.populateFromRanking(mTmpRanking);
}
}
}
@@ -833,6 +854,7 @@
public boolean isNotificationForCurrentProfiles(StatusBarNotification sbn);
public String getCurrentMediaNotificationKey();
public NotificationGroupManager getGroupManager();
+
/**
* @return true iff the device is dozing
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
similarity index 91%
rename from packages/SystemUI/src/com/android/systemui/statusbar/NotificationEntryManager.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index 06f26c9..dc58cab 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -13,12 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License
*/
-package com.android.systemui.statusbar;
+package com.android.systemui.statusbar.notification;
import static com.android.systemui.statusbar.NotificationRemoteInputManager.ENABLE_REMOTE_INPUT;
import static com.android.systemui.statusbar.NotificationRemoteInputManager
.FORCE_REMOTE_INPUT_HISTORY;
+import android.annotation.Nullable;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
@@ -37,6 +38,7 @@
import android.service.notification.NotificationStats;
import android.service.notification.StatusBarNotification;
import android.text.TextUtils;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.EventLog;
import android.util.Log;
@@ -56,10 +58,19 @@
import com.android.systemui.R;
import com.android.systemui.UiOffloadThread;
import com.android.systemui.recents.misc.SystemServicesProxy;
-import com.android.systemui.statusbar.notification.InflationException;
-import com.android.systemui.statusbar.notification.NotificationInflater;
-import com.android.systemui.statusbar.notification.RowInflaterTask;
-import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.NotificationListener;
+import com.android.systemui.statusbar.NotificationLockscreenUserManager;
+import com.android.systemui.statusbar.NotificationMediaManager;
+import com.android.systemui.statusbar.NotificationPresenter;
+import com.android.systemui.statusbar.NotificationRemoteInputManager;
+import com.android.systemui.statusbar.NotificationUiAdjustment;
+import com.android.systemui.statusbar.NotificationUpdateHandler;
+import com.android.systemui.statusbar.SmartReplyController;
+import com.android.systemui.statusbar.notification.row.NotificationInflater;
+import com.android.systemui.statusbar.notification.row.RowInflaterTask;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
+import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
@@ -726,10 +737,10 @@
&& !mPresenter.isPresenterFullyCollapsed();
row.setUseIncreasedCollapsedHeight(useIncreasedCollapsedHeight);
row.setUseIncreasedHeadsUpHeight(useIncreasedHeadsUp);
+ row.setSmartActions(entry.smartActions);
row.updateNotification(entry);
}
-
protected void addNotificationViews(NotificationData.Entry entry) {
if (entry == null) {
return;
@@ -740,12 +751,13 @@
updateNotifications();
}
- protected NotificationData.Entry createNotificationViews(StatusBarNotification sbn)
+ protected NotificationData.Entry createNotificationViews(
+ StatusBarNotification sbn, NotificationListenerService.Ranking ranking)
throws InflationException {
if (DEBUG) {
- Log.d(TAG, "createNotificationViews(notification=" + sbn);
+ Log.d(TAG, "createNotificationViews(notification=" + sbn + " " + ranking);
}
- NotificationData.Entry entry = new NotificationData.Entry(sbn);
+ NotificationData.Entry entry = new NotificationData.Entry(sbn, ranking);
Dependency.get(LeakDetector.class).trackInstance(entry);
entry.createIcons(mContext, sbn);
// Construct the expanded view.
@@ -754,12 +766,14 @@
}
private void addNotificationInternal(StatusBarNotification notification,
- NotificationListenerService.RankingMap ranking) throws InflationException {
+ NotificationListenerService.RankingMap rankingMap) throws InflationException {
String key = notification.getKey();
if (DEBUG) Log.d(TAG, "addNotification key=" + key);
- mNotificationData.updateRanking(ranking);
- NotificationData.Entry shadeEntry = createNotificationViews(notification);
+ mNotificationData.updateRanking(rankingMap);
+ NotificationListenerService.Ranking ranking = new NotificationListenerService.Ranking();
+ rankingMap.getRanking(key, ranking);
+ NotificationData.Entry shadeEntry = createNotificationViews(notification, ranking);
boolean isHeadsUped = shouldPeek(shadeEntry);
if (!isHeadsUped && notification.getNotification().fullScreenIntent != null) {
if (shouldSuppressFullScreenIntent(shadeEntry)) {
@@ -905,11 +919,57 @@
mPresenter.updateNotificationViews();
}
- public void updateNotificationRanking(NotificationListenerService.RankingMap ranking) {
- mNotificationData.updateRanking(ranking);
+ public void updateNotificationRanking(NotificationListenerService.RankingMap rankingMap) {
+ List<NotificationData.Entry> entries = new ArrayList<>();
+ entries.addAll(mNotificationData.getActiveNotifications());
+ entries.addAll(mPendingNotifications.values());
+
+ // Has a copy of the current UI adjustments.
+ ArrayMap<String, NotificationUiAdjustment> oldAdjustments = new ArrayMap<>();
+ for (NotificationData.Entry entry : entries) {
+ NotificationUiAdjustment adjustment =
+ NotificationUiAdjustment.extractFromNotificationEntry(entry);
+ oldAdjustments.put(entry.key, adjustment);
+ }
+
+ // Populate notification entries from the new rankings.
+ mNotificationData.updateRanking(rankingMap);
+ updateRankingOfPendingNotifications(rankingMap);
+
+ // By comparing the old and new UI adjustments, reinflate the view accordingly.
+ for (NotificationData.Entry entry : entries) {
+ NotificationUiAdjustment newAdjustment =
+ NotificationUiAdjustment.extractFromNotificationEntry(entry);
+
+ if (NotificationUiAdjustment.needReinflate(
+ oldAdjustments.get(entry.key), newAdjustment)) {
+ if (entry.row != null) {
+ entry.reset();
+ PackageManager pmUser = StatusBar.getPackageManagerForUser(mContext,
+ entry.notification.getUser().getIdentifier());
+ updateNotification(entry, pmUser, entry.notification, entry.row);
+ } else {
+ // Once the RowInflaterTask is done, it will pick up the updated entry, so
+ // no-op here.
+ }
+ }
+ }
+
updateNotifications();
}
+ private void updateRankingOfPendingNotifications(
+ @Nullable NotificationListenerService.RankingMap rankingMap) {
+ if (rankingMap == null) {
+ return;
+ }
+ NotificationListenerService.Ranking tmpRanking = new NotificationListenerService.Ranking();
+ for (NotificationData.Entry pendingNotification : mPendingNotifications.values()) {
+ rankingMap.getRanking(pendingNotification.key, tmpRanking);
+ pendingNotification.populateFromRanking(tmpRanking);
+ }
+ }
+
protected boolean shouldPeek(NotificationData.Entry entry) {
return shouldPeek(entry, entry.notification);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/PropertyAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/PropertyAnimator.java
index 2efcd16..aacb22d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/PropertyAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/PropertyAnimator.java
@@ -24,11 +24,10 @@
import android.view.View;
import android.view.animation.Interpolator;
-import com.android.keyguard.KeyguardStatusView;
import com.android.systemui.Interpolators;
-import com.android.systemui.statusbar.stack.AnimationFilter;
-import com.android.systemui.statusbar.stack.AnimationProperties;
-import com.android.systemui.statusbar.stack.ViewState;
+import com.android.systemui.statusbar.notification.stack.AnimationFilter;
+import com.android.systemui.statusbar.notification.stack.AnimationProperties;
+import com.android.systemui.statusbar.notification.stack.ViewState;
/**
* An animator to animate properties
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java
index 879ac92..07b8c35 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java
@@ -29,7 +29,7 @@
import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.statusbar.CrossFadeHelper;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.TransformableView;
import com.android.systemui.statusbar.ViewTransformationHelper;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisibilityLocationProvider.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisibilityLocationProvider.java
index 4a52acc..81208c4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisibilityLocationProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisibilityLocationProvider.java
@@ -16,7 +16,7 @@
package com.android.systemui.statusbar.notification;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
/**
* An object that can determine the visibility of a Notification.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
index 7fe01c0..da8954a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
@@ -19,8 +19,7 @@
import androidx.collection.ArraySet;
import android.view.View;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
-import com.android.systemui.statusbar.NotificationData;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
import java.util.ArrayList;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationCounters.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationCounters.java
similarity index 96%
rename from packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationCounters.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationCounters.java
index 9a12e8b..28c07a3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationCounters.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationCounters.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package com.android.systemui.statusbar.notification;
+package com.android.systemui.statusbar.notification.logging;
/**
* Constants for counter tags for Notification-related actions/views.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
similarity index 96%
rename from packages/SystemUI/src/com/android/systemui/statusbar/NotificationLogger.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
index 8e8e718..767b07f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLogger.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License
*/
-package com.android.systemui.statusbar;
+package com.android.systemui.statusbar.notification.logging;
import android.content.Context;
import android.os.Handler;
@@ -29,6 +29,10 @@
import com.android.internal.statusbar.NotificationVisibility;
import com.android.systemui.Dependency;
import com.android.systemui.UiOffloadThread;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.NotificationListener;
+import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import java.util.ArrayList;
import java.util.Collection;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
similarity index 97%
rename from packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
index 6a38797..58db03c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package com.android.systemui.statusbar;
+package com.android.systemui.statusbar.notification.row;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -37,11 +37,12 @@
import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.classifier.FalsingManager;
+import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.notification.FakeShadowView;
import com.android.systemui.statusbar.notification.NotificationUtils;
import com.android.systemui.statusbar.phone.DoubleTapHelper;
-import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
-import com.android.systemui.statusbar.stack.StackStateAnimator;
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
+import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
/**
* Base class for both {@link ExpandableNotificationRow} and {@link NotificationShelf}
@@ -98,8 +99,8 @@
= new PathInterpolator(0.6f, 0, 0.5f, 1);
private static final Interpolator ACTIVATE_INVERSE_ALPHA_INTERPOLATOR
= new PathInterpolator(0, 0, 0.5f, 1);
- private final int mTintedRippleColor;
- protected final int mNormalRippleColor;
+ private int mTintedRippleColor;
+ protected int mNormalRippleColor;
private final AccessibilityManager mAccessibilityManager;
private final DoubleTapHelper mDoubleTapHelper;
@@ -132,7 +133,7 @@
private ValueAnimator mBackgroundColorAnimator;
private float mAppearAnimationFraction = -1.0f;
private float mAppearAnimationTranslation;
- private final int mNormalColor;
+ private int mNormalColor;
private boolean mIsBelowSpeedBump;
private FalsingManager mFalsingManager;
@@ -188,11 +189,7 @@
mSlowOutLinearInInterpolator = new PathInterpolator(0.8f, 0.0f, 1.0f, 1.0f);
setClipChildren(false);
setClipToPadding(false);
- mNormalColor = context.getColor(R.color.notification_material_background_color);
- mTintedRippleColor = context.getColor(
- R.color.notification_ripple_tinted_color);
- mNormalRippleColor = context.getColor(
- R.color.notification_ripple_untinted_color);
+ updateColors();
mFalsingManager = FalsingManager.getInstance(context);
mAccessibilityManager = AccessibilityManager.getInstance(mContext);
@@ -206,6 +203,16 @@
initDimens();
}
+ private void updateColors() {
+ mNormalColor = mContext.getColor(R.color.notification_material_background_color);
+ mTintedRippleColor = mContext.getColor(
+ R.color.notification_ripple_tinted_color);
+ mNormalRippleColor = mContext.getColor(
+ R.color.notification_ripple_untinted_color);
+ mDimmedAlpha = Color.alpha(mContext.getColor(
+ R.color.notification_material_background_dimmed_color));
+ }
+
private void initDimens() {
mHeadsUpAddStartLocation = getResources().getDimensionPixelSize(
com.android.internal.R.dimen.notification_content_margin_start);
@@ -217,6 +224,12 @@
initDimens();
}
+ public void onUiModeChanged() {
+ updateColors();
+ initBackground();
+ updateBackgroundTint();
+ }
+
@Override
protected void onFinishInflate() {
super.onFinishInflate();
@@ -224,8 +237,6 @@
mFakeShadow = findViewById(R.id.fake_shadow);
mShadowHidden = mFakeShadow.getVisibility() != VISIBLE;
mBackgroundDimmed = findViewById(R.id.backgroundDimmed);
- mDimmedAlpha = Color.alpha(mContext.getColor(
- R.color.notification_material_background_dimmed_color));
initBackground();
updateBackground();
updateBackgroundTint();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AppOpsInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/AppOpsInfo.java
similarity index 98%
rename from packages/SystemUI/src/com/android/systemui/statusbar/AppOpsInfo.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/row/AppOpsInfo.java
index 7999a6c..10fc990 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/AppOpsInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/AppOpsInfo.java
@@ -14,15 +14,13 @@
* limitations under the License
*/
-package com.android.systemui.statusbar;
+package com.android.systemui.statusbar.notification.row;
import android.app.AppOpsManager;
import android.content.Context;
import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
-import android.os.RemoteException;
import android.service.notification.StatusBarNotification;
import android.util.ArraySet;
import android.util.AttributeSet;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
similarity index 97%
rename from packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 83db6aa..67967d4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -11,19 +11,20 @@
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
- * limitations under the License.
+ * limitations under the License
*/
-package com.android.systemui.statusbar;
+package com.android.systemui.statusbar.notification.row;
import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters;
-import static com.android.systemui.statusbar.notification.NotificationInflater.InflationCallback;
+import static com.android.systemui.statusbar.notification.row.NotificationInflater.InflationCallback;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.annotation.Nullable;
+import android.app.Notification;
import android.app.NotificationChannel;
import android.content.Context;
import android.content.pm.PackageInfo;
@@ -74,23 +75,24 @@
import com.android.systemui.plugins.PluginManager;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem;
-import com.android.systemui.statusbar.NotificationGuts.GutsContent;
+import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.NotificationShelf;
+import com.android.systemui.statusbar.RemoteInputController;
+import com.android.systemui.statusbar.StatusBarIconView;
import com.android.systemui.statusbar.notification.AboveShelfChangedListener;
import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
-import com.android.systemui.statusbar.notification.HybridNotificationView;
-import com.android.systemui.statusbar.notification.NotificationCounters;
-import com.android.systemui.statusbar.notification.NotificationInflater;
+import com.android.systemui.statusbar.notification.logging.NotificationCounters;
import com.android.systemui.statusbar.notification.NotificationUtils;
-import com.android.systemui.statusbar.notification.NotificationViewWrapper;
+import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.policy.HeadsUpManager;
-import com.android.systemui.statusbar.stack.AmbientState;
-import com.android.systemui.statusbar.stack.AnimationProperties;
-import com.android.systemui.statusbar.stack.ExpandableViewState;
-import com.android.systemui.statusbar.stack.NotificationChildrenContainer;
-import com.android.systemui.statusbar.stack.StackScrollState;
+import com.android.systemui.statusbar.notification.stack.AmbientState;
+import com.android.systemui.statusbar.notification.stack.AnimationProperties;
+import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
+import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainer;
+import com.android.systemui.statusbar.notification.stack.StackScrollState;
import java.util.ArrayList;
import java.util.List;
@@ -124,12 +126,12 @@
private final NotificationInflater mNotificationInflater;
private int mIconTransformContentShift;
private int mIconTransformContentShiftNoIcon;
- private int mNotificationMinHeightLegacy;
- private int mNotificationMinHeightBeforeP;
- private int mMaxHeadsUpHeightLegacy;
+ private int mMaxHeadsUpHeightBeforeN;
private int mMaxHeadsUpHeightBeforeP;
private int mMaxHeadsUpHeight;
private int mMaxHeadsUpHeightIncreased;
+ private int mNotificationMinHeightBeforeN;
+ private int mNotificationMinHeightBeforeP;
private int mNotificationMinHeight;
private int mNotificationMinHeightLarge;
private int mNotificationMaxHeight;
@@ -546,7 +548,7 @@
boolean beforeP = mEntry.targetSdk < Build.VERSION_CODES.P;
int minHeight;
if (customView && beforeP && !mIsSummaryWithChildren) {
- minHeight = beforeN ? mNotificationMinHeightLegacy : mNotificationMinHeightBeforeP;
+ minHeight = beforeN ? mNotificationMinHeightBeforeN : mNotificationMinHeightBeforeP;
} else if (mUseIncreasedCollapsedHeight && layout == mPrivateLayout) {
minHeight = mNotificationMinHeightLarge;
} else {
@@ -555,20 +557,20 @@
boolean headsUpCustom = layout.getHeadsUpChild() != null &&
layout.getHeadsUpChild().getId()
!= com.android.internal.R.id.status_bar_latest_event_content;
- int headsUpheight;
+ int headsUpHeight;
if (headsUpCustom && beforeP) {
- headsUpheight = beforeN ? mMaxHeadsUpHeightLegacy : mMaxHeadsUpHeightBeforeP;
+ headsUpHeight = beforeN ? mMaxHeadsUpHeightBeforeN : mMaxHeadsUpHeightBeforeP;
} else if (mUseIncreasedHeadsUpHeight && layout == mPrivateLayout) {
- headsUpheight = mMaxHeadsUpHeightIncreased;
+ headsUpHeight = mMaxHeadsUpHeightIncreased;
} else {
- headsUpheight = mMaxHeadsUpHeight;
+ headsUpHeight = mMaxHeadsUpHeight;
}
NotificationViewWrapper headsUpWrapper = layout.getVisibleWrapper(
NotificationContentView.VISIBLE_TYPE_HEADSUP);
if (headsUpWrapper != null) {
- headsUpheight = Math.max(headsUpheight, headsUpWrapper.getMinLayoutHeight());
+ headsUpHeight = Math.max(headsUpHeight, headsUpWrapper.getMinLayoutHeight());
}
- layout.setHeights(minHeight, headsUpheight, mNotificationMaxHeight,
+ layout.setHeights(minHeight, headsUpHeight, mNotificationMaxHeight,
mNotificationAmbientHeight);
}
@@ -985,9 +987,9 @@
}
public void setGutsView(MenuItem item) {
- if (mGuts != null && item.getGutsView() instanceof GutsContent) {
- ((GutsContent) item.getGutsView()).setGutsParent(mGuts);
- mGuts.setGutsContent((GutsContent) item.getGutsView());
+ if (mGuts != null && item.getGutsView() instanceof NotificationGuts.GutsContent) {
+ ((NotificationGuts.GutsContent) item.getGutsView()).setGutsParent(mGuts);
+ mGuts.setGutsContent((NotificationGuts.GutsContent) item.getGutsView());
}
}
@@ -1053,6 +1055,10 @@
super.onDensityOrFontScaleChanged();
initDimens();
initBackground();
+ reInflateViews();
+ }
+
+ private void reInflateViews() {
// Let's update our childrencontainer. This is intentionally not guarded with
// mIsSummaryWithChildren since we might have had children but not anymore.
if (mChildrenContainer != null) {
@@ -1079,7 +1085,7 @@
l.initView();
l.reInflateViews();
}
- mNotificationInflater.onDensityOrFontScaleChanged();
+ mNotificationInflater.clearCachesAndReInflate();
onNotificationUpdated();
}
@@ -1090,6 +1096,17 @@
}
}
+ @Override
+ public void onUiModeChanged() {
+ super.onUiModeChanged();
+ reInflateViews();
+ if (mChildrenContainer != null) {
+ for (ExpandableNotificationRow child : mChildrenContainer.getNotificationChildren()) {
+ child.onUiModeChanged();
+ }
+ }
+ }
+
public void setContentBackground(int customBackgroundColor, boolean animate,
NotificationContentView notificationContentView) {
if (getShowingLayout() == notificationContentView) {
@@ -1448,6 +1465,10 @@
mNotificationInflater.setUsesIncreasedHeight(use);
}
+ public void setSmartActions(List<Notification.Action> smartActions) {
+ mNotificationInflater.setSmartActions(smartActions);
+ }
+
public void setUseIncreasedHeadsUpHeight(boolean use) {
mUseIncreasedHeadsUpHeight = use;
mNotificationInflater.setUsesIncreasedHeadsUpHeight(use);
@@ -1487,7 +1508,7 @@
}
private void initDimens() {
- mNotificationMinHeightLegacy = NotificationUtils.getFontScaledHeight(mContext,
+ mNotificationMinHeightBeforeN = NotificationUtils.getFontScaledHeight(mContext,
R.dimen.notification_min_height_legacy);
mNotificationMinHeightBeforeP = NotificationUtils.getFontScaledHeight(mContext,
R.dimen.notification_min_height_before_p);
@@ -1499,7 +1520,7 @@
R.dimen.notification_max_height);
mNotificationAmbientHeight = NotificationUtils.getFontScaledHeight(mContext,
R.dimen.notification_ambient_height);
- mMaxHeadsUpHeightLegacy = NotificationUtils.getFontScaledHeight(mContext,
+ mMaxHeadsUpHeightBeforeN = NotificationUtils.getFontScaledHeight(mContext,
R.dimen.notification_max_heads_up_height_legacy);
mMaxHeadsUpHeightBeforeP = NotificationUtils.getFontScaledHeight(mContext,
R.dimen.notification_max_heads_up_height_before_p);
@@ -1540,7 +1561,7 @@
return mOnAppOpsClickListener;
}
- protected void setAppOpsOnClickListener(ExpandableNotificationRow.OnAppOpsClickListener l) {
+ public void setAppOpsOnClickListener(ExpandableNotificationRow.OnAppOpsClickListener l) {
mOnAppOpsClickListener = v -> {
createMenu();
MenuItem menuItem = getProvider().getAppOpsMenuItem(mContext);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java
similarity index 98%
rename from packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java
index 584b637..a7aed5f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package com.android.systemui.statusbar;
+package com.android.systemui.statusbar.notification.row;
import android.content.Context;
import android.content.res.Resources;
@@ -31,8 +31,8 @@
import com.android.systemui.R;
import com.android.systemui.statusbar.notification.AnimatableProperty;
import com.android.systemui.statusbar.notification.PropertyAnimator;
-import com.android.systemui.statusbar.stack.AnimationProperties;
-import com.android.systemui.statusbar.stack.StackStateAnimator;
+import com.android.systemui.statusbar.notification.stack.AnimationProperties;
+import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
/**
* Like {@link ExpandableView}, but setting an outline for the height and clipping.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
similarity index 98%
rename from packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
index ae8d844..46019e3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package com.android.systemui.statusbar;
+package com.android.systemui.statusbar.notification.row;
import android.animation.AnimatorListenerAdapter;
import android.content.Context;
@@ -25,9 +25,9 @@
import android.view.ViewGroup;
import android.widget.FrameLayout;
-import com.android.systemui.statusbar.stack.ExpandableViewState;
-import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
-import com.android.systemui.statusbar.stack.StackScrollState;
+import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
+import com.android.systemui.statusbar.notification.stack.StackScrollState;
import java.util.ArrayList;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/FooterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java
similarity index 93%
rename from packages/SystemUI/src/com/android/systemui/statusbar/FooterView.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java
index dc5bb9a..1f15ed0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/FooterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package com.android.systemui.statusbar;
+package com.android.systemui.statusbar.notification.row;
import android.annotation.ColorInt;
import android.content.Context;
@@ -23,8 +23,8 @@
import android.view.View;
import com.android.systemui.R;
-import com.android.systemui.statusbar.stack.ExpandableViewState;
-import com.android.systemui.statusbar.stack.StackScrollState;
+import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
+import com.android.systemui.statusbar.notification.stack.StackScrollState;
public class FooterView extends StackScrollerDecorView {
private final int mClearAllTopPadding;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/FooterViewButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterViewButton.java
similarity index 90%
rename from packages/SystemUI/src/com/android/systemui/statusbar/FooterViewButton.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterViewButton.java
index 16ca0f2..e1c4a0c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/FooterViewButton.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterViewButton.java
@@ -14,14 +14,15 @@
* limitations under the License
*/
-package com.android.systemui.statusbar;
+package com.android.systemui.statusbar.notification.row;
import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.ViewGroup;
-import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
+import com.android.systemui.statusbar.AlphaOptimizedButton;
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
public class FooterViewButton extends AlphaOptimizedButton {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/HybridGroupManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridGroupManager.java
similarity index 96%
rename from packages/SystemUI/src/com/android/systemui/statusbar/notification/HybridGroupManager.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridGroupManager.java
index ec94df1..33badaf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/HybridGroupManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridGroupManager.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package com.android.systemui.statusbar.notification;
+package com.android.systemui.statusbar.notification.row;
import android.app.Notification;
import android.content.Context;
@@ -26,6 +26,8 @@
import android.widget.TextView;
import com.android.systemui.R;
+import com.android.systemui.statusbar.notification.NotificationDozeHelper;
+import com.android.systemui.statusbar.notification.NotificationUtils;
/**
* A class managing hybrid groups that include {@link HybridNotificationView} and the notification
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/HybridNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridNotificationView.java
similarity index 97%
rename from packages/SystemUI/src/com/android/systemui/statusbar/notification/HybridNotificationView.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridNotificationView.java
index 85f2a63..be25d63 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/HybridNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridNotificationView.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package com.android.systemui.statusbar.notification;
+package com.android.systemui.statusbar.notification.row;
import android.annotation.Nullable;
import android.content.Context;
@@ -28,6 +28,7 @@
import com.android.systemui.statusbar.CrossFadeHelper;
import com.android.systemui.statusbar.TransformableView;
import com.android.systemui.statusbar.ViewTransformationHelper;
+import com.android.systemui.statusbar.notification.TransformState;
/**
* A hybrid view which may contain information about one ore more notifications.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBackgroundView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
similarity index 99%
rename from packages/SystemUI/src/com/android/systemui/statusbar/NotificationBackgroundView.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
index 969e9d9..1ed726d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBackgroundView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package com.android.systemui.statusbar;
+package com.android.systemui.statusbar.notification.row;
import android.content.Context;
import android.content.res.ColorStateList;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBlockingHelperManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManager.java
similarity index 95%
rename from packages/SystemUI/src/com/android/systemui/statusbar/NotificationBlockingHelperManager.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManager.java
index c78ab8d..1a4ef09 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBlockingHelperManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManager.java
@@ -14,20 +14,18 @@
* limitations under the License
*/
-package com.android.systemui.statusbar;
+package com.android.systemui.statusbar.notification.row;
import android.content.Context;
-import android.content.pm.PackageManager;
-import android.os.UserHandle;
-import android.service.notification.StatusBarNotification;
+
import androidx.annotation.VisibleForTesting;
import android.util.Log;
import com.android.internal.logging.MetricsLogger;
import com.android.systemui.Dependency;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
-import com.android.systemui.statusbar.notification.NotificationCounters;
-import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.logging.NotificationCounters;
import java.util.Collections;
import java.util.HashSet;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
similarity index 99%
rename from packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index a90ddf0..da1fd3b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package com.android.systemui.statusbar;
+package com.android.systemui.statusbar.notification.row;
import android.app.Notification;
import android.app.PendingIntent;
@@ -39,11 +39,13 @@
import com.android.internal.util.ContrastColorUtil;
import com.android.systemui.Dependency;
import com.android.systemui.R;
-import com.android.systemui.statusbar.notification.HybridGroupManager;
-import com.android.systemui.statusbar.notification.HybridNotificationView;
-import com.android.systemui.statusbar.notification.NotificationCustomViewWrapper;
+import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.RemoteInputController;
+import com.android.systemui.statusbar.SmartReplyController;
+import com.android.systemui.statusbar.TransformableView;
+import com.android.systemui.statusbar.notification.row.wrapper.NotificationCustomViewWrapper;
import com.android.systemui.statusbar.notification.NotificationUtils;
-import com.android.systemui.statusbar.notification.NotificationViewWrapper;
+import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.policy.RemoteInputView;
import com.android.systemui.statusbar.policy.SmartReplyConstants;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java
similarity index 98%
rename from packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java
index 4a8f4bbf..0a197da 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package com.android.systemui.statusbar;
+package com.android.systemui.statusbar.notification.row;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -35,7 +35,7 @@
import com.android.systemui.Dependency;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
-import com.android.systemui.statusbar.stack.StackStateAnimator;
+import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
/**
* The guts of a notification revealed when performing a long press.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
similarity index 97%
rename from packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
index 91a381f..ef0be880 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License
*/
-package com.android.systemui.statusbar;
+package com.android.systemui.statusbar.notification.row;
import static android.app.AppOpsManager.OP_CAMERA;
import static android.app.AppOpsManager.OP_RECORD_AUDIO;
@@ -45,14 +45,14 @@
import com.android.systemui.Dependency;
import com.android.systemui.Dumpable;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.NotificationLockscreenUserManager;
+import com.android.systemui.statusbar.NotificationPresenter;
+import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.phone.StatusBar;
import java.io.FileDescriptor;
import java.io.PrintWriter;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
/**
* Handles various NotificationGuts related tasks, such as binding guts to a row, opening and
@@ -330,7 +330,7 @@
* @param menuItem MenuItem the guts should display
* @return true if guts was opened
*/
- boolean openGuts(
+ public boolean openGuts(
View view,
int x,
int y,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInflater.java
similarity index 95%
rename from packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInflater.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInflater.java
index 1303057..aa4765a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInflater.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInflater.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package com.android.systemui.statusbar.notification;
+package com.android.systemui.statusbar.notification.row;
import android.annotation.Nullable;
import android.app.Notification;
@@ -27,15 +27,18 @@
import android.widget.RemoteViews;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.R;
import com.android.systemui.statusbar.InflationTask;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
-import com.android.systemui.statusbar.NotificationContentView;
-import com.android.systemui.statusbar.NotificationData;
+import com.android.systemui.statusbar.notification.InflationException;
+import com.android.systemui.statusbar.notification.MediaNotificationProcessor;
+import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
+import com.android.systemui.statusbar.notification.NotificationData;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.util.Assert;
+import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashMap;
+import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
@@ -67,6 +70,7 @@
private boolean mIsChildInGroup;
private InflationCallback mCallback;
private boolean mRedactAmbient;
+ private List<Notification.Action> mSmartActions;
public NotificationInflater(ExpandableNotificationRow row) {
mRow = row;
@@ -95,6 +99,10 @@
mUsesIncreasedHeight = usesIncreasedHeight;
}
+ public void setSmartActions(List<Notification.Action> smartActions) {
+ mSmartActions = smartActions;
+ }
+
public void setUsesIncreasedHeadsUpHeight(boolean usesIncreasedHeight) {
mUsesIncreasedHeadsUpHeight = usesIncreasedHeight;
}
@@ -140,7 +148,7 @@
AsyncInflationTask task = new AsyncInflationTask(sbn, reInflateFlags, mRow,
mIsLowPriority,
mIsChildInGroup, mUsesIncreasedHeight, mUsesIncreasedHeadsUpHeight, mRedactAmbient,
- mCallback, mRemoteViewClickHandler);
+ mCallback, mRemoteViewClickHandler, mSmartActions);
if (mCallback != null && mCallback.doInflateSynchronous()) {
task.onPostExecute(task.doInBackground());
} else {
@@ -554,7 +562,7 @@
}
}
- public void onDensityOrFontScaleChanged() {
+ public void clearCachesAndReInflate() {
NotificationData.Entry entry = mRow.getEntry();
entry.cachedAmbientContentView = null;
entry.cachedBigContentView = null;
@@ -586,13 +594,15 @@
private Exception mError;
private RemoteViews.OnClickHandler mRemoteViewClickHandler;
private CancellationSignal mCancellationSignal;
+ private List<Notification.Action> mSmartActions;
private AsyncInflationTask(StatusBarNotification notification,
int reInflateFlags, ExpandableNotificationRow row, boolean isLowPriority,
boolean isChildInGroup, boolean usesIncreasedHeight,
boolean usesIncreasedHeadsUpHeight, boolean redactAmbient,
InflationCallback callback,
- RemoteViews.OnClickHandler remoteViewClickHandler) {
+ RemoteViews.OnClickHandler remoteViewClickHandler,
+ List<Notification.Action> smartActions) {
mRow = row;
mSbn = notification;
mReInflateFlags = reInflateFlags;
@@ -604,6 +614,9 @@
mRedactAmbient = redactAmbient;
mRemoteViewClickHandler = remoteViewClickHandler;
mCallback = callback;
+ mSmartActions = smartActions == null
+ ? Collections.emptyList()
+ : new ArrayList<>(smartActions);
NotificationData.Entry entry = row.getEntry();
entry.setInflationTask(this);
}
@@ -619,6 +632,9 @@
final Notification.Builder recoveredBuilder
= Notification.Builder.recoverBuilder(mContext,
mSbn.getNotification());
+
+ applyChanges(recoveredBuilder);
+
Context packageContext = mSbn.getPackageContext(mContext);
Notification notification = mSbn.getNotification();
if (notification.isMediaNotification()) {
@@ -646,6 +662,18 @@
}
}
+ /**
+ * Apply changes to the given notification builder, like adding smart actions suggested by
+ * a {@link android.service.notification.NotificationAssistantService}.
+ */
+ private void applyChanges(Notification.Builder builder) {
+ if (mSmartActions != null) {
+ for (Notification.Action smartAction : mSmartActions) {
+ builder.addAction(smartAction);
+ }
+ }
+ }
+
private void handleError(Exception e) {
mRow.getEntry().onInflationTaskFinished();
StatusBarNotification sbn = mRow.getStatusBarNotification();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
similarity index 99%
rename from packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
index bd40686..3e380d1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package com.android.systemui.statusbar;
+package com.android.systemui.statusbar.notification.row;
import static android.app.NotificationManager.IMPORTANCE_MIN;
import static android.app.NotificationManager.IMPORTANCE_NONE;
@@ -54,7 +54,7 @@
import com.android.systemui.Dependency;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
-import com.android.systemui.statusbar.notification.NotificationCounters;
+import com.android.systemui.statusbar.notification.logging.NotificationCounters;
import java.util.List;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMenuRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
similarity index 98%
rename from packages/SystemUI/src/com/android/systemui/statusbar/NotificationMenuRow.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
index ada1a17..dec88d4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMenuRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
@@ -11,10 +11,10 @@
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
- * limitations under the License.
+ * limitations under the License
*/
-package com.android.systemui.statusbar;
+package com.android.systemui.statusbar.notification.row;
import static com.android.systemui.SwipeHelper.SWIPED_FAR_ENOUGH_SIZE_FRACTION;
@@ -24,8 +24,9 @@
import com.android.systemui.R;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
-import com.android.systemui.statusbar.NotificationGuts.GutsContent;
-import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
+import com.android.systemui.statusbar.AlphaOptimizedImageView;
+import com.android.systemui.statusbar.notification.row.NotificationGuts.GutsContent;
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSnooze.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java
similarity index 97%
rename from packages/SystemUI/src/com/android/systemui/statusbar/NotificationSnooze.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java
index d84d3dc..75b05c2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSnooze.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java
@@ -1,4 +1,3 @@
-package com.android.systemui.statusbar;
/*
* Copyright (C) 2017 The Android Open Source Project
*
@@ -15,6 +14,8 @@
* limitations under the License
*/
+package com.android.systemui.statusbar.notification.row;
+
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
@@ -256,10 +257,14 @@
int count = showInHours ? (minutes / 60) : minutes;
String description = res.getQuantityString(pluralResId, count, count);
String resultText = String.format(res.getString(R.string.snoozed_for_time), description);
+ AccessibilityAction action = new AccessibilityAction(accessibilityActionId, description);
+ final int index = resultText.indexOf(description);
+ if (index == -1) {
+ return new NotificationSnoozeOption(null, minutes, description, resultText, action);
+ }
SpannableString string = new SpannableString(resultText);
string.setSpan(new StyleSpan(Typeface.BOLD),
- resultText.length() - description.length(), resultText.length(), 0 /* flags */);
- AccessibilityAction action = new AccessibilityAction(accessibilityActionId, description);
+ index, index + description.length(), 0 /* flags */);
return new NotificationSnoozeOption(null, minutes, description, string,
action);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationUndoLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationUndoLayout.java
new file mode 100644
index 0000000..3ea8195
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationUndoLayout.java
@@ -0,0 +1,139 @@
+/*
+ * 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.notification.row;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.FrameLayout;
+
+import com.android.systemui.R;
+
+/**
+ * Custom view for the NotificationInfo confirmation views so that the confirmation text can
+ * occupy the full width of the notification and push the undo button down to the next line if
+ * necessary.
+ *
+ * @see NotificationInfo
+ */
+public class NotificationUndoLayout extends FrameLayout {
+ /**
+ * View for the prompt/confirmation text to tell the user the previous action was successful.
+ */
+ private View mConfirmationTextView;
+ /** Undo button (actionable text) view. */
+ private View mUndoView;
+
+ /**
+ * Whether {@link #mConfirmationTextView} is multiline and will require the full width of the
+ * parent (which causes the {@link #mUndoView} to push down).
+ */
+ private boolean mIsMultiline = false;
+ private int mMultilineTopMargin;
+
+ public NotificationUndoLayout(Context context) {
+ this(context, null);
+ }
+
+ public NotificationUndoLayout(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public NotificationUndoLayout(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+
+ mConfirmationTextView = findViewById(R.id.confirmation_text);
+ mUndoView = findViewById(R.id.undo);
+
+ mMultilineTopMargin = getResources().getDimensionPixelOffset(
+ com.android.internal.R.dimen.notification_content_margin_start);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
+ LayoutParams confirmationLayoutParams =
+ (LayoutParams) mConfirmationTextView.getLayoutParams();
+ LayoutParams undoLayoutParams =(LayoutParams) mUndoView.getLayoutParams();
+
+ int measuredWidth = getMeasuredWidth();
+ // Ignore the left margin on the undo button - no need for additional extra space between
+ // the text and the button.
+ int requiredWidth = mConfirmationTextView.getMeasuredWidth()
+ + confirmationLayoutParams.rightMargin
+ + confirmationLayoutParams.leftMargin
+ + mUndoView.getMeasuredWidth()
+ + undoLayoutParams.rightMargin;
+ // If the measured width isn't enough to accommodate both the undo button and the text in
+ // the same line, we'll need to adjust the view to be multi-line. Otherwise, we're done.
+ if (requiredWidth > measuredWidth) {
+ mIsMultiline = true;
+
+ // Update height requirement to the text height and the button's height (along with
+ // additional spacing for the top of the text).
+ int updatedHeight = mMultilineTopMargin
+ + mConfirmationTextView.getMeasuredHeight()
+ + mUndoView.getMeasuredHeight()
+ + undoLayoutParams.topMargin
+ + undoLayoutParams.bottomMargin;
+
+ setMeasuredDimension(measuredWidth, updatedHeight);
+ } else {
+ mIsMultiline = false;
+ }
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ // If the text view and undo view don't fit on the same line, we'll need to manually lay
+ // out the content.
+ if (mIsMultiline) {
+ // Re-align parent right/bottom values. Left and top are considered to be 0.
+ int parentBottom = getMeasuredHeight();
+ int parentRight = getMeasuredWidth();
+
+ LayoutParams confirmationLayoutParams =
+ (LayoutParams) mConfirmationTextView.getLayoutParams();
+ LayoutParams undoLayoutParams = (LayoutParams) mUndoView.getLayoutParams();
+
+ // The confirmation text occupies the full width as computed earlier. Both side margins
+ // are equivalent, so we only need to grab the left one here.
+ mConfirmationTextView.layout(
+ confirmationLayoutParams.leftMargin,
+ mMultilineTopMargin,
+ confirmationLayoutParams.leftMargin + mConfirmationTextView.getMeasuredWidth(),
+ mMultilineTopMargin + mConfirmationTextView.getMeasuredHeight());
+
+ // The undo button is aligned bottom|end with the parent in the case of multiline text.
+ int undoViewLeft = getLayoutDirection() == View.LAYOUT_DIRECTION_RTL
+ ? undoLayoutParams.rightMargin
+ : parentRight - mUndoView.getMeasuredWidth() - undoLayoutParams.rightMargin;
+ mUndoView.layout(
+ undoViewLeft,
+ parentBottom - mUndoView.getMeasuredHeight() - undoLayoutParams.bottomMargin,
+ undoViewLeft + mUndoView.getMeasuredWidth(),
+ parentBottom - undoLayoutParams.bottomMargin);
+ } else {
+ super.onLayout(changed, left, top, right, bottom);
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/RowInflaterTask.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowInflaterTask.java
similarity index 93%
rename from packages/SystemUI/src/com/android/systemui/statusbar/notification/RowInflaterTask.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowInflaterTask.java
index 9b9dfc9..a21794b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/RowInflaterTask.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowInflaterTask.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package com.android.systemui.statusbar.notification;
+package com.android.systemui.statusbar.notification.row;
import android.content.Context;
import androidx.asynclayoutinflater.view.AsyncLayoutInflater;
@@ -24,8 +24,7 @@
import com.android.systemui.R;
import com.android.systemui.statusbar.InflationTask;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
-import com.android.systemui.statusbar.NotificationData;
+import com.android.systemui.statusbar.notification.NotificationData;
/**
* An inflater task that asynchronously inflates a ExpandableNotificationRow
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StackScrollerDecorView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java
similarity index 97%
rename from packages/SystemUI/src/com/android/systemui/statusbar/StackScrollerDecorView.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java
index c5b3560..8a061a6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StackScrollerDecorView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package com.android.systemui.statusbar;
+package com.android.systemui.statusbar.notification.row;
import android.animation.AnimatorListenerAdapter;
import android.content.Context;
@@ -24,6 +24,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.Interpolators;
+import com.android.systemui.statusbar.notification.row.ExpandableView;
/**
* A common base class for all views in the notification stack scroller which don't have a
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationBigPictureTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapper.java
similarity index 88%
rename from packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationBigPictureTemplateViewWrapper.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapper.java
index cf12e94..2da4d2c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationBigPictureTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapper.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package com.android.systemui.statusbar.notification;
+package com.android.systemui.statusbar.notification.row.wrapper;
import android.app.Notification;
import android.content.Context;
@@ -23,7 +23,8 @@
import android.service.notification.StatusBarNotification;
import android.view.View;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.ImageTransformState;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
/**
* Wraps a notification containing a big picture template
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationBigTextTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigTextTemplateViewWrapper.java
similarity index 93%
rename from packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationBigTextTemplateViewWrapper.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigTextTemplateViewWrapper.java
index 20a3d8f..133df3c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationBigTextTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigTextTemplateViewWrapper.java
@@ -14,14 +14,14 @@
* limitations under the License
*/
-package com.android.systemui.statusbar.notification;
+package com.android.systemui.statusbar.notification.row.wrapper;
import android.content.Context;
import android.service.notification.StatusBarNotification;
import android.view.View;
import com.android.internal.widget.ImageFloatingTextView;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.TransformableView;
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationCustomViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapper.java
similarity index 92%
rename from packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationCustomViewWrapper.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapper.java
index 7a51fe1..db7b4fc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationCustomViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapper.java
@@ -14,13 +14,13 @@
* limitations under the License
*/
-package com.android.systemui.statusbar.notification;
+package com.android.systemui.statusbar.notification.row.wrapper;
import android.content.Context;
import android.view.View;
import com.android.systemui.R;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
/**
* Wraps a notification containing a custom view.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
similarity index 95%
rename from packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
index ade27f9..6ca07ed 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
@@ -14,9 +14,9 @@
* limitations under the License
*/
-package com.android.systemui.statusbar.notification;
+package com.android.systemui.statusbar.notification.row.wrapper;
-import static com.android.systemui.statusbar.ExpandableNotificationRow
+import static com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
.DEFAULT_HEADER_VISIBLE_AMOUNT;
import static com.android.systemui.statusbar.notification.TransformState.TRANSFORM_Y;
@@ -34,7 +34,10 @@
import com.android.internal.widget.NotificationExpandButton;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.CustomInterpolatorTransformation;
+import com.android.systemui.statusbar.notification.ImageTransformState;
+import com.android.systemui.statusbar.notification.TransformState;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.TransformableView;
import com.android.systemui.statusbar.ViewTransformationHelper;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationMediaTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java
similarity index 93%
rename from packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationMediaTemplateViewWrapper.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java
index 548f006..37d2f6b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationMediaTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java
@@ -14,12 +14,12 @@
* limitations under the License
*/
-package com.android.systemui.statusbar.notification;
+package com.android.systemui.statusbar.notification.row.wrapper;
import android.content.Context;
import android.view.View;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.TransformableView;
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationMessagingTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMessagingTemplateViewWrapper.java
similarity index 90%
rename from packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationMessagingTemplateViewWrapper.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMessagingTemplateViewWrapper.java
index fb5644f..13c5960 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationMessagingTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMessagingTemplateViewWrapper.java
@@ -14,20 +14,16 @@
* limitations under the License
*/
-package com.android.systemui.statusbar.notification;
+package com.android.systemui.statusbar.notification.row.wrapper;
import com.android.internal.widget.MessagingLayout;
import com.android.internal.widget.MessagingLinearLayout;
import com.android.systemui.R;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
-import com.android.systemui.statusbar.TransformableView;
+import com.android.systemui.statusbar.notification.NotificationUtils;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import android.content.Context;
-import android.text.TextUtils;
import android.view.View;
-import android.widget.TextView;
-
-import java.util.ArrayList;
/**
* Wraps a notification containing a messaging template
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java
similarity index 97%
rename from packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java
index d4b0be8..d934902 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package com.android.systemui.statusbar.notification;
+package com.android.systemui.statusbar.notification.row.wrapper;
import android.app.PendingIntent;
import android.content.Context;
@@ -37,9 +37,12 @@
import com.android.systemui.R;
import com.android.systemui.UiOffloadThread;
import com.android.systemui.statusbar.CrossFadeHelper;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.ImageTransformState;
+import com.android.systemui.statusbar.notification.TransformState;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.TransformableView;
import com.android.systemui.statusbar.ViewTransformationHelper;
+import com.android.systemui.statusbar.notification.row.HybridNotificationView;
/**
* Wraps a notification view inflated from a template.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
similarity index 96%
rename from packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
index 93058b8..2ca7282 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package com.android.systemui.statusbar.notification;
+package com.android.systemui.statusbar.notification.row.wrapper;
import android.content.Context;
import android.graphics.drawable.ColorDrawable;
@@ -23,7 +23,8 @@
import android.view.View;
import com.android.systemui.statusbar.CrossFadeHelper;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.TransformState;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.TransformableView;
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
similarity index 96%
rename from packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
index db3f0d9..bb07f33 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
@@ -14,17 +14,17 @@
* limitations under the License
*/
-package com.android.systemui.statusbar.stack;
+package com.android.systemui.statusbar.notification.stack;
import android.annotation.Nullable;
import android.content.Context;
import android.view.View;
import com.android.systemui.R;
-import com.android.systemui.statusbar.ActivatableNotificationView;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
-import com.android.systemui.statusbar.ExpandableView;
-import com.android.systemui.statusbar.NotificationData;
+import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.ExpandableView;
+import com.android.systemui.statusbar.notification.NotificationData;
import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.policy.HeadsUpManager;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AnimationFilter.java
similarity index 98%
rename from packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AnimationFilter.java
index fd49b26..c6f953c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AnimationFilter.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package com.android.systemui.statusbar.stack;
+package com.android.systemui.statusbar.notification.stack;
import androidx.collection.ArraySet;
import android.util.Property;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationProperties.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AnimationProperties.java
similarity index 97%
rename from packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationProperties.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AnimationProperties.java
index 47df226..87a3cc9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationProperties.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AnimationProperties.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package com.android.systemui.statusbar.stack;
+package com.android.systemui.statusbar.notification.stack;
import android.animation.AnimatorListenerAdapter;
import android.util.ArrayMap;
@@ -22,8 +22,6 @@
import android.view.View;
import android.view.animation.Interpolator;
-import java.util.HashMap;
-
/**
* Properties for a View animation
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/ExpandableViewState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ExpandableViewState.java
similarity index 98%
rename from packages/SystemUI/src/com/android/systemui/statusbar/stack/ExpandableViewState.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ExpandableViewState.java
index a7925aa..8c1a788 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/ExpandableViewState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ExpandableViewState.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package com.android.systemui.statusbar.stack;
+package com.android.systemui.statusbar.notification.stack;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -24,8 +24,8 @@
import com.android.systemui.Interpolators;
import com.android.systemui.R;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
-import com.android.systemui.statusbar.ExpandableView;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.ExpandableView;
/**
* A state of an expandable view
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/HeadsUpAppearInterpolator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/HeadsUpAppearInterpolator.java
similarity index 96%
rename from packages/SystemUI/src/com/android/systemui/statusbar/stack/HeadsUpAppearInterpolator.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/HeadsUpAppearInterpolator.java
index 59ce0ca..24e1f32 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/HeadsUpAppearInterpolator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/HeadsUpAppearInterpolator.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package com.android.systemui.statusbar.stack;
+package com.android.systemui.statusbar.notification.stack;
import android.graphics.Path;
import android.view.animation.PathInterpolator;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
similarity index 98%
rename from packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
index 2dd3d4e..3d44e3c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package com.android.systemui.statusbar.stack;
+package com.android.systemui.statusbar.notification.stack;
import android.app.Notification;
import android.content.Context;
@@ -33,12 +33,12 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.R;
import com.android.systemui.statusbar.CrossFadeHelper;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.NotificationHeaderUtil;
-import com.android.systemui.statusbar.notification.HybridGroupManager;
-import com.android.systemui.statusbar.notification.HybridNotificationView;
+import com.android.systemui.statusbar.notification.row.HybridGroupManager;
+import com.android.systemui.statusbar.notification.row.HybridNotificationView;
import com.android.systemui.statusbar.notification.NotificationUtils;
-import com.android.systemui.statusbar.notification.NotificationViewWrapper;
+import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
import java.util.ArrayList;
@@ -49,10 +49,14 @@
*/
public class NotificationChildrenContainer extends ViewGroup {
- private static final int NUMBER_OF_CHILDREN_WHEN_COLLAPSED = 2;
- private static final int NUMBER_OF_CHILDREN_WHEN_SYSTEM_EXPANDED = 5;
- private static final int NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED = 8;
- private static final int NUMBER_OF_CHILDREN_WHEN_AMBIENT = 1;
+ @VisibleForTesting
+ static final int NUMBER_OF_CHILDREN_WHEN_COLLAPSED = 2;
+ @VisibleForTesting
+ static final int NUMBER_OF_CHILDREN_WHEN_SYSTEM_EXPANDED = 5;
+ @VisibleForTesting
+ static final int NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED = 8;
+ @VisibleForTesting
+ static final int NUMBER_OF_CHILDREN_WHEN_AMBIENT = 1;
private static final AnimationProperties ALPHA_FADE_IN = new AnimationProperties() {
private AnimationFilter mAnimationFilter = new AnimationFilter().animateAlpha();
@@ -699,15 +703,18 @@
return childState.height != intrinsicHeight && !childState.hidden;
}
- private int getMaxAllowedVisibleChildren() {
+ @VisibleForTesting
+ int getMaxAllowedVisibleChildren() {
return getMaxAllowedVisibleChildren(false /* likeCollapsed */);
}
- private int getMaxAllowedVisibleChildren(boolean likeCollapsed) {
+ @VisibleForTesting
+ int getMaxAllowedVisibleChildren(boolean likeCollapsed) {
if (mContainingNotification.isShowingAmbient()) {
return NUMBER_OF_CHILDREN_WHEN_AMBIENT;
}
- if (!likeCollapsed && (mChildrenExpanded || mContainingNotification.isUserLocked())) {
+ if (!likeCollapsed && (mChildrenExpanded || mContainingNotification.isUserLocked())
+ && !showingAsLowPriority()) {
return NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED;
}
if (mIsLowPriority || !mContainingNotification.isOnKeyguard()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java
similarity index 94%
rename from packages/SystemUI/src/com/android/systemui/statusbar/NotificationListContainer.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java
index af9a3a3..fa75c71 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package com.android.systemui.statusbar;
+package com.android.systemui.statusbar.notification.stack;
import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters;
@@ -22,6 +22,10 @@
import android.view.ViewGroup;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.ExpandableView;
+import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.logging.NotificationLogger;
/**
* Interface representing the entity that contains notifications. It can have
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationRoundnessManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java
similarity index 94%
rename from packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationRoundnessManager.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java
index f98b3d9..e32df42 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationRoundnessManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java
@@ -14,12 +14,12 @@
* limitations under the License
*/
-package com.android.systemui.statusbar.stack;
+package com.android.systemui.statusbar.notification.stack;
import android.view.View;
-import com.android.systemui.statusbar.ActivatableNotificationView;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
import java.util.HashSet;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
similarity index 97%
rename from packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 7bed01b..e8cd32b0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package com.android.systemui.statusbar.stack;
+package com.android.systemui.statusbar.notification.stack;
import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator
.ExpandAnimationParameters;
@@ -34,7 +34,6 @@
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
-import android.graphics.Path;
import android.graphics.PointF;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
@@ -46,11 +45,9 @@
import androidx.annotation.VisibleForTesting;
import androidx.core.graphics.ColorUtils;
import android.util.AttributeSet;
-import android.util.FloatProperty;
import android.util.Log;
import android.util.MathUtils;
import android.util.Pair;
-import android.util.Property;
import android.view.ContextThemeWrapper;
import android.view.InputDevice;
import android.view.MotionEvent;
@@ -80,19 +77,18 @@
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
-import com.android.systemui.statusbar.ActivatableNotificationView;
+import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
import com.android.systemui.statusbar.EmptyShadeView;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
-import com.android.systemui.statusbar.ExpandableView;
-import com.android.systemui.statusbar.FooterView;
-import com.android.systemui.statusbar.NotificationBlockingHelperManager;
-import com.android.systemui.statusbar.NotificationData;
-import com.android.systemui.statusbar.NotificationGuts;
-import com.android.systemui.statusbar.NotificationListContainer;
-import com.android.systemui.statusbar.NotificationLogger;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.ExpandableView;
+import com.android.systemui.statusbar.notification.row.FooterView;
+import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager;
+import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.row.NotificationGuts;
+import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.NotificationShelf;
-import com.android.systemui.statusbar.NotificationSnooze;
-import com.android.systemui.statusbar.StackScrollerDecorView;
+import com.android.systemui.statusbar.notification.row.NotificationSnooze;
+import com.android.systemui.statusbar.notification.row.StackScrollerDecorView;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.FakeShadowView;
import com.android.systemui.statusbar.notification.NotificationUtils;
@@ -375,25 +371,22 @@
private boolean mScrollable;
private View mForcedScroll;
private View mNeedingPulseAnimation;
- private float mDarkAmount = 0f;
+
+ /**
+ * @see #setDarkAmount(float, float)
+ */
+ private float mInterpolatedDarkAmount = 0f;
+
+ /**
+ * @see #setDarkAmount(float, float)
+ */
+ private float mLinearDarkAmount = 0f;
/**
* How fast the background scales in the X direction as a factor of the Y expansion.
*/
private float mBackgroundXFactor = 1f;
- private static final Property<NotificationStackScrollLayout, Float> DARK_AMOUNT =
- new FloatProperty<NotificationStackScrollLayout>("darkAmount") {
- @Override
- public void setValue(NotificationStackScrollLayout object, float value) {
- object.setDarkAmount(value);
- }
- @Override
- public Float get(NotificationStackScrollLayout object) {
- return object.getDarkAmount();
- }
- };
- private ObjectAnimator mDarkAmountAnimator;
private boolean mUsingLightTheme;
private boolean mQsExpanded;
private boolean mForwardScrollable;
@@ -422,6 +415,9 @@
private int mHeadsUpInset;
private HeadsUpAppearanceController mHeadsUpAppearanceController;
private NotificationIconAreaController mIconAreaController;
+ private float mVerticalPanelTranslation;
+
+ private Interpolator mDarkXInterpolator = Interpolators.FAST_OUT_SLOW_IN;
public NotificationStackScrollLayout(Context context) {
this(context, null);
@@ -517,6 +513,20 @@
mSwipeHelper.onMenuShown(row);
}
+ public void onUiModeChanged() {
+ mBgColor = mContext.getColor(R.color.notification_shade_background_color);
+ updateBackgroundDimming();
+
+ // Re-inflate all notification views
+ int childCount = getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ View child = getChildAt(i);
+ if (child instanceof ActivatableNotificationView) {
+ ((ActivatableNotificationView) child).onUiModeChanged();
+ }
+ }
+ }
+
protected void onDraw(Canvas canvas) {
if (mShouldDrawNotificationBackground
&& (mCurrentBounds.top < mCurrentBounds.bottom || mAmbientState.isDark())) {
@@ -557,16 +567,16 @@
canvas.drawRect(darkLeft, darkTop, darkRight, darkBottom, mBackgroundPaint);
}
} else {
- float inverseDark = 1 - mDarkAmount;
- float yProgress = Interpolators.FAST_OUT_SLOW_IN.getInterpolation(inverseDark);
- float xProgress = Interpolators.FAST_OUT_SLOW_IN
- .getInterpolation(inverseDark * mBackgroundXFactor);
+ float yProgress = 1 - mInterpolatedDarkAmount;
+ float xProgress = mDarkXInterpolator.getInterpolation(
+ (1 - mLinearDarkAmount) * mBackgroundXFactor);
mBackgroundAnimationRect.set(
(int) MathUtils.lerp(darkLeft, lockScreenLeft, xProgress),
(int) MathUtils.lerp(darkTop, lockScreenTop, yProgress),
(int) MathUtils.lerp(darkRight, lockScreenRight, xProgress),
(int) MathUtils.lerp(darkBottom, lockScreenBottom, yProgress));
+
if (!mAmbientState.isDark() || mFirstVisibleBackgroundChild != null) {
canvas.drawRoundRect(mBackgroundAnimationRect.left, mBackgroundAnimationRect.top,
mBackgroundAnimationRect.right, mBackgroundAnimationRect.bottom,
@@ -584,14 +594,15 @@
float alpha =
BACKGROUND_ALPHA_DIMMED + (1 - BACKGROUND_ALPHA_DIMMED) * (1.0f - mDimAmount);
- alpha *= 1f - mDarkAmount;
+ alpha *= 1f - mInterpolatedDarkAmount;
// We need to manually blend in the background color.
int scrimColor = mScrimController.getBackgroundColor();
int awakeColor = ColorUtils.blendARGB(scrimColor, mBgColor, alpha);
// Interpolate between semi-transparent notification panel background color
// and white AOD separator.
- float colorInterpolation = Interpolators.DECELERATE_QUINT.getInterpolation(mDarkAmount);
+ float colorInterpolation = MathUtils.smoothStep(0.4f /* start */, 1f /* end */,
+ mLinearDarkAmount);
int color = ColorUtils.blendARGB(awakeColor, Color.WHITE, colorInterpolation);
if (mCachedBackgroundColor != color) {
@@ -664,11 +675,15 @@
int width = MeasureSpec.getSize(widthMeasureSpec);
int childWidthSpec = MeasureSpec.makeMeasureSpec(width - mSidePaddings * 2,
MeasureSpec.getMode(widthMeasureSpec));
+ // Don't constrain the height of the children so we know how big they'd like to be
+ int childHeightSpec = MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(heightMeasureSpec),
+ MeasureSpec.UNSPECIFIED);
+
// We need to measure all children even the GONE ones, such that the heights are calculated
// correctly as they are used to calculate how many we can fit on the screen.
final int size = getChildCount();
for (int i = 0; i < size; i++) {
- measureChild(getChildAt(i), childWidthSpec, heightMeasureSpec);
+ measureChild(getChildAt(i), childWidthSpec, childHeightSpec);
}
}
@@ -735,7 +750,8 @@
}
private void updateAlgorithmHeightAndPadding() {
- mTopPadding = (int) MathUtils.lerp(mRegularTopPadding, mDarkTopPadding, mDarkAmount);
+ mTopPadding = (int) MathUtils.lerp(mRegularTopPadding, mDarkTopPadding,
+ mInterpolatedDarkAmount);
mAmbientState.setLayoutHeight(getLayoutHeight());
updateAlgorithmLayoutMinHeight();
mAmbientState.setTopPadding(mTopPadding);
@@ -960,7 +976,7 @@
}
public void updateClipping() {
- boolean animatingClipping = mDarkAmount > 0 && mDarkAmount < 1;
+ boolean animatingClipping = mInterpolatedDarkAmount > 0 && mInterpolatedDarkAmount < 1;
boolean clipped = mRequestedClipBounds != null && !mInHeadsUpPinnedMode
&& !mHeadsUpAnimatingAway;
if (mIsClipped != clipped) {
@@ -982,7 +998,7 @@
* Measured relative to the resting position.
*/
private float getExpandTranslationStart() {
- return - mTopPadding;
+ return -mTopPadding + getMinExpansionHeight();
}
/**
@@ -1422,7 +1438,8 @@
*/
private int targetScrollForView(ExpandableView v, int positionInLinearLayout) {
return positionInLinearLayout + v.getIntrinsicHeight() +
- getImeInset() - getHeight() + getTopPadding();
+ getImeInset() - getHeight()
+ + ((!isExpanded() && isPinnedHeadsUp(v)) ? mHeadsUpInset : getTopPadding());
}
@Override
@@ -2053,9 +2070,15 @@
}
private int getScrollRange() {
- int scrollRange = Math.max(0, mContentHeight - mMaxLayoutHeight);
+ // In current design, it only use the top HUN to treat all of HUNs
+ // although there are more than one HUNs
+ int contentHeight = mContentHeight;
+ if (!isExpanded() && mHeadsUpManager.hasPinnedHeadsUp()) {
+ contentHeight = mHeadsUpInset + getTopHeadsUpPinnedHeight();
+ }
+ int scrollRange = Math.max(0, contentHeight - mMaxLayoutHeight);
int imeInset = getImeInset();
- scrollRange += Math.min(imeInset, Math.max(0, mContentHeight - (getHeight() - imeInset)));
+ scrollRange += Math.min(imeInset, Math.max(0, contentHeight - (getHeight() - imeInset)));
return scrollRange;
}
@@ -2420,7 +2443,7 @@
return;
}
- final boolean awake = mDarkAmount != 0 || mAmbientState.isDark();
+ final boolean awake = mInterpolatedDarkAmount != 0 || mAmbientState.isDark();
mScrimController.setExcludedBackgroundArea(
mFadingOut || mParentNotFullyVisible || awake || mIsClipped ? null
: mCurrentBounds);
@@ -3168,7 +3191,7 @@
private void startAnimationToState() {
if (mNeedsAnimation) {
- generateChildHierarchyEvents();
+ generateAllAnimationEvents();
mNeedsAnimation = false;
}
if (!mAnimationEvents.isEmpty() || isCurrentlyAnimating()) {
@@ -3185,7 +3208,7 @@
mGoToFullShadeDelay = 0;
}
- private void generateChildHierarchyEvents() {
+ private void generateAllAnimationEvents() {
generateHeadsUpAnimationEvents();
generateChildRemovalEvents();
generateChildAdditionEvents();
@@ -3202,7 +3225,6 @@
generateGroupExpansionEvent();
generateAnimateEverythingEvent();
generatePulsingAnimationEvent();
- mNeedsAnimation = false;
}
private void generateHeadsUpAnimationEvents() {
@@ -3413,7 +3435,6 @@
.animateY(mShelf));
ev.darkAnimationOriginIndex = mDarkAnimationOriginIndex;
mAnimationEvents.add(ev);
- startDarkAmountAnimation();
}
mDarkNeedsAnimation = false;
}
@@ -3989,11 +4010,8 @@
if (animate && mAnimationsEnabled) {
mDarkNeedsAnimation = true;
mDarkAnimationOriginIndex = findDarkAnimationOriginIndex(touchWakeUpScreenLocation);
- mNeedsAnimation = true;
+ mNeedsAnimation = true;
} else {
- if (mDarkAmountAnimator != null) {
- mDarkAmountAnimator.cancel();
- }
setDarkAmount(dark ? 1f : 0f);
updateBackground();
}
@@ -4003,8 +4021,13 @@
notifyHeightChangeListener(mShelf);
}
- private void updateAntiBurnInTranslation() {
- setTranslationX(mAntiBurnInOffsetX * mDarkAmount);
+ private void updatePanelTranslation() {
+ setTranslationX(mVerticalPanelTranslation + mAntiBurnInOffsetX * mInterpolatedDarkAmount);
+ }
+
+ public void setVerticalPanelTranslation(float verticalPanelTranslation) {
+ mVerticalPanelTranslation = verticalPanelTranslation;
+ updatePanelTranslation();
}
/**
@@ -4018,9 +4041,22 @@
}
private void setDarkAmount(float darkAmount) {
- mDarkAmount = darkAmount;
+ setDarkAmount(darkAmount, darkAmount);
+ }
+
+ /**
+ * Sets the current dark amount.
+ *
+ * @param linearDarkAmount The dark amount that follows linear interpoloation in the animation,
+ * i.e. animates from 0 to 1 or vice-versa in a linear manner.
+ * @param interpolatedDarkAmount The dark amount that follows the actual interpolation of the
+ * animation curve.
+ */
+ public void setDarkAmount(float linearDarkAmount, float interpolatedDarkAmount) {
+ mLinearDarkAmount = linearDarkAmount;
+ mInterpolatedDarkAmount = interpolatedDarkAmount;
boolean wasFullyDark = mAmbientState.isFullyDark();
- mAmbientState.setDarkAmount(darkAmount);
+ mAmbientState.setDarkAmount(interpolatedDarkAmount);
boolean nowFullyDark = mAmbientState.isFullyDark();
if (nowFullyDark != wasFullyDark) {
updateContentHeight();
@@ -4034,46 +4070,28 @@
}
updateAlgorithmHeightAndPadding();
updateBackgroundDimming();
- updateAntiBurnInTranslation();
+ updatePanelTranslation();
requestChildrenUpdate();
}
- public float getDarkAmount() {
- return mDarkAmount;
+ public void notifyDarkAnimationStart(boolean dark) {
+ // We only swap the scaling factor if we're fully dark or fully awake to avoid
+ // interpolation issues when playing with the power button.
+ if (mInterpolatedDarkAmount == 0 || mInterpolatedDarkAmount == 1) {
+ mBackgroundXFactor = dark ? 1.8f : 1.5f;
+ mDarkXInterpolator = dark
+ ? Interpolators.FAST_OUT_SLOW_IN_REVERSE
+ : Interpolators.FAST_OUT_SLOW_IN;
+ }
}
- /**
- * Cancel any previous dark animations - to avoid race conditions - and creates a new one.
- * This function also sets {@code mBackgroundXFactor} based on the current {@code mDarkAmount}.
- */
- private void startDarkAmountAnimation() {
- boolean dark = mAmbientState.isDark();
- if (mDarkAmountAnimator != null) {
- mDarkAmountAnimator.cancel();
- }
-
+ public long getDarkAnimationDuration(boolean dark) {
long duration = StackStateAnimator.ANIMATION_DURATION_WAKEUP;
// Longer animation when sleeping with more than 1 notification
if (dark && getNotGoneChildCount() > 2) {
duration *= 1.2f;
}
-
- mDarkAmountAnimator = ObjectAnimator.ofFloat(this, DARK_AMOUNT, mDarkAmount,
- dark ? 1f : 0);
- // We only swap the scaling factor if we're fully dark or fully awake to avoid
- // interpolation issues when playing with the power button.
- if (mDarkAmount == 0 || mDarkAmount == 1) {
- mBackgroundXFactor = dark ? 2.5f : 1.5f;
- }
- mDarkAmountAnimator.setDuration(duration);
- mDarkAmountAnimator.setInterpolator(Interpolators.LINEAR);
- mDarkAmountAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mDarkAmountAnimator = null;
- }
- });
- mDarkAmountAnimator.start();
+ return duration;
}
private int findDarkAnimationOriginIndex(@Nullable PointF screenLocation) {
@@ -4602,7 +4620,7 @@
public void setAntiBurnInOffsetX(int antiBurnInOffsetX) {
mAntiBurnInOffsetX = antiBurnInOffsetX;
- updateAntiBurnInTranslation();
+ updatePanelTranslation();
}
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
similarity index 96%
rename from packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
index ee006d3..742d89d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package com.android.systemui.statusbar.stack;
+package com.android.systemui.statusbar.notification.stack;
import android.content.Context;
import android.content.res.Resources;
@@ -23,9 +23,9 @@
import android.view.ViewGroup;
import com.android.systemui.R;
import com.android.systemui.statusbar.EmptyShadeView;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
-import com.android.systemui.statusbar.ExpandableView;
-import com.android.systemui.statusbar.FooterView;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.ExpandableView;
+import com.android.systemui.statusbar.notification.row.FooterView;
import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.notification.NotificationUtils;
@@ -34,7 +34,7 @@
import java.util.List;
/**
- * The Algorithm of the {@link com.android.systemui.statusbar.stack
+ * The Algorithm of the {@link com.android.systemui.statusbar.notification.stack
* .NotificationStackScrollLayout} which can be queried for {@link com.android.systemui.statusbar
* .stack.StackScrollState}
*/
@@ -473,6 +473,15 @@
childState.yTranslation = topState.yTranslation + topState.height
- childState.height;
}
+
+ // heads up notification show and this row is the top entry of heads up
+ // notifications. i.e. this row should be the only one row that has input field
+ // To check if the row need to do translation according to scroll Y
+ // heads up show full of row's content and any scroll y indicate that the
+ // translationY need to move up the HUN.
+ if (!mIsExpanded && isTopEntry && ambientState.getScrollY() > 0) {
+ childState.yTranslation -= ambientState.getScrollY();
+ }
}
if (row.isHeadsUpAnimatingAway()) {
childState.hidden = false;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollState.java
similarity index 92%
rename from packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollState.java
index 588b758..c03fd22 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollState.java
@@ -14,21 +14,21 @@
* limitations under the License
*/
-package com.android.systemui.statusbar.stack;
+package com.android.systemui.statusbar.notification.stack;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
-import com.android.systemui.R;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
-import com.android.systemui.statusbar.ExpandableView;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.ExpandableView;
import java.util.List;
import java.util.WeakHashMap;
/**
- * A state of a {@link com.android.systemui.statusbar.stack.NotificationStackScrollLayout} which
+ * A state of a
+ * {@link com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout} which
* can be applied to a viewGroup.
*/
public class StackScrollState {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
similarity index 98%
rename from packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
index b83a09d..da3fb66 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
@@ -14,21 +14,20 @@
* limitations under the License
*/
-package com.android.systemui.statusbar.stack;
+package com.android.systemui.statusbar.notification.stack;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.util.Property;
import android.view.View;
-import android.view.ViewGroup;
import android.view.animation.Interpolator;
import com.android.keyguard.KeyguardSliceView;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
-import com.android.systemui.statusbar.ExpandableView;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.ExpandableView;
import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.StatusBarIconView;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/ViewState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ViewState.java
similarity index 98%
rename from packages/SystemUI/src/com/android/systemui/statusbar/stack/ViewState.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ViewState.java
index 4b3643f..1f3244f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/ViewState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ViewState.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package com.android.systemui.statusbar.stack;
+package com.android.systemui.statusbar.notification.stack;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -27,15 +27,15 @@
import com.android.systemui.Interpolators;
import com.android.systemui.R;
-import com.android.systemui.statusbar.ExpandableView;
+import com.android.systemui.statusbar.notification.row.ExpandableView;
import com.android.systemui.statusbar.notification.AnimatableProperty;
import com.android.systemui.statusbar.notification.PropertyAnimator;
import com.android.systemui.statusbar.policy.HeadsUpUtil;
/**
* A state of a view. This can be used to apply a set of view properties to a view with
- * {@link com.android.systemui.statusbar.stack.StackScrollState} or start animations with
- * {@link com.android.systemui.statusbar.stack.StackStateAnimator}.
+ * {@link com.android.systemui.statusbar.notification.stack.StackScrollState} or start
+ * animations with {@link com.android.systemui.statusbar.notification.stack.StackStateAnimator}.
*/
public class ViewState {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
similarity index 73%
rename from packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
index f0b1a82..2087a16 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.phone;
+import android.hardware.biometrics.BiometricSourceType;
import android.content.Context;
import android.os.Handler;
import android.os.PowerManager;
@@ -35,14 +36,14 @@
import java.io.PrintWriter;
/**
- * Controller which coordinates all the fingerprint unlocking actions with the UI.
+ * Controller which coordinates all the biometric unlocking actions with the UI.
*/
-public class FingerprintUnlockController extends KeyguardUpdateMonitorCallback {
+public class BiometricUnlockController extends KeyguardUpdateMonitorCallback {
- private static final String TAG = "FingerprintController";
- private static final boolean DEBUG_FP_WAKELOCK = KeyguardConstants.DEBUG_FP_WAKELOCK;
- private static final long FINGERPRINT_WAKELOCK_TIMEOUT_MS = 15 * 1000;
- private static final String FINGERPRINT_WAKE_LOCK_NAME = "wake-and-unlock wakelock";
+ private static final String TAG = "BiometricUnlockController";
+ private static final boolean DEBUG_BIO_WAKELOCK = KeyguardConstants.DEBUG_BIOMETRIC_WAKELOCK;
+ private static final long BIOMETRIC_WAKELOCK_TIMEOUT_MS = 15 * 1000;
+ private static final String BIOMETRIC_WAKE_LOCK_NAME = "wake-and-unlock wakelock";
/**
* Mode in which we don't need to wake up the device when we get a fingerprint.
@@ -90,9 +91,9 @@
public static final int MODE_WAKE_AND_UNLOCK_FROM_DREAM = 7;
/**
- * How much faster we collapse the lockscreen when authenticating with fingerprint.
+ * How much faster we collapse the lockscreen when authenticating with biometric.
*/
- private static final float FINGERPRINT_COLLAPSE_SPEEDUP_FACTOR = 1.1f;
+ private static final float BIOMETRIC_COLLAPSE_SPEEDUP_FACTOR = 1.1f;
private PowerManager mPowerManager;
private Handler mHandler = new Handler();
@@ -108,15 +109,16 @@
private final UnlockMethodCache mUnlockMethodCache;
private final Context mContext;
private int mPendingAuthenticatedUserId = -1;
+ private BiometricSourceType mPendingAuthenticatedBioSourceType = null;
private boolean mPendingShowBouncer;
private boolean mHasScreenTurnedOnSinceAuthenticating;
- public FingerprintUnlockController(Context context,
- DozeScrimController dozeScrimController,
- KeyguardViewMediator keyguardViewMediator,
- ScrimController scrimController,
- StatusBar statusBar,
- UnlockMethodCache unlockMethodCache) {
+ public BiometricUnlockController(Context context,
+ DozeScrimController dozeScrimController,
+ KeyguardViewMediator keyguardViewMediator,
+ ScrimController scrimController,
+ StatusBar statusBar,
+ UnlockMethodCache unlockMethodCache) {
mContext = context;
mPowerManager = context.getSystemService(PowerManager.class);
mUpdateMonitor = KeyguardUpdateMonitor.getInstance(context);
@@ -136,21 +138,21 @@
mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
}
- private final Runnable mReleaseFingerprintWakeLockRunnable = new Runnable() {
+ private final Runnable mReleaseBiometricWakeLockRunnable = new Runnable() {
@Override
public void run() {
- if (DEBUG_FP_WAKELOCK) {
- Log.i(TAG, "fp wakelock: TIMEOUT!!");
+ if (DEBUG_BIO_WAKELOCK) {
+ Log.i(TAG, "biometric wakelock: TIMEOUT!!");
}
- releaseFingerprintWakeLock();
+ releaseBiometricWakeLock();
}
};
- private void releaseFingerprintWakeLock() {
+ private void releaseBiometricWakeLock() {
if (mWakeLock != null) {
- mHandler.removeCallbacks(mReleaseFingerprintWakeLockRunnable);
- if (DEBUG_FP_WAKELOCK) {
- Log.i(TAG, "releasing fp wakelock");
+ mHandler.removeCallbacks(mReleaseBiometricWakeLockRunnable);
+ if (DEBUG_BIO_WAKELOCK) {
+ Log.i(TAG, "releasing biometric wakelock");
}
mWakeLock.release();
mWakeLock = null;
@@ -158,24 +160,27 @@
}
@Override
- public void onFingerprintAcquired() {
- Trace.beginSection("FingerprintUnlockController#onFingerprintAcquired");
- releaseFingerprintWakeLock();
+ public void onBiometricAcquired(BiometricSourceType biometricSourceType) {
+ Trace.beginSection("BiometricUnlockController#onBiometricAcquired");
+ releaseBiometricWakeLock();
if (!mUpdateMonitor.isDeviceInteractive()) {
if (LatencyTracker.isEnabled(mContext)) {
- LatencyTracker.getInstance(mContext).onActionStart(
- LatencyTracker.ACTION_FINGERPRINT_WAKE_AND_UNLOCK);
+ int action = LatencyTracker.ACTION_FINGERPRINT_WAKE_AND_UNLOCK;
+ if (biometricSourceType == BiometricSourceType.FACE) {
+ action = LatencyTracker.ACTION_FACE_WAKE_AND_UNLOCK;
+ }
+ LatencyTracker.getInstance(mContext).onActionStart(action);
}
mWakeLock = mPowerManager.newWakeLock(
- PowerManager.PARTIAL_WAKE_LOCK, FINGERPRINT_WAKE_LOCK_NAME);
+ PowerManager.PARTIAL_WAKE_LOCK, BIOMETRIC_WAKE_LOCK_NAME);
Trace.beginSection("acquiring wake-and-unlock");
mWakeLock.acquire();
Trace.endSection();
- if (DEBUG_FP_WAKELOCK) {
- Log.i(TAG, "fingerprint acquired, grabbing fp wakelock");
+ if (DEBUG_BIO_WAKELOCK) {
+ Log.i(TAG, "biometric acquired, grabbing biometric wakelock");
}
- mHandler.postDelayed(mReleaseFingerprintWakeLockRunnable,
- FINGERPRINT_WAKELOCK_TIMEOUT_MS);
+ mHandler.postDelayed(mReleaseBiometricWakeLockRunnable,
+ BIOMETRIC_WAKELOCK_TIMEOUT_MS);
}
Trace.endSection();
}
@@ -187,10 +192,11 @@
}
@Override
- public void onFingerprintAuthenticated(int userId) {
- Trace.beginSection("FingerprintUnlockController#onFingerprintAuthenticated");
+ public void onBiometricAuthenticated(int userId, BiometricSourceType biometricSourceType) {
+ Trace.beginSection("BiometricUnlockController#onBiometricAuthenticated");
if (mUpdateMonitor.isGoingToSleep()) {
mPendingAuthenticatedUserId = userId;
+ mPendingAuthenticatedBioSourceType = biometricSourceType;
Trace.endSection();
return;
}
@@ -210,15 +216,28 @@
// until the clock and the notifications are faded out.
mStatusBarWindowManager.setForceDozeBrightness(true);
}
- if (!wasDeviceInteractive) {
- if (DEBUG_FP_WAKELOCK) {
- Log.i(TAG, "fp wakelock: Authenticated, waking up...");
+ // During wake and unlock, we need to draw black before waking up to avoid abrupt
+ // brightness changes due to display state transitions.
+ boolean alwaysOnEnabled = DozeParameters.getInstance(mContext).getAlwaysOn();
+ boolean delayWakeUp = mode == MODE_WAKE_AND_UNLOCK && alwaysOnEnabled;
+ Runnable wakeUp = ()-> {
+ if (!wasDeviceInteractive) {
+ if (DEBUG_BIO_WAKELOCK) {
+ Log.i(TAG, "bio wakelock: Authenticated, waking up...");
+ }
+ mPowerManager.wakeUp(SystemClock.uptimeMillis(), "android.policy:BIOMETRIC");
}
- mPowerManager.wakeUp(SystemClock.uptimeMillis(), "android.policy:FINGERPRINT");
+ if (delayWakeUp) {
+ mKeyguardViewMediator.onWakeAndUnlocking();
+ }
+ Trace.beginSection("release wake-and-unlock");
+ releaseBiometricWakeLock();
+ Trace.endSection();
+ };
+
+ if (!delayWakeUp) {
+ wakeUp.run();
}
- Trace.beginSection("release wake-and-unlock");
- releaseFingerprintWakeLock();
- Trace.endSection();
switch (mMode) {
case MODE_DISMISS_BOUNCER:
Trace.beginSection("MODE_DISMISS");
@@ -251,7 +270,11 @@
mUpdateMonitor.awakenFromDream();
}
mStatusBarWindowManager.setStatusBarFocusable(false);
- mKeyguardViewMediator.onWakeAndUnlocking();
+ if (delayWakeUp) {
+ mHandler.postDelayed(wakeUp, 50);
+ } else {
+ mKeyguardViewMediator.onWakeAndUnlocking();
+ }
if (mStatusBar.getNavigationBarView() != null) {
mStatusBar.getNavigationBarView().setWakeAndUnlocking(true);
}
@@ -261,7 +284,7 @@
case MODE_NONE:
break;
}
- mStatusBar.notifyFpAuthModeChanged();
+ mStatusBar.notifyBiometricAuthModeChanged();
Trace.endSection();
}
@@ -270,7 +293,7 @@
mStatusBarKeyguardViewManager.showBouncer(false);
}
mStatusBarKeyguardViewManager.animateCollapsePanels(
- FINGERPRINT_COLLAPSE_SPEEDUP_FACTOR);
+ BIOMETRIC_COLLAPSE_SPEEDUP_FACTOR);
mPendingShowBouncer = false;
}
@@ -278,28 +301,31 @@
public void onStartedGoingToSleep(int why) {
resetMode();
mPendingAuthenticatedUserId = -1;
+ mPendingAuthenticatedBioSourceType = null;
}
@Override
public void onFinishedGoingToSleep(int why) {
- Trace.beginSection("FingerprintUnlockController#onFinishedGoingToSleep");
+ Trace.beginSection("BiometricUnlockController#onFinishedGoingToSleep");
if (mPendingAuthenticatedUserId != -1) {
// Post this to make sure it's executed after the device is fully locked.
mHandler.post(new Runnable() {
@Override
public void run() {
- onFingerprintAuthenticated(mPendingAuthenticatedUserId);
+ onBiometricAuthenticated(mPendingAuthenticatedUserId,
+ mPendingAuthenticatedBioSourceType);
}
});
}
mPendingAuthenticatedUserId = -1;
+ mPendingAuthenticatedBioSourceType = null;
Trace.endSection();
}
public boolean hasPendingAuthentication() {
return mPendingAuthenticatedUserId != -1
- && mUpdateMonitor.isUnlockingWithFingerprintAllowed()
+ && mUpdateMonitor.isUnlockingWithBiometricAllowed()
&& mPendingAuthenticatedUserId == KeyguardUpdateMonitor.getCurrentUser();
}
@@ -308,7 +334,7 @@
}
private int calculateMode() {
- boolean unlockingAllowed = mUpdateMonitor.isUnlockingWithFingerprintAllowed();
+ boolean unlockingAllowed = mUpdateMonitor.isUnlockingWithBiometricAllowed();
boolean deviceDreaming = mUpdateMonitor.isDreaming();
if (!mUpdateMonitor.isDeviceInteractive()) {
@@ -338,17 +364,18 @@
}
@Override
- public void onFingerprintAuthFailed() {
+ public void onBiometricAuthFailed(BiometricSourceType biometricSourceType) {
cleanup();
}
@Override
- public void onFingerprintError(int msgId, String errString) {
+ public void onBiometricError(int msgId, String errString,
+ BiometricSourceType biometricSourceType) {
cleanup();
}
private void cleanup() {
- releaseFingerprintWakeLock();
+ releaseBiometricWakeLock();
}
public void startKeyguardFadingAway() {
@@ -372,7 +399,7 @@
if (mStatusBar.getNavigationBarView() != null) {
mStatusBar.getNavigationBarView().setWakeAndUnlocking(false);
}
- mStatusBar.notifyFpAuthModeChanged();
+ mStatusBar.notifyBiometricAuthModeChanged();
}
private final WakefulnessLifecycle.Observer mWakefulnessObserver =
@@ -380,7 +407,7 @@
@Override
public void onFinishedWakingUp() {
if (mPendingShowBouncer) {
- FingerprintUnlockController.this.showBouncer();
+ BiometricUnlockController.this.showBouncer();
}
}
};
@@ -398,7 +425,7 @@
}
public void dump(PrintWriter pw) {
- pw.println(" FingerprintUnlockController:");
+ pw.println(" BiometricUnlockController:");
pw.print(" mMode="); pw.println(mMode);
pw.print(" mWakeLock="); pw.println(mWakeLock);
}
@@ -415,7 +442,7 @@
/**
* Successful authentication with fingerprint when the screen was either on or off.
*/
- public boolean isFingerprintUnlock() {
+ public boolean isBiometricUnlock() {
return isWakeAndUnlock() || mMode == MODE_UNLOCK;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
index ea70ebb..a781be6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
@@ -189,6 +189,14 @@
state |= DISABLE_SYSTEM_INFO;
state |= DISABLE_CLOCK;
}
+
+ // In landscape, the heads up show but shouldHideNotificationIcons() return false
+ // because the visual icon is in notification icon area rather than heads up's space.
+ // whether the notification icon show or not, clock should hide when heads up show.
+ if (mStatusBarComponent.isHeadsUpShouldBeVisible()) {
+ state |= DISABLE_CLOCK;
+ }
+
if (mNetworkController != null && EncryptionHelper.IS_DATA_ENCRYPTED) {
if (mNetworkController.hasEmergencyCryptKeeperText()) {
state |= DISABLE_NOTIFICATION_ICONS;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.java
index 6f53844..7ddca17 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.java
@@ -64,8 +64,9 @@
final float fontScale = newConfig.fontScale;
final int density = newConfig.densityDpi;
int uiMode = newConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK;
+ boolean uiModeChanged = uiMode != mUiMode;
if (density != mDensity || fontScale != mFontScale
- || (mInCarMode && uiMode != mUiMode)) {
+ || (mInCarMode && uiModeChanged)) {
listeners.forEach(l -> {
if (mListeners.contains(l)) {
l.onDensityOrFontScaleChanged();
@@ -73,7 +74,6 @@
});
mDensity = density;
mFontScale = fontScale;
- mUiMode = uiMode;
}
final LocaleList localeList = newConfig.getLocales();
@@ -86,6 +86,15 @@
});
}
+ if (uiModeChanged) {
+ mUiMode = uiMode;
+ listeners.forEach(l -> {
+ if (mListeners.contains(l)) {
+ l.onUiModeChanged();
+ }
+ });
+ }
+
if ((mLastConfig.updateFrom(newConfig) & ActivityInfo.CONFIG_ASSETS_PATHS) != 0) {
listeners.forEach(l -> {
if (mListeners.contains(l)) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java
index e0e991b..e052e53 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java
@@ -203,7 +203,7 @@
v.set(icon);
v.setStaticDrawableColor(mColor);
v.setDecorColor(mColor);
- addView(v, 0, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, mIconSize));
+ addView(v, 0, createLayoutParams());
}
public void addDemoWifiView(WifiIconState state) {
@@ -223,7 +223,7 @@
mWifiView = view;
mWifiView.applyWifiState(state);
mWifiView.setStaticDrawableColor(mColor);
- addView(view, viewIndex);
+ addView(view, viewIndex, createLayoutParams());
}
public void updateWifiState(WifiIconState state) {
@@ -244,7 +244,7 @@
// mobile always goes at the end
mMobileViews.add(view);
- addView(view, getChildCount());
+ addView(view, getChildCount(), createLayoutParams());
}
public void updateMobileState(MobileIconState state) {
@@ -290,6 +290,10 @@
return null;
}
+ private LayoutParams createLayoutParams() {
+ return new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, mIconSize);
+ }
+
@Override
public void onDarkChanged(Rect area, float darkIntensity, int tint) {
setColor(DarkIconDispatcher.getTint(area, mStatusIcons, tint));
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
index e1936fa..9acaf21 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
@@ -26,12 +26,12 @@
import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.statusbar.CrossFadeHelper;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.HeadsUpStatusBarView;
-import com.android.systemui.statusbar.NotificationData;
+import com.android.systemui.statusbar.notification.NotificationData;
import com.android.systemui.statusbar.policy.DarkIconDispatcher;
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
-import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
index 0a26e73..89107bb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
@@ -22,7 +22,6 @@
import android.content.res.Configuration;
import android.content.res.Resources;
import androidx.collection.ArraySet;
-import android.graphics.Rect;
import android.graphics.Region;
import android.graphics.Region.Op;
import android.util.Log;
@@ -31,14 +30,13 @@
import android.view.Gravity;
import android.view.View;
import android.view.ViewTreeObserver;
-import android.view.ViewTreeObserver.InternalInsetsInfo;
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.Dumpable;
import com.android.systemui.R;
import com.android.systemui.ScreenDecorations;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
-import com.android.systemui.statusbar.NotificationData;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.NotificationData;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -48,7 +46,6 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.HashSet;
-import java.util.List;
import java.util.Stack;
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
index 182293f..e804460 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
@@ -21,10 +21,10 @@
import android.view.ViewConfiguration;
import com.android.systemui.Gefingerpoken;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
-import com.android.systemui.statusbar.ExpandableView;
-import com.android.systemui.statusbar.NotificationData;
-import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.ExpandableView;
+import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
/**
* A helper class to handle touches on the heads-up views.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java
index 46d9827..e2f3319 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java
@@ -367,10 +367,10 @@
}
}
- private void startFinishingCircleAnimation(float velocity, Runnable mAnimationEndRunnable,
+ private void startFinishingCircleAnimation(float velocity, Runnable animationEndRunnable,
boolean right) {
KeyguardAffordanceView targetView = right ? mRightIcon : mLeftIcon;
- targetView.finishAnimation(velocity, mAnimationEndRunnable);
+ targetView.finishAnimation(velocity, animationEndRunnable);
}
private void setTranslation(float translation, boolean isReset, boolean animateReset) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index 3b12051..d89bcda 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -28,6 +28,7 @@
import android.app.ActivityOptions;
import android.app.ActivityTaskManager;
import android.app.admin.DevicePolicyManager;
+import android.hardware.biometrics.BiometricSourceType;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
@@ -173,6 +174,7 @@
private int mIndicationBottomMarginAmbient;
private float mDarkAmount;
private int mBurnInXOffset;
+ private int mBurnInYOffset;
public KeyguardBottomAreaView(Context context) {
this(context, null);
@@ -246,6 +248,8 @@
R.dimen.keyguard_indication_margin_bottom);
mIndicationBottomMarginAmbient = getResources().getDimensionPixelSize(
R.dimen.keyguard_indication_margin_bottom_ambient);
+ mBurnInYOffset = getResources().getDimensionPixelSize(
+ R.dimen.charging_indication_burn_in_prevention_offset_y);
updateCameraVisibility();
mUnlockMethodCache = UnlockMethodCache.getInstance(getContext());
mUnlockMethodCache.addListener(this);
@@ -318,6 +322,8 @@
R.dimen.keyguard_indication_margin_bottom);
mIndicationBottomMarginAmbient = getResources().getDimensionPixelSize(
R.dimen.keyguard_indication_margin_bottom_ambient);
+ mBurnInYOffset = getResources().getDimensionPixelSize(
+ R.dimen.charging_indication_burn_in_prevention_offset_y);
MarginLayoutParams mlp = (MarginLayoutParams) mIndicationArea.getLayoutParams();
if (mlp.bottomMargin != mIndicationBottomMargin) {
mlp.bottomMargin = mIndicationBottomMargin;
@@ -561,12 +567,6 @@
return;
}
mDarkAmount = darkAmount;
- // Let's randomize the bottom margin every time we wake up to avoid burn-in.
- if (darkAmount == 0) {
- mIndicationBottomMarginAmbient = getResources().getDimensionPixelSize(
- R.dimen.keyguard_indication_margin_bottom_ambient)
- + (int) (Math.random() * mIndicationText.getTextSize());
- }
mIndicationArea.setAlpha(MathUtils.lerp(1f, 0.7f, darkAmount));
mIndicationArea.setTranslationY(MathUtils.lerp(0,
mIndicationBottomMargin - mIndicationBottomMarginAmbient, darkAmount));
@@ -774,7 +774,8 @@
}
@Override
- public void onFingerprintRunningStateChanged(boolean running) {
+ public void onBiometricRunningStateChanged(boolean running,
+ BiometricSourceType biometricSourceType) {
mLockIcon.update();
}
@@ -842,8 +843,9 @@
public void dozeTimeTick() {
if (mDarkAmount == 1) {
// Move indication every minute to avoid burn-in
- final int dozeTranslation = mIndicationBottomMargin - mIndicationBottomMarginAmbient;
- mIndicationArea.setTranslationY(dozeTranslation + (float) Math.random() * 5);
+ int dozeTranslation = mIndicationBottomMargin - mIndicationBottomMarginAmbient;
+ int burnInYOffset = (int) (-mBurnInYOffset + Math.random() * mBurnInYOffset * 2);
+ mIndicationArea.setTranslationY(dozeTranslation + burnInYOffset);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
index e47dcea..20ea27a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
@@ -70,6 +70,7 @@
private static final int LAYOUT_CUTOUT = 1;
private static final int LAYOUT_NO_CUTOUT = 2;
+ private boolean mShowPercentAvailable;
private boolean mBatteryCharging;
private boolean mKeyguardUserSwitcherShowing;
private boolean mBatteryListening;
@@ -168,6 +169,8 @@
R.dimen.system_icons_super_container_avatarless_margin_end);
mCutoutSideNudge = getResources().getDimensionPixelSize(
R.dimen.display_cutout_margin_consumption);
+ mShowPercentAvailable = getContext().getResources().getBoolean(
+ com.android.internal.R.bool.config_battery_percentage_setting_available);
}
private void updateVisibilities() {
@@ -189,7 +192,7 @@
mMultiUserSwitch.setVisibility(View.GONE);
}
}
- mBatteryView.setForceShowPercent(mBatteryCharging);
+ mBatteryView.setForceShowPercent(mBatteryCharging && mShowPercentAvailable);
}
private void updateSystemIconsLayoutParams() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
index 8b8cbfe..8cace72 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
@@ -44,7 +44,7 @@
private final DarkIconDispatcher mStatusBarIconController;
private final BatteryController mBatteryController;
- private FingerprintUnlockController mFingerprintUnlockController;
+ private BiometricUnlockController mBiometricUnlockController;
private LightBarTransitionsController mNavigationBarController;
private int mSystemUiVisibility;
@@ -89,9 +89,9 @@
updateNavigation();
}
- public void setFingerprintUnlockController(
- FingerprintUnlockController fingerprintUnlockController) {
- mFingerprintUnlockController = fingerprintUnlockController;
+ public void setBiometricUnlockController(
+ BiometricUnlockController biometricUnlockController) {
+ mBiometricUnlockController = biometricUnlockController;
}
public void onSystemUiVisibilityChanged(int fullscreenStackVis, int dockedStackVis,
@@ -178,12 +178,12 @@
}
private boolean animateChange() {
- if (mFingerprintUnlockController == null) {
+ if (mBiometricUnlockController == null) {
return false;
}
- int unlockMode = mFingerprintUnlockController.getMode();
- return unlockMode != FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING
- && unlockMode != FingerprintUnlockController.MODE_WAKE_AND_UNLOCK;
+ int unlockMode = mBiometricUnlockController.getMode();
+ return unlockMode != BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING
+ && unlockMode != BiometricUnlockController.MODE_WAKE_AND_UNLOCK;
}
private void updateStatus(Rect fullscreenStackBounds, Rect dockedStackBounds) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
index 4b66ee5a..8928530 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
@@ -306,7 +306,7 @@
private int getState() {
KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
boolean fingerprintRunning = updateMonitor.isFingerprintDetectionRunning();
- boolean unlockingAllowed = updateMonitor.isUnlockingWithFingerprintAllowed();
+ boolean unlockingAllowed = updateMonitor.isUnlockingWithBiometricAllowed();
if (mTransientFpError) {
return STATE_FINGERPRINT_ERROR;
} else if (mUnlockMethodCache.canSkipBouncer()) {
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 1003833..22833b8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -33,8 +33,6 @@
import android.animation.ObjectAnimator;
import android.annotation.IdRes;
import android.annotation.Nullable;
-import android.app.ActivityManager;
-import android.app.ActivityManagerNative;
import android.app.ActivityTaskManager;
import android.app.Fragment;
import android.app.IActivityManager;
@@ -98,7 +96,7 @@
import com.android.systemui.statusbar.policy.KeyButtonDrawable;
import com.android.systemui.statusbar.policy.KeyButtonView;
import com.android.systemui.statusbar.policy.RotationLockController;
-import com.android.systemui.statusbar.stack.StackStateAnimator;
+import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
import java.io.FileDescriptor;
import java.io.PrintWriter;
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 dce7537..ed1ae10 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
@@ -75,7 +75,6 @@
private int mTouchDownY;
private boolean mDownOnRecents;
private VelocityTracker mVelocityTracker;
- private boolean mIsInScreenPinning;
private boolean mNotificationsVisibleOnDown;
private boolean mDockWindowEnabled;
@@ -110,7 +109,6 @@
public boolean onInterceptTouchEvent(MotionEvent event) {
if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
- mIsInScreenPinning = mNavigationBarView.inScreenPinning();
mNotificationsVisibleOnDown = !mStatusBar.isPresenterFullyCollapsed();
}
if (!canHandleGestures()) {
@@ -277,8 +275,7 @@
}
private boolean canHandleGestures() {
- return !mIsInScreenPinning && !mStatusBar.isKeyguardShowing()
- && !mNotificationsVisibleOnDown;
+ return !mStatusBar.isKeyguardShowing() && !mNotificationsVisibleOnDown;
}
private int calculateDragMode() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index 8b9e12c..6077e79 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -18,6 +18,7 @@
import static android.view.MotionEvent.ACTION_DOWN;
import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_BACK;
+import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_DEAD_ZONE;
import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_HOME;
import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_NONE;
@@ -161,6 +162,13 @@
private int mRotateBtnStyle = R.style.RotateButtonCCWStart90;
+ /**
+ * Helper that is responsible for showing the right toast when a disallowed activity operation
+ * occurred. In pinned mode, we show instructions on how to break out of this mode, whilst in
+ * fully locked mode we only show that unlocking is blocked.
+ */
+ private ScreenPinningNotify mScreenPinningNotify;
+
private class NavTransitionListener implements TransitionListener {
private boolean mBackTransitioning;
private boolean mHomeAppearing;
@@ -286,6 +294,7 @@
mConfiguration.updateFrom(context.getResources().getConfiguration());
reloadNavIcons();
+ mScreenPinningNotify = new ScreenPinningNotify(mContext);
mBarTransitions = new NavigationBarTransitions(this);
mButtonDispatchers.put(R.id.back, new ButtonDispatcher(R.id.back));
@@ -328,15 +337,15 @@
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
- if (shouldDeadZoneConsumeTouchEvents(event)) {
- return true;
- }
+ final boolean deadZoneConsumed = shouldDeadZoneConsumeTouchEvents(event);
switch (event.getActionMasked()) {
case ACTION_DOWN:
int x = (int) event.getX();
int y = (int) event.getY();
mDownHitTarget = HIT_TARGET_NONE;
- if (getBackButton().isVisible() && mBackButtonBounds.contains(x, y)) {
+ if (deadZoneConsumed) {
+ mDownHitTarget = HIT_TARGET_DEAD_ZONE;
+ } else if (getBackButton().isVisible() && mBackButtonBounds.contains(x, y)) {
mDownHitTarget = HIT_TARGET_BACK;
} else if (getHomeButton().isVisible() && mHomeButtonBounds.contains(x, y)) {
mDownHitTarget = HIT_TARGET_HOME;
@@ -353,9 +362,7 @@
@Override
public boolean onTouchEvent(MotionEvent event) {
- if (shouldDeadZoneConsumeTouchEvents(event)) {
- return true;
- }
+ shouldDeadZoneConsumeTouchEvents(event);
if (mGestureHelper.onTouchEvent(event)) {
return true;
}
@@ -764,7 +771,7 @@
showSwipeUpUI ? mQuickStepAccessibilityDelegate : null);
}
- private void updateSlippery() {
+ public void updateSlippery() {
setSlippery(!isQuickStepSwipeUpEnabled() || mPanelView.isFullyExpanded());
}
@@ -983,6 +990,18 @@
mBarTransitions.reapplyDarkIntensity();
}
+ public void showPinningEnterExitToast(boolean entering) {
+ if (entering) {
+ mScreenPinningNotify.showPinningStartToast();
+ } else {
+ mScreenPinningNotify.showPinningExitToast();
+ }
+ }
+
+ public void showPinningEscapeToast() {
+ mScreenPinningNotify.showEscapeToast(isRecentsButtonVisible());
+ }
+
public boolean isVertical() {
return mVertical;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
index 8858381..37c2fdf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
@@ -22,8 +22,8 @@
import androidx.annotation.Nullable;
import android.util.Log;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
-import com.android.systemui.statusbar.NotificationData;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.NotificationData;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
index a5b56eb..cb3a0d7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
@@ -14,15 +14,15 @@
import com.android.internal.util.ContrastColorUtil;
import com.android.systemui.Dependency;
import com.android.systemui.R;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
-import com.android.systemui.statusbar.NotificationData;
-import com.android.systemui.statusbar.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.StatusBarIconView;
import com.android.systemui.statusbar.notification.NotificationUtils;
import com.android.systemui.statusbar.policy.DarkIconDispatcher;
import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver;
-import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import java.util.ArrayList;
import java.util.function.Function;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
index 653471d..0db408c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
@@ -35,9 +35,9 @@
import com.android.systemui.R;
import com.android.systemui.statusbar.AlphaOptimizedFrameLayout;
import com.android.systemui.statusbar.StatusBarIconView;
-import com.android.systemui.statusbar.stack.AnimationFilter;
-import com.android.systemui.statusbar.stack.AnimationProperties;
-import com.android.systemui.statusbar.stack.ViewState;
+import com.android.systemui.statusbar.notification.stack.AnimationFilter;
+import com.android.systemui.statusbar.notification.stack.AnimationProperties;
+import com.android.systemui.statusbar.notification.stack.ViewState;
import java.util.ArrayList;
import java.util.HashMap;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 165b9b4..1dfa5b7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -47,6 +47,7 @@
import android.view.ViewGroup;
import android.view.WindowInsets;
import android.view.accessibility.AccessibilityManager;
+import android.view.animation.Interpolator;
import android.widget.FrameLayout;
import com.android.internal.logging.MetricsLogger;
@@ -59,13 +60,13 @@
import com.android.systemui.fragments.FragmentHostManager;
import com.android.systemui.fragments.FragmentHostManager.FragmentListener;
import com.android.systemui.plugins.qs.QS;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
-import com.android.systemui.statusbar.ExpandableView;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.ExpandableView;
import com.android.systemui.statusbar.FlingAnimationUtils;
import com.android.systemui.statusbar.GestureRecorder;
import com.android.systemui.statusbar.KeyguardAffordanceView;
import com.android.systemui.statusbar.KeyguardIndicationController;
-import com.android.systemui.statusbar.NotificationData;
+import com.android.systemui.statusbar.notification.NotificationData;
import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
@@ -73,9 +74,9 @@
import com.android.systemui.statusbar.notification.PropertyAnimator;
import com.android.systemui.statusbar.policy.KeyguardUserSwitcher;
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
-import com.android.systemui.statusbar.stack.AnimationProperties;
-import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
-import com.android.systemui.statusbar.stack.StackStateAnimator;
+import com.android.systemui.statusbar.notification.stack.AnimationProperties;
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
+import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -109,17 +110,20 @@
private static final AnimationProperties CLOCK_ANIMATION_PROPERTIES = new AnimationProperties()
.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
private static final FloatProperty<NotificationPanelView> SET_DARK_AMOUNT_PROPERTY =
- new FloatProperty<NotificationPanelView>("mDarkAmount") {
+ new FloatProperty<NotificationPanelView>("mInterpolatedDarkAmount") {
+
@Override
public void setValue(NotificationPanelView object, float value) {
- object.setDarkAmount(value);
+ object.setDarkAmount(value, object.mDarkInterpolator.getInterpolation(value));
}
@Override
public Float get(NotificationPanelView object) {
- return object.mDarkAmount;
+ return object.mLinearDarkAmount;
}
};
+
+ private Interpolator mDarkInterpolator;
private final PowerManager mPowerManager;
private final AccessibilityManager mAccessibilityManager;
@@ -223,6 +227,7 @@
private boolean mClosingWithAlphaFadeOut;
private boolean mHeadsUpAnimatingAway;
private boolean mLaunchingAffordance;
+ private boolean mAffordanceHasPreview;
private FalsingManager mFalsingManager;
private String mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_AFFORDANCE;
@@ -238,7 +243,18 @@
private int mIndicationBottomPadding;
private int mAmbientIndicationBottomPadding;
private boolean mIsFullWidth;
- private float mDarkAmount;
+
+ /**
+ * Current dark amount that follows regular interpolation curve of animation.
+ */
+ private float mInterpolatedDarkAmount;
+
+ /**
+ * Dark amount that animates from 0 to 1 or vice-versa in linear manner, even if the
+ * interpolation curve is different.
+ */
+ private float mLinearDarkAmount;
+
private float mDarkAmountTarget;
private boolean mPulsing;
private LockscreenGestureLogger mLockscreenGestureLogger = new LockscreenGestureLogger();
@@ -393,7 +409,7 @@
false);
addView(mKeyguardBottomArea, index);
initBottomArea();
- setDarkAmount(mDarkAmount);
+ setDarkAmount(mLinearDarkAmount, mInterpolatedDarkAmount);
setKeyguardStatusViewVisibility(mStatusBarState, false, false);
setKeyguardBottomAreaVisibility(mStatusBarState, false);
@@ -507,7 +523,7 @@
getExpandedFraction(),
totalHeight,
mKeyguardStatusView.getHeight(),
- mDarkAmount,
+ mInterpolatedDarkAmount,
mStatusBar.isKeyguardCurrentlySecure(),
mPulsing,
mBouncerTop);
@@ -1918,7 +1934,7 @@
if (view == null && mQsExpanded) {
return;
}
- if (needsAnimation && mDarkAmount == 0) {
+ if (needsAnimation && mInterpolatedDarkAmount == 0) {
mAnimateNextPositionUpdate = true;
}
ExpandableView firstChildNotGone = mNotificationStackScroller.getFirstChildNotGone();
@@ -2491,7 +2507,7 @@
}
protected void setVerticalPanelTranslation(float translation) {
- mNotificationStackScroller.setTranslationX(translation);
+ mNotificationStackScroller.setVerticalPanelTranslation(translation);
mQsFrame.setTranslationX(translation);
int size = mVerticalTranslationListener.size();
for (int i = 0; i < size; i++) {
@@ -2517,7 +2533,8 @@
}
private void updateStatusBarIcons() {
- boolean showIconsWhenExpanded = isFullWidth() && getExpandedHeight() < getOpeningHeight();
+ boolean showIconsWhenExpanded = (isPanelVisibleBecauseOfHeadsUp() || isFullWidth())
+ && getExpandedHeight() < getOpeningHeight();
if (showIconsWhenExpanded && mNoVisibleNotifications && isOnKeyguard()) {
showIconsWhenExpanded = false;
}
@@ -2571,6 +2588,7 @@
} else {
animate = false;
}
+ mAffordanceHasPreview = mKeyguardBottomArea.getRightPreview() != null;
mAffordanceHelper.launchAffordance(animate, getLayoutDirection() == LAYOUT_DIRECTION_RTL);
}
@@ -2616,6 +2634,13 @@
}
/**
+ * Return true when a bottom affordance is launching an occluded activity with a splash screen.
+ */
+ public boolean isLaunchingAffordanceWithPreview() {
+ return mLaunchingAffordance && mAffordanceHasPreview;
+ }
+
+ /**
* Whether the camera application can be launched for the camera launch gesture.
*
* @param keyguardIsShowing whether keyguard is being shown
@@ -2720,20 +2745,28 @@
}
mDarkAmountTarget = darkAmount;
if (animate) {
+ if (mInterpolatedDarkAmount == 0f || mInterpolatedDarkAmount == 1f) {
+ mDarkInterpolator = dozing
+ ? Interpolators.FAST_OUT_SLOW_IN
+ : Interpolators.TOUCH_RESPONSE_REVERSE;
+ }
+ mNotificationStackScroller.notifyDarkAnimationStart(dozing);
mDarkAnimator = ObjectAnimator.ofFloat(this, SET_DARK_AMOUNT_PROPERTY, darkAmount);
- mDarkAnimator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
- mDarkAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_WAKEUP);
+ mDarkAnimator.setInterpolator(Interpolators.LINEAR);
+ mDarkAnimator.setDuration(mNotificationStackScroller.getDarkAnimationDuration(dozing));
mDarkAnimator.start();
} else {
- setDarkAmount(darkAmount);
+ setDarkAmount(darkAmount, darkAmount);
}
}
- private void setDarkAmount(float amount) {
- mDarkAmount = amount;
- mKeyguardStatusView.setDarkAmount(mDarkAmount);
- mKeyguardBottomArea.setDarkAmount(mDarkAmount);
+ private void setDarkAmount(float linearAmount, float amount) {
+ mInterpolatedDarkAmount = amount;
+ mLinearDarkAmount = linearAmount;
+ mKeyguardStatusView.setDarkAmount(mInterpolatedDarkAmount);
+ mKeyguardBottomArea.setDarkAmount(mInterpolatedDarkAmount);
positionClockAndNotifications();
+ mNotificationStackScroller.setDarkAmount(linearAmount, mInterpolatedDarkAmount);
}
public void setPulsing(boolean pulsing) {
@@ -2758,7 +2791,7 @@
public void dozeTimeTick() {
mKeyguardStatusView.dozeTimeTick();
mKeyguardBottomArea.dozeTimeTick();
- if (mDarkAmount > 0) {
+ if (mInterpolatedDarkAmount > 0) {
positionClockAndNotifications();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
index 641f485..e7ede6f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
@@ -29,13 +29,11 @@
import android.widget.FrameLayout;
import com.android.systemui.R;
-import com.android.systemui.SysUiServiceProvider;
import com.android.systemui.fragments.FragmentHostManager;
import com.android.systemui.fragments.FragmentHostManager.FragmentListener;
import com.android.systemui.plugins.qs.QS;
-import com.android.systemui.statusbar.NotificationData.Entry;
import com.android.systemui.statusbar.notification.AboveShelfObserver;
-import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
/**
* The container with notification stack scroller and quick settings inside.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
index 18bc4e5..5d23494 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
@@ -33,6 +33,7 @@
private static final String STATE = "state";
private boolean mBouncerShowing;
private boolean mExpanded;
+ protected float mPanelFraction;
public static final void LOG(String fmt, Object... args) {
if (!DEBUG) return;
@@ -99,6 +100,14 @@
if (mPanel != null) mPanel.setImportantForAccessibility(important);
}
+ public float getExpansionFraction() {
+ return mPanelFraction;
+ }
+
+ public boolean isExpanded() {
+ return mExpanded;
+ }
+
private void updateVisibility() {
mPanel.setVisibility(mExpanded || mBouncerShowing ? VISIBLE : INVISIBLE);
}
@@ -153,6 +162,7 @@
if (SPEW) LOG("panelExpansionChanged: start state=%d", mState);
PanelView pv = mPanel;
mExpanded = expanded;
+ mPanelFraction = frac;
updateVisibility();
// adjust any other panels that may be partially visible
if (expanded) {
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 075883a..59863ec 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -59,7 +59,6 @@
private final PhoneStatusBarTransitions mBarTransitions;
private ScrimController mScrimController;
private float mMinFraction;
- private float mPanelFraction;
private Runnable mHideExpandedRunnable = new Runnable() {
@Override
public void run() {
@@ -269,7 +268,6 @@
@Override
public void panelExpansionChanged(float frac, boolean expanded) {
super.panelExpansionChanged(frac, expanded);
- mPanelFraction = frac;
updateScrimFraction();
if ((frac == 0 || frac == 1) && mBar.getNavigationBarView() != null) {
mBar.getNavigationBarView().onPanelExpandedChange(expanded);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
index ea1b980..2516a9b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
@@ -22,20 +22,21 @@
import static com.android.systemui.Interpolators.ALPHA_OUT;
import static com.android.systemui.OverviewProxyService.DEBUG_OVERVIEW_PROXY;
import static com.android.systemui.OverviewProxyService.TAG_OPS;
+import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_DEAD_ZONE;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
-import android.animation.ArgbEvaluator;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
-import android.graphics.Color;
import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.RadialGradient;
import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
+import android.graphics.Shader;
import android.os.Handler;
import android.os.RemoteException;
import android.util.FloatProperty;
@@ -54,7 +55,6 @@
import com.android.systemui.shared.recents.IOverviewProxy;
import com.android.systemui.shared.recents.utilities.Utilities;
import com.android.systemui.shared.system.NavigationBarCompat;
-import com.android.internal.graphics.ColorUtils;
/**
* Class to detect gestures on the navigation bar and implement quick scrub.
@@ -65,6 +65,7 @@
private static final int ANIM_IN_DURATION_MS = 150;
private static final int ANIM_OUT_DURATION_MS = 134;
private static final float TRACK_SCALE = 0.95f;
+ private static final float GRADIENT_WIDTH = .75f;
private NavigationBarView mNavigationBarView;
@@ -78,23 +79,23 @@
private boolean mIsRTL;
private float mTrackAlpha;
private float mTrackScale = TRACK_SCALE;
- private int mLightTrackColor;
- private int mDarkTrackColor;
private float mDarkIntensity;
+ private RadialGradient mHighlight;
+ private float mHighlightCenter;
private AnimatorSet mTrackAnimator;
private ButtonDispatcher mHitTarget;
private View mCurrentNavigationBarView;
+ private boolean mIsInScreenPinning;
private final Handler mHandler = new Handler();
private final Rect mTrackRect = new Rect();
- private final Drawable mTrackDrawable;
private final OverviewProxyService mOverviewEventSender;
private final int mTrackThickness;
private final int mTrackEndPadding;
private final Context mContext;
private final Matrix mTransformGlobalMatrix = new Matrix();
private final Matrix mTransformLocalMatrix = new Matrix();
- private final ArgbEvaluator mTrackColorEvaluator = new ArgbEvaluator();
+ private final Paint mTrackPaint = new Paint();
private final FloatProperty<QuickStepController> mTrackAlphaProperty =
new FloatProperty<QuickStepController>("TrackAlpha") {
@@ -155,7 +156,8 @@
mOverviewEventSender = Dependency.get(OverviewProxyService.class);
mTrackThickness = res.getDimensionPixelSize(R.dimen.nav_quick_scrub_track_thickness);
mTrackEndPadding = res.getDimensionPixelSize(R.dimen.nav_quick_scrub_track_edge_padding);
- mTrackDrawable = context.getDrawable(R.drawable.qs_scrubber_track).mutate();
+ mTrackPaint.setAntiAlias(true);
+ mTrackPaint.setDither(true);
}
public void setComponents(NavigationBarView navigationBarView) {
@@ -184,6 +186,8 @@
}
private boolean handleTouchEvent(MotionEvent event) {
+ final boolean deadZoneConsumed =
+ mNavigationBarView.getDownHitTarget() == HIT_TARGET_DEAD_ZONE;
if (mOverviewEventSender.getProxy() == null || (!mNavigationBarView.isQuickScrubEnabled()
&& !mNavigationBarView.isQuickStepSwipeUpEnabled())) {
return false;
@@ -195,6 +199,7 @@
case MotionEvent.ACTION_DOWN: {
int x = (int) event.getX();
int y = (int) event.getY();
+ mIsInScreenPinning = mNavigationBarView.inScreenPinning();
// End any existing quickscrub animations before starting the new transition
if (mTrackAnimator != null) {
@@ -287,6 +292,8 @@
} catch (RemoteException e) {
Log.e(TAG, "Failed to send progress of quick scrub.", e);
}
+ mHighlightCenter = x;
+ mNavigationBarView.invalidate();
}
break;
}
@@ -298,11 +305,11 @@
// Proxy motion events to launcher if not handled by quick scrub
// Proxy motion events up/cancel that would be sent after long press on any nav button
- if (!mQuickScrubActive && (mAllowGestureDetection || action == MotionEvent.ACTION_CANCEL
- || action == MotionEvent.ACTION_UP)) {
+ if (!mQuickScrubActive && !mIsInScreenPinning && (mAllowGestureDetection
+ || action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP)) {
proxyMotionEvents(event);
}
- return mQuickScrubActive || mQuickStepStarted;
+ return mQuickScrubActive || mQuickStepStarted || deadZoneConsumed;
}
@Override
@@ -310,18 +317,18 @@
if (!mNavigationBarView.isQuickScrubEnabled()) {
return;
}
- int color = (int) mTrackColorEvaluator.evaluate(mDarkIntensity, mLightTrackColor,
- mDarkTrackColor);
- int colorAlpha = ColorUtils.setAlphaComponent(color,
- (int) (Color.alpha(color) * mTrackAlpha));
- mTrackDrawable.setTint(colorAlpha);
+ mTrackPaint.setAlpha(Math.round(255f * mTrackAlpha));
// Scale the track, but apply the inverse scale from the nav bar
+ final float radius = mTrackRect.height() / 2;
canvas.save();
+ float translate = Utilities.clamp(mHighlightCenter, mTrackRect.left, mTrackRect.right);
+ canvas.translate(translate, 0);
canvas.scale(mTrackScale / mNavigationBarView.getScaleX(),
1f / mNavigationBarView.getScaleY(),
mTrackRect.centerX(), mTrackRect.centerY());
- mTrackDrawable.draw(canvas);
+ canvas.drawRoundRect(mTrackRect.left - translate, mTrackRect.top,
+ mTrackRect.right - translate, mTrackRect.bottom, radius, radius, mTrackPaint);
canvas.restore();
}
@@ -346,12 +353,20 @@
x2 = x1 + width - 2 * mTrackEndPadding;
}
mTrackRect.set(x1, y1, x2, y2);
- mTrackDrawable.setBounds(mTrackRect);
+ updateHighlight();
}
@Override
public void onDarkIntensityChange(float intensity) {
+ final float oldIntensity = mDarkIntensity;
mDarkIntensity = intensity;
+
+ // When in quick scrub, invalidate gradient if changing intensity from black to white and
+ // vice-versa
+ if (mNavigationBarView.isQuickScrubEnabled()
+ && Math.round(intensity) != Math.round(oldIntensity)) {
+ updateHighlight();
+ }
mNavigationBarView.invalidate();
}
@@ -382,6 +397,12 @@
}
private void startQuickStep(MotionEvent event) {
+ if (mIsInScreenPinning) {
+ mNavigationBarView.showPinningEscapeToast();
+ mAllowGestureDetection = false;
+ return;
+ }
+
mQuickStepStarted = true;
event.transform(mTransformGlobalMatrix);
try {
@@ -407,11 +428,15 @@
}
private void startQuickScrub() {
- if (!mQuickScrubActive) {
- mQuickScrubActive = true;
- mLightTrackColor = mContext.getColor(R.color.quick_step_track_background_light);
- mDarkTrackColor = mContext.getColor(R.color.quick_step_track_background_dark);
+ if (mIsInScreenPinning) {
+ mNavigationBarView.showPinningEscapeToast();
+ mAllowGestureDetection = false;
+ return;
+ }
+ if (!mQuickScrubActive) {
+ updateHighlight();
+ mQuickScrubActive = true;
ObjectAnimator trackAnimator = ObjectAnimator.ofPropertyValuesHolder(this,
PropertyValuesHolder.ofFloat(mTrackAlphaProperty, 1f),
PropertyValuesHolder.ofFloat(mTrackScaleProperty, 1f));
@@ -424,6 +449,9 @@
mTrackAnimator.playTogether(trackAnimator, navBarAnimator);
mTrackAnimator.start();
+ // Disable slippery for quick scrub to not cancel outside the nav bar
+ mNavigationBarView.updateSlippery();
+
try {
mOverviewEventSender.getProxy().onQuickScrubStart();
if (DEBUG_OVERVIEW_PROXY) {
@@ -483,6 +511,25 @@
mQuickScrubActive = false;
mAllowGestureDetection = false;
mCurrentNavigationBarView = null;
+ updateHighlight();
+ }
+
+ private void updateHighlight() {
+ if (mTrackRect.isEmpty()) {
+ return;
+ }
+ int colorBase, colorGrad;
+ if (mDarkIntensity > 0.5f) {
+ colorBase = mContext.getColor(R.color.quick_step_track_background_background_dark);
+ colorGrad = mContext.getColor(R.color.quick_step_track_background_foreground_dark);
+ } else {
+ colorBase = mContext.getColor(R.color.quick_step_track_background_background_light);
+ colorGrad = mContext.getColor(R.color.quick_step_track_background_foreground_light);
+ }
+ mHighlight = new RadialGradient(0, mTrackRect.height() / 2,
+ mTrackRect.width() * GRADIENT_WIDTH, colorGrad, colorBase,
+ Shader.TileMode.CLAMP);
+ mTrackPaint.setShader(mHighlight);
}
private boolean proxyMotionEvents(MotionEvent event) {
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 fe141661..0c361ce 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -47,7 +47,7 @@
import com.android.systemui.R;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.statusbar.ScrimView;
-import com.android.systemui.statusbar.stack.ViewState;
+import com.android.systemui.statusbar.notification.stack.ViewState;
import com.android.systemui.util.AlarmTimeout;
import com.android.systemui.util.wakelock.DelayedWakeLock;
import com.android.systemui.util.wakelock.WakeLock;
@@ -113,6 +113,7 @@
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private final DozeParameters mDozeParameters;
private final AlarmTimeout mTimeTicker;
+ private final KeyguardVisibilityCallback mKeyguardVisibilityCallback;
private final SysuiColorExtractor mColorExtractor;
private GradientColors mLockColors;
@@ -171,6 +172,8 @@
mUnlockMethodCache = UnlockMethodCache.getInstance(mContext);
mDarkenWhileDragging = !mUnlockMethodCache.canSkipBouncer();
mKeyguardUpdateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
+ mKeyguardVisibilityCallback = new KeyguardVisibilityCallback();
+ mKeyguardUpdateMonitor.registerCallback(mKeyguardVisibilityCallback);
mScrimBehindAlphaResValue = mContext.getResources().getFloat(R.dimen.scrim_behind_alpha);
mTimeTicker = new AlarmTimeout(alarmManager, this::onHideWallpaperTimeout,
"hide_aod_wallpaper", new Handler());
@@ -889,9 +892,24 @@
}
public void setHasBackdrop(boolean hasBackdrop) {
- ScrimState[] states = ScrimState.values();
- for (int i = 0; i < states.length; i++) {
- states[i].setHasBackdrop(hasBackdrop);
+ for (ScrimState state : ScrimState.values()) {
+ state.setHasBackdrop(hasBackdrop);
+ }
+
+ // Backdrop event may arrive after state was already applied,
+ // in this case, back-scrim needs to be re-evaluated
+ if (mState == ScrimState.AOD || mState == ScrimState.PULSING) {
+ float newBehindAlpha = mState.getBehindAlpha(mNotificationDensity);
+ if (mCurrentBehindAlpha != newBehindAlpha) {
+ mCurrentBehindAlpha = newBehindAlpha;
+ updateScrims();
+ }
+ }
+ }
+
+ public void setLaunchingAffordanceWithPreview(boolean launchingAffordanceWithPreview) {
+ for (ScrimState state : ScrimState.values()) {
+ state.setLaunchingAffordanceWithPreview(launchingAffordanceWithPreview);
}
}
@@ -905,4 +923,16 @@
default void onCancelled() {
}
}
+
+ /**
+ * Simple keyguard callback that updates scrims when keyguard visibility changes.
+ */
+ private class KeyguardVisibilityCallback extends KeyguardUpdateMonitorCallback {
+
+ @Override
+ public void onKeyguardVisibilityChanged(boolean showing) {
+ mNeedsDrawableColorUpdate = true;
+ scheduleUpdate();
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
index ec6ada4..085f7b6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
@@ -21,7 +21,7 @@
import android.util.MathUtils;
import com.android.systemui.statusbar.ScrimView;
-import com.android.systemui.statusbar.stack.StackStateAnimator;
+import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
/**
* Possible states of the ScrimController state machine.
@@ -105,7 +105,6 @@
public void prepare(ScrimState previousState) {
final boolean alwaysOnEnabled = mDozeParameters.getAlwaysOn();
mBlankScreen = mDisplayRequiresBlanking;
- mCurrentBehindAlpha = mWallpaperSupportsAmbientMode && !mHasBackdrop ? 0f : 1f;
mCurrentInFrontAlpha = alwaysOnEnabled ? mAodFrontScrimAlpha : 1f;
mCurrentInFrontTint = Color.BLACK;
mCurrentBehindTint = Color.BLACK;
@@ -116,6 +115,11 @@
}
@Override
+ public float getBehindAlpha(float busyness) {
+ return mWallpaperSupportsAmbientMode && !mHasBackdrop ? 0f : 1f;
+ }
+
+ @Override
public boolean isLowPowerState() {
return true;
}
@@ -129,10 +133,14 @@
public void prepare(ScrimState previousState) {
mCurrentInFrontAlpha = 0;
mCurrentInFrontTint = Color.BLACK;
- mCurrentBehindAlpha = mWallpaperSupportsAmbientMode && !mHasBackdrop ? 0f : 1f;
mCurrentBehindTint = Color.BLACK;
mBlankScreen = mDisplayRequiresBlanking;
}
+
+ @Override
+ public float getBehindAlpha(float busyness) {
+ return mWallpaperSupportsAmbientMode && !mHasBackdrop ? 0f : 1f;
+ }
},
/**
@@ -144,8 +152,9 @@
mCurrentBehindAlpha = 0;
mCurrentInFrontAlpha = 0;
mAnimationDuration = StatusBar.FADE_KEYGUARD_DURATION;
+ mAnimateChange = !mLaunchingAffordanceWithPreview;
- if (previousState == ScrimState.AOD || previousState == ScrimState.PULSING) {
+ if (previousState == ScrimState.AOD) {
// Fade from black to transparent when coming directly from AOD
updateScrimColor(mScrimInFront, 1, Color.BLACK);
updateScrimColor(mScrimBehind, 1, Color.BLACK);
@@ -177,6 +186,7 @@
boolean mWallpaperSupportsAmbientMode;
int mIndex;
boolean mHasBackdrop;
+ boolean mLaunchingAffordanceWithPreview;
ScrimState(int index) {
mIndex = index;
@@ -249,6 +259,10 @@
mWallpaperSupportsAmbientMode = wallpaperSupportsAmbientMode;
}
+ public void setLaunchingAffordanceWithPreview(boolean launchingAffordanceWithPreview) {
+ mLaunchingAffordanceWithPreview = launchingAffordanceWithPreview;
+ }
+
public boolean isLowPowerState() {
return false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadowKeyDrawable.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadowKeyDrawable.java
index 8311dfd..2471e34 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadowKeyDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadowKeyDrawable.java
@@ -82,7 +82,12 @@
if (bounds.isEmpty()) {
return;
}
- if (mState.mLastDrawnBitmap == null) {
+
+ // If no cache or previous cached bitmap is hardware/software acceleration does not match
+ // the current canvas on draw then regenerate
+ if (mState.mLastDrawnBitmap == null
+ || mState.mIsHardwareBitmap != canvas.isHardwareAccelerated()) {
+ mState.mIsHardwareBitmap = canvas.isHardwareAccelerated();
regenerateBitmapCache();
}
canvas.drawBitmap(mState.mLastDrawnBitmap, null, bounds, mPaint);
@@ -171,7 +176,10 @@
d.draw(canvas);
}
- bitmap = bitmap.copy(Bitmap.Config.HARDWARE, false);
+ if (mState.mIsHardwareBitmap) {
+ bitmap = bitmap.copy(Bitmap.Config.HARDWARE, false);
+ }
+
mState.mLastDrawnBitmap = bitmap;
canvas.restore();
}
@@ -186,6 +194,7 @@
int mShadowSize;
int mShadowColor;
+ boolean mIsHardwareBitmap;
Bitmap mLastDrawnBitmap;
ConstantState mChildState;
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 0a1ac1b..c400893 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -55,6 +55,7 @@
import android.app.PendingIntent;
import android.app.StatusBarManager;
import android.app.TaskStackBuilder;
+import android.app.UiModeManager;
import android.app.WallpaperColors;
import android.app.WallpaperInfo;
import android.app.WallpaperManager;
@@ -187,26 +188,26 @@
import com.android.systemui.shared.system.WindowManagerWrapper;
import com.android.systemui.stackdivider.Divider;
import com.android.systemui.stackdivider.WindowManagerProxy;
-import com.android.systemui.statusbar.ActivatableNotificationView;
-import com.android.systemui.statusbar.AppOpsListener;
+import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
+import com.android.systemui.statusbar.notification.AppOpsListener;
import com.android.systemui.statusbar.BackDropView;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.CrossFadeHelper;
import com.android.systemui.statusbar.DragDownHelper;
import com.android.systemui.statusbar.EmptyShadeView;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
-import com.android.systemui.statusbar.FooterView;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.FooterView;
import com.android.systemui.statusbar.GestureRecorder;
import com.android.systemui.statusbar.KeyboardShortcuts;
import com.android.systemui.statusbar.KeyguardIndicationController;
-import com.android.systemui.statusbar.NotificationData;
-import com.android.systemui.statusbar.NotificationData.Entry;
-import com.android.systemui.statusbar.NotificationEntryManager;
-import com.android.systemui.statusbar.NotificationGutsManager;
-import com.android.systemui.statusbar.NotificationInfo;
+import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.NotificationData.Entry;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
+import com.android.systemui.statusbar.notification.row.NotificationInfo;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
-import com.android.systemui.statusbar.NotificationLogger;
+import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.NotificationPresenter;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
@@ -219,6 +220,7 @@
import com.android.systemui.statusbar.notification.AboveShelfObserver;
import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.phone.UnlockMethodCache.OnUnlockMethodChangedListener;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
@@ -242,7 +244,6 @@
import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
import com.android.systemui.statusbar.policy.UserSwitcherController;
import com.android.systemui.statusbar.policy.ZenModeController;
-import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
import com.android.systemui.volume.VolumeComponent;
import java.io.FileDescriptor;
@@ -361,7 +362,7 @@
private VolumeComponent mVolumeComponent;
private BrightnessMirrorController mBrightnessMirrorController;
private boolean mBrightnessMirrorVisible;
- protected FingerprintUnlockController mFingerprintUnlockController;
+ protected BiometricUnlockController mBiometricUnlockController;
private LightBarController mLightBarController;
protected LockscreenWallpaper mLockscreenWallpaper;
@@ -423,13 +424,6 @@
protected KeyguardViewMediator mKeyguardViewMediator;
private ZenModeController mZenController;
- /**
- * Helper that is responsible for showing the right toast when a disallowed activity operation
- * occurred. In pinned mode, we show instructions on how to break out of this mode, whilst in
- * fully locked mode we only show that unlocking is blocked.
- */
- private ScreenPinningNotify mScreenPinningNotify;
-
// for disabling the status bar
private int mDisabled1 = 0;
private int mDisabled2 = 0;
@@ -512,7 +506,7 @@
protected NotificationLockscreenUserManager mLockscreenUserManager;
protected NotificationRemoteInputManager mRemoteInputManager;
- private BroadcastReceiver mWallpaperChangedReceiver = new BroadcastReceiver() {
+ private final BroadcastReceiver mWallpaperChangedReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
WallpaperManager wallpaperManager = context.getSystemService(WallpaperManager.class);
@@ -520,7 +514,7 @@
Log.w(TAG, "WallpaperManager not available");
return;
}
- WallpaperInfo info = wallpaperManager.getWallpaperInfo();
+ WallpaperInfo info = wallpaperManager.getWallpaperInfo(UserHandle.USER_CURRENT);
final boolean supportsAmbientMode = info != null &&
info.getSupportsAmbientMode();
@@ -580,7 +574,7 @@
= (KeyguardMonitorImpl) Dependency.get(KeyguardMonitor.class);
private BatteryController mBatteryController;
protected boolean mPanelExpanded;
- private IOverlayManager mOverlayManager;
+ private UiModeManager mUiModeManager;
private boolean mKeyguardRequested;
private boolean mIsKeyguard;
private LogMaker mStatusBarStateLog;
@@ -641,8 +635,7 @@
mWakefulnessLifecycle.addObserver(mWakefulnessObserver);
mBatteryController = Dependency.get(BatteryController.class);
mAssistManager = Dependency.get(AssistManager.class);
- mOverlayManager = IOverlayManager.Stub.asInterface(
- ServiceManager.getService(Context.OVERLAY_SERVICE));
+ mUiModeManager = mContext.getSystemService(UiModeManager.class);
mLockscreenUserManager = Dependency.get(NotificationLockscreenUserManager.class);
mGutsManager = Dependency.get(NotificationGutsManager.class);
mMediaManager = Dependency.get(NotificationMediaManager.class);
@@ -714,7 +707,8 @@
// Make sure we always have the most current wallpaper info.
IntentFilter wallpaperChangedFilter = new IntentFilter(Intent.ACTION_WALLPAPER_CHANGED);
- mContext.registerReceiver(mWallpaperChangedReceiver, wallpaperChangedFilter);
+ mContext.registerReceiverAsUser(mWallpaperChangedReceiver, UserHandle.ALL,
+ wallpaperChangedFilter, null /* broadcastPermission */, null /* scheduler */);
mWallpaperChangedReceiver.onReceive(mContext, null);
mLockscreenUserManager.setUpWithPresenter(this, mEntryManager);
@@ -839,6 +833,7 @@
CollapsedStatusBarFragment statusBarFragment =
(CollapsedStatusBarFragment) fragment;
statusBarFragment.initNotificationIconArea(mNotificationIconAreaController);
+ PhoneStatusBarView oldStatusBarView = mStatusBarView;
mStatusBarView = (PhoneStatusBarView) fragment.getView();
mStatusBarView.setBar(this);
mStatusBarView.setPanel(mNotificationPanel);
@@ -855,6 +850,11 @@
mNotificationPanel.notifyBarPanelExpansionChanged();
}
mStatusBarView.setBouncerShowing(mBouncerShowing);
+ if (oldStatusBarView != null) {
+ float fraction = oldStatusBarView.getExpansionFraction();
+ boolean expanded = oldStatusBarView.isExpanded();
+ mStatusBarView.panelExpansionChanged(fraction, expanded);
+ }
HeadsUpAppearanceController oldController = mHeadsUpAppearanceController;
if (mHeadsUpAppearanceController != null) {
@@ -864,6 +864,7 @@
mHeadsUpAppearanceController = new HeadsUpAppearanceController(
mNotificationIconAreaController, mHeadsUpManager, mStatusBarWindow);
mHeadsUpAppearanceController.readFrom(oldController);
+ mStatusBarWindow.setStatusBarView(mStatusBarView);
setAreThereNotifications();
checkBarModes();
}).getFragmentManager()
@@ -901,7 +902,6 @@
} catch (RemoteException ex) {
// no window manager? good luck with that
}
- mScreenPinningNotify = new ScreenPinningNotify(mContext);
mStackScroller.setLongPressListener(mEntryManager.getNotificationLongClicker());
mStackScroller.setStatusBar(this);
mStackScroller.setGroupManager(mGroupManager);
@@ -1188,6 +1188,21 @@
}
}
+ @Override
+ public void onUiModeChanged() {
+ // UiMode will change the style was already evaluated.
+ // We need to force the re-evaluation to make sure that all parents
+ // are up to date and new attrs will be rettrieved.
+ mContext.getTheme().applyStyle(mContext.getThemeResId(), true);
+
+ if (mBrightnessMirrorController != null) {
+ mBrightnessMirrorController.onUiModeChanged();
+ }
+ if (mStackScroller != null) {
+ mStackScroller.onUiModeChanged();
+ }
+ }
+
private void inflateEmptyShadeView() {
if (mStackScroller == null) {
return;
@@ -1329,18 +1344,18 @@
protected void startKeyguard() {
Trace.beginSection("StatusBar#startKeyguard");
KeyguardViewMediator keyguardViewMediator = getComponent(KeyguardViewMediator.class);
- mFingerprintUnlockController = new FingerprintUnlockController(mContext,
+ mBiometricUnlockController = new BiometricUnlockController(mContext,
mDozeScrimController, keyguardViewMediator,
mScrimController, this, UnlockMethodCache.getInstance(mContext));
mStatusBarKeyguardViewManager = keyguardViewMediator.registerStatusBar(this,
- getBouncerContainer(), mNotificationPanel, mFingerprintUnlockController);
+ getBouncerContainer(), mNotificationPanel, mBiometricUnlockController);
mKeyguardIndicationController
.setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager);
- mFingerprintUnlockController.setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager);
+ mBiometricUnlockController.setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager);
mRemoteInputManager.getController().addCallback(mStatusBarKeyguardViewManager);
mKeyguardViewMediatorCallback = keyguardViewMediator.getViewMediatorCallback();
- mLightBarController.setFingerprintUnlockController(mFingerprintUnlockController);
+ mLightBarController.setBiometricUnlockController(mBiometricUnlockController);
Dependency.get(KeyguardDismissUtil.class).setDismissHandler(this::executeWhenUnlocked);
Trace.endSection();
}
@@ -1624,8 +1639,8 @@
return; // called too early
}
- boolean wakeAndUnlock = mFingerprintUnlockController != null
- && mFingerprintUnlockController.isWakeAndUnlock();
+ boolean wakeAndUnlock = mBiometricUnlockController != null
+ && mBiometricUnlockController.isWakeAndUnlock();
if (mLaunchTransitionFadingAway || wakeAndUnlock) {
mBackdrop.setVisibility(View.INVISIBLE);
Trace.endSection();
@@ -1677,8 +1692,8 @@
if ((hasArtwork || DEBUG_MEDIA_FAKE_ARTWORK)
&& (mState != StatusBarState.SHADE || allowWhenShade)
- && mFingerprintUnlockController.getMode()
- != FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING
+ && mBiometricUnlockController.getMode()
+ != BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING
&& !hideBecauseOccluded) {
// time to show some art!
if (mBackdrop.getVisibility() != View.VISIBLE) {
@@ -1743,8 +1758,8 @@
Log.v(TAG, "DEBUG_MEDIA: Fading out album artwork");
}
boolean cannotAnimateDoze = mDozing && !ScrimState.AOD.getAnimateChange();
- if (mFingerprintUnlockController.getMode()
- == FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING
+ if (mBiometricUnlockController.getMode()
+ == BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING
|| hideBecauseOccluded || cannotAnimateDoze) {
// We are unlocking directly - no animation!
@@ -2088,17 +2103,6 @@
updateTheme();
}
- public boolean isUsingDarkTheme() {
- OverlayInfo themeInfo = null;
- try {
- themeInfo = mOverlayManager.getOverlayInfo("com.android.systemui.theme.dark",
- mLockscreenUserManager.getCurrentUserId());
- } catch (RemoteException e) {
- e.printStackTrace();
- }
- return themeInfo != null && themeInfo.isEnabled();
- }
-
@Nullable
public View getAmbientIndicationContainer() {
return mAmbientIndicationContainer;
@@ -2154,6 +2158,10 @@
}
}
+ public boolean isHeadsUpShouldBeVisible() {
+ return mHeadsUpAppearanceController.shouldBeVisible();
+ }
+
/**
* All changes to the status bar and notifications funnel through here and are batched.
*/
@@ -2240,17 +2248,16 @@
@Override
public void showPinningEnterExitToast(boolean entering) {
- if (entering) {
- mScreenPinningNotify.showPinningStartToast();
- } else {
- mScreenPinningNotify.showPinningExitToast();
+ if (getNavigationBarView() != null) {
+ getNavigationBarView().showPinningEnterExitToast(entering);
}
}
@Override
public void showPinningEscapeToast() {
- mScreenPinningNotify.showEscapeToast(getNavigationBarView() == null
- || getNavigationBarView().isRecentsButtonVisible());
+ if (getNavigationBarView() != null) {
+ getNavigationBarView().showPinningEscapeToast();
+ }
}
boolean panelsEnabled() {
@@ -2477,8 +2484,8 @@
return mGestureRec;
}
- public FingerprintUnlockController getFingerprintUnlockController() {
- return mFingerprintUnlockController;
+ public BiometricUnlockController getBiometricUnlockController() {
+ return mBiometricUnlockController;
}
@Override // CommandQueue
@@ -2802,18 +2809,18 @@
mStackScroller.dump(fd, pw, args);
}
pw.println(" Theme:");
- if (mOverlayManager == null) {
- pw.println(" overlay manager not initialized!");
- } else {
- pw.println(" dark overlay on: " + isUsingDarkTheme());
- }
+ String nightMode = mUiModeManager == null ? "null" : mUiModeManager.getNightMode() + "";
+ pw.println(" dark theme: " + nightMode +
+ " (auto: " + UiModeManager.MODE_NIGHT_AUTO +
+ ", yes: " + UiModeManager.MODE_NIGHT_YES +
+ ", no: " + UiModeManager.MODE_NIGHT_NO + ")");
final boolean lightWpTheme = mContext.getThemeResId() == R.style.Theme_SystemUI_Light;
pw.println(" light wallpaper theme: " + lightWpTheme);
DozeLog.dump(pw);
- if (mFingerprintUnlockController != null) {
- mFingerprintUnlockController.dump(pw);
+ if (mBiometricUnlockController != null) {
+ mBiometricUnlockController.dump(pw);
}
if (mKeyguardIndicationController != null) {
@@ -3121,10 +3128,10 @@
&& mUnlockMethodCache.canSkipBouncer()
&& !mLeaveOpenOnKeyguardHide
&& isPulsing()) {
- // Reuse the fingerprint wake-and-unlock transition if we dismiss keyguard from a pulse.
- // TODO: Factor this transition out of FingerprintUnlockController.
- mFingerprintUnlockController.startWakeAndUnlock(
- FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING);
+ // Reuse the biometric wake-and-unlock transition if we dismiss keyguard from a pulse.
+ // TODO: Factor this transition out of BiometricUnlockController.
+ mBiometricUnlockController.startWakeAndUnlock(
+ BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING);
}
if (mStatusBarKeyguardViewManager.isShowing()) {
mStatusBarKeyguardViewManager.dismissWithAction(action, cancelAction,
@@ -3164,6 +3171,7 @@
updateNotificationViews();
mMediaManager.clearCurrentMediaNotification();
setLockscreenUser(newUserId);
+ mWallpaperChangedReceiver.onReceive(mContext, null);
}
@Override
@@ -3528,8 +3536,8 @@
}
private boolean updateIsKeyguard() {
- boolean wakeAndUnlocking = mFingerprintUnlockController.getMode()
- == FingerprintUnlockController.MODE_WAKE_AND_UNLOCK;
+ boolean wakeAndUnlocking = mBiometricUnlockController.getMode()
+ == BiometricUnlockController.MODE_WAKE_AND_UNLOCK;
// For dozing, keyguard needs to be shown whenever the device is non-interactive. Otherwise
// there's no surface we can show to the user. Note that the device goes fully interactive
@@ -3576,8 +3584,8 @@
}
private void updatePanelExpansionForKeyguard() {
- if (mState == StatusBarState.KEYGUARD && mFingerprintUnlockController.getMode()
- != FingerprintUnlockController.MODE_WAKE_AND_UNLOCK) {
+ if (mState == StatusBarState.KEYGUARD && mBiometricUnlockController.getMode()
+ != BiometricUnlockController.MODE_WAKE_AND_UNLOCK) {
instantExpandNotificationsPanel();
} else if (mState == StatusBarState.FULLSCREEN_USER_SWITCHER) {
instantCollapseNotificationPanel();
@@ -3875,22 +3883,6 @@
protected void updateTheme() {
final boolean inflated = mStackScroller != null && mStatusBarWindowManager != null;
- // The system wallpaper defines if QS should be light or dark.
- WallpaperColors systemColors = mColorExtractor
- .getWallpaperColors(WallpaperManager.FLAG_SYSTEM);
- final boolean useDarkTheme = systemColors != null
- && (systemColors.getColorHints() & WallpaperColors.HINT_SUPPORTS_DARK_THEME) != 0;
- if (isUsingDarkTheme() != useDarkTheme) {
- mUiOffloadThread.submit(() -> {
- try {
- mOverlayManager.setEnabled("com.android.systemui.theme.dark",
- useDarkTheme, mLockscreenUserManager.getCurrentUserId());
- } catch (RemoteException e) {
- Log.w(TAG, "Can't change theme", e);
- }
- });
- }
-
// Lock wallpaper defines the color of the majority of the views, hence we'll use it
// to set our default theme.
final boolean lockDarkText = mColorExtractor.getColors(WallpaperManager.FLAG_LOCK, true
@@ -4705,7 +4697,7 @@
|| mScreenLifecycle.getScreenState() == ScreenLifecycle.SCREEN_ON;
}
- public void notifyFpAuthModeChanged() {
+ public void notifyBiometricAuthModeChanged() {
updateDozing();
updateScrimController();
}
@@ -4714,18 +4706,17 @@
Trace.beginSection("StatusBar#updateDozing");
// When in wake-and-unlock while pulsing, keep dozing state until fully unlocked.
boolean dozing = mDozingRequested && mState == StatusBarState.KEYGUARD
- || mFingerprintUnlockController.getMode()
- == FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING;
- final boolean alwaysOn = DozeParameters.getInstance(mContext).getAlwaysOn();
+ || mBiometricUnlockController.getMode()
+ == BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING;
// When in wake-and-unlock we may not have received a change to mState
// but we still should not be dozing, manually set to false.
- if (mFingerprintUnlockController.getMode() ==
- FingerprintUnlockController.MODE_WAKE_AND_UNLOCK) {
+ if (mBiometricUnlockController.getMode() ==
+ mBiometricUnlockController.MODE_WAKE_AND_UNLOCK) {
dozing = false;
}
if (mDozing != dozing) {
mDozing = dozing;
- mKeyguardViewMediator.setAodShowing(mDozing && alwaysOn);
+ mKeyguardViewMediator.setAodShowing(mDozing);
mStatusBarWindowManager.setDozing(mDozing);
mStatusBarKeyguardViewManager.setDozing(mDozing);
if (mAmbientIndicationContainer instanceof DozeReceiver) {
@@ -4744,22 +4735,25 @@
// We don't want to end up in KEYGUARD state when we're unlocking with
// fingerprint from doze. We should cross fade directly from black.
- boolean wakeAndUnlocking = mFingerprintUnlockController.isWakeAndUnlock();
+ boolean wakeAndUnlocking = mBiometricUnlockController.isWakeAndUnlock();
// Do not animate the scrim expansion when triggered by the fingerprint sensor.
mScrimController.setExpansionAffectsAlpha(
- !mFingerprintUnlockController.isFingerprintUnlock());
+ !mBiometricUnlockController.isBiometricUnlock());
+
+ boolean launchingAffordanceWithPreview =
+ mNotificationPanel.isLaunchingAffordanceWithPreview();
+ mScrimController.setLaunchingAffordanceWithPreview(launchingAffordanceWithPreview);
if (mBouncerShowing) {
// Bouncer needs the front scrim when it's on top of an activity,
// tapping on a notification, editing QS or being dismissed by
// FLAG_DISMISS_KEYGUARD_ACTIVITY.
- ScrimState state = mIsOccluded || mStatusBarKeyguardViewManager.bouncerNeedsScrimming()
- || mStatusBarKeyguardViewManager.willDismissWithAction()
- || mStatusBarKeyguardViewManager.isFullscreenBouncer() ?
- ScrimState.BOUNCER_SCRIMMED : ScrimState.BOUNCER;
+ ScrimState state = mStatusBarKeyguardViewManager.bouncerNeedsScrimming()
+ ? ScrimState.BOUNCER_SCRIMMED : ScrimState.BOUNCER;
mScrimController.transitionTo(state);
- } else if (mLaunchCameraOnScreenTurningOn || isInLaunchTransition()) {
+ } else if (isInLaunchTransition() || mLaunchCameraOnScreenTurningOn
+ || launchingAffordanceWithPreview) {
mScrimController.transitionTo(ScrimState.UNLOCKED, mUnlockScrimCallback);
} else if (mBrightnessMirrorVisible) {
mScrimController.transitionTo(ScrimState.BRIGHTNESS_MIRROR);
@@ -4852,6 +4846,7 @@
}
private void setPulsing(boolean pulsing) {
+ mKeyguardViewMediator.setPulsing(pulsing);
mNotificationPanel.setPulsing(pulsing);
mVisualStabilityManager.setPulsing(pulsing);
mIgnoreTouchWhilePulsing = false;
@@ -4892,8 +4887,8 @@
@Override
public boolean isPulsingBlocked() {
- return mFingerprintUnlockController.getMode()
- == FingerprintUnlockController.MODE_WAKE_AND_UNLOCK;
+ return mBiometricUnlockController.getMode()
+ == BiometricUnlockController.MODE_WAKE_AND_UNLOCK;
}
@Override
@@ -4904,7 +4899,7 @@
@Override
public boolean isBlockingDoze() {
- if (mFingerprintUnlockController.hasPendingAuthentication()) {
+ if (mBiometricUnlockController.hasPendingAuthentication()) {
Log.i(TAG, "Blocking AOD because fingerprint has authenticated");
return true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 914bba2..378910a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -17,8 +17,8 @@
package com.android.systemui.statusbar.phone;
import static com.android.keyguard.KeyguardHostView.OnDismissAction;
-import static com.android.systemui.statusbar.phone.FingerprintUnlockController.MODE_WAKE_AND_UNLOCK;
-import static com.android.systemui.statusbar.phone.FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING;
+import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK;
+import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING;
import android.content.ComponentCallbacks2;
import android.content.Context;
@@ -91,7 +91,7 @@
protected ViewMediatorCallback mViewMediatorCallback;
protected StatusBar mStatusBar;
private NotificationPanelView mNotificationPanelView;
- private FingerprintUnlockController mFingerprintUnlockController;
+ private BiometricUnlockController mBiometricUnlockController;
private ViewGroup mContainer;
@@ -108,7 +108,7 @@
private boolean mLastBouncerDismissible;
protected boolean mLastRemoteInputActive;
private boolean mLastDozing;
- private int mLastFpMode;
+ private int mLastBiometricMode;
private boolean mGoingToSleepVisibleNotOccluded;
private OnDismissAction mAfterKeyguardGoneAction;
@@ -142,11 +142,11 @@
public void registerStatusBar(StatusBar statusBar,
ViewGroup container,
NotificationPanelView notificationPanelView,
- FingerprintUnlockController fingerprintUnlockController,
+ BiometricUnlockController biometricUnlockController,
DismissCallbackRegistry dismissCallbackRegistry) {
mStatusBar = statusBar;
mContainer = container;
- mFingerprintUnlockController = fingerprintUnlockController;
+ mBiometricUnlockController = biometricUnlockController;
mBouncer = SystemUIFactory.getInstance().createKeyguardBouncer(mContext,
mViewMediatorCallback, mLockPatternUtils, container, dismissCallbackRegistry,
mExpansionCallback);
@@ -170,8 +170,7 @@
// • Full-screen user switcher is displayed.
if (mNotificationPanelView.isUnlockHintRunning()) {
mBouncer.setExpansion(KeyguardBouncer.EXPANSION_HIDDEN);
- } else if (mOccluded || mBouncer.willDismissWithAction() || mBouncer.isShowingScrimmed()
- || mStatusBar.isFullScreenUserSwitcherState()) {
+ } else if (bouncerNeedsScrimming()) {
mBouncer.setExpansion(KeyguardBouncer.EXPANSION_VISIBLE);
} else if (mShowing && !mDozing) {
if (!isWakeAndUnlocking() && !mStatusBar.isInLaunchTransition()) {
@@ -260,7 +259,7 @@
}
private boolean isWakeAndUnlocking() {
- int mode = mFingerprintUnlockController.getMode();
+ int mode = mBiometricUnlockController.getMode();
return mode == MODE_WAKE_AND_UNLOCK || mode == MODE_WAKE_AND_UNLOCK_PULSING;
}
@@ -443,13 +442,13 @@
} else {
executeAfterKeyguardGoneAction();
boolean wakeUnlockPulsing =
- mFingerprintUnlockController.getMode() == MODE_WAKE_AND_UNLOCK_PULSING;
+ mBiometricUnlockController.getMode() == MODE_WAKE_AND_UNLOCK_PULSING;
if (wakeUnlockPulsing) {
delay = 0;
fadeoutDuration = 240;
}
mStatusBar.setKeyguardFadingAway(startTime, delay, fadeoutDuration);
- mFingerprintUnlockController.startKeyguardFadingAway();
+ mBiometricUnlockController.startKeyguardFadingAway();
hideBouncer(true /* destroyView */);
if (wakeUnlockPulsing) {
mStatusBar.fadeKeyguardWhilePulsing();
@@ -461,7 +460,7 @@
wakeAndUnlockDejank();
} else {
mStatusBar.finishKeyguardFadingAway();
- mFingerprintUnlockController.finishKeyguardFadingAway();
+ mBiometricUnlockController.finishKeyguardFadingAway();
}
}
updateStates();
@@ -485,14 +484,14 @@
mContainer.postDelayed(() -> mStatusBarWindowManager.setKeyguardFadingAway(false),
100);
mStatusBar.finishKeyguardFadingAway();
- mFingerprintUnlockController.finishKeyguardFadingAway();
+ mBiometricUnlockController.finishKeyguardFadingAway();
WindowManagerGlobal.getInstance().trimMemory(
ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN);
}
private void wakeAndUnlockDejank() {
- if (mFingerprintUnlockController.getMode() == MODE_WAKE_AND_UNLOCK
+ if (mBiometricUnlockController.getMode() == MODE_WAKE_AND_UNLOCK
&& LatencyTracker.isEnabled(mContext)) {
DejankUtils.postAfterTraversal(() ->
LatencyTracker.getInstance(mContext).onActionEnd(
@@ -619,7 +618,7 @@
mLastBouncerDismissible = bouncerDismissible;
mLastRemoteInputActive = remoteInputActive;
mLastDozing = mDozing;
- mLastFpMode = mFingerprintUnlockController.getMode();
+ mLastBiometricMode = mBiometricUnlockController.getMode();
mStatusBar.onKeyguardViewManagerStatesUpdated();
}
@@ -644,9 +643,9 @@
* @return Whether the navigation bar should be made visible based on the current state.
*/
protected boolean isNavBarVisible() {
- int fpMode = mFingerprintUnlockController.getMode();
+ int biometricMode = mBiometricUnlockController.getMode();
boolean keyguardShowing = mShowing && !mOccluded;
- boolean hideWhileDozing = mDozing && fpMode != MODE_WAKE_AND_UNLOCK_PULSING;
+ boolean hideWhileDozing = mDozing && biometricMode != MODE_WAKE_AND_UNLOCK_PULSING;
return (!keyguardShowing && !hideWhileDozing || mBouncer.isShowing()
|| mRemoteInputActive);
}
@@ -656,7 +655,7 @@
*/
protected boolean getLastNavBarVisible() {
boolean keyguardShowing = mLastShowing && !mLastOccluded;
- boolean hideWhileDozing = mLastDozing && mLastFpMode != MODE_WAKE_AND_UNLOCK_PULSING;
+ boolean hideWhileDozing = mLastDozing && mLastBiometricMode != MODE_WAKE_AND_UNLOCK_PULSING;
return (!keyguardShowing && !hideWhileDozing || mLastBouncerShowing
|| mLastRemoteInputActive);
}
@@ -731,12 +730,9 @@
}
}
- public boolean willDismissWithAction() {
- return mBouncer.willDismissWithAction();
- }
-
public boolean bouncerNeedsScrimming() {
- return mBouncer.isShowingScrimmed();
+ return mOccluded || mBouncer.willDismissWithAction() || mBouncer.needsFullscreenBouncer()
+ || mStatusBar.isFullScreenUserSwitcherState() || mBouncer.isShowingScrimmed();
}
public void dump(PrintWriter pw) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
index fadc0ea..a38328a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
@@ -180,6 +180,15 @@
mLpChanged.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
}
+ private void applyExpandedFlag(State state) {
+ if (state.panelExpanded || state.isKeyguardShowingAndNotOccluded() || state.bouncerShowing
+ || ENABLE_REMOTE_INPUT && state.remoteInputActive) {
+ mLpChanged.privateFlags |= LayoutParams.PRIVATE_FLAG_STATUS_BAR_EXPANDED;
+ } else {
+ mLpChanged.privateFlags &= ~LayoutParams.PRIVATE_FLAG_STATUS_BAR_EXPANDED;
+ }
+ }
+
private void applyHeight(State state) {
boolean expanded = isExpanded(state);
if (state.forcePluginOpen) {
@@ -234,6 +243,7 @@
applyKeyguardFlags(state);
applyForceStatusBarVisibleFlag(state);
applyFocusableFlag(state);
+ applyExpandedFlag(state);
adjustScreenOrientation(state);
applyHeight(state);
applyUserActivityTimeout(state);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
index 237ca25..5cc0202 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
@@ -61,7 +61,7 @@
import com.android.systemui.classifier.FalsingManager;
import com.android.systemui.statusbar.DragDownHelper;
import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -76,6 +76,7 @@
private NotificationStackScrollLayout mStackScrollLayout;
private NotificationPanelView mNotificationPanel;
private View mBrightnessMirror;
+ private PhoneStatusBarView mStatusBarView;
private int mRightInset = 0;
private int mLeftInset = 0;
@@ -204,6 +205,10 @@
}
}
+ public void setStatusBarView(PhoneStatusBarView statusBarView) {
+ mStatusBarView = statusBarView;
+ }
+
public void setService(StatusBar service) {
mService = service;
setDragDownHelper(new DragDownHelper(getContext(), this, mStackScrollLayout, mService));
@@ -326,7 +331,7 @@
expandingBelowNotch = true;
}
if (expandingBelowNotch) {
- return mNotificationPanel.dispatchTouchEvent(ev);
+ return mStatusBarView.dispatchTouchEvent(ev);
}
return super.dispatchTouchEvent(ev);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
index 0811179..56a177e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
@@ -33,9 +33,9 @@
import com.android.keyguard.AlphaOptimizedLinearLayout;
import com.android.systemui.R;
import com.android.systemui.statusbar.StatusIconDisplayable;
-import com.android.systemui.statusbar.stack.AnimationFilter;
-import com.android.systemui.statusbar.stack.AnimationProperties;
-import com.android.systemui.statusbar.stack.ViewState;
+import com.android.systemui.statusbar.notification.stack.AnimationFilter;
+import com.android.systemui.statusbar.notification.stack.AnimationProperties;
+import com.android.systemui.statusbar.notification.stack.ViewState;
import java.util.ArrayList;
/**
@@ -301,11 +301,7 @@
vs.initFrom(child);
vs.alpha = 1.0f;
- if (child instanceof StatusIconDisplayable) {
- vs.hidden = !((StatusIconDisplayable)child).isIconVisible();
- } else {
- vs.hidden = false;
- }
+ vs.hidden = false;
}
}
@@ -333,22 +329,33 @@
}
StatusIconDisplayable icon = (StatusIconDisplayable) view;
AnimationProperties animationProperties = null;
- boolean animate = false;
+ boolean animateVisibility = true;
- if (justAdded) {
+ // Figure out which properties of the state transition (if any) we need to animate
+ if (justAdded
+ || icon.getVisibleState() == STATE_HIDDEN && visibleState == STATE_ICON) {
+ // Icon is appearing, fade it in by putting it where it will be and animating alpha
super.applyToView(view);
+ view.setAlpha(0.f);
+ icon.setVisibleState(STATE_HIDDEN);
animationProperties = ADD_ICON_PROPERTIES;
- animate = true;
} else if (icon.getVisibleState() != visibleState) {
- animationProperties = DOT_ANIMATION_PROPERTIES;
- animate = true;
+ if (icon.getVisibleState() == STATE_ICON && visibleState == STATE_HIDDEN) {
+ // Disappearing, don't do anything fancy
+ animateVisibility = false;
+ } else {
+ // all other transitions (to/from dot, etc)
+ animationProperties = ANIMATE_ALL_PROPERTIES;
+ }
+ } else if (visibleState != STATE_HIDDEN && xTranslation != view.getTranslationX()) {
+ // Visibility isn't changing, just animate position
+ animationProperties = X_ANIMATION_PROPERTIES;
}
- if (animate) {
+ icon.setVisibleState(visibleState, animateVisibility);
+ if (animationProperties != null) {
animateTo(view, animationProperties);
- icon.setVisibleState(visibleState);
} else {
- icon.setVisibleState(visibleState);
super.applyToView(view);
}
@@ -365,7 +372,7 @@
}
}.setDuration(200).setDelay(50);
- private static final AnimationProperties DOT_ANIMATION_PROPERTIES = new AnimationProperties() {
+ private static final AnimationProperties X_ANIMATION_PROPERTIES = new AnimationProperties() {
private AnimationFilter mAnimationFilter = new AnimationFilter().animateX();
@Override
@@ -373,4 +380,14 @@
return mAnimationFilter;
}
}.setDuration(200);
+
+ private static final AnimationProperties ANIMATE_ALL_PROPERTIES = new AnimationProperties() {
+ private AnimationFilter mAnimationFilter = new AnimationFilter().animateX().animateY()
+ .animateAlpha().animateScale();
+
+ @Override
+ public AnimationFilter getAnimationFilter() {
+ return mAnimationFilter;
+ }
+ }.setDuration(200);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockMethodCache.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockMethodCache.java
index f9c2130..e5925f0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockMethodCache.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockMethodCache.java
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.phone;
+import android.hardware.biometrics.BiometricSourceType;
import android.content.Context;
import android.os.Trace;
@@ -135,9 +136,9 @@
}
@Override
- public void onFingerprintAuthenticated(int userId) {
- Trace.beginSection("KeyguardUpdateMonitorCallback#onFingerprintAuthenticated");
- if (!mKeyguardUpdateMonitor.isUnlockingWithFingerprintAllowed()) {
+ public void onBiometricAuthenticated(int userId, BiometricSourceType biometricSourceType) {
+ Trace.beginSection("KeyguardUpdateMonitorCallback#onBiometricAuthenticated");
+ if (!mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed()) {
Trace.endSection();
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java
index e9bdc68..b198678 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java
@@ -19,7 +19,6 @@
import android.annotation.NonNull;
import android.content.res.Resources;
import android.util.ArraySet;
-import android.view.ContextThemeWrapper;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.FrameLayout;
@@ -105,11 +104,9 @@
}
private void reinflate() {
- ContextThemeWrapper qsThemeContext =
- new ContextThemeWrapper(mBrightnessMirror.getContext(), R.style.qs_theme);
int index = mStatusBarWindow.indexOfChild(mBrightnessMirror);
mStatusBarWindow.removeView(mBrightnessMirror);
- mBrightnessMirror = LayoutInflater.from(qsThemeContext).inflate(
+ mBrightnessMirror = LayoutInflater.from(mBrightnessMirror.getContext()).inflate(
R.layout.brightness_mirror, mStatusBarWindow, false);
mStatusBarWindow.addView(mBrightnessMirror, index);
@@ -129,6 +126,10 @@
mBrightnessMirrorListeners.remove(listener);
}
+ public void onUiModeChanged() {
+ reinflate();
+ }
+
public interface BrightnessMirrorListener {
void onBrightnessMirrorReinflated(View brightnessMirror);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ConfigurationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ConfigurationController.java
index 3dca371..8c631d9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ConfigurationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ConfigurationController.java
@@ -28,6 +28,7 @@
default void onConfigChanged(Configuration newConfig) {}
default void onDensityOrFontScaleChanged() {}
default void onOverlayChanged() {}
+ default void onUiModeChanged() {}
default void onLocaleListChanged() {}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
index aeda55a..6694b93a3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
@@ -31,8 +31,8 @@
import com.android.internal.logging.MetricsLogger;
import com.android.systemui.R;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
-import com.android.systemui.statusbar.NotificationData;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.NotificationData;
import java.io.FileDescriptor;
import java.io.PrintWriter;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
index d6d0673..3c16329 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
@@ -134,6 +134,10 @@
@Override
public void setHotspotEnabled(boolean enabled) {
+ if (mWaitingForCallback) {
+ if (DEBUG) Log.d(TAG, "Ignoring setHotspotEnabled; waiting for callback.");
+ return;
+ }
if (enabled) {
OnStartTetheringCallback callback = new OnStartTetheringCallback();
mWaitingForCallback = true;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index 25261c0..f729120 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -976,7 +976,7 @@
private SubscriptionInfo addSignalController(int id, int simSlotIndex) {
SubscriptionInfo info = new SubscriptionInfo(id, "", simSlotIndex, "", "", 0, 0, "", 0,
- null, 0, 0, "");
+ null, null, null, "");
MobileSignalController controller = new MobileSignalController(mContext,
mConfig, mHasMobileDataFeature, mPhone, mCallbackHandler, this, info,
mSubDefaults, mReceiverHandler.getLooper());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/OnHeadsUpChangedListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/OnHeadsUpChangedListener.java
index 5444f06..5028fd1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/OnHeadsUpChangedListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/OnHeadsUpChangedListener.java
@@ -16,8 +16,8 @@
package com.android.systemui.statusbar.policy;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
-import com.android.systemui.statusbar.NotificationData;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.NotificationData;
/**
* A listener to heads up changes
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 b814478..52b813f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -39,7 +39,6 @@
import android.view.View;
import android.view.ViewAnimationUtils;
import android.view.ViewGroup;
-import android.view.ViewParent;
import android.view.accessibility.AccessibilityEvent;
import android.view.inputmethod.CompletionInfo;
import android.view.inputmethod.EditorInfo;
@@ -56,10 +55,10 @@
import com.android.systemui.Dependency;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
-import com.android.systemui.statusbar.NotificationData;
+import com.android.systemui.statusbar.notification.NotificationData;
import com.android.systemui.statusbar.RemoteInputController;
-import com.android.systemui.statusbar.notification.NotificationViewWrapper;
-import com.android.systemui.statusbar.stack.StackStateAnimator;
+import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
+import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
import java.util.function.Consumer;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
index cda9d04..252ab22 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
@@ -31,7 +31,7 @@
import com.android.keyguard.KeyguardHostView.OnDismissAction;
import com.android.systemui.Dependency;
import com.android.systemui.R;
-import com.android.systemui.statusbar.NotificationData;
+import com.android.systemui.statusbar.notification.NotificationData;
import com.android.systemui.statusbar.SmartReplyController;
import com.android.systemui.statusbar.notification.NotificationUtils;
import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
index 59b376f..ef51bf0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
@@ -32,32 +32,33 @@
import android.os.UserManager;
import android.provider.Settings.Global;
import android.provider.Settings.Secure;
-import android.service.notification.Condition;
import android.service.notification.ZenModeConfig;
import android.service.notification.ZenModeConfig.ZenRule;
+import android.text.format.DateFormat;
import android.util.Log;
-import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.Dumpable;
import com.android.systemui.qs.GlobalSetting;
import com.android.systemui.settings.CurrentUserTracker;
import com.android.systemui.util.Utils;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.LinkedHashMap;
import java.util.Objects;
/** Platform implementation of the zen mode controller. **/
-public class ZenModeControllerImpl extends CurrentUserTracker implements ZenModeController {
+public class ZenModeControllerImpl extends CurrentUserTracker
+ implements ZenModeController, Dumpable {
private static final String TAG = "ZenModeController";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
- private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>();
+ private final ArrayList<Callback> mCallbacks = new ArrayList<>();
private final Context mContext;
private final GlobalSetting mModeSetting;
private final GlobalSetting mConfigSetting;
private final NotificationManager mNoMan;
- private final LinkedHashMap<Uri, Condition> mConditions = new LinkedHashMap<Uri, Condition>();
private final AlarmManager mAlarmManager;
private final SetupObserver mSetupObserver;
private final UserManager mUserManager;
@@ -66,6 +67,7 @@
private boolean mRegistered;
private ZenModeConfig mConfig;
private int mZenMode;
+ private long mZenUpdateTime;
public ZenModeControllerImpl(Context context, Handler handler) {
super(context);
@@ -84,7 +86,6 @@
}
};
mNoMan = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
- mConfig = mNoMan.getZenModeConfig();
mModeSetting.setListening(true);
updateZenMode(mModeSetting.getValue());
mConfigSetting.setListening(true);
@@ -209,6 +210,7 @@
@VisibleForTesting
protected void updateZenMode(int mode) {
mZenMode = mode;
+ mZenUpdateTime = System.currentTimeMillis();
}
@VisibleForTesting
@@ -217,6 +219,7 @@
if (Objects.equals(config, mConfig)) return;
final ZenRule oldRule = mConfig != null ? mConfig.manualRule : null;
mConfig = config;
+ mZenUpdateTime = System.currentTimeMillis();
fireConfigChanged(config);
final ZenRule newRule = config != null ? config.manualRule : null;
if (Objects.equals(oldRule, newRule)) return;
@@ -235,6 +238,14 @@
}
};
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("ZenModeControllerImpl:");
+ pw.println(" mZenMode=" + mZenMode);
+ pw.println(" mConfig=" + mConfig);
+ pw.println(" mZenUpdateTime=" + DateFormat.format("MM-dd HH:mm:ss", mZenUpdateTime));
+ }
+
private final class SetupObserver extends ContentObserver {
private final ContentResolver mResolver;
@@ -261,6 +272,7 @@
Global.getUriFor(Global.DEVICE_PROVISIONED), false, this);
mResolver.registerContentObserver(
Secure.getUriFor(Secure.USER_SETUP_COMPLETE), false, this, mUserId);
+ mRegistered = true;
fireZenAvailableChanged(isZenAvailable());
}
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerActivity.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerActivity.java
index 526e69b..1d4f9b3 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunerActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerActivity.java
@@ -15,21 +15,25 @@
*/
package com.android.systemui.tuner;
+import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentTransaction;
import android.os.Bundle;
-import androidx.preference.PreferenceFragment;
-import androidx.preference.Preference;
-import androidx.preference.PreferenceScreen;
import android.util.Log;
import android.view.MenuItem;
+import android.view.Window;
+import android.view.WindowManager;
+import android.widget.Toolbar;
-import com.android.settingslib.drawer.SettingsDrawerActivity;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceFragment;
+import androidx.preference.PreferenceScreen;
+
import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.fragments.FragmentService;
-public class TunerActivity extends SettingsDrawerActivity implements
+public class TunerActivity extends Activity implements
PreferenceFragment.OnPreferenceStartFragmentCallback,
PreferenceFragment.OnPreferenceStartScreenCallback {
@@ -37,6 +41,15 @@
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
+ requestWindowFeature(Window.FEATURE_NO_TITLE);
+ setContentView(R.layout.tuner_activity);
+ Toolbar toolbar = findViewById(R.id.action_bar);
+ if (toolbar != null) {
+ setActionBar(toolbar);
+ }
+
Dependency.initDependencies(this);
if (getFragmentManager().findFragmentByTag(TAG_TUNER) == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java b/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java
index 67fa049..c468fef 100644
--- a/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java
+++ b/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java
@@ -113,7 +113,7 @@
NotificationChannel screenshotChannel = new NotificationChannel(SCREENSHOTS_HEADSUP,
name, NotificationManager.IMPORTANCE_HIGH); // pop on screen
- screenshotChannel.setSound(Uri.parse(""), // silent
+ screenshotChannel.setSound(null, // silent
new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_NOTIFICATION).build());
screenshotChannel.setBlockableSystem(true);
diff --git a/packages/SystemUI/src/com/android/systemui/util/wakelock/DelayedWakeLock.java b/packages/SystemUI/src/com/android/systemui/util/wakelock/DelayedWakeLock.java
index a901e88..b835909 100644
--- a/packages/SystemUI/src/com/android/systemui/util/wakelock/DelayedWakeLock.java
+++ b/packages/SystemUI/src/com/android/systemui/util/wakelock/DelayedWakeLock.java
@@ -23,7 +23,7 @@
*/
public class DelayedWakeLock implements WakeLock {
- private static final long RELEASE_DELAY_MS = 140;
+ private static final long RELEASE_DELAY_MS = 100;
private final Handler mHandler;
private final WakeLock mInner;
diff --git a/packages/SystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java
index d7fad67..2cbb78a 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java
@@ -193,11 +193,11 @@
}
public void show(int reason) {
- mHandler.obtainMessage(H.SHOW, reason, 0).sendToTarget();
+ mHandler.obtainMessage(H.SHOW, reason).sendToTarget();
}
public void dismiss(int reason) {
- mHandler.obtainMessage(H.DISMISS, reason, 0).sendToTarget();
+ mHandler.obtainMessage(H.DISMISS, reason).sendToTarget();
}
private void showH(int reason) {
@@ -223,7 +223,7 @@
mHandler.removeMessages(H.DISMISS);
final int timeout = computeTimeoutH();
mHandler.sendMessageDelayed(mHandler
- .obtainMessage(H.DISMISS, Events.DISMISS_REASON_TIMEOUT, 0), timeout);
+ .obtainMessage(H.DISMISS, Events.DISMISS_REASON_TIMEOUT), timeout);
if (D.BUG) {
Log.d(TAG, "rescheduleTimeout " + timeout + " " + Debug.getCaller());
@@ -246,7 +246,6 @@
}
mListView.animate().cancel();
- mShowing = false;
mListView.setTranslationY(0);
mListView.setAlpha(1);
@@ -260,6 +259,7 @@
Log.d(TAG, "mDialog.dismiss()");
}
mDialog.dismiss();
+ mShowing = false;
}, DISMISS_DELAY_IN_MILLIS))
.start();
@@ -351,11 +351,11 @@
listItem.setOnSeekBarChangeListener(
new CarVolumeDialogImpl.VolumeSeekBarChangeListener(volumeGroupId, mCarAudioManager));
Drawable primaryIcon = mContext.getResources().getDrawable(volumeItem.icon);
- primaryIcon.setTint(color);
+ primaryIcon.mutate().setTint(color);
listItem.setPrimaryActionIcon(primaryIcon);
if (supplementalIconId != 0) {
Drawable supplementalIcon = mContext.getResources().getDrawable(supplementalIconId);
- supplementalIcon.setTint(color);
+ supplementalIcon.mutate().setTint(color);
listItem.setSupplementalIcon(supplementalIcon, true,
supplementalIconOnClickListener);
} else {
@@ -436,7 +436,8 @@
public boolean onTouchEvent(MotionEvent event) {
if (isShowing()) {
if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {
- dismissH(Events.DISMISS_REASON_TOUCH_OUTSIDE);
+ mHandler.obtainMessage(
+ H.DISMISS, Events.DISMISS_REASON_TOUCH_OUTSIDE).sendToTarget();
return true;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
index dd55264..2861dff 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
@@ -61,7 +61,7 @@
private final VolumeDialogControllerImpl mController;
private final InterestingConfigChanges mConfigChanges = new InterestingConfigChanges(
ActivityInfo.CONFIG_FONT_SCALE | ActivityInfo.CONFIG_LOCALE
- | ActivityInfo.CONFIG_ASSETS_PATHS);
+ | ActivityInfo.CONFIG_ASSETS_PATHS | ActivityInfo.CONFIG_UI_MODE);
private VolumeDialog mDialog;
private VolumePolicy mVolumePolicy = new VolumePolicy(
DEFAULT_VOLUME_DOWN_TO_ENTER_SILENT, // volumeDownToEnterSilent
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ExpandHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/ExpandHelperTest.java
index 08c4235..c180ff8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ExpandHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ExpandHelperTest.java
@@ -25,7 +25,7 @@
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.NotificationTestHelper;
import org.junit.Before;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
index d19715d..5ecf0c0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
@@ -24,6 +24,7 @@
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;
@@ -66,6 +67,8 @@
public static final long BELOW_HYBRID_THRESHOLD = TimeUnit.HOURS.toMillis(2);
public static final long ABOVE_HYBRID_THRESHOLD = TimeUnit.HOURS.toMillis(4);
private static final long ABOVE_CHARGE_CYCLE_THRESHOLD = Duration.ofHours(8).toMillis();
+ private static final int OLD_BATTERY_LEVEL_NINE = 9;
+ private static final int OLD_BATTERY_LEVEL_10 = 10;
private HardwarePropertiesManager mHardProps;
private WarningsUI mMockWarnings;
private PowerUI mPowerUI;
@@ -307,8 +310,8 @@
.thenReturn(new Estimate(BELOW_HYBRID_THRESHOLD, true));
mPowerUI.mBatteryStatus = BatteryManager.BATTERY_HEALTH_GOOD;
- mPowerUI.maybeShowBatteryWarning(UNPLUGGED, UNPLUGGED, ABOVE_WARNING_BUCKET,
- ABOVE_WARNING_BUCKET);
+ mPowerUI.maybeShowBatteryWarning(OLD_BATTERY_LEVEL_NINE, UNPLUGGED, UNPLUGGED,
+ ABOVE_WARNING_BUCKET, ABOVE_WARNING_BUCKET);
// reduce battery level to handle time based trigger -> level trigger interactions
mPowerUI.mBatteryLevel = 10;
@@ -449,6 +452,33 @@
verify(mMockWarnings, never()).dismissLowBatteryWarning();
}
+ @Test
+ public void testMaybeShowBatteryWarning_onlyQueriesEstimateOnBatteryLevelChangeOrNull() {
+ mPowerUI.start();
+ Estimate estimate = new Estimate(BELOW_HYBRID_THRESHOLD, true);
+ when(mEnhancedEstimates.isHybridNotificationEnabled()).thenReturn(true);
+ when(mEnhancedEstimates.getLowWarningThreshold()).thenReturn(PowerUI.THREE_HOURS_IN_MILLIS);
+ when(mEnhancedEstimates.getSevereWarningThreshold()).thenReturn(ONE_HOUR_MILLIS);
+ when(mEnhancedEstimates.getEstimate()).thenReturn(estimate);
+ mPowerUI.mBatteryStatus = BatteryManager.BATTERY_HEALTH_GOOD;
+
+ // we expect that the first time it will query even if the level is the same
+ mPowerUI.mBatteryLevel = 9;
+ mPowerUI.maybeShowBatteryWarning(OLD_BATTERY_LEVEL_NINE, UNPLUGGED, UNPLUGGED,
+ ABOVE_WARNING_BUCKET, ABOVE_WARNING_BUCKET);
+ verify(mEnhancedEstimates, times(1)).getEstimate();
+
+ // We should NOT query again if the battery level hasn't changed
+ mPowerUI.maybeShowBatteryWarning(OLD_BATTERY_LEVEL_NINE, UNPLUGGED, UNPLUGGED,
+ ABOVE_WARNING_BUCKET, ABOVE_WARNING_BUCKET);
+ verify(mEnhancedEstimates, times(1)).getEstimate();
+
+ // Battery level has changed, so we should query again
+ mPowerUI.maybeShowBatteryWarning(OLD_BATTERY_LEVEL_10, UNPLUGGED, UNPLUGGED,
+ ABOVE_WARNING_BUCKET, ABOVE_WARNING_BUCKET);
+ verify(mEnhancedEstimates, times(2)).getEstimate();
+ }
+
private void setCurrentTemp(float temp) {
when(mHardProps.getDeviceTemperatures(DEVICE_TEMPERATURE_SKIN, TEMPERATURE_CURRENT))
.thenReturn(new float[] { temp });
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java
index 2af0c3e..9121473 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java
@@ -27,6 +27,11 @@
import com.android.systemui.Dependency;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.logging.NotificationLogger;
+import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
+import com.android.systemui.statusbar.notification.row.NotificationInfo;
+import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.StatusBarWindowManager;
import com.android.systemui.statusbar.policy.HeadsUpManager;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java
index 26f91b3..3cafaf4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java
@@ -33,6 +33,8 @@
import android.testing.TestableLooper;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
import org.junit.Before;
import org.junit.Test;
@@ -40,9 +42,6 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import java.util.HashSet;
-import java.util.Set;
-
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
index 2401519..a7758a6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
@@ -41,6 +41,7 @@
import android.testing.TestableLooper;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.google.android.collect.Lists;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
index 7a2cb3a..afe2cf6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
@@ -19,6 +19,9 @@
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.google.android.collect.Sets;
import org.junit.Before;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
index c6bcd36..b2170fa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
@@ -32,7 +32,9 @@
import android.widget.RemoteViews;
import com.android.systemui.R;
-import com.android.systemui.statusbar.notification.NotificationInflaterTest;
+import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.row.NotificationInflaterTest;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.policy.HeadsUpManager;
@@ -43,8 +45,11 @@
*/
public class NotificationTestHelper {
- static final String PKG = "com.android.systemui";
- static final int UID = 1000;
+ /** Package name for testing purposes. */
+ public static final String PKG = "com.android.systemui";
+ /** System UI id for testing purposes. */
+ public static final int UID = 1000;
+
private static final String GROUP_KEY = "gruKey";
private final Context mContext;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationUiAdjustmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationUiAdjustmentTest.java
new file mode 100644
index 0000000..ce47e60
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationUiAdjustmentTest.java
@@ -0,0 +1,180 @@
+/*
+ * 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.systemui.statusbar;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.Notification;
+import android.app.PendingIntent;
+import android.app.RemoteInput;
+import android.content.Intent;
+import android.graphics.drawable.Icon;
+import android.support.test.filters.SmallTest;
+
+import com.android.internal.R;
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Test;
+
+import java.util.Collections;
+
+@SmallTest
+public class NotificationUiAdjustmentTest extends SysuiTestCase {
+
+ @Test
+ public void needReinflate_differentLength() {
+ PendingIntent pendingIntent =
+ PendingIntent.getActivity(mContext, 0, new Intent(), 0);
+ Notification.Action action =
+ createActionBuilder("first", R.drawable.ic_corp_icon, pendingIntent).build();
+ assertThat(NotificationUiAdjustment.needReinflate(
+ new NotificationUiAdjustment("first", Collections.emptyList()),
+ new NotificationUiAdjustment("second", Collections.singletonList(action))))
+ .isTrue();
+ }
+
+ @Test
+ public void needReinflate_differentLabels() {
+ PendingIntent pendingIntent =
+ PendingIntent.getActivity(mContext, 0, new Intent(), 0);
+ Notification.Action firstAction =
+ createActionBuilder("first", R.drawable.ic_corp_icon, pendingIntent).build();
+ Notification.Action secondAction =
+ createActionBuilder("second", R.drawable.ic_corp_icon, pendingIntent).build();
+
+ assertThat(NotificationUiAdjustment.needReinflate(
+ new NotificationUiAdjustment("first", Collections.singletonList(firstAction)),
+ new NotificationUiAdjustment("second", Collections.singletonList(secondAction))))
+ .isTrue();
+ }
+
+ @Test
+ public void needReinflate_differentIcons() {
+ PendingIntent pendingIntent =
+ PendingIntent.getActivity(mContext, 0, new Intent(), 0);
+ Notification.Action firstAction =
+ createActionBuilder("same", R.drawable.ic_corp_icon, pendingIntent).build();
+ Notification.Action secondAction =
+ createActionBuilder("same", R.drawable.ic_account_circle, pendingIntent)
+ .build();
+
+ assertThat(NotificationUiAdjustment.needReinflate(
+ new NotificationUiAdjustment("first", Collections.singletonList(firstAction)),
+ new NotificationUiAdjustment("second", Collections.singletonList(secondAction))))
+ .isTrue();
+ }
+
+ @Test
+ public void needReinflate_differentPendingIntent() {
+ PendingIntent firstPendingIntent =
+ PendingIntent.getActivity(mContext, 0, new Intent(Intent.ACTION_VIEW), 0);
+ PendingIntent secondPendingIntent =
+ PendingIntent.getActivity(mContext, 0, new Intent(Intent.ACTION_PROCESS_TEXT), 0);
+ Notification.Action firstAction =
+ createActionBuilder("same", R.drawable.ic_corp_icon, firstPendingIntent)
+ .build();
+ Notification.Action secondAction =
+ createActionBuilder("same", R.drawable.ic_corp_icon, secondPendingIntent)
+ .build();
+
+ assertThat(NotificationUiAdjustment.needReinflate(
+ new NotificationUiAdjustment("first", Collections.singletonList(firstAction)),
+ new NotificationUiAdjustment("second", Collections.singletonList(secondAction))))
+ .isTrue();
+ }
+
+ @Test
+ public void needReinflate_differentChoices() {
+ PendingIntent pendingIntent =
+ PendingIntent.getActivity(mContext, 0, new Intent(), 0);
+
+ RemoteInput firstRemoteInput =
+ createRemoteInput("same", "same", new CharSequence[] {"first"});
+ RemoteInput secondRemoteInput =
+ createRemoteInput("same", "same", new CharSequence[] {"second"});
+
+ Notification.Action firstAction =
+ createActionBuilder("same", R.drawable.ic_corp_icon, pendingIntent)
+ .addRemoteInput(firstRemoteInput)
+ .build();
+ Notification.Action secondAction =
+ createActionBuilder("same", R.drawable.ic_corp_icon, pendingIntent)
+ .addRemoteInput(secondRemoteInput)
+ .build();
+
+ assertThat(NotificationUiAdjustment.needReinflate(
+ new NotificationUiAdjustment("first", Collections.singletonList(firstAction)),
+ new NotificationUiAdjustment("second", Collections.singletonList(secondAction))))
+ .isTrue();
+ }
+
+ @Test
+ public void needReinflate_differentRemoteInputLabel() {
+ PendingIntent pendingIntent =
+ PendingIntent.getActivity(mContext, 0, new Intent(), 0);
+
+ RemoteInput firstRemoteInput =
+ createRemoteInput("same", "first", new CharSequence[] {"same"});
+ RemoteInput secondRemoteInput =
+ createRemoteInput("same", "second", new CharSequence[] {"same"});
+
+ Notification.Action firstAction =
+ createActionBuilder("same", R.drawable.ic_corp_icon, pendingIntent)
+ .addRemoteInput(firstRemoteInput)
+ .build();
+ Notification.Action secondAction =
+ createActionBuilder("same", R.drawable.ic_corp_icon, pendingIntent)
+ .addRemoteInput(secondRemoteInput)
+ .build();
+
+ assertThat(NotificationUiAdjustment.needReinflate(
+ new NotificationUiAdjustment("first", Collections.singletonList(firstAction)),
+ new NotificationUiAdjustment("second", Collections.singletonList(secondAction))))
+ .isTrue();
+ }
+
+ @Test
+ public void needReinflate_negative() {
+ PendingIntent pendingIntent =
+ PendingIntent.getActivity(mContext, 0, new Intent(), 0);
+ RemoteInput firstRemoteInput =
+ createRemoteInput("same", "same", new CharSequence[] {"same"});
+ RemoteInput secondRemoteInput =
+ createRemoteInput("same", "same", new CharSequence[] {"same"});
+
+ Notification.Action firstAction =
+ createActionBuilder("same", R.drawable.ic_corp_icon, pendingIntent)
+ .addRemoteInput(firstRemoteInput).build();
+ Notification.Action secondAction =
+ createActionBuilder("same", R.drawable.ic_corp_icon, pendingIntent)
+ .addRemoteInput(secondRemoteInput).build();
+
+ assertThat(NotificationUiAdjustment.needReinflate(
+ new NotificationUiAdjustment("first", Collections.singletonList(firstAction)),
+ new NotificationUiAdjustment("second", Collections.singletonList(secondAction))))
+ .isFalse();
+ }
+
+ private Notification.Action.Builder createActionBuilder(
+ String title, int drawableRes, PendingIntent pendingIntent) {
+ return new Notification.Action.Builder(
+ Icon.createWithResource(mContext, drawableRes), title, pendingIntent);
+ }
+
+ private RemoteInput createRemoteInput(String resultKey, String label, CharSequence[] choices) {
+ return new RemoteInput.Builder(resultKey).setLabel(label).setChoices(choices).build();
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
index 0d0d1f86..15c18e9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
@@ -34,7 +34,13 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
+import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.notification.logging.NotificationLogger;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.ExpandableView;
+import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.google.android.collect.Lists;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java
index e91530d..ada5785 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java
@@ -33,6 +33,8 @@
import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
import org.junit.Before;
import org.junit.Test;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AboveShelfObserverTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AboveShelfObserverTest.java
index 00e9995..b7aa21b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AboveShelfObserverTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AboveShelfObserverTest.java
@@ -26,7 +26,7 @@
import android.widget.FrameLayout;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.NotificationTestHelper;
import org.junit.Assert;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AppOpsListenerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AppOpsListenerTest.java
similarity index 95%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/AppOpsListenerTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AppOpsListenerTest.java
index 0feaa5a..78be783 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AppOpsListenerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AppOpsListenerTest.java
@@ -11,10 +11,10 @@
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
- * limitations under the License.
+ * limitations under the License
*/
-package com.android.systemui.statusbar;
+package com.android.systemui.statusbar.notification;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -29,6 +29,7 @@
import com.android.systemui.ForegroundServiceController;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.NotificationPresenter;
import org.junit.Before;
import org.junit.Test;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java
similarity index 87%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationDataTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java
index 77522e4..c3683b3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java
@@ -11,10 +11,10 @@
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
- * limitations under the License.
+ * limitations under the License
*/
-package com.android.systemui.statusbar;
+package com.android.systemui.statusbar.notification;
import static android.app.AppOpsManager.OP_ACCEPT_HANDOVER;
import static android.app.AppOpsManager.OP_CAMERA;
@@ -38,11 +38,16 @@
import android.Manifest;
import android.app.Notification;
import android.app.NotificationChannel;
+import android.app.PendingIntent;
+import android.content.Intent;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
+import android.graphics.drawable.Icon;
import android.media.session.MediaSession;
import android.os.Bundle;
import android.service.notification.NotificationListenerService;
+import android.service.notification.NotificationListenerService.Ranking;
+import android.service.notification.SnoozeCriterion;
import android.service.notification.StatusBarNotification;
import android.support.test.annotation.UiThreadTest;
import android.support.test.filters.SmallTest;
@@ -52,6 +57,8 @@
import com.android.systemui.ForegroundServiceController;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.NotificationTestHelper;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import org.junit.Assert;
@@ -72,6 +79,8 @@
private static final int UID_ALLOW_DURING_SETUP = 456;
private static final String TEST_HIDDEN_NOTIFICATION_KEY = "testHiddenNotificationKey";
private static final String TEST_EXEMPT_DND_VISUAL_SUPPRESSION_KEY = "exempt";
+ private static final NotificationChannel NOTIFICATION_CHANNEL =
+ new NotificationChannel("id", "name", NotificationChannel.USER_LOCKED_IMPORTANCE);
private final StatusBarNotification mMockStatusBarNotification =
mock(StatusBarNotification.class);
@@ -145,11 +154,9 @@
@Test
public void testChannelSetWhenAdded() {
mNotificationData.add(mRow.getEntry());
- Assert.assertTrue(mRow.getEntry().channel != null);
+ assertEquals(NOTIFICATION_CHANNEL, mRow.getEntry().channel);
}
-
-
@Test
public void testAllRelevantNotisTaggedWithAppOps() throws Exception {
mNotificationData.add(mRow.getEntry());
@@ -295,7 +302,7 @@
mNotificationData.getNotificationsForCurrentUser();
assertEquals(reuslt.size(), 1);
- assertEquals(reuslt.get(0), row2.getEntry());
+ junit.framework.Assert.assertEquals(reuslt.get(0), row2.getEntry());
}
@Test
@@ -373,6 +380,32 @@
assertFalse(mNotificationData.isExemptFromDndVisualSuppression(entry));
}
+ @Test
+ public void testCreateNotificationDataEntry_RankingUpdate() {
+ Ranking ranking = mock(Ranking.class);
+
+ ArrayList<Notification.Action> smartActions = new ArrayList<>();
+ smartActions.add(createAction());
+ when(ranking.getSmartActions()).thenReturn(smartActions);
+
+ when(ranking.getChannel()).thenReturn(NOTIFICATION_CHANNEL);
+
+ when(ranking.getUserSentiment()).thenReturn(Ranking.USER_SENTIMENT_NEGATIVE);
+
+ SnoozeCriterion snoozeCriterion = new SnoozeCriterion("id", "explanation", "confirmation");
+ ArrayList<SnoozeCriterion> snoozeCriterions = new ArrayList<>();
+ snoozeCriterions.add(snoozeCriterion);
+ when(ranking.getSnoozeCriteria()).thenReturn(snoozeCriterions);
+
+ NotificationData.Entry entry =
+ new NotificationData.Entry(mMockStatusBarNotification, ranking);
+
+ assertEquals(smartActions, entry.smartActions);
+ assertEquals(NOTIFICATION_CHANNEL, entry.channel);
+ assertEquals(Ranking.USER_SENTIMENT_NEGATIVE, entry.userSentiment);
+ assertEquals(snoozeCriterions, entry.snoozeCriteria);
+ }
+
private void initStatusBarNotification(boolean allowDuringSetup) {
Bundle bundle = new Bundle();
bundle.putBoolean(Notification.EXTRA_ALLOW_DURING_SETUP, allowDuringSetup);
@@ -388,12 +421,7 @@
}
@Override
- public NotificationChannel getChannel(String key) {
- return new NotificationChannel(null, null, 0);
- }
-
- @Override
- protected boolean getRanking(String key, NotificationListenerService.Ranking outRanking) {
+ protected boolean getRanking(String key, Ranking outRanking) {
super.getRanking(key, outRanking);
if (key.equals(TEST_HIDDEN_NOTIFICATION_KEY)) {
outRanking.populate(key, outRanking.getRank(),
@@ -401,23 +429,31 @@
outRanking.getVisibilityOverride(), outRanking.getSuppressedVisualEffects(),
outRanking.getImportance(), outRanking.getImportanceExplanation(),
outRanking.getOverrideGroupKey(), outRanking.getChannel(), null, null,
- outRanking.canShowBadge(), outRanking.getUserSentiment(), true);
+ outRanking.canShowBadge(), outRanking.getUserSentiment(), true,
+ null);
} else if (key.equals(TEST_EXEMPT_DND_VISUAL_SUPPRESSION_KEY)) {
outRanking.populate(key, outRanking.getRank(),
outRanking.matchesInterruptionFilter(),
outRanking.getVisibilityOverride(), 255,
outRanking.getImportance(), outRanking.getImportanceExplanation(),
outRanking.getOverrideGroupKey(), outRanking.getChannel(), null, null,
- outRanking.canShowBadge(), outRanking.getUserSentiment(), true);
+ outRanking.canShowBadge(), outRanking.getUserSentiment(), true, null);
} else {
outRanking.populate(key, outRanking.getRank(),
outRanking.matchesInterruptionFilter(),
outRanking.getVisibilityOverride(), outRanking.getSuppressedVisualEffects(),
outRanking.getImportance(), outRanking.getImportanceExplanation(),
- outRanking.getOverrideGroupKey(), outRanking.getChannel(), null, null,
- outRanking.canShowBadge(), outRanking.getUserSentiment(), false);
+ outRanking.getOverrideGroupKey(), NOTIFICATION_CHANNEL, null, null,
+ outRanking.canShowBadge(), outRanking.getUserSentiment(), false, null);
}
return true;
}
}
+
+ private Notification.Action createAction() {
+ return new Notification.Action.Builder(
+ Icon.createWithResource(getContext(), android.R.drawable.sym_def_app_icon),
+ "action",
+ PendingIntent.getBroadcast(getContext(), 0, new Intent("Action"), 0)).build();
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
similarity index 79%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationEntryManagerTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
index afe16cf..61c1ecb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationEntryManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
@@ -11,10 +11,10 @@
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
- * limitations under the License.
+ * limitations under the License
*/
-package com.android.systemui.statusbar;
+package com.android.systemui.statusbar.notification;
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertNull;
@@ -37,7 +37,10 @@
import android.app.AppOpsManager;
import android.app.Notification;
import android.app.NotificationManager;
+import android.app.PendingIntent;
import android.content.Context;
+import android.content.Intent;
+import android.graphics.drawable.Icon;
import android.os.Handler;
import android.os.Looper;
import android.os.UserHandle;
@@ -54,7 +57,18 @@
import com.android.systemui.ForegroundServiceController;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.NotificationListener;
+import com.android.systemui.statusbar.NotificationLockscreenUserManager;
+import com.android.systemui.statusbar.NotificationMediaManager;
+import com.android.systemui.statusbar.NotificationPresenter;
+import com.android.systemui.statusbar.NotificationRemoteInputManager;
+import com.android.systemui.statusbar.RemoteInputController;
+import com.android.systemui.statusbar.SmartReplyController;
+import com.android.systemui.statusbar.StatusBarIconView;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
+import com.android.systemui.statusbar.notification.row.RowInflaterTask;
+import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
@@ -68,6 +82,9 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -99,6 +116,7 @@
@Mock private VisualStabilityManager mVisualStabilityManager;
@Mock private MetricsLogger mMetricsLogger;
@Mock private SmartReplyController mSmartReplyController;
+ @Mock private RowInflaterTask mAsyncInflationTask;
private NotificationData.Entry mEntry;
private StatusBarNotification mSbn;
@@ -139,7 +157,26 @@
0,
NotificationManager.IMPORTANCE_DEFAULT,
null, null,
- null, null, null, true, sentiment, false);
+ null, null, null, true, sentiment, false, null);
+ return true;
+ }).when(mRankingMap).getRanking(eq(key), any(NotificationListenerService.Ranking.class));
+ }
+
+ private void setSmartActions(String key, ArrayList<Notification.Action> smartActions) {
+ doAnswer(invocationOnMock -> {
+ NotificationListenerService.Ranking ranking = (NotificationListenerService.Ranking)
+ invocationOnMock.getArguments()[1];
+ ranking.populate(
+ key,
+ 0,
+ false,
+ 0,
+ 0,
+ NotificationManager.IMPORTANCE_DEFAULT,
+ null, null,
+ null, null, null, true,
+ NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL, false,
+ smartActions);
return true;
}).when(mRankingMap).getRanking(eq(key), any(NotificationListenerService.Ranking.class));
}
@@ -427,4 +464,71 @@
Assert.assertTrue(newSbn.getNotification().extras
.getBoolean(Notification.EXTRA_HIDE_SMART_REPLIES, false));
}
+
+ @Test
+ public void testUpdateNotificationRanking() {
+ when(mPresenter.isDeviceProvisioned()).thenReturn(true);
+ when(mPresenter.isNotificationForCurrentProfiles(any())).thenReturn(true);
+
+ mEntry.row = mRow;
+ mEntry.setInflationTask(mAsyncInflationTask);
+ mEntryManager.getNotificationData().add(mEntry);
+ setSmartActions(mEntry.key, new ArrayList<>(Arrays.asList(createAction())));
+
+ mEntryManager.updateNotificationRanking(mRankingMap);
+ verify(mRow).updateNotification(eq(mEntry));
+ assertEquals(1, mEntry.smartActions.size());
+ assertEquals("action", mEntry.smartActions.get(0).title);
+ }
+
+ @Test
+ public void testUpdateNotificationRanking_noChange() {
+ when(mPresenter.isDeviceProvisioned()).thenReturn(true);
+ when(mPresenter.isNotificationForCurrentProfiles(any())).thenReturn(true);
+
+ mEntry.row = mRow;
+ mEntryManager.getNotificationData().add(mEntry);
+ setSmartActions(mEntry.key, null);
+
+ mEntryManager.updateNotificationRanking(mRankingMap);
+ verify(mRow, never()).updateNotification(eq(mEntry));
+ assertEquals(0, mEntry.smartActions.size());
+ }
+
+ @Test
+ public void testUpdateNotificationRanking_rowNotInflatedYet() {
+ when(mPresenter.isDeviceProvisioned()).thenReturn(true);
+ when(mPresenter.isNotificationForCurrentProfiles(any())).thenReturn(true);
+
+ mEntry.row = null;
+ mEntryManager.getNotificationData().add(mEntry);
+ setSmartActions(mEntry.key, new ArrayList<>(Arrays.asList(createAction())));
+
+ mEntryManager.updateNotificationRanking(mRankingMap);
+ verify(mRow, never()).updateNotification(eq(mEntry));
+ assertEquals(1, mEntry.smartActions.size());
+ assertEquals("action", mEntry.smartActions.get(0).title);
+ }
+
+ @Test
+ public void testUpdateNotificationRanking_pendingNotification() {
+ when(mPresenter.isDeviceProvisioned()).thenReturn(true);
+ when(mPresenter.isNotificationForCurrentProfiles(any())).thenReturn(true);
+
+ mEntry.row = null;
+ mEntryManager.mPendingNotifications.put(mEntry.key, mEntry);
+ setSmartActions(mEntry.key, new ArrayList<>(Arrays.asList(createAction())));
+
+ mEntryManager.updateNotificationRanking(mRankingMap);
+ verify(mRow, never()).updateNotification(eq(mEntry));
+ assertEquals(1, mEntry.smartActions.size());
+ assertEquals("action", mEntry.smartActions.get(0).title);
+ }
+
+ private Notification.Action createAction() {
+ return new Notification.Action.Builder(
+ Icon.createWithResource(getContext(), android.R.drawable.sym_def_app_icon),
+ "action",
+ PendingIntent.getBroadcast(getContext(), 0, new Intent("Action"), 0)).build();
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationViewWrapperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationViewWrapperTest.java
index 7e2e505..63d1e8d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationViewWrapperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationViewWrapperTest.java
@@ -25,10 +25,10 @@
import android.view.View;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.NotificationTestHelper;
+import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
-import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/PropertyAnimatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/PropertyAnimatorTest.java
index f0ca3ef..6359234 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/PropertyAnimatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/PropertyAnimatorTest.java
@@ -35,11 +35,10 @@
import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.stack.AnimationFilter;
-import com.android.systemui.statusbar.stack.AnimationProperties;
-import com.android.systemui.statusbar.stack.ViewState;
+import com.android.systemui.statusbar.notification.stack.AnimationFilter;
+import com.android.systemui.statusbar.notification.stack.AnimationProperties;
+import com.android.systemui.statusbar.notification.stack.ViewState;
-import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/VisualStabilityManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/VisualStabilityManagerTest.java
index 95ce0d8..ffb1c2d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/VisualStabilityManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/VisualStabilityManagerTest.java
@@ -21,10 +21,7 @@
import android.test.suitebuilder.annotation.SmallTest;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
-import com.android.systemui.statusbar.NotificationData;
-import com.android.systemui.statusbar.notification.VisibilityLocationProvider;
-import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import org.junit.Before;
import org.junit.Test;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLoggerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
similarity index 92%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLoggerTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
index 42bf290..ca62c3b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLoggerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
@@ -11,10 +11,10 @@
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
- * limitations under the License.
+ * limitations under the License
*/
-package com.android.systemui.statusbar;
+package com.android.systemui.statusbar.notification.logging;
import static org.junit.Assert.assertArrayEquals;
import static org.mockito.ArgumentMatchers.any;
@@ -38,6 +38,12 @@
import com.android.internal.statusbar.NotificationVisibility;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.NotificationListener;
+import com.android.systemui.statusbar.NotificationPresenter;
+import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.google.android.collect.Lists;
import org.junit.Before;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AppOpsInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/AppOpsInfoTest.java
similarity index 98%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/AppOpsInfoTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/AppOpsInfoTest.java
index 660d2dc..dd5cb58 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AppOpsInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/AppOpsInfoTest.java
@@ -11,10 +11,10 @@
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
- * limitations under the License.
+ * limitations under the License
*/
-package com.android.systemui.statusbar;
+package com.android.systemui.statusbar.notification.row;
import static android.app.AppOpsManager.OP_CAMERA;
import static android.app.AppOpsManager.OP_RECORD_AUDIO;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
similarity index 96%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/ExpandableNotificationRowTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
index ff12c53..743b307 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/ExpandableNotificationRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package com.android.systemui.statusbar;
+package com.android.systemui.statusbar.notification.row;
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
@@ -42,8 +42,9 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
+import com.android.systemui.statusbar.NotificationTestHelper;
import com.android.systemui.statusbar.notification.AboveShelfChangedListener;
-import com.android.systemui.statusbar.stack.NotificationChildrenContainer;
+import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainer;
import org.junit.Assert;
import org.junit.Before;
@@ -308,4 +309,12 @@
mGroupRow.resetTranslation();
assertEquals(0, mGroupRow.getEntry().expandedIcon.getScrollX());
}
+
+ @Test
+ public void testIsExpanded_userExpanded() {
+ mGroupRow.setExpandable(true);
+ Assert.assertFalse(mGroupRow.isExpanded());
+ mGroupRow.setUserExpanded(true);
+ Assert.assertTrue(mGroupRow.isExpanded());
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/FooterViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/FooterViewTest.java
similarity index 97%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/FooterViewTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/FooterViewTest.java
index e6fdfa4..aa7889a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/FooterViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/FooterViewTest.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package com.android.systemui.statusbar;
+package com.android.systemui.statusbar.notification.row;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationBlockingHelperManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManagerTest.java
similarity index 97%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationBlockingHelperManagerTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManagerTest.java
index 4366032..4efab53 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationBlockingHelperManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManagerTest.java
@@ -14,10 +14,12 @@
* limitations under the License
*/
-package com.android.systemui.statusbar;
+package com.android.systemui.statusbar.notification.row;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.NotificationTestHelper;
import android.content.Context;
import android.support.test.filters.FlakyTest;
@@ -27,12 +29,9 @@
import android.view.View;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import org.mockito.junit.MockitoJUnit;
-import org.mockito.junit.MockitoRule;
import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE;
import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationContentViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java
similarity index 98%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationContentViewTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java
index 1fb4c37..c189c95 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationContentViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package com.android.systemui.statusbar;
+package com.android.systemui.statusbar.notification.row;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyFloat;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationGutsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
similarity index 97%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationGutsManagerTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
index 72255f3..e56ccef 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationGutsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package com.android.systemui.statusbar;
+package com.android.systemui.statusbar.notification.row;
import static android.app.AppOpsManager.OP_CAMERA;
import static android.app.AppOpsManager.OP_RECORD_AUDIO;
@@ -29,7 +29,6 @@
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -55,7 +54,10 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
-import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.NotificationPresenter;
+import com.android.systemui.statusbar.NotificationTestHelper;
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import org.junit.Before;
import org.junit.Rule;
@@ -66,9 +68,6 @@
import org.mockito.junit.MockitoRule;
import org.mockito.junit.MockitoJUnit;
-import java.util.HashSet;
-import java.util.Set;
-
/**
* Tests for {@link NotificationGutsManager}.
*/
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationInflaterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInflaterTest.java
similarity index 95%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationInflaterTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInflaterTest.java
index aa8a08c..81e79d1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationInflaterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInflaterTest.java
@@ -14,10 +14,12 @@
* limitations under the License
*/
-package com.android.systemui.statusbar.notification;
+package com.android.systemui.statusbar.notification.row;
-import static com.android.systemui.statusbar.notification.NotificationInflater.FLAG_REINFLATE_ALL;
+import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_REINFLATE_ALL;
+
+import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_REINFLATE_EXPANDED_VIEW;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
@@ -38,9 +40,8 @@
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
import com.android.systemui.statusbar.InflationTask;
-import com.android.systemui.statusbar.NotificationData;
+import com.android.systemui.statusbar.notification.NotificationData;
import com.android.systemui.statusbar.NotificationTestHelper;
import org.junit.Assert;
@@ -114,7 +115,7 @@
mRow.getPrivateLayout().removeAllViews();
mRow.getEntry().cachedBigContentView = null;
runThenWaitForInflation(() -> mNotificationInflater.inflateNotificationViews(
- NotificationInflater.FLAG_REINFLATE_EXPANDED_VIEW), mNotificationInflater);
+ FLAG_REINFLATE_EXPANDED_VIEW), mNotificationInflater);
assertTrue(mRow.getPrivateLayout().getChildCount() == 1);
assertTrue(mRow.getPrivateLayout().getChildAt(0)
== mRow.getPrivateLayout().getExpandedChild());
@@ -154,8 +155,7 @@
new NotificationInflater.InflationProgress();
result.packageContext = mContext;
CountDownLatch countDownLatch = new CountDownLatch(1);
- NotificationInflater.applyRemoteView(result,
- NotificationInflater.FLAG_REINFLATE_EXPANDED_VIEW, 0, mRow,
+ NotificationInflater.applyRemoteView(result, FLAG_REINFLATE_EXPANDED_VIEW, 0, mRow,
false /* redactAmbient */, true /* isNewView */, new RemoteViews.OnClickHandler(),
new NotificationInflater.InflationCallback() {
@Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
similarity index 99%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
index a72fed4..5ce53cf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
@@ -11,10 +11,10 @@
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
- * limitations under the License.
+ * limitations under the License
*/
-package com.android.systemui.statusbar;
+package com.android.systemui.statusbar.notification.row;
import static android.app.NotificationChannel.USER_LOCKED_IMPORTANCE;
import static android.app.NotificationManager.IMPORTANCE_LOW;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationMenuRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java
similarity index 97%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationMenuRowTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java
index 2a5a1ee..06265e5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationMenuRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java
@@ -12,7 +12,7 @@
* permissions and limitations under the License.
*/
-package com.android.systemui.statusbar;
+package com.android.systemui.statusbar.notification.row;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertTrue;
@@ -28,7 +28,6 @@
import android.testing.TestableLooper.RunWithLooper;
import android.testing.ViewUtils;
import android.testing.ViewUtils;
-import android.view.View;
import android.view.ViewGroup;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationSnoozeTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationSnoozeTest.java
similarity index 96%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationSnoozeTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationSnoozeTest.java
index 756bb1c..f8f7af0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationSnoozeTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationSnoozeTest.java
@@ -11,10 +11,10 @@
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
- * limitations under the License.
+ * limitations under the License
*/
-package com.android.systemui.statusbar;
+package com.android.systemui.statusbar.notification.row;
import android.provider.Settings;
import android.test.suitebuilder.annotation.SmallTest;
@@ -36,10 +36,8 @@
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertTrue;
-import static org.mockito.Mockito.anyString;
import static org.mockito.Matchers.isNull;
import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
@SmallTest
@RunWith(AndroidTestingRunner.class)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationCustomViewWrapperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapperTest.java
similarity index 90%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationCustomViewWrapperTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapperTest.java
index a34588d..4b94a25 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationCustomViewWrapperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapperTest.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package com.android.systemui.statusbar;
+package com.android.systemui.statusbar.notification.row.wrapper;
import android.support.test.filters.SmallTest;
import android.testing.AndroidTestingRunner;
@@ -24,8 +24,8 @@
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.notification.NotificationCustomViewWrapper;
-import com.android.systemui.statusbar.notification.NotificationViewWrapper;
+import com.android.systemui.statusbar.NotificationTestHelper;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import org.junit.Assert;
import org.junit.Before;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java
new file mode 100644
index 0000000..087aa59
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java
@@ -0,0 +1,156 @@
+/*
+ * 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.systemui.statusbar.notification.stack;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper.RunWithLooper;
+import android.view.NotificationHeaderView;
+import android.view.View;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.NotificationTestHelper;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper(setAsMainLooper = true)
+public class NotificationChildrenContainerTest extends SysuiTestCase {
+
+ private ExpandableNotificationRow mGroup;
+ private int mId;
+ private NotificationTestHelper mNotificationTestHelper;
+ private NotificationChildrenContainer mChildrenContainer;
+
+ @Before
+ public void setUp() throws Exception {
+ mNotificationTestHelper = new NotificationTestHelper(mContext);
+ mGroup = mNotificationTestHelper.createGroup();
+ mChildrenContainer = mGroup.getChildrenContainer();
+ }
+
+ @Test
+ public void testGetMaxAllowedVisibleChildren_ambient() {
+ mGroup.setShowAmbient(true);
+ Assert.assertEquals(mChildrenContainer.getMaxAllowedVisibleChildren(),
+ NotificationChildrenContainer.NUMBER_OF_CHILDREN_WHEN_AMBIENT);
+ }
+
+ @Test
+ public void testGetMaxAllowedVisibleChildren_lowPriority() {
+ mChildrenContainer.setIsLowPriority(true);
+ Assert.assertEquals(mChildrenContainer.getMaxAllowedVisibleChildren(),
+ NotificationChildrenContainer.NUMBER_OF_CHILDREN_WHEN_SYSTEM_EXPANDED);
+ }
+
+ @Test
+ public void testGetMaxAllowedVisibleChildren_headsUp() {
+ mGroup.setHeadsUp(true);
+ Assert.assertEquals(mChildrenContainer.getMaxAllowedVisibleChildren(),
+ NotificationChildrenContainer.NUMBER_OF_CHILDREN_WHEN_SYSTEM_EXPANDED);
+ }
+
+ @Test
+ public void testGetMaxAllowedVisibleChildren_lowPriority_expandedChildren() {
+ mChildrenContainer.setIsLowPriority(true);
+ mChildrenContainer.setChildrenExpanded(true);
+ Assert.assertEquals(mChildrenContainer.getMaxAllowedVisibleChildren(),
+ NotificationChildrenContainer.NUMBER_OF_CHILDREN_WHEN_SYSTEM_EXPANDED);
+ }
+
+ @Test
+ public void testGetMaxAllowedVisibleChildren_lowPriority_userLocked() {
+ mChildrenContainer.setIsLowPriority(true);
+ mChildrenContainer.setUserLocked(true);
+ Assert.assertEquals(mChildrenContainer.getMaxAllowedVisibleChildren(),
+ NotificationChildrenContainer.NUMBER_OF_CHILDREN_WHEN_SYSTEM_EXPANDED);
+ }
+
+ @Test
+ public void testGetMaxAllowedVisibleChildren_likeCollapsed() {
+ Assert.assertEquals(mChildrenContainer.getMaxAllowedVisibleChildren(true),
+ NotificationChildrenContainer.NUMBER_OF_CHILDREN_WHEN_COLLAPSED);
+ }
+
+
+ @Test
+ public void testGetMaxAllowedVisibleChildren_expandedChildren() {
+ mChildrenContainer.setChildrenExpanded(true);
+ Assert.assertEquals(mChildrenContainer.getMaxAllowedVisibleChildren(),
+ NotificationChildrenContainer.NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED);
+ }
+
+ @Test
+ public void testGetMaxAllowedVisibleChildren_userLocked() {
+ mGroup.setUserLocked(true);
+ Assert.assertEquals(mChildrenContainer.getMaxAllowedVisibleChildren(),
+ NotificationChildrenContainer.NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED);
+ }
+
+ @Test
+ public void testShowingAsLowPriority_lowPriority() {
+ mChildrenContainer.setIsLowPriority(true);
+ Assert.assertTrue(mChildrenContainer.showingAsLowPriority());
+ }
+
+ @Test
+ public void testShowingAsLowPriority_notLowPriority() {
+ Assert.assertFalse(mChildrenContainer.showingAsLowPriority());
+ }
+
+ @Test
+ public void testShowingAsLowPriority_lowPriority_expanded() {
+ mChildrenContainer.setIsLowPriority(true);
+ mGroup.setExpandable(true);
+ mGroup.setUserExpanded(true, false);
+ Assert.assertFalse(mChildrenContainer.showingAsLowPriority());
+ }
+
+ @Test
+ public void testGetMaxAllowedVisibleChildren_userLocked_expandedChildren_lowPriority() {
+ mGroup.setUserLocked(true);
+ mGroup.setExpandable(true);
+ mGroup.setUserExpanded(true);
+ mChildrenContainer.setIsLowPriority(true);
+ Assert.assertEquals(mChildrenContainer.getMaxAllowedVisibleChildren(),
+ NotificationChildrenContainer.NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED);
+ }
+
+ @Test
+ public void testLowPriorityHeaderCleared() {
+ mGroup.setIsLowPriority(true);
+ NotificationHeaderView lowPriorityHeaderView = mChildrenContainer.getLowPriorityHeaderView();
+ Assert.assertTrue(lowPriorityHeaderView.getVisibility() == View.VISIBLE);
+ Assert.assertTrue(lowPriorityHeaderView.getParent() == mChildrenContainer);
+ mGroup.setIsLowPriority(false);
+ Assert.assertTrue(lowPriorityHeaderView.getParent() == null);
+ Assert.assertTrue(mChildrenContainer.getLowPriorityHeaderView() == null);
+ }
+
+ @Test
+ public void testRecreateNotificationHeader_hasHeader() {
+ mChildrenContainer.recreateNotificationHeader(null);
+ Assert.assertNotNull("Children container must have a header after recreation",
+ mChildrenContainer.getCurrentHeaderView());
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationRoundnessManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java
similarity index 97%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationRoundnessManagerTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java
index 16e69f4..f2431b4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationRoundnessManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package com.android.systemui.statusbar.stack;
+package com.android.systemui.statusbar.notification.stack;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.mock;
@@ -26,7 +26,7 @@
import android.view.View;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.NotificationTestHelper;
import org.junit.Assert;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
similarity index 96%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayoutTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index 5400e3b..8fb2447 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package com.android.systemui.statusbar.stack;
+package com.android.systemui.statusbar.notification.stack;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -34,8 +34,8 @@
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.EmptyShadeView;
-import com.android.systemui.statusbar.FooterView;
-import com.android.systemui.statusbar.NotificationBlockingHelperManager;
+import com.android.systemui.statusbar.notification.row.FooterView;
+import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager;
import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
index fe7bf25..a4004ae 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
@@ -17,8 +17,6 @@
package com.android.systemui.statusbar.phone;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyObject;
-import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
@@ -31,23 +29,18 @@
import android.view.View;
import android.widget.TextView;
-import com.android.systemui.Dependency;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.TestableDependency;
-import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.HeadsUpStatusBarView;
import com.android.systemui.statusbar.NotificationTestHelper;
import com.android.systemui.statusbar.policy.DarkIconDispatcher;
-import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import java.util.HashSet;
-
@SmallTest
@RunWith(AndroidTestingRunner.class)
@RunWithLooper(setAsMainLooper = true)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
index aa991cb..1837909 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
@@ -29,8 +29,8 @@
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
-import com.android.systemui.statusbar.NotificationData;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.NotificationData;
import com.android.systemui.statusbar.StatusBarIconView;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index 89d562a..9c55874 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -152,6 +152,20 @@
}
@Test
+ public void setHasBackdrop_withAodWallpaperAndAlbumArt() {
+ mScrimController.setWallpaperSupportsAmbientMode(true);
+ mScrimController.transitionTo(ScrimState.AOD);
+ mScrimController.finishAnimationsImmediately();
+ mScrimController.setHasBackdrop(true);
+ mScrimController.finishAnimationsImmediately();
+ // Front scrim should be transparent
+ // Back scrim should be visible with tint
+ assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_FULLY_OPAQUE);
+ assertScrimTint(mScrimBehind, true /* tinted */);
+ assertScrimTint(mScrimInFront, true /* tinted */);
+ }
+
+ @Test
public void transitionToAod_withFrontAlphaUpdates() {
// Assert that setting the AOD front scrim alpha doesn't take effect in a non-AOD state.
mScrimController.transitionTo(ScrimState.KEYGUARD);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index 94ab9d2..6933328 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -60,7 +60,7 @@
@Mock
private NotificationPanelView mNotificationPanelView;
@Mock
- private FingerprintUnlockController mFingerprintUnlockController;
+ private BiometricUnlockController mBiometrucUnlockController;
@Mock
private DismissCallbackRegistry mDismissCallbackRegistry;
private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
@@ -72,7 +72,7 @@
mStatusBarKeyguardViewManager = new TestableStatusBarKeyguardViewManager(getContext(),
mViewMediatorCallback, mLockPatternUtils);
mStatusBarKeyguardViewManager.registerStatusBar(mStatusBar, mContainer,
- mNotificationPanelView, mFingerprintUnlockController, mDismissCallbackRegistry);
+ mNotificationPanelView, mBiometrucUnlockController, mDismissCallbackRegistry);
mStatusBarKeyguardViewManager.show(null);
}
@@ -170,8 +170,8 @@
@Test
public void onPanelExpansionChanged_neverTranslatesBouncerWhenWakeAndUnlock() {
- when(mFingerprintUnlockController.getMode())
- .thenReturn(FingerprintUnlockController.MODE_WAKE_AND_UNLOCK);
+ when(mBiometrucUnlockController.getMode())
+ .thenReturn(BiometricUnlockController.MODE_WAKE_AND_UNLOCK);
mStatusBarKeyguardViewManager.onPanelExpansionChanged(KeyguardBouncer.EXPANSION_VISIBLE,
false /* tracking */);
verify(mBouncer, never()).setExpansion(anyFloat());
@@ -196,7 +196,7 @@
@Override
public void registerStatusBar(StatusBar statusBar, ViewGroup container,
NotificationPanelView notificationPanelView,
- FingerprintUnlockController fingerprintUnlockController,
+ BiometricUnlockController fingerprintUnlockController,
DismissCallbackRegistry dismissCallbackRegistry) {
super.registerStatusBar(statusBar, container, notificationPanelView,
fingerprintUnlockController, dismissCallbackRegistry);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index cf23bfc..e39238d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -41,12 +41,10 @@
import android.app.StatusBarManager;
import android.app.trust.TrustManager;
import android.content.Context;
-import android.content.pm.UserInfo;
import android.hardware.fingerprint.FingerprintManager;
import android.metrics.LogMaker;
import android.os.Binder;
import android.os.Handler;
-import android.os.HandlerThread;
import android.os.IPowerManager;
import android.os.Looper;
import android.os.PowerManager;
@@ -59,8 +57,6 @@
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import android.util.SparseArray;
-import android.view.Gravity;
-import android.view.View;
import android.view.ViewGroup.LayoutParams;
import com.android.internal.logging.MetricsLogger;
@@ -76,21 +72,20 @@
import com.android.systemui.classifier.FalsingManager;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.recents.misc.SystemServicesProxy;
-import com.android.systemui.statusbar.ActivatableNotificationView;
-import com.android.systemui.statusbar.AppOpsListener;
+import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
+import com.android.systemui.statusbar.notification.AppOpsListener;
import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
-import com.android.systemui.statusbar.FooterView;
-import com.android.systemui.statusbar.FooterViewButton;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.FooterView;
import com.android.systemui.statusbar.KeyguardIndicationController;
-import com.android.systemui.statusbar.NotificationData;
-import com.android.systemui.statusbar.NotificationData.Entry;
-import com.android.systemui.statusbar.NotificationEntryManager;
-import com.android.systemui.statusbar.NotificationGutsManager;
-import com.android.systemui.statusbar.NotificationListContainer;
+import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.NotificationData.Entry;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
+import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
-import com.android.systemui.statusbar.NotificationLogger;
+import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.NotificationPresenter;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
@@ -104,7 +99,7 @@
import com.android.systemui.statusbar.policy.KeyguardMonitor;
import com.android.systemui.statusbar.policy.KeyguardMonitorImpl;
import com.android.systemui.statusbar.policy.UserSwitcherController;
-import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import org.junit.Before;
import org.junit.Test;
@@ -116,7 +111,6 @@
import java.io.ByteArrayOutputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.function.Predicate;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@@ -132,7 +126,7 @@
@Mock private IStatusBarService mBarService;
@Mock private ScrimController mScrimController;
@Mock private ArrayList<Entry> mNotificationList;
- @Mock private FingerprintUnlockController mFingerprintUnlockController;
+ @Mock private BiometricUnlockController mBiometricUnlockController;
@Mock private NotificationData mNotificationData;
// Mock dependencies:
@@ -207,7 +201,7 @@
mKeyguardIndicationController, mStackScroller, mHeadsUpManager,
mPowerManager, mNotificationPanelView, mBarService, mNotificationListener,
mNotificationLogger, mVisualStabilityManager, mViewHierarchyManager,
- mEntryManager, mScrimController, mFingerprintUnlockController,
+ mEntryManager, mScrimController, mBiometricUnlockController,
mock(ActivityLaunchAnimator.class), mKeyguardViewMediator,
mRemoteInputManager, mock(NotificationGroupManager.class),
mock(FalsingManager.class), mock(StatusBarWindowManager.class),
@@ -572,15 +566,15 @@
@Test
public void testFingerprintNotification_UpdatesScrims() {
- mStatusBar.notifyFpAuthModeChanged();
+ mStatusBar.notifyBiometricAuthModeChanged();
verify(mScrimController).transitionTo(any(), any());
}
@Test
public void testFingerprintUnlock_UpdatesScrims() {
// Simulate unlocking from AoD with fingerprint.
- when(mFingerprintUnlockController.getMode())
- .thenReturn(FingerprintUnlockController.MODE_WAKE_AND_UNLOCK);
+ when(mBiometricUnlockController.getMode())
+ .thenReturn(BiometricUnlockController.MODE_WAKE_AND_UNLOCK);
mStatusBar.updateScrimController();
verify(mScrimController).transitionTo(eq(ScrimState.UNLOCKED), any());
}
@@ -717,7 +711,7 @@
VisualStabilityManager visualStabilityManager,
NotificationViewHierarchyManager viewHierarchyManager,
TestableNotificationEntryManager entryManager, ScrimController scrimController,
- FingerprintUnlockController fingerprintUnlockController,
+ BiometricUnlockController biometricUnlockController,
ActivityLaunchAnimator launchAnimator, KeyguardViewMediator keyguardViewMediator,
NotificationRemoteInputManager notificationRemoteInputManager,
NotificationGroupManager notificationGroupManager,
@@ -743,7 +737,7 @@
mViewHierarchyManager = viewHierarchyManager;
mEntryManager = entryManager;
mScrimController = scrimController;
- mFingerprintUnlockController = fingerprintUnlockController;
+ mBiometricUnlockController = biometricUnlockController;
mActivityLaunchAnimator = launchAnimator;
mKeyguardViewMediator = keyguardViewMediator;
mClearAllEnabled = true;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowViewTest.java
index a068a5e..33705e5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowViewTest.java
@@ -20,19 +20,14 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.content.Context;
import android.os.SystemClock;
-import android.service.notification.StatusBarNotification;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import android.view.MotionEvent;
-import android.view.View;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.DragDownHelper;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
-import com.android.systemui.statusbar.NotificationData;
import com.android.systemui.statusbar.StatusBarState;
import org.junit.Before;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
index a6fa4f5..3164c04 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
@@ -32,7 +32,7 @@
import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.NotificationTestHelper;
import com.android.systemui.statusbar.RemoteInputController;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
index c573ca8..5ac2190 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
@@ -44,7 +44,7 @@
import com.android.keyguard.KeyguardHostView.OnDismissAction;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.NotificationData;
+import com.android.systemui.statusbar.notification.NotificationData;
import com.android.systemui.statusbar.SmartReplyController;
import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationChildrenContainerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationChildrenContainerTest.java
deleted file mode 100644
index 8a74019..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationChildrenContainerTest.java
+++ /dev/null
@@ -1,69 +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.systemui.statusbar.stack;
-
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper.RunWithLooper;
-import android.view.NotificationHeaderView;
-import android.view.View;
-
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
-import com.android.systemui.statusbar.NotificationTestHelper;
-
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-@RunWithLooper(setAsMainLooper = true)
-public class NotificationChildrenContainerTest extends SysuiTestCase {
-
- private ExpandableNotificationRow mGroup;
- private int mId;
- private NotificationTestHelper mNotificationTestHelper;
-
- @Before
- public void setUp() throws Exception {
- mNotificationTestHelper = new NotificationTestHelper(mContext);
- mGroup = mNotificationTestHelper.createGroup();
- }
-
- @Test
- public void testLowPriorityHeaderCleared() {
- mGroup.setIsLowPriority(true);
- NotificationChildrenContainer childrenContainer = mGroup.getChildrenContainer();
- NotificationHeaderView lowPriorityHeaderView = childrenContainer.getLowPriorityHeaderView();
- Assert.assertTrue(lowPriorityHeaderView.getVisibility() == View.VISIBLE);
- Assert.assertTrue(lowPriorityHeaderView.getParent() == childrenContainer);
- mGroup.setIsLowPriority(false);
- Assert.assertTrue(lowPriorityHeaderView.getParent() == null);
- Assert.assertTrue(childrenContainer.getLowPriorityHeaderView() == null);
- }
-
- @Test
- public void testRecreateNotificationHeader_hasHeader() {
- NotificationChildrenContainer childrenContainer = mGroup.getChildrenContainer();
- childrenContainer.recreateNotificationHeader(null);
- Assert.assertNotNull("Children container must have a header after recreation",
- childrenContainer.getCurrentHeaderView());
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/ChannelsTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/ChannelsTest.java
index 50b4f3f..0d398be 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/ChannelsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/ChannelsTest.java
@@ -103,7 +103,7 @@
NotificationManager.IMPORTANCE_MIN);
NotificationChannel newChannel =
NotificationChannels.createScreenshotChannel("newName", legacyChannel);
- assertEquals(Uri.EMPTY, newChannel.getSound());
+ assertEquals(null, newChannel.getSound());
assertEquals("newName", newChannel.getName());
// MIN importance not user locked, so HIGH wins out.
assertEquals(NotificationManager.IMPORTANCE_HIGH, newChannel.getImportance());
@@ -113,7 +113,7 @@
public void testInheritFromLegacy_noLegacyExists() {
NotificationChannel newChannel =
NotificationChannels.createScreenshotChannel("newName", null);
- assertEquals(Uri.EMPTY, newChannel.getSound());
+ assertEquals(null, newChannel.getSound());
assertEquals("newName", newChannel.getName());
assertEquals(NotificationManager.IMPORTANCE_HIGH, newChannel.getImportance());
}
diff --git a/packages/VpnDialogs/res/drawable-hdpi/ic_vpn_dialog.png b/packages/VpnDialogs/res/drawable-hdpi/ic_vpn_dialog.png
deleted file mode 100644
index a0b4b61..0000000
--- a/packages/VpnDialogs/res/drawable-hdpi/ic_vpn_dialog.png
+++ /dev/null
Binary files differ
diff --git a/packages/VpnDialogs/res/drawable-mdpi/ic_vpn_dialog.png b/packages/VpnDialogs/res/drawable-mdpi/ic_vpn_dialog.png
deleted file mode 100644
index df5dfe8..0000000
--- a/packages/VpnDialogs/res/drawable-mdpi/ic_vpn_dialog.png
+++ /dev/null
Binary files differ
diff --git a/packages/VpnDialogs/res/drawable-xhdpi/ic_vpn_dialog.png b/packages/VpnDialogs/res/drawable-xhdpi/ic_vpn_dialog.png
deleted file mode 100644
index 18d5a3a..0000000
--- a/packages/VpnDialogs/res/drawable-xhdpi/ic_vpn_dialog.png
+++ /dev/null
Binary files differ
diff --git a/packages/VpnDialogs/res/drawable-xxhdpi/ic_vpn_dialog.png b/packages/VpnDialogs/res/drawable-xxhdpi/ic_vpn_dialog.png
deleted file mode 100644
index 4d475dc..0000000
--- a/packages/VpnDialogs/res/drawable-xxhdpi/ic_vpn_dialog.png
+++ /dev/null
Binary files differ
diff --git a/packages/VpnDialogs/res/drawable-xxxhdpi/ic_vpn_dialog.png b/packages/VpnDialogs/res/drawable-xxxhdpi/ic_vpn_dialog.png
deleted file mode 100644
index 9d458b4..0000000
--- a/packages/VpnDialogs/res/drawable-xxxhdpi/ic_vpn_dialog.png
+++ /dev/null
Binary files differ
diff --git a/packages/VpnDialogs/res/drawable/ic_vpn_dialog.xml b/packages/VpnDialogs/res/drawable/ic_vpn_dialog.xml
new file mode 100644
index 0000000..24f622e
--- /dev/null
+++ b/packages/VpnDialogs/res/drawable/ic_vpn_dialog.xml
@@ -0,0 +1,30 @@
+<?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.
+*/
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="17dp"
+ android:height="17dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12.09,9C11.11,7.5 9.43,6.5 7.5,6.5C4.46,6.5 2,8.96 2,12c0,3.04 2.46,5.5 5.5,5.5c1.93,0 3.61,-1 4.59,-2.5H14v3h6v-3h2V9H12.09zM20,13h-2v3h-2v-3h-5.16c-0.43,1.44 -1.76,2.5 -3.34,2.5C5.57,15.5 4,13.93 4,12c0,-1.93 1.57,-3.5 3.5,-3.5c1.58,0 2.9,1.06 3.34,2.5H20V13z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M7.5,12m-1.5,0a1.5,1.5 0,1 1,3 0a1.5,1.5 0,1 1,-3 0"/>
+</vector>
diff --git a/packages/overlays/DisplayCutoutEmulationCornerOverlay/res/values/config.xml b/packages/overlays/DisplayCutoutEmulationCornerOverlay/res/values/config.xml
index 80d8066..9254b4d 100644
--- a/packages/overlays/DisplayCutoutEmulationCornerOverlay/res/values/config.xml
+++ b/packages/overlays/DisplayCutoutEmulationCornerOverlay/res/values/config.xml
@@ -37,6 +37,8 @@
@right
</string>
+ <string translatable="false" name="config_mainBuiltInDisplayCutoutRectApproximation">@*android:string/config_mainBuiltInDisplayCutout</string>
+
<!-- Whether the display cutout region of the main built-in display should be forced to
black in software (to avoid aliasing or emulate a cutout that is not physically existent).
-->
diff --git a/packages/overlays/DisplayCutoutEmulationDoubleOverlay/res/values/config.xml b/packages/overlays/DisplayCutoutEmulationDoubleOverlay/res/values/config.xml
index ca261f9..80c997a 100644
--- a/packages/overlays/DisplayCutoutEmulationDoubleOverlay/res/values/config.xml
+++ b/packages/overlays/DisplayCutoutEmulationDoubleOverlay/res/values/config.xml
@@ -49,6 +49,8 @@
@dp
</string>
+ <string translatable="false" name="config_mainBuiltInDisplayCutoutRectApproximation">@*android:string/config_mainBuiltInDisplayCutout</string>
+
<!-- Whether the display cutout region of the main built-in display should be forced to
black in software (to avoid aliasing or emulate a cutout that is not physically existent).
-->
diff --git a/packages/overlays/DisplayCutoutEmulationNarrowOverlay/res/values/config.xml b/packages/overlays/DisplayCutoutEmulationNarrowOverlay/res/values/config.xml
index c22b2e7..6fb3c7f 100644
--- a/packages/overlays/DisplayCutoutEmulationNarrowOverlay/res/values/config.xml
+++ b/packages/overlays/DisplayCutoutEmulationNarrowOverlay/res/values/config.xml
@@ -40,6 +40,8 @@
@dp
</string>
+ <string translatable="false" name="config_mainBuiltInDisplayCutoutRectApproximation">@*android:string/config_mainBuiltInDisplayCutout</string>
+
<!-- Whether the display cutout region of the main built-in display should be forced to
black in software (to avoid aliasing or emulate a cutout that is not physically existent).
-->
diff --git a/packages/overlays/DisplayCutoutEmulationTallOverlay/res/values/config.xml b/packages/overlays/DisplayCutoutEmulationTallOverlay/res/values/config.xml
index 401e092..7c29ffb 100644
--- a/packages/overlays/DisplayCutoutEmulationTallOverlay/res/values/config.xml
+++ b/packages/overlays/DisplayCutoutEmulationTallOverlay/res/values/config.xml
@@ -40,6 +40,8 @@
@dp
</string>
+ <string translatable="false" name="config_mainBuiltInDisplayCutoutRectApproximation">@*android:string/config_mainBuiltInDisplayCutout</string>
+
<!-- Whether the display cutout region of the main built-in display should be forced to
black in software (to avoid aliasing or emulate a cutout that is not physically existent).
-->
diff --git a/packages/overlays/DisplayCutoutEmulationWideOverlay/res/values/config.xml b/packages/overlays/DisplayCutoutEmulationWideOverlay/res/values/config.xml
index f328b83..5fb8b9e 100644
--- a/packages/overlays/DisplayCutoutEmulationWideOverlay/res/values/config.xml
+++ b/packages/overlays/DisplayCutoutEmulationWideOverlay/res/values/config.xml
@@ -40,6 +40,8 @@
@dp
</string>
+ <string translatable="false" name="config_mainBuiltInDisplayCutoutRectApproximation">@*android:string/config_mainBuiltInDisplayCutout</string>
+
<!-- Whether the display cutout region of the main built-in display should be forced to
black in software (to avoid aliasing or emulate a cutout that is not physically existent).
-->
diff --git a/packages/overlays/SysuiDarkThemeOverlay/Android.mk b/packages/overlays/SysuiDarkThemeOverlay/Android.mk
deleted file mode 100644
index 7b277bc..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/Android.mk
+++ /dev/null
@@ -1,14 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_RRO_THEME := SysuiDarkTheme
-LOCAL_CERTIFICATE := platform
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-
-LOCAL_PACKAGE_NAME := SysuiDarkThemeOverlay
-LOCAL_SDK_VERSION := current
-
-include $(BUILD_RRO_PACKAGE)
diff --git a/packages/overlays/SysuiDarkThemeOverlay/AndroidManifest.xml b/packages/overlays/SysuiDarkThemeOverlay/AndroidManifest.xml
deleted file mode 100644
index 8b6ee2b..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/AndroidManifest.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.systemui.theme.dark"
- android:versionCode="1"
- android:versionName="1.0">
- <overlay android:targetPackage="com.android.systemui" android:priority="1"/>
-
- <application android:label="@string/sysui_overlay_dark" android:hasCode="false"/>
-</manifest>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-af/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-af/strings.xml
deleted file mode 100644
index 33c6982..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-af/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="sysui_overlay_dark" msgid="557336259295713662">"Donker"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-am/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-am/strings.xml
deleted file mode 100644
index 5979569..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-am/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="sysui_overlay_dark" msgid="557336259295713662">"ጨለማ"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-ar/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-ar/strings.xml
deleted file mode 100644
index 7b20c01..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-ar/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="sysui_overlay_dark" msgid="557336259295713662">"داكن"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-as/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-as/strings.xml
deleted file mode 100644
index 0910e7e..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-as/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="sysui_overlay_dark" msgid="557336259295713662">"গাঢ়"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-az/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-az/strings.xml
deleted file mode 100644
index a9db75c..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-az/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="sysui_overlay_dark" msgid="557336259295713662">"Qaranlıq"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-b+sr+Latn/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-b+sr+Latn/strings.xml
deleted file mode 100644
index b63dcbc..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-b+sr+Latn/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="sysui_overlay_dark" msgid="557336259295713662">"Tamno"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-be/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-be/strings.xml
deleted file mode 100644
index eb875b3..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-be/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="sysui_overlay_dark" msgid="557336259295713662">"Цёмная"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-bg/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-bg/strings.xml
deleted file mode 100644
index 7b39462..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-bg/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="sysui_overlay_dark" msgid="557336259295713662">"Тъмно"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-bn/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-bn/strings.xml
deleted file mode 100644
index 0910e7e..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-bn/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="sysui_overlay_dark" msgid="557336259295713662">"গাঢ়"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-bs/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-bs/strings.xml
deleted file mode 100644
index b63dcbc..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-bs/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="sysui_overlay_dark" msgid="557336259295713662">"Tamno"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-ca/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-ca/strings.xml
deleted file mode 100644
index 02ee226..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-ca/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="sysui_overlay_dark" msgid="557336259295713662">"Fosc"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-cs/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-cs/strings.xml
deleted file mode 100644
index 5d11f07..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-cs/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="sysui_overlay_dark" msgid="557336259295713662">"Tmavé"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-da/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-da/strings.xml
deleted file mode 100644
index 460ebe7..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-da/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="sysui_overlay_dark" msgid="557336259295713662">"Mørk"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-de/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-de/strings.xml
deleted file mode 100644
index 4b54b8e..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-de/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="sysui_overlay_dark" msgid="557336259295713662">"Dunkel"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-el/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-el/strings.xml
deleted file mode 100644
index c58061d..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-el/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="sysui_overlay_dark" msgid="557336259295713662">"Σκοτεινό"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-en-rAU/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-en-rAU/strings.xml
deleted file mode 100644
index 7c94a51..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-en-rAU/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="sysui_overlay_dark" msgid="557336259295713662">"Dark"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-en-rCA/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-en-rCA/strings.xml
deleted file mode 100644
index 7c94a51..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-en-rCA/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="sysui_overlay_dark" msgid="557336259295713662">"Dark"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-en-rGB/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-en-rGB/strings.xml
deleted file mode 100644
index 7c94a51..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-en-rGB/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="sysui_overlay_dark" msgid="557336259295713662">"Dark"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-en-rIN/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-en-rIN/strings.xml
deleted file mode 100644
index 7c94a51..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-en-rIN/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="sysui_overlay_dark" msgid="557336259295713662">"Dark"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-en-rXC/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-en-rXC/strings.xml
deleted file mode 100644
index cbdd3d2..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-en-rXC/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="sysui_overlay_dark" msgid="557336259295713662">"Dark"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-es-rUS/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-es-rUS/strings.xml
deleted file mode 100644
index 2717f0f..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-es-rUS/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="sysui_overlay_dark" msgid="557336259295713662">"Oscuro"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-es/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-es/strings.xml
deleted file mode 100644
index 2717f0f..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-es/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="sysui_overlay_dark" msgid="557336259295713662">"Oscuro"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-et/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-et/strings.xml
deleted file mode 100644
index e0cce05..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-et/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="sysui_overlay_dark" msgid="557336259295713662">"Tume"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-eu/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-eu/strings.xml
deleted file mode 100644
index 44cee4c..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-eu/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="sysui_overlay_dark" msgid="557336259295713662">"Iluna"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-fa/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-fa/strings.xml
deleted file mode 100644
index fdd1df5..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-fa/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="sysui_overlay_dark" msgid="557336259295713662">"تیره"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-fi/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-fi/strings.xml
deleted file mode 100644
index 237fe70..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-fi/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="sysui_overlay_dark" msgid="557336259295713662">"Tumma"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-fr-rCA/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-fr-rCA/strings.xml
deleted file mode 100644
index f92c2ef..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-fr-rCA/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="sysui_overlay_dark" msgid="557336259295713662">"Sombre"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-fr/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-fr/strings.xml
deleted file mode 100644
index eac51d3..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-fr/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="sysui_overlay_dark" msgid="557336259295713662">"Foncé"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-gl/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-gl/strings.xml
deleted file mode 100644
index 300868f..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-gl/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="sysui_overlay_dark" msgid="557336259295713662">"Escuro"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-gu/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-gu/strings.xml
deleted file mode 100644
index 6a4cd62..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-gu/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="sysui_overlay_dark" msgid="557336259295713662">"ઘેરી"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-hi/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-hi/strings.xml
deleted file mode 100644
index c5bc0e2..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-hi/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="sysui_overlay_dark" msgid="557336259295713662">"गहरे रंग की थीम"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-hr/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-hr/strings.xml
deleted file mode 100644
index b63dcbc..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-hr/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="sysui_overlay_dark" msgid="557336259295713662">"Tamno"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-hu/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-hu/strings.xml
deleted file mode 100644
index 84a3ab8..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-hu/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="sysui_overlay_dark" msgid="557336259295713662">"Sötét"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-hy/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-hy/strings.xml
deleted file mode 100644
index 555cb64..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-hy/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="sysui_overlay_dark" msgid="557336259295713662">"Մուգ"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-in/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-in/strings.xml
deleted file mode 100644
index 391451b..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-in/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="sysui_overlay_dark" msgid="557336259295713662">"Gelap"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-is/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-is/strings.xml
deleted file mode 100644
index f4d1531..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-is/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="sysui_overlay_dark" msgid="557336259295713662">"Dökkt"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-it/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-it/strings.xml
deleted file mode 100644
index b59155b..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-it/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="sysui_overlay_dark" msgid="557336259295713662">"Scuro"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-iw/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-iw/strings.xml
deleted file mode 100644
index 3ecf444..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-iw/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="sysui_overlay_dark" msgid="557336259295713662">"כהה"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-ja/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-ja/strings.xml
deleted file mode 100644
index 3a2dba0..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-ja/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="sysui_overlay_dark" msgid="557336259295713662">"ダーク"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-ka/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-ka/strings.xml
deleted file mode 100644
index 36bf77e..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-ka/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="sysui_overlay_dark" msgid="557336259295713662">"მუქი"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-kk/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-kk/strings.xml
deleted file mode 100644
index 913c0b1..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-kk/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="sysui_overlay_dark" msgid="557336259295713662">"Қараңғы"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-km/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-km/strings.xml
deleted file mode 100644
index b56c490..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-km/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="sysui_overlay_dark" msgid="557336259295713662">"ងងឹត"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-kn/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-kn/strings.xml
deleted file mode 100644
index e757116..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-kn/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="sysui_overlay_dark" msgid="557336259295713662">"ಕತ್ತಲೆ"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-ko/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-ko/strings.xml
deleted file mode 100644
index ca4ab1e..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-ko/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="sysui_overlay_dark" msgid="557336259295713662">"어두움"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-ky/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-ky/strings.xml
deleted file mode 100644
index e8e8279..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-ky/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="sysui_overlay_dark" msgid="557336259295713662">"Караңгы"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-lo/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-lo/strings.xml
deleted file mode 100644
index 0434a41..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-lo/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="sysui_overlay_dark" msgid="557336259295713662">"ມືດ"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-lt/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-lt/strings.xml
deleted file mode 100644
index 147779b..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-lt/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="sysui_overlay_dark" msgid="557336259295713662">"Tamsi"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-lv/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-lv/strings.xml
deleted file mode 100644
index 7a296ec..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-lv/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="sysui_overlay_dark" msgid="557336259295713662">"Tumšs"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-mk/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-mk/strings.xml
deleted file mode 100644
index 6be693a..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-mk/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="sysui_overlay_dark" msgid="557336259295713662">"Темна"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-ml/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-ml/strings.xml
deleted file mode 100644
index f8a24fa..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-ml/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="sysui_overlay_dark" msgid="557336259295713662">"ഡാർക്ക്"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-mn/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-mn/strings.xml
deleted file mode 100644
index e65d9c7..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-mn/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="sysui_overlay_dark" msgid="557336259295713662">"Бараан"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-mr/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-mr/strings.xml
deleted file mode 100644
index 854af00..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-mr/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="sysui_overlay_dark" msgid="557336259295713662">"गडद"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-ms/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-ms/strings.xml
deleted file mode 100644
index 391451b..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-ms/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="sysui_overlay_dark" msgid="557336259295713662">"Gelap"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-my/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-my/strings.xml
deleted file mode 100644
index 008e9c6..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-my/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="sysui_overlay_dark" msgid="557336259295713662">"မှောင်သော"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-nb/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-nb/strings.xml
deleted file mode 100644
index 460ebe7..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-nb/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="sysui_overlay_dark" msgid="557336259295713662">"Mørk"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-ne/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-ne/strings.xml
deleted file mode 100644
index 8f2c5ba..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-ne/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="sysui_overlay_dark" msgid="557336259295713662">"अँध्यारो"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-nl/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-nl/strings.xml
deleted file mode 100644
index 33c6982..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-nl/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="sysui_overlay_dark" msgid="557336259295713662">"Donker"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-or/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-or/strings.xml
deleted file mode 100644
index d8045bd..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-or/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="sysui_overlay_dark" msgid="557336259295713662">"ଗାଢ଼"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-pa/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-pa/strings.xml
deleted file mode 100644
index 7110303..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-pa/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="sysui_overlay_dark" msgid="557336259295713662">"ਗੂੜ੍ਹਾ"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-pl/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-pl/strings.xml
deleted file mode 100644
index 25ca20f..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-pl/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="sysui_overlay_dark" msgid="557336259295713662">"Ciemna"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-pt-rBR/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-pt-rBR/strings.xml
deleted file mode 100644
index 300868f..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-pt-rBR/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="sysui_overlay_dark" msgid="557336259295713662">"Escuro"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-pt-rPT/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-pt-rPT/strings.xml
deleted file mode 100644
index 300868f..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-pt-rPT/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="sysui_overlay_dark" msgid="557336259295713662">"Escuro"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-pt/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-pt/strings.xml
deleted file mode 100644
index 300868f..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-pt/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="sysui_overlay_dark" msgid="557336259295713662">"Escuro"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-ro/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-ro/strings.xml
deleted file mode 100644
index de73f36..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-ro/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="sysui_overlay_dark" msgid="557336259295713662">"Întunecată"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-ru/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-ru/strings.xml
deleted file mode 100644
index b05e844..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-ru/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="sysui_overlay_dark" msgid="557336259295713662">"Темный"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-si/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-si/strings.xml
deleted file mode 100644
index f0f5725..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-si/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="sysui_overlay_dark" msgid="557336259295713662">"අඳුරු"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-sk/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-sk/strings.xml
deleted file mode 100644
index 5df6895..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-sk/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="sysui_overlay_dark" msgid="557336259295713662">"Tmavý"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-sl/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-sl/strings.xml
deleted file mode 100644
index ad58250..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-sl/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="sysui_overlay_dark" msgid="557336259295713662">"Temno"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-sq/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-sq/strings.xml
deleted file mode 100644
index 0e1eae7..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-sq/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="sysui_overlay_dark" msgid="557336259295713662">"E errët"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-sr/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-sr/strings.xml
deleted file mode 100644
index 1561ee2..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-sr/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="sysui_overlay_dark" msgid="557336259295713662">"Тамно"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-sv/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-sv/strings.xml
deleted file mode 100644
index 676de42..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-sv/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="sysui_overlay_dark" msgid="557336259295713662">"Mörk"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-sw/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-sw/strings.xml
deleted file mode 100644
index cc1f120..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-sw/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="sysui_overlay_dark" msgid="557336259295713662">"Nyeusi"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-ta/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-ta/strings.xml
deleted file mode 100644
index af98172..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-ta/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="sysui_overlay_dark" msgid="557336259295713662">"டார்க்"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-te/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-te/strings.xml
deleted file mode 100644
index 446455f..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-te/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="sysui_overlay_dark" msgid="557336259295713662">"ముదురు రంగు"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-th/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-th/strings.xml
deleted file mode 100644
index 9e3462b..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-th/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="sysui_overlay_dark" msgid="557336259295713662">"เข้ม"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-tl/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-tl/strings.xml
deleted file mode 100644
index 5502d90..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-tl/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="sysui_overlay_dark" msgid="557336259295713662">"Madilim"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-tr/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-tr/strings.xml
deleted file mode 100644
index 368b398..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-tr/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="sysui_overlay_dark" msgid="557336259295713662">"Koyu"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-uk/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-uk/strings.xml
deleted file mode 100644
index 6e67e45..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-uk/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="sysui_overlay_dark" msgid="557336259295713662">"Темна тема"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-ur/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-ur/strings.xml
deleted file mode 100644
index 1d5d6de..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-ur/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="sysui_overlay_dark" msgid="557336259295713662">"گہرا"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-uz/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-uz/strings.xml
deleted file mode 100644
index 957c28f..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-uz/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="sysui_overlay_dark" msgid="557336259295713662">"Tungi"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-vi/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-vi/strings.xml
deleted file mode 100644
index a458889..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-vi/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="sysui_overlay_dark" msgid="557336259295713662">"Tối"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-zh-rCN/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-zh-rCN/strings.xml
deleted file mode 100644
index c9b43dc..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-zh-rCN/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="sysui_overlay_dark" msgid="557336259295713662">"深色"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-zh-rHK/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-zh-rHK/strings.xml
deleted file mode 100644
index c9b43dc..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-zh-rHK/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="sysui_overlay_dark" msgid="557336259295713662">"深色"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-zh-rTW/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-zh-rTW/strings.xml
deleted file mode 100644
index c9b43dc..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-zh-rTW/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="sysui_overlay_dark" msgid="557336259295713662">"深色"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values-zu/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values-zu/strings.xml
deleted file mode 100644
index 6d328da..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values-zu/strings.xml
+++ /dev/null
@@ -1,23 +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.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="sysui_overlay_dark" msgid="557336259295713662">"Emnyama"</string>
-</resources>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values/strings.xml
deleted file mode 100644
index 71f48d6..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values/strings.xml
+++ /dev/null
@@ -1,24 +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.
- */
--->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
- <string name="sysui_overlay_dark">Dark</string>
-
-</resources>
-
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values/styles.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values/styles.xml
deleted file mode 100644
index 41a2940..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values/styles.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<resources>
- <style name="qs_base" parent="android:Theme.DeviceDefault">
- <item name="android:colorPrimary">@*android:color/primary_device_default_settings</item>
- <item name="android:colorPrimaryDark">@*android:color/primary_dark_device_default_settings</item>
- <item name="android:colorSecondary">@*android:color/secondary_device_default_settings</item>
- <item name="android:colorAccent">@*android:color/accent_device_default_dark</item>
- <item name="android:colorControlNormal">?android:attr/textColorPrimary</item>
- <item name="android:colorBackgroundFloating">@*android:color/material_grey_900</item>
- <item name="android:panelColorBackground">@*android:color/material_grey_800</item>
- </style>
-</resources>
\ No newline at end of file
diff --git a/proto/Android.bp b/proto/Android.bp
index f3811bd..5fd2885 100644
--- a/proto/Android.bp
+++ b/proto/Android.bp
@@ -17,3 +17,33 @@
},
},
}
+
+cc_library {
+ name: "libmetricprotos",
+ host_supported: true,
+ proto: {
+ export_proto_headers: true,
+ include_dirs: ["external/protobuf/src"],
+ },
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wno-unused-parameter",
+ ],
+ srcs: ["src/metrics_constants.proto"],
+ target: {
+ host: {
+ proto: {
+ type: "full",
+ },
+ },
+ android: {
+ proto: {
+ type: "lite",
+ },
+ shared: {
+ enabled: false,
+ },
+ },
+ },
+}
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index d2990de..deb936c 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -6118,9 +6118,56 @@
// ---- End P Constants, all P constants go above this line ----
- // First Q constant in master goes here:
- // Please delete these lines and use 1500 for your first enum.
+ // Time since this notification last interrupted (visibly or audible) the user
+ NOTIFICATION_SINCE_INTERRUPTION_MILLIS = 1500;
+ // OPEN: Notification interrupted the user, either audibly or visually.
+ // Tagged data: NOTIFICATION_SINCE_INTERRUPTION_MILLIS
+ // CATEGORY: NOTIFICATION
+ NOTIFICATION_INTERRUPTION = 1501;
+
+ // OPEN: Settings
+ // CATEGORY: SETTINGS
+ // OS: Q
+ SETTINGS_HOMEPAGE = 1502;
+
+ // OPEN: Settings > Create shortcut(widget)
+ // CATEGORY: SETTINGS
+ // OS: Q
+ SETTINGS_CREATE_SHORTCUT = 1503;
+
+ // ACTION: Authenticate using fingerprint
+ // CATEGORY: SETTINGS
+ // OS: Q
+ ACTION_FACE_AUTH = 1504;
+
+ // ACTION: Add fingerprint > Enroll fingerprint
+ // CATEGORY: SETTINGS
+ // OS: Q
+ ACTION_FACE_ENROLL = 1505;
+
+ // OPEN: Face Enroll introduction
+ // CATEGORY: SETTINGS
+ // OS: Q
+ FACE_ENROLL_INTRO = 1506;
+
+ // OPEN: Face Enroll introduction
+ // CATEGORY: SETTINGS
+ // OS: Q
+ FACE_ENROLL_ENROLLING = 1507;
+
+ // OPEN: Face Enroll introduction
+ // CATEGORY: SETTINGS
+ // OS: Q
+ FACE_ENROLL_FINISHED = 1508;
+
+ // OPEN: Face Enroll sidecar
+ // CATEGORY: SETTINGS
+ // OS: Q
+ FACE_ENROLL_SIDECAR = 1509;
+
+ // OPEN: Settings > Add face > Error dialog
+ DIALOG_FACE_ERROR = 1510;
// ---- End Q Constants, all Q 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 a9a14ca..fba639c 100644
--- a/proto/src/system_messages.proto
+++ b/proto/src/system_messages.proto
@@ -212,6 +212,10 @@
// Package: android
NOTE_AUTO_SAVER_SUGGESTION = 49;
+ // Notify the user that their softap config preference has changed.
+ // Package: android
+ NOTE_SOFTAP_CONFIG_CHANGED = 50;
+
// 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/proto/src/wifi.proto b/proto/src/wifi.proto
index a23c6d3..15c0468 100644
--- a/proto/src/wifi.proto
+++ b/proto/src/wifi.proto
@@ -459,6 +459,20 @@
// Data on wifi radio usage
optional WifiRadioUsage wifi_radio_usage = 118;
+
+ // Stores settings values used for metrics testing.
+ optional ExperimentValues experiment_values = 119;
+
+ // List of WifiIsUnusableEvents which get logged when we notice that WiFi is unusable.
+ // Collected only when WIFI_IS_UNUSABLE_EVENT_METRICS_ENABLED Settings is enabled.
+ repeated WifiIsUnusableEvent wifi_is_unusable_event_list = 120;
+
+ // Counts the occurrences of each link speed (Mbps) level
+ // with rssi (dBm) and rssi^2 sums (dBm^2)
+ repeated LinkSpeedCount link_speed_counts = 121;
+
+ // Number of times the SarManager failed to register SAR sensor listener
+ optional int32 num_sar_sensor_registration_failures = 122;
}
// Information that gets logged for every WiFi connection.
@@ -644,6 +658,22 @@
optional int32 count = 2;
}
+// Number of occurrences of a specific link speed (Mbps)
+// and sum of rssi (dBm) and rssi^2 (dBm^2)
+message LinkSpeedCount {
+ // Link speed (Mbps)
+ optional int32 link_speed_mbps = 1;
+
+ // Number of RSSI polls with link_speed
+ optional int32 count = 2;
+
+ // Sum of absolute values of rssi values (dBm)
+ optional int32 rssi_sum_dbm = 3;
+
+ // Sum of squares of rssi values (dBm^2)
+ optional int64 rssi_sum_of_squares_dbm_sq = 4;
+}
+
// Number of occurrences of Soft AP session durations
message SoftApDurationBucket {
// Bucket covers duration : [duration_sec, duration_sec + bucket_size_sec)
@@ -876,7 +906,7 @@
STATE_INVALID = 12;
}
- // Bit mask of all supplicant state changes that occured since the last event
+ // Bit mask of all supplicant state changes that occurred since the last event
optional uint32 supplicant_state_changes_bitmask = 9 [default = 0];
// The number of milliseconds that have elapsed since the device booted
@@ -884,7 +914,7 @@
optional FrameworkDisconnectReason framework_disconnect_reason = 11 [default = DISCONNECT_UNKNOWN];
- // Flag which indicates if an association rejection event occured due to a timeout
+ // Flag which indicates if an association rejection event occurred due to a timeout
optional bool association_timed_out = 12 [default = false];
// Authentication failure reason, as reported by WifiManager (calculated from state & deauth code)
@@ -1513,4 +1543,75 @@
// Total time for which the radio is awake due to scan.
optional int64 scan_time_ms = 2;
-}
\ No newline at end of file
+}
+
+message ExperimentValues {
+ // Indicates if we are logging WifiIsUnusableEvent in metrics
+ optional bool wifi_is_unusable_logging_enabled = 1;
+
+ // Minimum number of txBad to trigger a data stall
+ optional int32 wifi_data_stall_min_tx_bad = 2;
+
+ // Minimum number of txSuccess to trigger a data stall
+ // when rxSuccess is 0
+ optional int32 wifi_data_stall_min_tx_success_without_rx = 3;
+
+ // Indicates if we are logging LinkSpeedCount in metrics
+ optional bool link_speed_counts_logging_enabled = 4;
+}
+
+message WifiIsUnusableEvent {
+ enum TriggerType {
+ // Default/Invalid event
+ TYPE_UNKNOWN = 0;
+
+ // There is a data stall from tx failures
+ TYPE_DATA_STALL_BAD_TX = 1;
+
+ // There is a data stall from rx failures
+ TYPE_DATA_STALL_TX_WITHOUT_RX = 2;
+
+ // There is a data stall from both tx and rx failures
+ TYPE_DATA_STALL_BOTH = 3;
+
+ // Firmware generated an alert
+ TYPE_FIRMWARE_ALERT = 4;
+ }
+
+ // What event triggered WifiIsUnusableEvent.
+ optional TriggerType type = 1;
+
+ // The timestamp at which this event occurred.
+ // Measured in milliseconds that have elapsed since the device booted.
+ optional int64 start_time_millis = 2;
+
+ // NetworkAgent score of connected wifi.
+ // Defaults to -1 if the score was never set.
+ optional int32 last_score = 3 [default = -1];
+
+ // Delta of successfully transmitted (ACKed) unicast data packets
+ // between the last two WifiLinkLayerStats.
+ optional int64 tx_success_delta = 4;
+
+ // Delta of transmitted unicast data retry packets
+ // between the last two WifiLinkLayerStats.
+ optional int64 tx_retries_delta = 5;
+
+ // Delta of lost (not ACKed) transmitted unicast data packets
+ // between the last two WifiLinkLayerStats.
+ optional int64 tx_bad_delta = 6;
+
+ // Delta of received unicast data packets
+ // between the last two WifiLinkLayerStats.
+ optional int64 rx_success_delta = 7;
+
+ // Time in millisecond between the last two WifiLinkLayerStats.
+ optional int64 packet_update_time_delta = 8;
+
+ // The timestamp at which the last WifiLinkLayerStats was updated.
+ // Measured in milliseconds that have elapsed since the device booted.
+ optional int64 last_link_layer_stats_update_time = 9;
+
+ // Firmware alert code. Only valid when the event was triggered by a firmware alert, otherwise -1.
+ optional int32 firmware_alert_code = 10 [default = -1];
+}
diff --git a/sax/tests/saxtests/src/android/sax/ExpatPerformanceTest.java b/sax/tests/saxtests/src/android/sax/ExpatPerformanceTest.java
deleted file mode 100644
index 892c490..0000000
--- a/sax/tests/saxtests/src/android/sax/ExpatPerformanceTest.java
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.sax;
-
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.util.Log;
-import android.util.Xml;
-import org.kxml2.io.KXmlParser;
-import org.xml.sax.SAXException;
-import org.xml.sax.helpers.DefaultHandler;
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-
-import com.android.frameworks.saxtests.R;
-
-public class ExpatPerformanceTest extends AndroidTestCase {
-
- private static final String TAG = ExpatPerformanceTest.class.getSimpleName();
-
- private byte[] mXmlBytes;
-
- @Override
- public void setUp() throws Exception {
- super.setUp();
- InputStream in = mContext.getResources().openRawResource(R.raw.youtube);
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- byte[] buffer = new byte[1024];
- int length;
- while ((length = in.read(buffer)) != -1) {
- out.write(buffer, 0, length);
- }
- mXmlBytes = out.toByteArray();
-
- Log.i("***", "File size: " + (mXmlBytes.length / 1024) + "k");
- }
-
- @LargeTest
- public void testPerformance() throws Exception {
-// try {
-// Debug.startMethodTracing("expat3");
-// for (int i = 0; i < 1; i++) {
- runJavaPullParser();
- runSax();
- runExpatPullParser();
-// }
-// } finally {
-// Debug.stopMethodTracing();
-// }
- }
-
- private InputStream newInputStream() {
- return new ByteArrayInputStream(mXmlBytes);
- }
-
- private void runSax() throws IOException, SAXException {
- long start = System.currentTimeMillis();
- Xml.parse(newInputStream(), Xml.Encoding.UTF_8, new DefaultHandler());
- long elapsed = System.currentTimeMillis() - start;
- Log.i(TAG, "expat SAX: " + elapsed + "ms");
- }
-
- private void runExpatPullParser() throws XmlPullParserException, IOException {
- long start = System.currentTimeMillis();
- XmlPullParser pullParser = Xml.newPullParser();
- pullParser.setInput(newInputStream(), "UTF-8");
- withPullParser(pullParser);
- long elapsed = System.currentTimeMillis() - start;
- Log.i(TAG, "expat pull: " + elapsed + "ms");
- }
-
- private void runJavaPullParser() throws XmlPullParserException, IOException {
- XmlPullParser pullParser;
- long start = System.currentTimeMillis();
- pullParser = new KXmlParser();
- pullParser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
- pullParser.setInput(newInputStream(), "UTF-8");
- withPullParser(pullParser);
- long elapsed = System.currentTimeMillis() - start;
- Log.i(TAG, "java pull parser: " + elapsed + "ms");
- }
-
- private static void withPullParser(XmlPullParser pullParser)
- throws IOException, XmlPullParserException {
- int eventType = pullParser.next();
- while (eventType != XmlPullParser.END_DOCUMENT) {
- switch (eventType) {
- case XmlPullParser.START_TAG:
- pullParser.getName();
-// int nattrs = pullParser.getAttributeCount();
-// for (int i = 0; i < nattrs; ++i) {
-// pullParser.getAttributeName(i);
-// pullParser.getAttributeValue(i);
-// }
- break;
- case XmlPullParser.END_TAG:
- pullParser.getName();
- break;
- case XmlPullParser.TEXT:
- pullParser.getText();
- break;
- }
- eventType = pullParser.next();
- }
- }
-}
diff --git a/services/Android.bp b/services/Android.bp
index d125adc..bea51be 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -2,6 +2,7 @@
// ============================================================
java_library {
name: "services",
+ installable: true,
dex_preopt: {
app_image: true,
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityGestureDetector.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityGestureDetector.java
index b95d2e6..075c2fe 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityGestureDetector.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityGestureDetector.java
@@ -235,14 +235,15 @@
* callback on mListener is called, and the return value of the callback is
* passed to the caller.
*
- * @param event The raw motion event. It's important that this be the raw
+ * @param event The transformed motion event to be handled.
+ * @param rawEvent The raw motion event. It's important that this be the raw
* event, before any transformations have been applied, so that measurements
* can be made in physical units.
* @param policyFlags Policy flags for the event.
*
* @return true if the event is consumed, else false
*/
- public boolean onMotionEvent(MotionEvent event, int policyFlags) {
+ public boolean onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
// Construct GestureDetector double-tap detector on demand, so that testable sub-class
// can use mock GestureDetector.
@@ -255,12 +256,14 @@
mGestureDetector.setOnDoubleTapListener(this);
}
- final float x = event.getX();
- final float y = event.getY();
- final long time = event.getEventTime();
+ // The accessibility gesture detector is interested in the movements in physical space,
+ // so it uses the rawEvent to ignore magnification and other transformations.
+ final float x = rawEvent.getX();
+ final float y = rawEvent.getY();
+ final long time = rawEvent.getEventTime();
mPolicyFlags = policyFlags;
- switch (event.getActionMasked()) {
+ switch (rawEvent.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
mDoubleTapDetected = false;
mSecondFingerDoubleTap = false;
@@ -311,7 +314,7 @@
// timeout, cancel gesture detection.
if (timeDelta > threshold) {
cancelGesture();
- return mListener.onGestureCancelled(event, policyFlags);
+ return mListener.onGestureCancelled(rawEvent, policyFlags);
}
}
@@ -327,7 +330,7 @@
case MotionEvent.ACTION_UP:
if (mDoubleTapDetected) {
- return finishDoubleTap(event, policyFlags);
+ return finishDoubleTap(rawEvent, policyFlags);
}
if (mGestureStarted) {
final float dX = Math.abs(x - mPreviousGestureX);
@@ -335,7 +338,7 @@
if (dX >= mMinPixelsBetweenSamplesX || dY >= mMinPixelsBetweenSamplesY) {
mStrokeBuffer.add(new GesturePoint(x, y, time));
}
- return recognizeGesture(event, policyFlags);
+ return recognizeGesture(rawEvent, policyFlags);
}
break;
@@ -344,7 +347,7 @@
// recognizing a gesture.
cancelGesture();
- if (event.getPointerCount() == 2) {
+ if (rawEvent.getPointerCount() == 2) {
// If this was the second finger, attempt to recognize double
// taps on it.
mSecondFingerDoubleTap = true;
@@ -360,7 +363,7 @@
// If we're detecting taps on the second finger, see if we
// should finish the double tap.
if (mSecondFingerDoubleTap && mDoubleTapDetected) {
- return finishDoubleTap(event, policyFlags);
+ return finishDoubleTap(rawEvent, policyFlags);
}
break;
@@ -372,7 +375,7 @@
// If we're detecting taps on the second finger, map events from the
// finger to the first finger.
if (mSecondFingerDoubleTap) {
- MotionEvent newEvent = mapSecondPointerToFirstPointer(event);
+ MotionEvent newEvent = mapSecondPointerToFirstPointer(rawEvent);
if (newEvent == null) {
return false;
}
@@ -385,7 +388,7 @@
return false;
}
- // Pass the event on to the standard gesture detector.
+ // Pass the transformed event on to the standard gesture detector.
return mGestureDetector.onTouchEvent(event);
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 4365760..2da0818 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -20,7 +20,6 @@
import static android.view.accessibility.AccessibilityEvent.WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED;
import static android.view.accessibility.AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS;
import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS;
-
import static com.android.internal.util.FunctionalUtils.ignoreRemoteException;
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
@@ -30,8 +29,6 @@
import android.accessibilityservice.IAccessibilityServiceClient;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.app.ActivityManagerInternal;
-import android.app.ActivityTaskManagerInternal;
import android.app.AlertDialog;
import android.app.AppOpsManager;
import android.app.PendingIntent;
@@ -112,10 +109,10 @@
import com.android.internal.util.IntPair;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.LocalServices;
+import com.android.server.SystemService;
+import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.WindowManagerInternal;
-import libcore.util.EmptyArray;
-
import org.xmlpull.v1.XmlPullParserException;
import java.io.FileDescriptor;
@@ -134,6 +131,8 @@
import java.util.function.Consumer;
import java.util.function.IntSupplier;
+import libcore.util.EmptyArray;
+
/**
* This class is instantiated by the system as a system level service and can be
* accessed only by the system. The task of this service is to be a centralized
@@ -262,6 +261,25 @@
return getUserStateLocked(mCurrentUserId);
}
+ public static final class Lifecycle extends SystemService {
+ private final AccessibilityManagerService mService;
+
+ public Lifecycle(Context context) {
+ super(context);
+ mService = new AccessibilityManagerService(context);
+ }
+
+ @Override
+ public void onStart() {
+ publishBinderService(Context.ACCESSIBILITY_SERVICE, mService);
+ }
+
+ @Override
+ public void onBootPhase(int phase) {
+ mService.onBootPhase(phase);
+ }
+ }
+
/**
* Creates a new instance.
*
@@ -298,6 +316,14 @@
return mFingerprintGestureDispatcher;
}
+ private void onBootPhase(int phase) {
+ if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
+ if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_APP_WIDGETS)) {
+ mAppWidgetService = LocalServices.getService(AppWidgetManagerInternal.class);
+ }
+ }
+ }
+
private UserState getUserState(int userId) {
synchronized (mLock) {
return getUserStateLocked(userId);
@@ -2686,16 +2712,6 @@
}
}
- private AppWidgetManagerInternal getAppWidgetManager() {
- synchronized (mLock) {
- if (mAppWidgetService == null
- && mPackageManager.hasSystemFeature(PackageManager.FEATURE_APP_WIDGETS)) {
- mAppWidgetService = LocalServices.getService(AppWidgetManagerInternal.class);
- }
- return mAppWidgetService;
- }
- }
-
@Override
public void onShellCommand(FileDescriptor in, FileDescriptor out,
FileDescriptor err, String[] args, ShellCallback callback,
@@ -3024,8 +3040,7 @@
return packageName.toString();
}
// Appwidget hosts get to pass packages for widgets they host
- final AppWidgetManagerInternal appWidgetManager = getAppWidgetManager();
- if (appWidgetManager != null && ArrayUtils.contains(appWidgetManager
+ if (mAppWidgetService != null && ArrayUtils.contains(mAppWidgetService
.getHostedWidgetPackages(resolvedUid), packageNameStr)) {
return packageName.toString();
}
@@ -3053,9 +3068,8 @@
// IMPORTANT: The target package is already vetted to be in the target UID
String[] uidPackages = new String[]{targetPackage};
// Appwidget hosts get to pass packages for widgets they host
- final AppWidgetManagerInternal appWidgetManager = getAppWidgetManager();
- if (appWidgetManager != null) {
- final ArraySet<String> widgetPackages = appWidgetManager
+ if (mAppWidgetService != null) {
+ final ArraySet<String> widgetPackages = mAppWidgetService
.getHostedWidgetPackages(targetUid);
if (widgetPackages != null && !widgetPackages.isEmpty()) {
final String[] validPackages = new String[uidPackages.length
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
index 105df92..8f92145 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
@@ -258,6 +258,7 @@
return;
}
mWasConnectedAndDied = true;
+ mSystemSupport.getKeyEventDispatcher().flush(this);
UserState userState = mUserStateWeakReference.get();
if (userState != null) {
userState.serviceDisconnectedLocked(this);
diff --git a/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
index 84a8d45..2cae060 100644
--- a/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
+++ b/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
@@ -268,10 +268,7 @@
mReceivedPointerTracker.onMotionEvent(rawEvent);
- // The motion detector is interested in the movements in physical space,
- // so it uses the rawEvent to ignore magnification and other
- // transformations.
- if (mGestureDetector.onMotionEvent(rawEvent, policyFlags)) {
+ if (mGestureDetector.onMotionEvent(event, rawEvent, policyFlags)) {
// Event was handled by the gesture detector.
return;
}
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index 41e9d2b..021fdcd 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -46,6 +46,7 @@
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
+import android.os.Parcelable;
import android.os.RemoteCallback;
import android.os.RemoteException;
import android.os.ResultReceiver;
@@ -622,6 +623,38 @@
return getWhitelistedCompatModePackages(getWhitelistedCompatModePackagesFromSettings());
}
+ private void send(@NonNull IResultReceiver receiver, int value) {
+ try {
+ receiver.send(value, null);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Error async reporting result to client: " + e);
+ }
+ }
+
+ private void send(@NonNull IResultReceiver receiver, @NonNull Bundle value) {
+ try {
+ receiver.send(0, value);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Error async reporting result to client: " + e);
+ }
+ }
+
+ private void send(@NonNull IResultReceiver receiver, @Nullable String value) {
+ send(receiver, AutofillManager.SyncResultReceiver.bundleFor(value));
+ }
+
+ private void send(@NonNull IResultReceiver receiver, @Nullable String[] value) {
+ send(receiver, AutofillManager.SyncResultReceiver.bundleFor(value));
+ }
+
+ private void send(@NonNull IResultReceiver receiver, @Nullable Parcelable value) {
+ send(receiver, AutofillManager.SyncResultReceiver.bundleFor(value));
+ }
+
+ private void send(@NonNull IResultReceiver receiver, boolean value) {
+ send(receiver, value ? 1 : 0);
+ }
+
@Nullable
@VisibleForTesting
static Map<String, String[]> getWhitelistedCompatModePackages(String setting) {
@@ -827,9 +860,10 @@
final class AutoFillManagerServiceStub extends IAutoFillManager.Stub {
@Override
- public int addClient(IAutoFillManagerClient client, int userId) {
+ public void addClient(IAutoFillManagerClient client, int userId,
+ @NonNull IResultReceiver receiver) {
+ int flags = 0;
synchronized (mLock) {
- int flags = 0;
if (getServiceForUserLocked(userId).addClientLocked(client)) {
flags |= AutofillManager.FLAG_ADD_CLIENT_ENABLED;
}
@@ -839,8 +873,8 @@
if (sVerbose) {
flags |= AutofillManager.FLAG_ADD_CLIENT_VERBOSE;
}
- return flags;
}
+ send(receiver, flags);
}
@Override
@@ -874,9 +908,9 @@
}
@Override
- public int startSession(IBinder activityToken, IBinder appCallback, AutofillId autofillId,
+ public void startSession(IBinder activityToken, IBinder appCallback, AutofillId autofillId,
Rect bounds, AutofillValue value, int userId, boolean hasCallback, int flags,
- ComponentName componentName, boolean compatMode) {
+ ComponentName componentName, boolean compatMode, IResultReceiver receiver) {
activityToken = Preconditions.checkNotNull(activityToken, "activityToken");
appCallback = Preconditions.checkNotNull(appCallback, "appCallback");
@@ -892,61 +926,63 @@
throw new IllegalArgumentException(packageName + " is not a valid package", e);
}
+ final int sessionId;
synchronized (mLock) {
final AutofillManagerServiceImpl service = getServiceForUserLocked(userId);
- return service.startSessionLocked(activityToken, getCallingUid(), appCallback,
+ sessionId = service.startSessionLocked(activityToken, getCallingUid(), appCallback,
autofillId, bounds, value, hasCallback, componentName, compatMode,
mAllowInstantService, flags);
}
+ send(receiver, sessionId);
}
@Override
- public FillEventHistory getFillEventHistory() throws RemoteException {
+ public void getFillEventHistory(@NonNull IResultReceiver receiver) throws RemoteException {
final int userId = UserHandle.getCallingUserId();
+ FillEventHistory fillEventHistory = null;
synchronized (mLock) {
final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
if (service != null) {
- return service.getFillEventHistory(getCallingUid());
+ fillEventHistory = service.getFillEventHistory(getCallingUid());
} else if (sVerbose) {
Slog.v(TAG, "getFillEventHistory(): no service for " + userId);
}
}
-
- return null;
+ send(receiver, fillEventHistory);
}
@Override
- public UserData getUserData() throws RemoteException {
+ public void getUserData(@NonNull IResultReceiver receiver) throws RemoteException {
final int userId = UserHandle.getCallingUserId();
+ UserData userData = null;
synchronized (mLock) {
final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
if (service != null) {
- return service.getUserData(getCallingUid());
+ userData = service.getUserData(getCallingUid());
} else if (sVerbose) {
Slog.v(TAG, "getUserData(): no service for " + userId);
}
}
-
- return null;
+ send(receiver, userData);
}
@Override
- public String getUserDataId() throws RemoteException {
+ public void getUserDataId(@NonNull IResultReceiver receiver) throws RemoteException {
final int userId = UserHandle.getCallingUserId();
+ UserData userData = null;
synchronized (mLock) {
final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
if (service != null) {
- final UserData userData = service.getUserData(getCallingUid());
- return userData == null ? null : userData.getId();
+ userData = service.getUserData(getCallingUid());
} else if (sVerbose) {
Slog.v(TAG, "getUserDataId(): no service for " + userId);
}
}
-
- return null;
+ final String userDataId = userData == null ? null : userData.getId();
+ send(receiver, userDataId);
}
@Override
@@ -964,89 +1000,96 @@
}
@Override
- public boolean isFieldClassificationEnabled() throws RemoteException {
+ public void isFieldClassificationEnabled(@NonNull IResultReceiver receiver)
+ throws RemoteException {
final int userId = UserHandle.getCallingUserId();
+ boolean enabled = false;
synchronized (mLock) {
final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
if (service != null) {
- return service.isFieldClassificationEnabled(getCallingUid());
+ enabled = service.isFieldClassificationEnabled(getCallingUid());
} else if (sVerbose) {
Slog.v(TAG, "isFieldClassificationEnabled(): no service for " + userId);
}
}
-
- return false;
+ send(receiver, enabled);
}
@Override
- public String getDefaultFieldClassificationAlgorithm() throws RemoteException {
+ public void getDefaultFieldClassificationAlgorithm(@NonNull IResultReceiver receiver)
+ throws RemoteException {
final int userId = UserHandle.getCallingUserId();
+ String algorithm = null;
synchronized (mLock) {
final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
if (service != null) {
- return service.getDefaultFieldClassificationAlgorithm(getCallingUid());
+ algorithm = service.getDefaultFieldClassificationAlgorithm(getCallingUid());
} else {
if (sVerbose) {
Slog.v(TAG, "getDefaultFcAlgorithm(): no service for " + userId);
}
- return null;
}
}
+ send(receiver, algorithm);
}
@Override
- public String[] getAvailableFieldClassificationAlgorithms() throws RemoteException {
+ public void getAvailableFieldClassificationAlgorithms(@NonNull IResultReceiver receiver)
+ throws RemoteException {
final int userId = UserHandle.getCallingUserId();
+ String[] algorithms = null;
synchronized (mLock) {
final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
if (service != null) {
- return service.getAvailableFieldClassificationAlgorithms(getCallingUid());
+ algorithms = service.getAvailableFieldClassificationAlgorithms(getCallingUid());
} else {
if (sVerbose) {
Slog.v(TAG, "getAvailableFcAlgorithms(): no service for " + userId);
}
- return null;
}
}
+ send(receiver, algorithms);
}
@Override
- public ComponentName getAutofillServiceComponentName() throws RemoteException {
+ public void getAutofillServiceComponentName(@NonNull IResultReceiver receiver)
+ throws RemoteException {
final int userId = UserHandle.getCallingUserId();
+ ComponentName componentName = null;
synchronized (mLock) {
final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
if (service != null) {
- return service.getServiceComponentName();
+ componentName = service.getServiceComponentName();
} else if (sVerbose) {
Slog.v(TAG, "getAutofillServiceComponentName(): no service for " + userId);
}
}
-
- return null;
+ send(receiver, componentName);
}
@Override
- public boolean restoreSession(int sessionId, IBinder activityToken, IBinder appCallback)
+ public void restoreSession(int sessionId, @NonNull IBinder activityToken,
+ @NonNull IBinder appCallback, @NonNull IResultReceiver receiver)
throws RemoteException {
final int userId = UserHandle.getCallingUserId();
activityToken = Preconditions.checkNotNull(activityToken, "activityToken");
appCallback = Preconditions.checkNotNull(appCallback, "appCallback");
+ boolean restored = false;
synchronized (mLock) {
final AutofillManagerServiceImpl service = mServicesCache.get(userId);
if (service != null) {
- return service.restoreSession(sessionId, getCallingUid(), activityToken,
+ restored = service.restoreSession(sessionId, getCallingUid(), activityToken,
appCallback);
} else if (sVerbose) {
Slog.v(TAG, "restoreSession(): no service for " + userId);
}
}
-
- return false;
+ send(receiver, restored);
}
@Override
@@ -1112,23 +1155,27 @@
}
@Override
- public boolean isServiceSupported(int userId) {
+ public void isServiceSupported(int userId, @NonNull IResultReceiver receiver) {
+ boolean supported = false;
synchronized (mLock) {
- return !mDisabledUsers.get(userId);
+ supported = !mDisabledUsers.get(userId);
}
+ send(receiver, supported);
}
@Override
- public boolean isServiceEnabled(int userId, String packageName) {
+ public void isServiceEnabled(int userId, @NonNull String packageName,
+ @NonNull IResultReceiver receiver) {
+ boolean enabled = false;
synchronized (mLock) {
final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
if (service != null) {
- return Objects.equals(packageName, service.getServicePackageName());
+ enabled = Objects.equals(packageName, service.getServicePackageName());
} else if (sVerbose) {
Slog.v(TAG, "isServiceEnabled(): no service for " + userId);
}
- return false;
}
+ send(receiver, enabled);
}
@Override
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index a2dc986..4206d9a 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -28,7 +28,6 @@
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
import android.app.ActivityManagerInternal;
-import android.app.ActivityTaskManagerInternal;
import android.app.AppGlobals;
import android.app.IActivityManager;
import android.app.IActivityTaskManager;
@@ -83,6 +82,7 @@
import com.android.server.LocalServices;
import com.android.server.autofill.AutofillManagerService.AutofillCompatState;
import com.android.server.autofill.ui.AutoFillUI;
+import com.android.server.wm.ActivityTaskManagerInternal;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -534,7 +534,7 @@
} catch (NameNotFoundException e) {
throw new SecurityException("Could not verify UID for " + componentName);
}
- if (callingUid != packageUid && !LocalServices.getService(ActivityTaskManagerInternal.class)
+ if (callingUid != packageUid && !LocalServices.getService(ActivityManagerInternal.class)
.hasRunningActivity(callingUid, packageName)) {
final String[] packages = pm.getPackagesForUid(callingUid);
final String callingPackage = packages != null ? packages[0] : "uid-" + callingUid;
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 48f27d8..18255c5 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -16,8 +16,6 @@
package com.android.server.autofill;
-import static android.app.ActivityTaskManagerInternal.ASSIST_KEY_RECEIVER_EXTRAS;
-import static android.app.ActivityTaskManagerInternal.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;
@@ -33,6 +31,8 @@
import static com.android.server.autofill.Helper.sVerbose;
import static com.android.server.autofill.Helper.toArray;
import static com.android.server.autofill.ViewState.STATE_RESTARTED_SESSION;
+import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_RECEIVER_EXTRAS;
+import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_STRUCTURE;
import android.annotation.NonNull;
import android.annotation.Nullable;
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 8da6d1e..d9519e0 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -26,7 +26,6 @@
"android.hardware.light-V2.0-java",
"android.hardware.power-V1.0-java",
"android.hardware.tv.cec-V1.0-java",
- "android.hidl.manager-V1.0-java",
],
static_libs: [
@@ -37,12 +36,14 @@
"android.hardware.health-V1.0-java",
"android.hardware.health-V2.0-java",
"android.hardware.weaver-V1.0-java",
+ "android.hardware.biometrics.face-V1.0-java",
"android.hardware.biometrics.fingerprint-V2.1-java",
"android.hardware.oemlock-V1.0-java",
"android.hardware.tetheroffload.control-V1.0-java",
"android.hardware.vibrator-V1.0-java",
"android.hardware.configstore-V1.0-java",
"android.hardware.contexthub-V1.0-java",
+ "android.hidl.manager-V1.0-java",
],
}
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index 6ca81c2..26ef42f 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -172,7 +172,8 @@
long mNativeData;
private long mNextWakeup;
private long mNextNonWakeup;
- private long mLastWakeupSet;
+ private long mNextWakeUpSetAt;
+ private long mNextNonWakeUpSetAt;
private long mLastWakeup;
private long mLastTrigger;
private long mLastTickSet;
@@ -1939,7 +1940,7 @@
TimeUtils.formatDuration(nowELAPSED - mLastAlarmDeliveryTime, pw);
pw.println();
pw.print(" Next non-wakeup delivery time: ");
- TimeUtils.formatDuration(nowELAPSED - mNextNonWakeupDeliveryTime, pw);
+ TimeUtils.formatDuration(mNextNonWakeupDeliveryTime, nowELAPSED, pw);
pw.println();
long nextWakeupRTC = mNextWakeup + (nowRTC - nowELAPSED);
@@ -1948,10 +1949,12 @@
TimeUtils.formatDuration(mNextNonWakeup, nowELAPSED, pw);
pw.print(" = "); pw.print(mNextNonWakeup);
pw.print(" = "); pw.println(sdf.format(new Date(nextNonWakeupRTC)));
+ pw.print(" set at "); TimeUtils.formatDuration(mNextNonWakeUpSetAt, nowELAPSED, pw);
+ pw.println();
pw.print(" Next wakeup alarm: "); TimeUtils.formatDuration(mNextWakeup, nowELAPSED, pw);
pw.print(" = "); pw.print(mNextWakeup);
pw.print(" = "); pw.println(sdf.format(new Date(nextWakeupRTC)));
- pw.print(" set at "); TimeUtils.formatDuration(mLastWakeupSet, nowELAPSED, pw);
+ pw.print(" set at "); TimeUtils.formatDuration(mNextWakeUpSetAt, nowELAPSED, pw);
pw.println();
pw.print(" Next kernel non-wakeup alarm: ");
@@ -2290,7 +2293,7 @@
proto.write(AlarmManagerServiceDumpProto.TIME_SINCE_LAST_WAKEUP_MS,
nowElapsed - mLastWakeup);
proto.write(AlarmManagerServiceDumpProto.TIME_SINCE_LAST_WAKEUP_SET_MS,
- nowElapsed - mLastWakeupSet);
+ nowElapsed - mNextWakeUpSetAt);
proto.write(AlarmManagerServiceDumpProto.TIME_CHANGE_EVENT_COUNT, mNumTimeChanged);
final TreeSet<Integer> users = new TreeSet<>();
@@ -2675,16 +2678,53 @@
DateFormat.format(pattern, info.getTriggerTime()).toString();
}
+ /**
+ * If the last time AlarmThread woke up precedes any due wakeup or non-wakeup alarm that we set
+ * by more than half a minute, log a wtf.
+ */
+ private void validateLastAlarmExpiredLocked(long nowElapsed) {
+ final StringBuilder errorMsg = new StringBuilder();
+ boolean stuck = false;
+ if (mNextNonWakeup < (nowElapsed - 10_000) && mLastWakeup < mNextNonWakeup) {
+ stuck = true;
+ errorMsg.append("[mNextNonWakeup=");
+ TimeUtils.formatDuration(mNextNonWakeup - nowElapsed, errorMsg);
+ errorMsg.append(" set at ");
+ TimeUtils.formatDuration(mNextNonWakeUpSetAt - nowElapsed, errorMsg);
+ errorMsg.append(", mLastWakeup=");
+ TimeUtils.formatDuration(mLastWakeup - nowElapsed, errorMsg);
+ errorMsg.append(", timerfd_gettime=" + getNextAlarm(mNativeData, ELAPSED_REALTIME));
+ errorMsg.append("];");
+ }
+ if (mNextWakeup < (nowElapsed - 10_000) && mLastWakeup < mNextWakeup) {
+ stuck = true;
+ errorMsg.append("[mNextWakeup=");
+ TimeUtils.formatDuration(mNextWakeup - nowElapsed, errorMsg);
+ errorMsg.append(" set at ");
+ TimeUtils.formatDuration(mNextWakeUpSetAt - nowElapsed, errorMsg);
+ errorMsg.append(", mLastWakeup=");
+ TimeUtils.formatDuration(mLastWakeup - nowElapsed, errorMsg);
+ errorMsg.append(", timerfd_gettime="
+ + getNextAlarm(mNativeData, ELAPSED_REALTIME_WAKEUP));
+ errorMsg.append("];");
+ }
+ if (stuck) {
+ Slog.wtf(TAG, "Alarm delivery stuck: " + errorMsg.toString());
+ }
+ }
+
void rescheduleKernelAlarmsLocked() {
// Schedule the next upcoming wakeup alarm. If there is a deliverable batch
// prior to that which contains no wakeups, we schedule that as well.
+ final long nowElapsed = SystemClock.elapsedRealtime();
+ validateLastAlarmExpiredLocked(nowElapsed);
long nextNonWakeup = 0;
if (mAlarmBatches.size() > 0) {
final Batch firstWakeup = findFirstWakeupBatchLocked();
final Batch firstBatch = mAlarmBatches.get(0);
if (firstWakeup != null) {
mNextWakeup = firstWakeup.start;
- mLastWakeupSet = SystemClock.elapsedRealtime();
+ mNextWakeUpSetAt = nowElapsed;
setLocked(ELAPSED_REALTIME_WAKEUP, firstWakeup.start);
}
if (firstBatch != firstWakeup) {
@@ -2698,6 +2738,7 @@
}
if (nextNonWakeup != 0) {
mNextNonWakeup = nextNonWakeup;
+ mNextNonWakeUpSetAt = nowElapsed;
setLocked(ELAPSED_REALTIME, nextNonWakeup);
}
}
@@ -3477,9 +3518,13 @@
private class AlarmThread extends Thread
{
+ private int mFalseWakeups;
+ private int mWtfThreshold;
public AlarmThread()
{
super("AlarmManager");
+ mFalseWakeups = 0;
+ mWtfThreshold = 10;
}
public void run()
@@ -3592,6 +3637,17 @@
}
mPendingNonWakeupAlarms.clear();
}
+ if (mLastTimeChangeRealtime != nowELAPSED && triggerList.isEmpty()) {
+ if (++mFalseWakeups >= mWtfThreshold) {
+ Slog.wtf(TAG, "Too many (" + mFalseWakeups
+ + ") false wakeups, nowElapsed=" + nowELAPSED);
+ if (mWtfThreshold < 100_000) {
+ mWtfThreshold *= 10;
+ } else {
+ mFalseWakeups = 0;
+ }
+ }
+ }
final ArraySet<Pair<String, Integer>> triggerPackages =
new ArraySet<>();
for (int i = 0; i < triggerList.size(); i++) {
diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java
index 786d757..b3f6bd1 100644
--- a/services/core/java/com/android/server/AppOpsService.java
+++ b/services/core/java/com/android/server/AppOpsService.java
@@ -22,6 +22,7 @@
import android.app.AppGlobals;
import android.app.AppOpsManager;
import android.app.AppOpsManagerInternal;
+import android.app.AppOpsManagerInternal.CheckOpsDelegate;
import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.ApplicationInfo;
@@ -198,7 +199,7 @@
@VisibleForTesting
final SparseArray<UidState> mUidStates = new SparseArray<>();
- long mLastUptime;
+ long mLastRealtime;
/*
* These are app op restrictions imposed per user from various parties.
@@ -207,6 +208,8 @@
SparseIntArray mProfileOwners;
+ private CheckOpsDelegate mCheckOpsDelegate;
+
/**
* All times are in milliseconds. These constants are kept synchronized with the system
* global Settings. Any access to this class or its fields should be done while
@@ -770,7 +773,7 @@
} else {
settleTime = mConstants.BG_STATE_SETTLE_TIME;
}
- uidState.pendingStateCommitTime = SystemClock.uptimeMillis() + settleTime;
+ uidState.pendingStateCommitTime = SystemClock.elapsedRealtime() + settleTime;
}
if (uidState.startNesting != 0) {
// There is some actively running operation... need to find it
@@ -1411,15 +1414,39 @@
}
}
+ public CheckOpsDelegate getAppOpsServiceDelegate() {
+ synchronized (this) {
+ return mCheckOpsDelegate;
+ }
+ }
+
+ public void setAppOpsServiceDelegate(CheckOpsDelegate delegate) {
+ synchronized (this) {
+ mCheckOpsDelegate = delegate;
+ }
+ }
+
@Override
public int checkOperation(int code, int uid, String packageName) {
- verifyIncomingUid(uid);
- verifyIncomingOp(code);
- String resolvedPackageName = resolvePackageName(uid, packageName);
- if (resolvedPackageName == null) {
- return AppOpsManager.MODE_IGNORED;
- }
+ final CheckOpsDelegate delegate;
synchronized (this) {
+ if (mCheckOpsDelegate == null) {
+ return checkOperationImpl(code, uid, packageName);
+ }
+ delegate = mCheckOpsDelegate;
+ }
+ return delegate.checkOperation(code, uid, packageName,
+ AppOpsService.this::checkOperationImpl);
+ }
+
+ private int checkOperationImpl(int code, int uid, String packageName) {
+ synchronized (this) {
+ verifyIncomingUid(uid);
+ verifyIncomingOp(code);
+ String resolvedPackageName = resolvePackageName(uid, packageName);
+ if (resolvedPackageName == null) {
+ return AppOpsManager.MODE_IGNORED;
+ }
if (isOpRestrictedLocked(uid, code, resolvedPackageName)) {
return AppOpsManager.MODE_IGNORED;
}
@@ -1439,20 +1466,33 @@
@Override
public int checkAudioOperation(int code, int usage, int uid, String packageName) {
- boolean suspended;
- try {
- suspended = isPackageSuspendedForUser(packageName, uid);
- } catch (IllegalArgumentException ex) {
- // Package not found.
- suspended = false;
- }
-
- if (suspended) {
- Slog.i(TAG, "Audio disabled for suspended package=" + packageName + " for uid=" + uid);
- return AppOpsManager.MODE_IGNORED;
- }
-
+ final CheckOpsDelegate delegate;
synchronized (this) {
+ if (mCheckOpsDelegate == null) {
+ return checkAudioOperationImpl(code, usage, uid, packageName);
+ }
+ delegate = mCheckOpsDelegate;
+ }
+ return delegate.checkAudioOperation(code, usage, uid, packageName,
+ AppOpsService.this::checkAudioOperationImpl);
+ }
+
+ private int checkAudioOperationImpl(int code, int usage, int uid, String packageName) {
+ synchronized (this) {
+ boolean suspended;
+ try {
+ suspended = isPackageSuspendedForUser(packageName, uid);
+ } catch (IllegalArgumentException ex) {
+ // Package not found.
+ suspended = false;
+ }
+
+ if (suspended) {
+ Slog.i(TAG, "Audio disabled for suspended package=" + packageName
+ + " for uid=" + uid);
+ return AppOpsManager.MODE_IGNORED;
+ }
+
final int mode = checkRestrictionLocked(code, usage, uid, packageName);
if (mode != AppOpsManager.MODE_ALLOWED) {
return mode;
@@ -1530,10 +1570,10 @@
}
@Override
- public int noteProxyOperation(int code, String proxyPackageName,
- int proxiedUid, String proxiedPackageName) {
+ public int noteProxyOperation(int code, int proxyUid,
+ String proxyPackageName, int proxiedUid, String proxiedPackageName) {
+ verifyIncomingUid(proxyUid);
verifyIncomingOp(code);
- final int proxyUid = Binder.getCallingUid();
String resolveProxyPackageName = resolvePackageName(proxyUid, proxyPackageName);
if (resolveProxyPackageName == null) {
return AppOpsManager.MODE_IGNORED;
@@ -1553,6 +1593,18 @@
@Override
public int noteOperation(int code, int uid, String packageName) {
+ final CheckOpsDelegate delegate;
+ synchronized (this) {
+ if (mCheckOpsDelegate == null) {
+ return noteOperationImpl(code, uid, packageName);
+ }
+ delegate = mCheckOpsDelegate;
+ }
+ return delegate.noteOperation(code, uid, packageName,
+ AppOpsService.this::noteOperationImpl);
+ }
+
+ private int noteOperationImpl(int code, int uid, String packageName) {
verifyIncomingUid(uid);
verifyIncomingOp(code);
String resolvedPackageName = resolvePackageName(uid, packageName);
@@ -1881,11 +1933,11 @@
mUidStates.put(uid, uidState);
} else {
if (uidState.pendingStateCommitTime != 0) {
- if (uidState.pendingStateCommitTime < mLastUptime) {
+ if (uidState.pendingStateCommitTime < mLastRealtime) {
commitUidPendingStateLocked(uidState);
} else {
- mLastUptime = SystemClock.uptimeMillis();
- if (uidState.pendingStateCommitTime < mLastUptime) {
+ mLastRealtime = SystemClock.elapsedRealtime();
+ if (uidState.pendingStateCommitTime < mLastRealtime) {
commitUidPendingStateLocked(uidState);
}
}
@@ -3284,7 +3336,7 @@
}
if (uidState.pendingStateCommitTime != 0) {
pw.print(" pendingStateCommitTime=");
- TimeUtils.formatDuration(uidState.pendingStateCommitTime, nowUptime, pw);
+ TimeUtils.formatDuration(uidState.pendingStateCommitTime, nowElapsed, pw);
pw.println();
}
if (uidState.startNesting != 0) {
diff --git a/services/core/java/com/android/server/BinderCallsStatsService.java b/services/core/java/com/android/server/BinderCallsStatsService.java
index ce1be6f..3d779d8 100644
--- a/services/core/java/com/android/server/BinderCallsStatsService.java
+++ b/services/core/java/com/android/server/BinderCallsStatsService.java
@@ -17,15 +17,20 @@
package com.android.server;
import android.app.AppGlobals;
+import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
+import android.database.ContentObserver;
+import android.net.Uri;
import android.os.Binder;
import android.os.RemoteException;
-import android.os.ServiceManager;
import android.os.SystemProperties;
import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.KeyValueListParser;
import android.util.Slog;
+import com.android.internal.os.BackgroundThread;
import com.android.internal.os.BinderCallsStats;
import java.io.FileDescriptor;
@@ -41,18 +46,90 @@
private static final String PERSIST_SYS_BINDER_CALLS_DETAILED_TRACKING
= "persist.sys.binder_calls_detailed_tracking";
- public static void start() {
- BinderCallsStatsService service = new BinderCallsStatsService();
- ServiceManager.addService("binder_calls_stats", service);
- boolean detailedTrackingEnabled = SystemProperties.getBoolean(
- PERSIST_SYS_BINDER_CALLS_DETAILED_TRACKING, false);
+ /** Listens for flag changes. */
+ private static class SettingsObserver extends ContentObserver {
+ private static final String SETTINGS_ENABLED_KEY = "enabled";
+ private static final String SETTINGS_DETAILED_TRACKING_KEY = "detailed_tracking";
+ private static final String SETTINGS_UPLOAD_DATA_KEY = "upload_data";
+ private static final String SETTINGS_SAMPLING_INTERVAL_KEY = "sampling_interval";
- if (detailedTrackingEnabled) {
- Slog.i(TAG, "Enabled CPU usage tracking for binder calls. Controlled by "
- + PERSIST_SYS_BINDER_CALLS_DETAILED_TRACKING
- + " or via dumpsys binder_calls_stats --enable-detailed-tracking");
- BinderCallsStats.getInstance().setDetailedTracking(true);
+ private final Uri mUri = Settings.Global.getUriFor(Settings.Global.BINDER_CALLS_STATS);
+ private final Context mContext;
+ private final KeyValueListParser mParser = new KeyValueListParser(',');
+
+ public SettingsObserver(Context context) {
+ super(BackgroundThread.getHandler());
+ mContext = context;
+ context.getContentResolver().registerContentObserver(mUri, false, this,
+ UserHandle.USER_SYSTEM);
+ // Always kick once to ensure that we match current state
+ onChange();
}
+
+ @Override
+ public void onChange(boolean selfChange, Uri uri, int userId) {
+ if (mUri.equals(uri)) {
+ onChange();
+ }
+ }
+
+ public void onChange() {
+ // Do not overwrite the default set manually.
+ if (!SystemProperties.get(PERSIST_SYS_BINDER_CALLS_DETAILED_TRACKING).isEmpty()) {
+ return;
+ }
+
+ BinderCallsStats stats = BinderCallsStats.getInstance();
+ try {
+ mParser.setString(Settings.Global.getString(mContext.getContentResolver(),
+ Settings.Global.BINDER_CALLS_STATS));
+ } catch (IllegalArgumentException e) {
+ Slog.e(TAG, "Bad binder call stats settings", e);
+ }
+ stats.setEnabled(
+ mParser.getBoolean(SETTINGS_ENABLED_KEY, BinderCallsStats.ENABLED_DEFAULT));
+ stats.setDetailedTracking(mParser.getBoolean(
+ SETTINGS_DETAILED_TRACKING_KEY, BinderCallsStats.DETAILED_TRACKING_DEFAULT));
+ stats.setSamplingInterval(mParser.getInt(
+ SETTINGS_SAMPLING_INTERVAL_KEY,
+ BinderCallsStats.PERIODIC_SAMPLING_INTERVAL_DEFAULT));
+ }
+ }
+
+ public static class LifeCycle extends SystemService {
+ private BinderCallsStatsService mService;
+
+ public LifeCycle(Context context) {
+ super(context);
+ }
+
+ @Override
+ public void onStart() {
+ mService = new BinderCallsStatsService();
+ publishBinderService("binder_calls_stats", mService);
+ boolean detailedTrackingEnabled = SystemProperties.getBoolean(
+ PERSIST_SYS_BINDER_CALLS_DETAILED_TRACKING, false);
+
+ if (detailedTrackingEnabled) {
+ Slog.i(TAG, "Enabled CPU usage tracking for binder calls. Controlled by "
+ + PERSIST_SYS_BINDER_CALLS_DETAILED_TRACKING
+ + " or via dumpsys binder_calls_stats --enable-detailed-tracking");
+ BinderCallsStats.getInstance().setDetailedTracking(true);
+ }
+ }
+
+ @Override
+ public void onBootPhase(int phase) {
+ if (SystemService.PHASE_SYSTEM_SERVICES_READY == phase) {
+ mService.systemReady(getContext());
+ }
+ }
+ }
+
+ private SettingsObserver mSettingsObserver;
+
+ public void systemReady(Context context) {
+ mSettingsObserver = new SettingsObserver(context);
}
public static void reset() {
@@ -106,7 +183,13 @@
}
Map<Integer,String> map = new HashMap<>();
for (PackageInfo pkg : packages) {
- map.put(pkg.applicationInfo.uid, pkg.packageName);
+ String name = pkg.packageName;
+ int uid = pkg.applicationInfo.uid;
+ // Use sharedUserId string as package name if there are collisions
+ if (pkg.sharedUserId != null && map.containsKey(uid)) {
+ name = "shared:" + pkg.sharedUserId;
+ }
+ map.put(uid, name);
}
return map;
}
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index aa426d3..78b7385 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -53,12 +53,16 @@
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.SystemClock;
+import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.UserManagerInternal;
import android.os.UserManagerInternal.UserRestrictionsListener;
import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException;
+import android.text.TextUtils;
+import android.util.FeatureFlagUtils;
+import android.util.Log;
import android.util.Slog;
import android.util.StatsLog;
@@ -386,6 +390,15 @@
mCallbacks = new RemoteCallbackList<IBluetoothManagerCallback>();
mStateChangeCallbacks = new RemoteCallbackList<IBluetoothStateChangeCallback>();
+ // TODO: We need a more generic way to initialize the persist keys of FeatureFlagUtils
+ boolean isHearingAidEnabled;
+ String value = SystemProperties.get(FeatureFlagUtils.PERSIST_PREFIX + FeatureFlagUtils.HEARING_AID_SETTINGS);
+ if (!TextUtils.isEmpty(value)) {
+ isHearingAidEnabled = Boolean.parseBoolean(value);
+ Log.v(TAG, "set feature flag HEARING_AID_SETTINGS to " + isHearingAidEnabled);
+ FeatureFlagUtils.setEnabled(context, FeatureFlagUtils.HEARING_AID_SETTINGS, isHearingAidEnabled);
+ }
+
IntentFilter filter = new IntentFilter();
filter.addAction(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED);
filter.addAction(BluetoothAdapter.ACTION_BLUETOOTH_ADDRESS_CHANGED);
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 5b55fa1..2054e0a 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -898,6 +898,10 @@
public boolean isTetheringSupported() {
return ConnectivityService.this.isTetheringSupported();
}
+ @Override
+ public NetworkRequest getDefaultNetworkRequest() {
+ return mDefaultRequest;
+ }
};
return new Tethering(mContext, mNetd, mStatsService, mPolicyManager,
IoThread.get().getLooper(), new MockableSystemProperties(),
@@ -915,7 +919,7 @@
private NetworkRequest createDefaultInternetRequestForTransport(
int transportType, NetworkRequest.Type type) {
- NetworkCapabilities netCap = new NetworkCapabilities();
+ final NetworkCapabilities netCap = new NetworkCapabilities();
netCap.addCapability(NET_CAPABILITY_INTERNET);
netCap.addCapability(NET_CAPABILITY_NOT_RESTRICTED);
if (transportType > -1) {
@@ -1020,7 +1024,8 @@
}
}
- private NetworkAgentInfo getNetworkAgentInfoForNetwork(Network network) {
+ @VisibleForTesting
+ protected NetworkAgentInfo getNetworkAgentInfoForNetwork(Network network) {
if (network == null) {
return null;
}
@@ -2224,6 +2229,7 @@
updateCapabilities(oldScore, nai, nai.networkCapabilities);
// If score has changed, rebroadcast to NetworkFactories. b/17726566
if (oldScore != nai.getCurrentScore()) sendUpdatedScoreToFactories(nai);
+ if (valid) handleFreshlyValidatedNetwork(nai);
}
updateInetCondition(nai);
// Let the NetworkAgent know the state of its network
@@ -2318,6 +2324,16 @@
mDefaultRequest.networkCapabilities, nai.networkCapabilities);
}
+ private void handleFreshlyValidatedNetwork(NetworkAgentInfo nai) {
+ if (nai == null) return;
+ // If the Private DNS mode is opportunistic, reprogram the DNS servers
+ // in order to restart a validation pass from within netd.
+ final PrivateDnsConfig cfg = mDnsManager.getPrivateDnsConfig();
+ if (cfg.useTls && TextUtils.isEmpty(cfg.hostname)) {
+ updateDnses(nai.linkProperties, null, nai.network.netId);
+ }
+ }
+
private void handlePrivateDnsSettingsChanged() {
final PrivateDnsConfig cfg = mDnsManager.getPrivateDnsConfig();
diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java
index 4e700d7..a34c2b9 100644
--- a/services/core/java/com/android/server/DeviceIdleController.java
+++ b/services/core/java/com/android/server/DeviceIdleController.java
@@ -19,7 +19,6 @@
import android.Manifest;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
-import android.app.ActivityTaskManagerInternal;
import android.app.AlarmManager;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
@@ -31,15 +30,15 @@
import android.content.pm.PackageManager.NameNotFoundException;
import android.database.ContentObserver;
import android.hardware.Sensor;
-import android.hardware.SensorManager;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
import android.hardware.TriggerEvent;
import android.hardware.TriggerEventListener;
-import android.location.LocationRequest;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
+import android.location.LocationRequest;
import android.net.ConnectivityManager;
import android.net.INetworkPolicyManager;
import android.net.NetworkInfo;
@@ -86,6 +85,7 @@
import com.android.internal.util.XmlUtils;
import com.android.server.am.BatteryStatsService;
import com.android.server.net.NetworkPolicyManagerInternal;
+import com.android.server.wm.ActivityTaskManagerInternal;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index caea282..4b8ece9 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -468,6 +468,13 @@
IBinder mCurFocusedWindow;
/**
+ * The last window token that we confirmed that IME started talking to. This is always updated
+ * upon reports from the input method. If the window state is already changed before the report
+ * is handled, this field just keeps the last value.
+ */
+ IBinder mLastImeTargetWindow;
+
+ /**
* {@link WindowManager.LayoutParams#softInputMode} of {@link #mCurFocusedWindow}.
*
* @see #mCurFocusedWindow
@@ -688,7 +695,7 @@
}
@GuardedBy("mMethodMap")
- private final WeakHashMap<IBinder, StartInputInfo> mStartInputMap = new WeakHashMap<>();
+ private final WeakHashMap<IBinder, IBinder> mImeTargetWindowMap = new WeakHashMap<>();
/**
* A ring buffer to store the history of {@link StartInputInfo}.
@@ -1809,7 +1816,7 @@
final StartInputInfo info = new StartInputInfo(mCurToken, mCurId, startInputReason,
!initial, mCurFocusedWindow, mCurAttribute, mCurFocusedWindowSoftInputMode,
mCurSeq);
- mStartInputMap.put(startInputToken, info);
+ mImeTargetWindowMap.put(startInputToken, mCurFocusedWindow);
mStartInputHistory.addEntry(info);
final SessionState session = mCurClient.curSession;
@@ -1827,50 +1834,6 @@
@GuardedBy("mMethodMap")
@NonNull
- InputBindResult startInputLocked(
- /* @InputMethodClient.StartInputReason */ final int startInputReason,
- IInputMethodClient client, IInputContext inputContext,
- /* @InputConnectionInspector.missingMethods */ final int missingMethods,
- @Nullable EditorInfo attribute, int controlFlags) {
- // If no method is currently selected, do nothing.
- if (mCurMethodId == null) {
- return InputBindResult.NO_IME;
- }
-
- ClientState cs = mClients.get(client.asBinder());
- if (cs == null) {
- throw new IllegalArgumentException("unknown client "
- + client.asBinder());
- }
-
- if (attribute == null) {
- Slog.w(TAG, "Ignoring startInput with null EditorInfo."
- + " uid=" + cs.uid + " pid=" + cs.pid);
- return InputBindResult.NULL_EDITOR_INFO;
- }
-
- try {
- if (!mIWindowManager.inputMethodClientHasFocus(cs.client)) {
- // Check with the window manager to make sure this client actually
- // has a window with focus. If not, reject. This is thread safe
- // because if the focus changes some time before or after, the
- // next client receiving focus that has any interest in input will
- // be calling through here after that change happens.
- if (DEBUG) {
- Slog.w(TAG, "Starting input on non-focused client " + cs.client
- + " (uid=" + cs.uid + " pid=" + cs.pid + ")");
- }
- return InputBindResult.NOT_IME_TARGET_WINDOW;
- }
- } catch (RemoteException e) {
- }
-
- return startInputUncheckedLocked(cs, inputContext, missingMethods, attribute,
- controlFlags, startInputReason);
- }
-
- @GuardedBy("mMethodMap")
- @NonNull
InputBindResult startInputUncheckedLocked(@NonNull ClientState cs, IInputContext inputContext,
/* @InputConnectionInspector.missingMethods */ final int missingMethods,
@NonNull EditorInfo attribute, int controlFlags,
@@ -1997,36 +1960,6 @@
return InputBindResult.IME_NOT_CONNECTED;
}
- @NonNull
- private InputBindResult startInput(
- /* @InputMethodClient.StartInputReason */ final int startInputReason,
- IInputMethodClient client, IInputContext inputContext,
- /* @InputConnectionInspector.missingMethods */ final int missingMethods,
- @Nullable EditorInfo attribute, int controlFlags) {
- if (!calledFromValidUser()) {
- return InputBindResult.INVALID_USER;
- }
- synchronized (mMethodMap) {
- if (DEBUG) {
- Slog.v(TAG, "startInput: reason="
- + InputMethodClient.getStartInputReason(startInputReason)
- + " client = " + client.asBinder()
- + " inputContext=" + inputContext
- + " missingMethods="
- + InputConnectionInspector.getMissingMethodFlagsAsString(missingMethods)
- + " attribute=" + attribute
- + " controlFlags=#" + Integer.toHexString(controlFlags));
- }
- final long ident = Binder.clearCallingIdentity();
- try {
- return startInputLocked(startInputReason, client, inputContext, missingMethods,
- attribute, controlFlags);
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
- }
-
@Override
public void finishInput(IInputMethodClient client) {
}
@@ -2293,15 +2226,12 @@
@BinderThread
@SuppressWarnings("deprecation")
@Override
- public void setImeWindowStatus(IBinder token, IBinder startInputToken, int vis,
- int backDisposition) {
+ public void setImeWindowStatus(IBinder token, int vis, int backDisposition) {
if (!calledWithValidToken(token)) {
return;
}
- final StartInputInfo info;
synchronized (mMethodMap) {
- info = mStartInputMap.get(startInputToken);
mImeWindowVis = vis;
mBackDisposition = backDisposition;
updateSystemUiLocked(token, vis, backDisposition);
@@ -2321,8 +2251,7 @@
break;
}
mWindowManagerInternal.updateInputMethodWindowStatus(token,
- (vis & InputMethodService.IME_VISIBLE) != 0,
- dismissImeOnBackKeyPressed, info != null ? info.mTargetWindow : null);
+ (vis & InputMethodService.IME_VISIBLE) != 0, dismissImeOnBackKeyPressed);
}
private void updateSystemUi(IBinder token, int vis, int backDisposition) {
@@ -2331,6 +2260,22 @@
}
}
+ @BinderThread
+ @Override
+ public void reportStartInput(IBinder token, IBinder startInputToken) {
+ if (!calledWithValidToken(token)) {
+ return;
+ }
+
+ synchronized (mMethodMap) {
+ final IBinder targetWindow = mImeTargetWindowMap.get(startInputToken);
+ if (targetWindow != null && mLastImeTargetWindow != targetWindow) {
+ mWindowManagerInternal.updateInputMethodTargetWindow(token, targetWindow);
+ }
+ mLastImeTargetWindow = targetWindow;
+ }
+ }
+
// Caution! This method is called in this class. Handle multi-user carefully
private void updateSystemUiLocked(IBinder token, int vis, int backDisposition) {
if (!calledWithValidToken(token)) {
@@ -2741,15 +2686,13 @@
int windowFlags, @Nullable EditorInfo attribute, IInputContext inputContext,
/* @InputConnectionInspector.missingMethods */ final int missingMethods,
int unverifiedTargetSdkVersion) {
- final InputBindResult result;
- if (windowToken != null) {
- result = windowGainedFocus(startInputReason, client, windowToken, controlFlags,
- softInputMode, windowFlags, attribute, inputContext, missingMethods,
- unverifiedTargetSdkVersion);
- } else {
- result = startInput(startInputReason, client, inputContext, missingMethods, attribute,
- controlFlags);
+ if (windowToken == null) {
+ Slog.e(TAG, "windowToken cannot be null.");
+ return InputBindResult.NULL;
}
+ final InputBindResult result = startInputOrWindowGainedFocusInternal(startInputReason,
+ client, windowToken, controlFlags, softInputMode, windowFlags, attribute,
+ inputContext, missingMethods, unverifiedTargetSdkVersion);
if (result == null) {
// This must never happen, but just in case.
Slog.wtf(TAG, "InputBindResult is @NonNull. startInputReason="
@@ -2762,9 +2705,9 @@
}
@NonNull
- private InputBindResult windowGainedFocus(
+ private InputBindResult startInputOrWindowGainedFocusInternal(
/* @InputMethodClient.StartInputReason */ final int startInputReason,
- IInputMethodClient client, IBinder windowToken, int controlFlags,
+ IInputMethodClient client, @NonNull IBinder windowToken, int controlFlags,
/* @android.view.WindowManager.LayoutParams.SoftInputModeFlags */ int softInputMode,
int windowFlags, EditorInfo attribute, IInputContext inputContext,
/* @InputConnectionInspector.missingMethods */ final int missingMethods,
@@ -2775,7 +2718,7 @@
long ident = Binder.clearCallingIdentity();
try {
synchronized (mMethodMap) {
- if (DEBUG) Slog.v(TAG, "windowGainedFocus: reason="
+ if (DEBUG) Slog.v(TAG, "startInputOrWindowGainedFocusInternal: reason="
+ InputMethodClient.getStartInputReason(startInputReason)
+ " client=" + client.asBinder()
+ " inputContext=" + inputContext
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index c5ab932..232c151 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -153,6 +153,10 @@
// default background throttling interval if not overriden in settings
private static final long DEFAULT_BACKGROUND_THROTTLE_INTERVAL_MS = 30 * 60 * 1000;
+ // Default value for maximum age of last location returned to applications with foreground-only
+ // location permissions.
+ private static final long DEFAULT_LAST_LOCATION_MAX_AGE_MS = 20 * 60 * 1000;
+
// Location Providers may sometimes deliver location updates
// slightly faster that requested - provide grace period so
// we don't unnecessarily filter events that are otherwise on
@@ -241,6 +245,9 @@
private int mCurrentUserId = UserHandle.USER_SYSTEM;
private int[] mCurrentUserProfiles = new int[]{UserHandle.USER_SYSTEM};
+ // Maximum age of last location returned to clients with foreground-only location permissions.
+ private long mLastLocationMaxAgeMs;
+
private GnssLocationProvider.GnssSystemInfoProvider mGnssSystemInfoProvider;
private GnssLocationProvider.GnssMetricsProvider mGnssMetricsProvider;
@@ -308,7 +315,8 @@
}
}
};
- mAppOps.startWatchingMode(AppOpsManager.OP_COARSE_LOCATION, null, callback);
+ mAppOps.startWatchingMode(AppOpsManager.OP_COARSE_LOCATION, null,
+ AppOpsManager.WATCH_FOREGROUND_CHANGES, callback);
PackageManager.OnPermissionsChangedListener permissionListener
= new PackageManager.OnPermissionsChangedListener() {
@@ -341,6 +349,7 @@
updateUserProfiles(mCurrentUserId);
updateBackgroundThrottlingWhitelistLocked();
+ updateLastLocationMaxAgeLocked();
// prepare providers
loadProvidersLocked();
@@ -370,6 +379,18 @@
}
}, UserHandle.USER_ALL);
mContext.getContentResolver().registerContentObserver(
+ Settings.Global.getUriFor(Settings.Global.LOCATION_LAST_LOCATION_MAX_AGE_MILLIS),
+ true,
+ new ContentObserver(mLocationHandler) {
+ @Override
+ public void onChange(boolean selfChange) {
+ synchronized (mLock) {
+ updateLastLocationMaxAgeLocked();
+ }
+ }
+ }
+ );
+ mContext.getContentResolver().registerContentObserver(
Settings.Global.getUriFor(
Settings.Global.LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST),
true,
@@ -382,6 +403,7 @@
}
}
}, UserHandle.USER_ALL);
+
mPackageMonitor.register(mContext, mLocationHandler.getLooper(), true);
// listen for user change
@@ -773,6 +795,7 @@
* location updates.
*/
private final class Receiver implements IBinder.DeathRecipient, PendingIntent.OnFinished {
+ private static final long WAKELOCK_TIMEOUT_MILLIS = 60 * 1000;
final Identity mIdentity;
final int mAllowedResolutionLevel; // resolution level allowed to receiver
@@ -816,6 +839,10 @@
workSource = new WorkSource(mIdentity.mUid, mIdentity.mPackageName);
}
mWakeLock.setWorkSource(workSource);
+
+ // For a non-reference counted wakelock, each acquire will reset the timeout, and we
+ // only need to release it once.
+ mWakeLock.setReferenceCounted(false);
}
@Override
@@ -841,6 +868,7 @@
for (String p : mUpdateRecords.keySet()) {
s.append(" ").append(mUpdateRecords.get(p).toString());
}
+ s.append(" monitoring location: ").append(mOpMonitoring);
s.append("]");
return s.toString();
}
@@ -913,7 +941,7 @@
}
} else {
if (!allowMonitoring
- || mAppOps.checkOpNoThrow(op, mIdentity.mUid, mIdentity.mPackageName)
+ || mAppOps.noteOpNoThrow(op, mIdentity.mUid, mIdentity.mPackageName)
!= AppOpsManager.MODE_ALLOWED) {
mAppOps.finishOp(op, mIdentity.mUid, mIdentity.mPackageName);
return false;
@@ -1076,9 +1104,8 @@
// this must be called while synchronized by caller in a synchronized block
// containing the sending of the broadcaset
private void incrementPendingBroadcastsLocked() {
- if (mPendingBroadcasts++ == 0) {
- mWakeLock.acquire();
- }
+ mPendingBroadcasts++;
+ mWakeLock.acquire(WAKELOCK_TIMEOUT_MILLIS);
}
private void decrementPendingBroadcastsLocked() {
@@ -1526,6 +1553,22 @@
return -1;
}
+ private static String resolutionLevelToOpStr(int allowedResolutionLevel) {
+ switch(allowedResolutionLevel) {
+ case RESOLUTION_LEVEL_COARSE:
+ return AppOpsManager.OPSTR_COARSE_LOCATION;
+ case RESOLUTION_LEVEL_FINE:
+ return AppOpsManager.OPSTR_FINE_LOCATION;
+ case RESOLUTION_LEVEL_NONE:
+ // The client is not allowed to get any location, so both FINE and COARSE ops will
+ // be denied. Pick the most restrictive one to be safe.
+ return AppOpsManager.OPSTR_FINE_LOCATION;
+ default:
+ // Use the most restrictive ops if not sure.
+ return AppOpsManager.OPSTR_FINE_LOCATION;
+ }
+ }
+
boolean reportLocationAccessNoThrow(
int pid, int uid, String packageName, int allowedResolutionLevel) {
int op = resolutionLevelToOp(allowedResolutionLevel);
@@ -1541,7 +1584,7 @@
boolean checkLocationAccess(int pid, int uid, String packageName, int allowedResolutionLevel) {
int op = resolutionLevelToOp(allowedResolutionLevel);
if (op >= 0) {
- if (mAppOps.checkOp(op, uid, packageName) != AppOpsManager.MODE_ALLOWED) {
+ if (mAppOps.noteOp(op, uid, packageName) != AppOpsManager.MODE_ALLOWED) {
return false;
}
}
@@ -1857,6 +1900,14 @@
Arrays.asList(setting.split(",")));
}
+ private void updateLastLocationMaxAgeLocked() {
+ mLastLocationMaxAgeMs =
+ Settings.Global.getLong(
+ mContext.getContentResolver(),
+ Settings.Global.LOCATION_LAST_LOCATION_MAX_AGE_MILLIS,
+ DEFAULT_LAST_LOCATION_MAX_AGE_MS);
+ }
+
private boolean isThrottlingExemptLocked(Identity identity) {
if (identity.mUid == Process.SYSTEM_UID) {
return true;
@@ -2262,6 +2313,17 @@
if (location == null) {
return null;
}
+
+ // Don't return stale location to apps with foreground-only location permission.
+ String op = resolutionLevelToOpStr(allowedResolutionLevel);
+ long locationAgeMs = SystemClock.elapsedRealtime() -
+ location.getElapsedRealtimeNanos() / NANOS_PER_MILLI;
+ if ((locationAgeMs > mLastLocationMaxAgeMs)
+ && (mAppOps.unsafeCheckOp(op, uid, packageName)
+ == AppOpsManager.MODE_FOREGROUND)) {
+ return null;
+ }
+
if (allowedResolutionLevel < RESOLUTION_LEVEL_FINE) {
Location noGPSLocation = location.getExtraLocation(
Location.EXTRA_NO_GPS_LOCATION);
diff --git a/services/core/java/com/android/server/NetworkTimeUpdateService.java b/services/core/java/com/android/server/NetworkTimeUpdateService.java
index b3a8fb6..1ff455ea 100644
--- a/services/core/java/com/android/server/NetworkTimeUpdateService.java
+++ b/services/core/java/com/android/server/NetworkTimeUpdateService.java
@@ -16,314 +16,15 @@
package com.android.server;
-import android.app.AlarmManager;
-import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.database.ContentObserver;
-import android.net.ConnectivityManager;
-import android.net.ConnectivityManager.NetworkCallback;
-import android.net.Network;
-import android.os.Binder;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Looper;
-import android.os.Message;
-import android.os.PowerManager;
-import android.os.SystemClock;
-import android.provider.Settings;
-import android.util.Log;
-import android.util.NtpTrustedTime;
-import android.util.TimeUtils;
-
-import com.android.internal.telephony.TelephonyIntents;
-import com.android.internal.util.DumpUtils;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
+import android.os.IBinder;
/**
- * Monitors the network time and updates the system time if it is out of sync
- * and there hasn't been any NITZ update from the carrier recently.
- * If looking up the network time fails for some reason, it tries a few times with a short
- * interval and then resets to checking on longer intervals.
- * <p>
- * If the user enables AUTO_TIME, it will check immediately for the network time, if NITZ wasn't
- * available.
- * </p>
+ * An interface for NetworkTimeUpdateService implementations. Eventually part or all of this service
+ * will be subsumed into {@link com.android.server.timedetector.TimeDetectorService}. In the
+ * meantime this interface allows Android to use either the old or new implementation.
*/
-public class NetworkTimeUpdateService extends Binder {
-
- private static final String TAG = "NetworkTimeUpdateService";
- private static final boolean DBG = false;
-
- private static final int EVENT_AUTO_TIME_CHANGED = 1;
- private static final int EVENT_POLL_NETWORK_TIME = 2;
- private static final int EVENT_NETWORK_CHANGED = 3;
-
- private static final String ACTION_POLL =
- "com.android.server.NetworkTimeUpdateService.action.POLL";
-
- private static final int POLL_REQUEST = 0;
-
- private static final long NOT_SET = -1;
- private long mNitzTimeSetTime = NOT_SET;
- private Network mDefaultNetwork = null;
-
- private final Context mContext;
- private final NtpTrustedTime mTime;
- private final AlarmManager mAlarmManager;
- private final ConnectivityManager mCM;
- private final PendingIntent mPendingPollIntent;
- private final PowerManager.WakeLock mWakeLock;
-
- // NTP lookup is done on this thread and handler
- private Handler mHandler;
- private SettingsObserver mSettingsObserver;
- private NetworkTimeUpdateCallback mNetworkTimeUpdateCallback;
-
- // Normal polling frequency
- private final long mPollingIntervalMs;
- // Try-again polling interval, in case the network request failed
- private final long mPollingIntervalShorterMs;
- // Number of times to try again
- private final int mTryAgainTimesMax;
- // If the time difference is greater than this threshold, then update the time.
- private final int mTimeErrorThresholdMs;
- // Keeps track of how many quick attempts were made to fetch NTP time.
- // During bootup, the network may not have been up yet, or it's taking time for the
- // connection to happen.
- private int mTryAgainCounter;
-
- public NetworkTimeUpdateService(Context context) {
- mContext = context;
- mTime = NtpTrustedTime.getInstance(context);
- mAlarmManager = mContext.getSystemService(AlarmManager.class);
- mCM = mContext.getSystemService(ConnectivityManager.class);
-
- Intent pollIntent = new Intent(ACTION_POLL, null);
- mPendingPollIntent = PendingIntent.getBroadcast(mContext, POLL_REQUEST, pollIntent, 0);
-
- mPollingIntervalMs = mContext.getResources().getInteger(
- com.android.internal.R.integer.config_ntpPollingInterval);
- mPollingIntervalShorterMs = mContext.getResources().getInteger(
- com.android.internal.R.integer.config_ntpPollingIntervalShorter);
- mTryAgainTimesMax = mContext.getResources().getInteger(
- com.android.internal.R.integer.config_ntpRetry);
- mTimeErrorThresholdMs = mContext.getResources().getInteger(
- com.android.internal.R.integer.config_ntpThreshold);
-
- mWakeLock = context.getSystemService(PowerManager.class).newWakeLock(
- PowerManager.PARTIAL_WAKE_LOCK, TAG);
- }
+public interface NetworkTimeUpdateService extends IBinder {
/** Initialize the receivers and initiate the first NTP request */
- public void systemRunning() {
- registerForTelephonyIntents();
- registerForAlarms();
-
- HandlerThread thread = new HandlerThread(TAG);
- thread.start();
- mHandler = new MyHandler(thread.getLooper());
- mNetworkTimeUpdateCallback = new NetworkTimeUpdateCallback();
- mCM.registerDefaultNetworkCallback(mNetworkTimeUpdateCallback, mHandler);
-
- mSettingsObserver = new SettingsObserver(mHandler, EVENT_AUTO_TIME_CHANGED);
- mSettingsObserver.observe(mContext);
- }
-
- private void registerForTelephonyIntents() {
- IntentFilter intentFilter = new IntentFilter();
- intentFilter.addAction(TelephonyIntents.ACTION_NETWORK_SET_TIME);
- mContext.registerReceiver(mNitzReceiver, intentFilter);
- }
-
- private void registerForAlarms() {
- mContext.registerReceiver(
- new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- mHandler.obtainMessage(EVENT_POLL_NETWORK_TIME).sendToTarget();
- }
- }, new IntentFilter(ACTION_POLL));
- }
-
- private void onPollNetworkTime(int event) {
- // If Automatic time is not set, don't bother. Similarly, if we don't
- // have any default network, don't bother.
- if (mDefaultNetwork == null) return;
- mWakeLock.acquire();
- try {
- onPollNetworkTimeUnderWakeLock(event);
- } finally {
- mWakeLock.release();
- }
- }
-
- private void onPollNetworkTimeUnderWakeLock(int event) {
- // Force an NTP fix when outdated
- if (mTime.getCacheAge() >= mPollingIntervalMs) {
- if (DBG) Log.d(TAG, "Stale NTP fix; forcing refresh");
- mTime.forceRefresh();
- }
-
- if (mTime.getCacheAge() < mPollingIntervalMs) {
- // Obtained fresh fix; schedule next normal update
- resetAlarm(mPollingIntervalMs);
- if (isAutomaticTimeRequested()) {
- updateSystemClock(event);
- }
-
- } else {
- // No fresh fix; schedule retry
- mTryAgainCounter++;
- if (mTryAgainTimesMax < 0 || mTryAgainCounter <= mTryAgainTimesMax) {
- resetAlarm(mPollingIntervalShorterMs);
- } else {
- // Try much later
- mTryAgainCounter = 0;
- resetAlarm(mPollingIntervalMs);
- }
- }
- }
-
- private long getNitzAge() {
- if (mNitzTimeSetTime == NOT_SET) {
- return Long.MAX_VALUE;
- } else {
- return SystemClock.elapsedRealtime() - mNitzTimeSetTime;
- }
- }
-
- /**
- * Consider updating system clock based on current NTP fix, if requested by
- * user, significant enough delta, and we don't have a recent NITZ.
- */
- private void updateSystemClock(int event) {
- final boolean forceUpdate = (event == EVENT_AUTO_TIME_CHANGED);
- if (!forceUpdate) {
- if (getNitzAge() < mPollingIntervalMs) {
- if (DBG) Log.d(TAG, "Ignoring NTP update due to recent NITZ");
- return;
- }
-
- final long skew = Math.abs(mTime.currentTimeMillis() - System.currentTimeMillis());
- if (skew < mTimeErrorThresholdMs) {
- if (DBG) Log.d(TAG, "Ignoring NTP update due to low skew");
- return;
- }
- }
-
- SystemClock.setCurrentTimeMillis(mTime.currentTimeMillis());
- }
-
- /**
- * Cancel old alarm and starts a new one for the specified interval.
- *
- * @param interval when to trigger the alarm, starting from now.
- */
- private void resetAlarm(long interval) {
- mAlarmManager.cancel(mPendingPollIntent);
- long now = SystemClock.elapsedRealtime();
- long next = now + interval;
- mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, next, mPendingPollIntent);
- }
-
- /**
- * Checks if the user prefers to automatically set the time.
- */
- private boolean isAutomaticTimeRequested() {
- return Settings.Global.getInt(
- mContext.getContentResolver(), Settings.Global.AUTO_TIME, 0) != 0;
- }
-
- /** Receiver for Nitz time events */
- private BroadcastReceiver mNitzReceiver = new BroadcastReceiver() {
-
- @Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- if (DBG) Log.d(TAG, "Received " + action);
- if (TelephonyIntents.ACTION_NETWORK_SET_TIME.equals(action)) {
- mNitzTimeSetTime = SystemClock.elapsedRealtime();
- }
- }
- };
-
- /** Handler to do the network accesses on */
- private class MyHandler extends Handler {
-
- public MyHandler(Looper l) {
- super(l);
- }
-
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case EVENT_AUTO_TIME_CHANGED:
- case EVENT_POLL_NETWORK_TIME:
- case EVENT_NETWORK_CHANGED:
- onPollNetworkTime(msg.what);
- break;
- }
- }
- }
-
- private class NetworkTimeUpdateCallback extends NetworkCallback {
- @Override
- public void onAvailable(Network network) {
- Log.d(TAG, String.format("New default network %s; checking time.", network));
- mDefaultNetwork = network;
- // Running on mHandler so invoke directly.
- onPollNetworkTime(EVENT_NETWORK_CHANGED);
- }
-
- @Override
- public void onLost(Network network) {
- if (network.equals(mDefaultNetwork)) mDefaultNetwork = null;
- }
- }
-
- /** Observer to watch for changes to the AUTO_TIME setting */
- private static class SettingsObserver extends ContentObserver {
-
- private int mMsg;
- private Handler mHandler;
-
- SettingsObserver(Handler handler, int msg) {
- super(handler);
- mHandler = handler;
- mMsg = msg;
- }
-
- void observe(Context context) {
- ContentResolver resolver = context.getContentResolver();
- resolver.registerContentObserver(Settings.Global.getUriFor(Settings.Global.AUTO_TIME),
- false, this);
- }
-
- @Override
- public void onChange(boolean selfChange) {
- mHandler.obtainMessage(mMsg).sendToTarget();
- }
- }
-
- @Override
- protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
- pw.print("PollingIntervalMs: ");
- TimeUtils.formatDuration(mPollingIntervalMs, pw);
- pw.print("\nPollingIntervalShorterMs: ");
- TimeUtils.formatDuration(mPollingIntervalShorterMs, pw);
- pw.println("\nTryAgainTimesMax: " + mTryAgainTimesMax);
- pw.print("TimeErrorThresholdMs: ");
- TimeUtils.formatDuration(mTimeErrorThresholdMs, pw);
- pw.println("\nTryAgainCounter: " + mTryAgainCounter);
- pw.println("NTP cache age: " + mTime.getCacheAge());
- pw.println("NTP cache certainty: " + mTime.getCacheCertainty());
- pw.println();
- }
+ void systemRunning();
}
diff --git a/services/core/java/com/android/server/NewNetworkTimeUpdateService.java b/services/core/java/com/android/server/NewNetworkTimeUpdateService.java
new file mode 100644
index 0000000..d21741a
--- /dev/null
+++ b/services/core/java/com/android/server/NewNetworkTimeUpdateService.java
@@ -0,0 +1,329 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.database.ContentObserver;
+import android.net.ConnectivityManager;
+import android.net.ConnectivityManager.NetworkCallback;
+import android.net.Network;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+import android.os.PowerManager;
+import android.os.SystemClock;
+import android.provider.Settings;
+import android.util.Log;
+import android.util.NtpTrustedTime;
+import android.util.TimeUtils;
+
+import com.android.internal.telephony.TelephonyIntents;
+import com.android.internal.util.DumpUtils;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+/**
+ * Monitors the network time and updates the system time if it is out of sync
+ * and there hasn't been any NITZ update from the carrier recently.
+ * If looking up the network time fails for some reason, it tries a few times with a short
+ * interval and then resets to checking on longer intervals.
+ * <p>
+ * If the user enables AUTO_TIME, it will check immediately for the network time, if NITZ wasn't
+ * available.
+ * </p>
+ */
+public class NewNetworkTimeUpdateService extends Binder implements NetworkTimeUpdateService {
+
+ private static final String TAG = "NetworkTimeUpdateService";
+ private static final boolean DBG = false;
+
+ private static final int EVENT_AUTO_TIME_CHANGED = 1;
+ private static final int EVENT_POLL_NETWORK_TIME = 2;
+ private static final int EVENT_NETWORK_CHANGED = 3;
+
+ private static final String ACTION_POLL =
+ "com.android.server.NetworkTimeUpdateService.action.POLL";
+
+ private static final int POLL_REQUEST = 0;
+
+ private static final long NOT_SET = -1;
+ private long mNitzTimeSetTime = NOT_SET;
+ private Network mDefaultNetwork = null;
+
+ private final Context mContext;
+ private final NtpTrustedTime mTime;
+ private final AlarmManager mAlarmManager;
+ private final ConnectivityManager mCM;
+ private final PendingIntent mPendingPollIntent;
+ private final PowerManager.WakeLock mWakeLock;
+
+ // NTP lookup is done on this thread and handler
+ private Handler mHandler;
+ private SettingsObserver mSettingsObserver;
+ private NetworkTimeUpdateCallback mNetworkTimeUpdateCallback;
+
+ // Normal polling frequency
+ private final long mPollingIntervalMs;
+ // Try-again polling interval, in case the network request failed
+ private final long mPollingIntervalShorterMs;
+ // Number of times to try again
+ private final int mTryAgainTimesMax;
+ // If the time difference is greater than this threshold, then update the time.
+ private final int mTimeErrorThresholdMs;
+ // Keeps track of how many quick attempts were made to fetch NTP time.
+ // During bootup, the network may not have been up yet, or it's taking time for the
+ // connection to happen.
+ private int mTryAgainCounter;
+
+ public NewNetworkTimeUpdateService(Context context) {
+ mContext = context;
+ mTime = NtpTrustedTime.getInstance(context);
+ mAlarmManager = mContext.getSystemService(AlarmManager.class);
+ mCM = mContext.getSystemService(ConnectivityManager.class);
+
+ Intent pollIntent = new Intent(ACTION_POLL, null);
+ mPendingPollIntent = PendingIntent.getBroadcast(mContext, POLL_REQUEST, pollIntent, 0);
+
+ mPollingIntervalMs = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_ntpPollingInterval);
+ mPollingIntervalShorterMs = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_ntpPollingIntervalShorter);
+ mTryAgainTimesMax = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_ntpRetry);
+ mTimeErrorThresholdMs = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_ntpThreshold);
+
+ mWakeLock = context.getSystemService(PowerManager.class).newWakeLock(
+ PowerManager.PARTIAL_WAKE_LOCK, TAG);
+ }
+
+ @Override
+ public void systemRunning() {
+ registerForTelephonyIntents();
+ registerForAlarms();
+
+ HandlerThread thread = new HandlerThread(TAG);
+ thread.start();
+ mHandler = new MyHandler(thread.getLooper());
+ mNetworkTimeUpdateCallback = new NetworkTimeUpdateCallback();
+ mCM.registerDefaultNetworkCallback(mNetworkTimeUpdateCallback, mHandler);
+
+ mSettingsObserver = new SettingsObserver(mHandler, EVENT_AUTO_TIME_CHANGED);
+ mSettingsObserver.observe(mContext);
+ }
+
+ private void registerForTelephonyIntents() {
+ IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(TelephonyIntents.ACTION_NETWORK_SET_TIME);
+ mContext.registerReceiver(mNitzReceiver, intentFilter);
+ }
+
+ private void registerForAlarms() {
+ mContext.registerReceiver(
+ new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ mHandler.obtainMessage(EVENT_POLL_NETWORK_TIME).sendToTarget();
+ }
+ }, new IntentFilter(ACTION_POLL));
+ }
+
+ private void onPollNetworkTime(int event) {
+ // If Automatic time is not set, don't bother. Similarly, if we don't
+ // have any default network, don't bother.
+ if (mDefaultNetwork == null) return;
+ mWakeLock.acquire();
+ try {
+ onPollNetworkTimeUnderWakeLock(event);
+ } finally {
+ mWakeLock.release();
+ }
+ }
+
+ private void onPollNetworkTimeUnderWakeLock(int event) {
+ // Force an NTP fix when outdated
+ if (mTime.getCacheAge() >= mPollingIntervalMs) {
+ if (DBG) Log.d(TAG, "Stale NTP fix; forcing refresh");
+ mTime.forceRefresh();
+ }
+
+ if (mTime.getCacheAge() < mPollingIntervalMs) {
+ // Obtained fresh fix; schedule next normal update
+ resetAlarm(mPollingIntervalMs);
+ if (isAutomaticTimeRequested()) {
+ updateSystemClock(event);
+ }
+
+ } else {
+ // No fresh fix; schedule retry
+ mTryAgainCounter++;
+ if (mTryAgainTimesMax < 0 || mTryAgainCounter <= mTryAgainTimesMax) {
+ resetAlarm(mPollingIntervalShorterMs);
+ } else {
+ // Try much later
+ mTryAgainCounter = 0;
+ resetAlarm(mPollingIntervalMs);
+ }
+ }
+ }
+
+ private long getNitzAge() {
+ if (mNitzTimeSetTime == NOT_SET) {
+ return Long.MAX_VALUE;
+ } else {
+ return SystemClock.elapsedRealtime() - mNitzTimeSetTime;
+ }
+ }
+
+ /**
+ * Consider updating system clock based on current NTP fix, if requested by
+ * user, significant enough delta, and we don't have a recent NITZ.
+ */
+ private void updateSystemClock(int event) {
+ final boolean forceUpdate = (event == EVENT_AUTO_TIME_CHANGED);
+ if (!forceUpdate) {
+ if (getNitzAge() < mPollingIntervalMs) {
+ if (DBG) Log.d(TAG, "Ignoring NTP update due to recent NITZ");
+ return;
+ }
+
+ final long skew = Math.abs(mTime.currentTimeMillis() - System.currentTimeMillis());
+ if (skew < mTimeErrorThresholdMs) {
+ if (DBG) Log.d(TAG, "Ignoring NTP update due to low skew");
+ return;
+ }
+ }
+
+ SystemClock.setCurrentTimeMillis(mTime.currentTimeMillis());
+ }
+
+ /**
+ * Cancel old alarm and starts a new one for the specified interval.
+ *
+ * @param interval when to trigger the alarm, starting from now.
+ */
+ private void resetAlarm(long interval) {
+ mAlarmManager.cancel(mPendingPollIntent);
+ long now = SystemClock.elapsedRealtime();
+ long next = now + interval;
+ mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, next, mPendingPollIntent);
+ }
+
+ /**
+ * Checks if the user prefers to automatically set the time.
+ */
+ private boolean isAutomaticTimeRequested() {
+ return Settings.Global.getInt(
+ mContext.getContentResolver(), Settings.Global.AUTO_TIME, 0) != 0;
+ }
+
+ /** Receiver for Nitz time events */
+ private BroadcastReceiver mNitzReceiver = new BroadcastReceiver() {
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (DBG) Log.d(TAG, "Received " + action);
+ if (TelephonyIntents.ACTION_NETWORK_SET_TIME.equals(action)) {
+ mNitzTimeSetTime = SystemClock.elapsedRealtime();
+ }
+ }
+ };
+
+ /** Handler to do the network accesses on */
+ private class MyHandler extends Handler {
+
+ public MyHandler(Looper l) {
+ super(l);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case EVENT_AUTO_TIME_CHANGED:
+ case EVENT_POLL_NETWORK_TIME:
+ case EVENT_NETWORK_CHANGED:
+ onPollNetworkTime(msg.what);
+ break;
+ }
+ }
+ }
+
+ private class NetworkTimeUpdateCallback extends NetworkCallback {
+ @Override
+ public void onAvailable(Network network) {
+ Log.d(TAG, String.format("New default network %s; checking time.", network));
+ mDefaultNetwork = network;
+ // Running on mHandler so invoke directly.
+ onPollNetworkTime(EVENT_NETWORK_CHANGED);
+ }
+
+ @Override
+ public void onLost(Network network) {
+ if (network.equals(mDefaultNetwork)) mDefaultNetwork = null;
+ }
+ }
+
+ /** Observer to watch for changes to the AUTO_TIME setting */
+ private static class SettingsObserver extends ContentObserver {
+
+ private int mMsg;
+ private Handler mHandler;
+
+ SettingsObserver(Handler handler, int msg) {
+ super(handler);
+ mHandler = handler;
+ mMsg = msg;
+ }
+
+ void observe(Context context) {
+ ContentResolver resolver = context.getContentResolver();
+ resolver.registerContentObserver(Settings.Global.getUriFor(Settings.Global.AUTO_TIME),
+ false, this);
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ mHandler.obtainMessage(mMsg).sendToTarget();
+ }
+ }
+
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
+ pw.print("PollingIntervalMs: ");
+ TimeUtils.formatDuration(mPollingIntervalMs, pw);
+ pw.print("\nPollingIntervalShorterMs: ");
+ TimeUtils.formatDuration(mPollingIntervalShorterMs, pw);
+ pw.println("\nTryAgainTimesMax: " + mTryAgainTimesMax);
+ pw.print("TimeErrorThresholdMs: ");
+ TimeUtils.formatDuration(mTimeErrorThresholdMs, pw);
+ pw.println("\nTryAgainCounter: " + mTryAgainCounter);
+ pw.println("NTP cache age: " + mTime.getCacheAge());
+ pw.println("NTP cache certainty: " + mTime.getCacheCertainty());
+ pw.println();
+ }
+}
diff --git a/services/core/java/com/android/server/OldNetworkTimeUpdateService.java b/services/core/java/com/android/server/OldNetworkTimeUpdateService.java
new file mode 100644
index 0000000..068b83d
--- /dev/null
+++ b/services/core/java/com/android/server/OldNetworkTimeUpdateService.java
@@ -0,0 +1,329 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.database.ContentObserver;
+import android.net.ConnectivityManager;
+import android.net.ConnectivityManager.NetworkCallback;
+import android.net.Network;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+import android.os.PowerManager;
+import android.os.SystemClock;
+import android.provider.Settings;
+import android.util.Log;
+import android.util.NtpTrustedTime;
+import android.util.TimeUtils;
+
+import com.android.internal.telephony.TelephonyIntents;
+import com.android.internal.util.DumpUtils;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+/**
+ * Monitors the network time and updates the system time if it is out of sync
+ * and there hasn't been any NITZ update from the carrier recently.
+ * If looking up the network time fails for some reason, it tries a few times with a short
+ * interval and then resets to checking on longer intervals.
+ * <p>
+ * If the user enables AUTO_TIME, it will check immediately for the network time, if NITZ wasn't
+ * available.
+ * </p>
+ */
+public class OldNetworkTimeUpdateService extends Binder implements NetworkTimeUpdateService {
+
+ private static final String TAG = "NetworkTimeUpdateService";
+ private static final boolean DBG = false;
+
+ private static final int EVENT_AUTO_TIME_CHANGED = 1;
+ private static final int EVENT_POLL_NETWORK_TIME = 2;
+ private static final int EVENT_NETWORK_CHANGED = 3;
+
+ private static final String ACTION_POLL =
+ "com.android.server.NetworkTimeUpdateService.action.POLL";
+
+ private static final int POLL_REQUEST = 0;
+
+ private static final long NOT_SET = -1;
+ private long mNitzTimeSetTime = NOT_SET;
+ private Network mDefaultNetwork = null;
+
+ private final Context mContext;
+ private final NtpTrustedTime mTime;
+ private final AlarmManager mAlarmManager;
+ private final ConnectivityManager mCM;
+ private final PendingIntent mPendingPollIntent;
+ private final PowerManager.WakeLock mWakeLock;
+
+ // NTP lookup is done on this thread and handler
+ private Handler mHandler;
+ private SettingsObserver mSettingsObserver;
+ private NetworkTimeUpdateCallback mNetworkTimeUpdateCallback;
+
+ // Normal polling frequency
+ private final long mPollingIntervalMs;
+ // Try-again polling interval, in case the network request failed
+ private final long mPollingIntervalShorterMs;
+ // Number of times to try again
+ private final int mTryAgainTimesMax;
+ // If the time difference is greater than this threshold, then update the time.
+ private final int mTimeErrorThresholdMs;
+ // Keeps track of how many quick attempts were made to fetch NTP time.
+ // During bootup, the network may not have been up yet, or it's taking time for the
+ // connection to happen.
+ private int mTryAgainCounter;
+
+ public OldNetworkTimeUpdateService(Context context) {
+ mContext = context;
+ mTime = NtpTrustedTime.getInstance(context);
+ mAlarmManager = mContext.getSystemService(AlarmManager.class);
+ mCM = mContext.getSystemService(ConnectivityManager.class);
+
+ Intent pollIntent = new Intent(ACTION_POLL, null);
+ mPendingPollIntent = PendingIntent.getBroadcast(mContext, POLL_REQUEST, pollIntent, 0);
+
+ mPollingIntervalMs = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_ntpPollingInterval);
+ mPollingIntervalShorterMs = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_ntpPollingIntervalShorter);
+ mTryAgainTimesMax = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_ntpRetry);
+ mTimeErrorThresholdMs = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_ntpThreshold);
+
+ mWakeLock = context.getSystemService(PowerManager.class).newWakeLock(
+ PowerManager.PARTIAL_WAKE_LOCK, TAG);
+ }
+
+ @Override
+ public void systemRunning() {
+ registerForTelephonyIntents();
+ registerForAlarms();
+
+ HandlerThread thread = new HandlerThread(TAG);
+ thread.start();
+ mHandler = new MyHandler(thread.getLooper());
+ mNetworkTimeUpdateCallback = new NetworkTimeUpdateCallback();
+ mCM.registerDefaultNetworkCallback(mNetworkTimeUpdateCallback, mHandler);
+
+ mSettingsObserver = new SettingsObserver(mHandler, EVENT_AUTO_TIME_CHANGED);
+ mSettingsObserver.observe(mContext);
+ }
+
+ private void registerForTelephonyIntents() {
+ IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(TelephonyIntents.ACTION_NETWORK_SET_TIME);
+ mContext.registerReceiver(mNitzReceiver, intentFilter);
+ }
+
+ private void registerForAlarms() {
+ mContext.registerReceiver(
+ new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ mHandler.obtainMessage(EVENT_POLL_NETWORK_TIME).sendToTarget();
+ }
+ }, new IntentFilter(ACTION_POLL));
+ }
+
+ private void onPollNetworkTime(int event) {
+ // If Automatic time is not set, don't bother. Similarly, if we don't
+ // have any default network, don't bother.
+ if (mDefaultNetwork == null) return;
+ mWakeLock.acquire();
+ try {
+ onPollNetworkTimeUnderWakeLock(event);
+ } finally {
+ mWakeLock.release();
+ }
+ }
+
+ private void onPollNetworkTimeUnderWakeLock(int event) {
+ // Force an NTP fix when outdated
+ if (mTime.getCacheAge() >= mPollingIntervalMs) {
+ if (DBG) Log.d(TAG, "Stale NTP fix; forcing refresh");
+ mTime.forceRefresh();
+ }
+
+ if (mTime.getCacheAge() < mPollingIntervalMs) {
+ // Obtained fresh fix; schedule next normal update
+ resetAlarm(mPollingIntervalMs);
+ if (isAutomaticTimeRequested()) {
+ updateSystemClock(event);
+ }
+
+ } else {
+ // No fresh fix; schedule retry
+ mTryAgainCounter++;
+ if (mTryAgainTimesMax < 0 || mTryAgainCounter <= mTryAgainTimesMax) {
+ resetAlarm(mPollingIntervalShorterMs);
+ } else {
+ // Try much later
+ mTryAgainCounter = 0;
+ resetAlarm(mPollingIntervalMs);
+ }
+ }
+ }
+
+ private long getNitzAge() {
+ if (mNitzTimeSetTime == NOT_SET) {
+ return Long.MAX_VALUE;
+ } else {
+ return SystemClock.elapsedRealtime() - mNitzTimeSetTime;
+ }
+ }
+
+ /**
+ * Consider updating system clock based on current NTP fix, if requested by
+ * user, significant enough delta, and we don't have a recent NITZ.
+ */
+ private void updateSystemClock(int event) {
+ final boolean forceUpdate = (event == EVENT_AUTO_TIME_CHANGED);
+ if (!forceUpdate) {
+ if (getNitzAge() < mPollingIntervalMs) {
+ if (DBG) Log.d(TAG, "Ignoring NTP update due to recent NITZ");
+ return;
+ }
+
+ final long skew = Math.abs(mTime.currentTimeMillis() - System.currentTimeMillis());
+ if (skew < mTimeErrorThresholdMs) {
+ if (DBG) Log.d(TAG, "Ignoring NTP update due to low skew");
+ return;
+ }
+ }
+
+ SystemClock.setCurrentTimeMillis(mTime.currentTimeMillis());
+ }
+
+ /**
+ * Cancel old alarm and starts a new one for the specified interval.
+ *
+ * @param interval when to trigger the alarm, starting from now.
+ */
+ private void resetAlarm(long interval) {
+ mAlarmManager.cancel(mPendingPollIntent);
+ long now = SystemClock.elapsedRealtime();
+ long next = now + interval;
+ mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, next, mPendingPollIntent);
+ }
+
+ /**
+ * Checks if the user prefers to automatically set the time.
+ */
+ private boolean isAutomaticTimeRequested() {
+ return Settings.Global.getInt(
+ mContext.getContentResolver(), Settings.Global.AUTO_TIME, 0) != 0;
+ }
+
+ /** Receiver for Nitz time events */
+ private BroadcastReceiver mNitzReceiver = new BroadcastReceiver() {
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (DBG) Log.d(TAG, "Received " + action);
+ if (TelephonyIntents.ACTION_NETWORK_SET_TIME.equals(action)) {
+ mNitzTimeSetTime = SystemClock.elapsedRealtime();
+ }
+ }
+ };
+
+ /** Handler to do the network accesses on */
+ private class MyHandler extends Handler {
+
+ public MyHandler(Looper l) {
+ super(l);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case EVENT_AUTO_TIME_CHANGED:
+ case EVENT_POLL_NETWORK_TIME:
+ case EVENT_NETWORK_CHANGED:
+ onPollNetworkTime(msg.what);
+ break;
+ }
+ }
+ }
+
+ private class NetworkTimeUpdateCallback extends NetworkCallback {
+ @Override
+ public void onAvailable(Network network) {
+ Log.d(TAG, String.format("New default network %s; checking time.", network));
+ mDefaultNetwork = network;
+ // Running on mHandler so invoke directly.
+ onPollNetworkTime(EVENT_NETWORK_CHANGED);
+ }
+
+ @Override
+ public void onLost(Network network) {
+ if (network.equals(mDefaultNetwork)) mDefaultNetwork = null;
+ }
+ }
+
+ /** Observer to watch for changes to the AUTO_TIME setting */
+ private static class SettingsObserver extends ContentObserver {
+
+ private int mMsg;
+ private Handler mHandler;
+
+ SettingsObserver(Handler handler, int msg) {
+ super(handler);
+ mHandler = handler;
+ mMsg = msg;
+ }
+
+ void observe(Context context) {
+ ContentResolver resolver = context.getContentResolver();
+ resolver.registerContentObserver(Settings.Global.getUriFor(Settings.Global.AUTO_TIME),
+ false, this);
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ mHandler.obtainMessage(mMsg).sendToTarget();
+ }
+ }
+
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
+ pw.print("PollingIntervalMs: ");
+ TimeUtils.formatDuration(mPollingIntervalMs, pw);
+ pw.print("\nPollingIntervalShorterMs: ");
+ TimeUtils.formatDuration(mPollingIntervalShorterMs, pw);
+ pw.println("\nTryAgainTimesMax: " + mTryAgainTimesMax);
+ pw.print("TimeErrorThresholdMs: ");
+ TimeUtils.formatDuration(mTimeErrorThresholdMs, pw);
+ pw.println("\nTryAgainCounter: " + mTryAgainCounter);
+ pw.println("NTP cache age: " + mTime.getCacheAge());
+ pw.println("NTP cache certainty: " + mTime.getCacheCertainty());
+ pw.println();
+ }
+}
diff --git a/services/core/java/com/android/server/PinnerService.java b/services/core/java/com/android/server/PinnerService.java
index 5a25f48..f5b29e9 100644
--- a/services/core/java/com/android/server/PinnerService.java
+++ b/services/core/java/com/android/server/PinnerService.java
@@ -16,8 +16,16 @@
package com.android.server;
+import static android.app.ActivityManager.UID_OBSERVER_ACTIVE;
+import static android.app.ActivityManager.UID_OBSERVER_GONE;
+
+import android.annotation.IntDef;
import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
+import android.app.IActivityManager;
+import android.app.IUidObserver;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -26,24 +34,29 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
+import android.database.ContentObserver;
import android.net.Uri;
import android.os.Binder;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
+import android.os.RemoteException;
import android.os.UserHandle;
import android.provider.MediaStore;
+import android.provider.Settings;
import android.system.ErrnoException;
import android.system.Os;
import android.system.OsConstants;
-import android.system.StructStat;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Slog;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.ResolverActivity;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.DumpUtils;
+import com.android.internal.util.function.pooled.PooledLambda;
import dalvik.system.DexFile;
import dalvik.system.VMRuntime;
@@ -53,12 +66,12 @@
import java.io.InputStream;
import java.io.DataInputStream;
import java.io.IOException;
-import java.io.EOFException;
import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.zip.ZipFile;
-import java.util.zip.ZipException;
import java.util.zip.ZipEntry;
/**
@@ -70,16 +83,50 @@
public final class PinnerService extends SystemService {
private static final boolean DEBUG = false;
private static final String TAG = "PinnerService";
- private static final int MAX_CAMERA_PIN_SIZE = 80 * (1 << 20); //80MB max
+
private static final String PIN_META_FILENAME = "pinlist.meta";
private static final int PAGE_SIZE = (int) Os.sysconf(OsConstants._SC_PAGESIZE);
+ private static final int MATCH_FLAGS = PackageManager.MATCH_DEFAULT_ONLY
+ | PackageManager.MATCH_DIRECT_BOOT_AWARE
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
+
+ private static final int KEY_CAMERA = 0;
+ private static final int KEY_HOME = 1;
+
+ private static final int MAX_CAMERA_PIN_SIZE = 80 * (1 << 20); // 80MB max for camera app.
+ private static final int MAX_HOME_PIN_SIZE = 6 * (1 << 20); // 6MB max for home app.
+
+ @IntDef({KEY_CAMERA, KEY_HOME})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface AppKey {}
private final Context mContext;
- private final boolean mShouldPinCamera;
+ private final ActivityManagerInternal mAmInternal;
+ private final IActivityManager mAm;
- /* These lists protected by PinnerService monitor lock */
- private final ArrayList<PinnedFile> mPinnedFiles = new ArrayList<PinnedFile>();
- private final ArrayList<PinnedFile> mPinnedCameraFiles = new ArrayList<PinnedFile>();
+ /** The list of the statically pinned files. */
+ @GuardedBy("this")
+ private final ArrayList<PinnedFile> mPinnedFiles = new ArrayList<>();
+
+ /** The list of the pinned apps. This is a map from {@link AppKey} to a pinned app. */
+ @GuardedBy("this")
+ private final ArrayMap<Integer, PinnedApp> mPinnedApps = new ArrayMap<>();
+
+ /**
+ * The list of the pinned apps that need to be repinned as soon as the all processes of a given
+ * uid are no longer active. Note that with background dex opt, the new dex/vdex files are only
+ * loaded into the processes once it restarts. So in case background dex opt recompiled these
+ * files, we still need to keep the old ones pinned until the processes restart.
+ * <p>
+ * This is a map from uid to {@link AppKey}
+ */
+ @GuardedBy("this")
+ private final ArrayMap<Integer, Integer> mPendingRepin = new ArrayMap<>();
+
+ /**
+ * A set of {@link AppKey} that are configured to be pinned.
+ */
+ private final ArraySet<Integer> mPinKeys = new ArraySet<>();
private BinderService mBinderService;
private PinnerHandler mPinnerHandler = null;
@@ -87,13 +134,13 @@
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- // If this user's camera app has been updated, update pinned files accordingly.
- if (intent.getAction() == Intent.ACTION_PACKAGE_REPLACED) {
+ // If an app has updated, update pinned files accordingly.
+ if (Intent.ACTION_PACKAGE_REPLACED.equals(intent.getAction())) {
Uri packageUri = intent.getData();
String packageName = packageUri.getSchemeSpecificPart();
ArraySet<String> updatedPackages = new ArraySet<>();
updatedPackages.add(packageName);
- update(updatedPackages);
+ update(updatedPackages, true /* force */);
}
}
};
@@ -102,14 +149,28 @@
super(context);
mContext = context;
- mShouldPinCamera = context.getResources().getBoolean(
+ boolean shouldPinCamera = context.getResources().getBoolean(
com.android.internal.R.bool.config_pinnerCameraApp);
+ boolean shouldPinHome = context.getResources().getBoolean(
+ com.android.internal.R.bool.config_pinnerHomeApp);
+ if (shouldPinCamera) {
+ mPinKeys.add(KEY_CAMERA);
+ }
+ if (shouldPinHome) {
+ mPinKeys.add(KEY_HOME);
+ }
mPinnerHandler = new PinnerHandler(BackgroundThread.get().getLooper());
+ mAmInternal = LocalServices.getService(ActivityManagerInternal.class);
+ mAm = ActivityManager.getService();
+
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
filter.addDataScheme("package");
mContext.registerReceiver(mBroadcastReceiver, filter);
+
+ registerUidListener();
+ registerUserSetupCompleteListener();
}
@Override
@@ -122,32 +183,39 @@
publishLocalService(PinnerService.class, this);
mPinnerHandler.obtainMessage(PinnerHandler.PIN_ONSTART_MSG).sendToTarget();
- mPinnerHandler.obtainMessage(PinnerHandler.PIN_CAMERA_MSG, UserHandle.USER_SYSTEM, 0)
- .sendToTarget();
+ sendPinAppsMessage(UserHandle.USER_SYSTEM);
}
/**
- * Pin camera on user switch.
- * If more than one user is using the device
- * each user may set a different preference for the camera app.
- * Make sure that user's preference is pinned into memory.
+ * Repin apps on user switch.
+ * <p>
+ * If more than one user is using the device each user may set a different preference for the
+ * individual apps. Make sure that user's preference is pinned into memory.
*/
@Override
public void onSwitchUser(int userHandle) {
- mPinnerHandler.obtainMessage(PinnerHandler.PIN_CAMERA_MSG, userHandle, 0).sendToTarget();
+ sendPinAppsMessage(userHandle);
+ }
+
+ @Override
+ public void onUnlockUser(int userHandle) {
+ sendPinAppsMessage(userHandle);
}
/**
* Update the currently pinned files.
- * Specifically, this only updates camera pinning.
+ * Specifically, this only updates pinning for the apps that need to be pinned.
* The other files pinned in onStart will not need to be updated.
*/
- public void update(ArraySet<String> updatedPackages) {
- ApplicationInfo cameraInfo = getCameraInfo(UserHandle.USER_SYSTEM);
- if (cameraInfo != null && updatedPackages.contains(cameraInfo.packageName)) {
- Slog.i(TAG, "Updating pinned files.");
- mPinnerHandler.obtainMessage(PinnerHandler.PIN_CAMERA_MSG, UserHandle.USER_SYSTEM, 0)
- .sendToTarget();
+ public void update(ArraySet<String> updatedPackages, boolean force) {
+ int currentUser = ActivityManager.getCurrentUser();
+ for (int i = mPinKeys.size() - 1; i >= 0; i--) {
+ int key = mPinKeys.valueAt(i);
+ ApplicationInfo info = getInfoForKey(key, currentUser);
+ if (info != null && updatedPackages.contains(info.packageName)) {
+ Slog.i(TAG, "Updating pinned files for " + info.packageName + " force=" + force);
+ sendPinAppMessage(key, currentUser, force);
+ }
}
}
@@ -175,24 +243,99 @@
}
/**
- * Handler for camera pinning message
+ * Registers a listener to repin the home app when user setup is complete, as the home intent
+ * initially resolves to setup wizard, but once setup is complete, it will resolve to the
+ * regular home app.
*/
- private void handlePinCamera(int userHandle) {
- if (!mShouldPinCamera) return;
- if (!pinCamera(userHandle)) {
- if (DEBUG) {
- Slog.v(TAG, "Failed to pin camera.");
+ private void registerUserSetupCompleteListener() {
+ Uri userSetupCompleteUri = Settings.Secure.getUriFor(
+ Settings.Secure.USER_SETUP_COMPLETE);
+ mContext.getContentResolver().registerContentObserver(userSetupCompleteUri,
+ false, new ContentObserver(null) {
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ if (userSetupCompleteUri.equals(uri)) {
+ sendPinAppMessage(KEY_HOME, ActivityManager.getCurrentUser(),
+ true /* force */);
+ }
+ }
+ }, UserHandle.USER_ALL);
+ }
+
+ private void registerUidListener() {
+ try {
+ mAm.registerUidObserver(new IUidObserver.Stub() {
+ @Override
+ public void onUidGone(int uid, boolean disabled) throws RemoteException {
+ mPinnerHandler.sendMessage(PooledLambda.obtainMessage(
+ PinnerService::handleUidGone, PinnerService.this, uid));
+ }
+
+ @Override
+ public void onUidActive(int uid) throws RemoteException {
+ mPinnerHandler.sendMessage(PooledLambda.obtainMessage(
+ PinnerService::handleUidActive, PinnerService.this, uid));
+ }
+
+ @Override
+ public void onUidIdle(int uid, boolean disabled) throws RemoteException {
+ }
+
+ @Override
+ public void onUidStateChanged(int uid, int procState, long procStateSeq)
+ throws RemoteException {
+ }
+
+ @Override
+ public void onUidCachedChanged(int uid, boolean cached) throws RemoteException {
+ }
+ }, UID_OBSERVER_GONE | UID_OBSERVER_ACTIVE, 0, "system");
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to register uid observer", e);
+ }
+ }
+
+ private void handleUidGone(int uid) {
+ updateActiveState(uid, false /* active */);
+ int key;
+ synchronized (this) {
+
+ // In case we have a pending repin, repin now. See mPendingRepin for more information.
+ key = mPendingRepin.getOrDefault(uid, -1);
+ if (key == -1) {
+ return;
+ }
+ mPendingRepin.remove(uid);
+ }
+ pinApp(key, ActivityManager.getCurrentUser(), false /* force */);
+ }
+
+ private void handleUidActive(int uid) {
+ updateActiveState(uid, true /* active */);
+ }
+
+ private void updateActiveState(int uid, boolean active) {
+ synchronized (this) {
+ for (int i = mPinnedApps.size() - 1; i >= 0; i--) {
+ PinnedApp app = mPinnedApps.valueAt(i);
+ if (app.uid == uid) {
+ app.active = active;
+ }
}
}
}
- private void unpinCameraApp() {
- ArrayList<PinnedFile> pinnedCameraFiles;
+ private void unpinApp(@AppKey int key) {
+ ArrayList<PinnedFile> pinnedAppFiles;
synchronized (this) {
- pinnedCameraFiles = new ArrayList<>(mPinnedCameraFiles);
- mPinnedCameraFiles.clear();
+ PinnedApp app = mPinnedApps.get(key);
+ if (app == null) {
+ return;
+ }
+ mPinnedApps.remove(key);
+ pinnedAppFiles = new ArrayList<>(app.mFiles);
}
- for (PinnedFile pinnedFile : pinnedCameraFiles) {
+ for (PinnedFile pinnedFile : pinnedAppFiles) {
pinnedFile.close();
}
}
@@ -206,64 +349,167 @@
// use INTENT_ACTION_STILL_IMAGE_CAMERA instead of _SECURE. On a
// device without a fbe enabled, the _SECURE intent will never get set.
Intent cameraIntent = new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA);
- PackageManager pm = mContext.getPackageManager();
- ResolveInfo cameraResolveInfo = pm.resolveActivityAsUser(cameraIntent,
- PackageManager.MATCH_DEFAULT_ONLY | PackageManager.MATCH_DIRECT_BOOT_AWARE
- | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
- userHandle);
- if (cameraResolveInfo == null ) {
- //this is not necessarily an error
- if (DEBUG) {
- Slog.v(TAG, "Unable to resolve camera intent");
- }
+ return getApplicationInfoForIntent(cameraIntent, userHandle);
+ }
+
+ private ApplicationInfo getHomeInfo(int userHandle) {
+ Intent intent = mAmInternal.getHomeIntent();
+ return getApplicationInfoForIntent(intent, userHandle);
+ }
+
+ private ApplicationInfo getApplicationInfoForIntent(Intent intent, int userHandle) {
+ if (intent == null) {
return null;
}
-
- if (isResolverActivity(cameraResolveInfo.activityInfo))
- {
- if (DEBUG) {
- Slog.v(TAG, "cameraIntent returned resolverActivity");
- }
+ ResolveInfo info = mContext.getPackageManager().resolveActivityAsUser(intent,
+ MATCH_FLAGS, userHandle);
+ if (info == null) {
return null;
}
+ if (isResolverActivity(info.activityInfo)) {
+ return null;
+ }
+ return info.activityInfo.applicationInfo;
+ }
- return cameraResolveInfo.activityInfo.applicationInfo;
+ private void sendPinAppsMessage(int userHandle) {
+ mPinnerHandler.sendMessage(PooledLambda.obtainMessage(PinnerService::pinApps, this,
+ userHandle));
+ }
+
+ private void pinApps(int userHandle) {
+ for (int i = mPinKeys.size() - 1; i >= 0; i--) {
+ int key = mPinKeys.valueAt(i);
+ pinApp(key, userHandle, true /* force */);
+ }
}
/**
- * If the camera app is already pinned, unpin and repin it.
+ * @see #pinApp(int, int, boolean)
*/
- private boolean pinCamera(int userHandle){
- ApplicationInfo cameraInfo = getCameraInfo(userHandle);
- if (cameraInfo == null) {
- return false;
+ private void sendPinAppMessage(int key, int userHandle, boolean force) {
+ mPinnerHandler.sendMessage(PooledLambda.obtainMessage(PinnerService::pinApp, this,
+ key, userHandle, force));
+ }
+
+ /**
+ * Pins an app of a specific type {@code key}.
+ *
+ * @param force If false, this will not repin the app if it's currently active. See
+ * {@link #mPendingRepin}.
+ */
+ private void pinApp(int key, int userHandle, boolean force) {
+ int uid = getUidForKey(key);
+
+ // In case the app is currently active, don't repin until next process restart. See
+ // mPendingRepin for more information.
+ if (!force && uid != -1) {
+ synchronized (this) {
+ mPendingRepin.put(uid, key);
+ }
+ return;
+ }
+ unpinApp(key);
+ ApplicationInfo info = getInfoForKey(key, userHandle);
+ if (info != null) {
+ pinApp(key, info);
+ }
+ }
+
+ /**
+ * Checks whether the pinned package with {@code key} is active or not.
+
+ * @return The uid of the pinned app, or {@code -1} otherwise.
+ */
+ private int getUidForKey(@AppKey int key) {
+ synchronized (this) {
+ PinnedApp existing = mPinnedApps.get(key);
+ return existing != null && existing.active
+ ? existing.uid
+ : -1;
+ }
+ }
+
+ /**
+ * Retrieves the current application info for the given app type.
+ *
+ * @param key The app type to retrieve the info for.
+ * @param userHandle The user id of the current user.
+ */
+ private @Nullable ApplicationInfo getInfoForKey(@AppKey int key, int userHandle) {
+ switch (key) {
+ case KEY_CAMERA:
+ return getCameraInfo(userHandle);
+ case KEY_HOME:
+ return getHomeInfo(userHandle);
+ default:
+ return null;
+ }
+ }
+
+ /**
+ * @return The app type name for {@code key}.
+ */
+ private String getNameForKey(@AppKey int key) {
+ switch (key) {
+ case KEY_CAMERA:
+ return "Camera";
+ case KEY_HOME:
+ return "Home";
+ default:
+ return null;
+ }
+ }
+
+ /**
+ * @return The maximum amount of bytes to be pinned for an app of type {@code key}.
+ */
+ private int getSizeLimitForKey(@AppKey int key) {
+ switch (key) {
+ case KEY_CAMERA:
+ return MAX_CAMERA_PIN_SIZE;
+ case KEY_HOME:
+ return MAX_HOME_PIN_SIZE;
+ default:
+ return 0;
+ }
+ }
+
+ /**
+ * Pins an application.
+ *
+ * @param key The key of the app to pin.
+ * @param appInfo The corresponding app info.
+ */
+ private void pinApp(@AppKey int key, @Nullable ApplicationInfo appInfo) {
+ if (appInfo == null) {
+ return;
}
- //unpin after checking that the camera intent has resolved
- //this prevents us from thrashing when switching users with
- //FBE enabled, because the intent won't resolve until the unlock
- unpinCameraApp();
+ PinnedApp pinnedApp = new PinnedApp(appInfo);
+ synchronized (this) {
+ mPinnedApps.put(key, pinnedApp);
+ }
- //pin APK
- String camAPK = cameraInfo.sourceDir;
- PinnedFile pf = pinFile(camAPK,
- MAX_CAMERA_PIN_SIZE,
- /*attemptPinIntrospection=*/true);
+ // pin APK
+ int pinSizeLimit = getSizeLimitForKey(key);
+ String apk = appInfo.sourceDir;
+ PinnedFile pf = pinFile(apk, pinSizeLimit, /*attemptPinIntrospection=*/true);
if (pf == null) {
- Slog.e(TAG, "Failed to pin " + camAPK);
- return false;
+ Slog.e(TAG, "Failed to pin " + apk);
+ return;
}
if (DEBUG) {
Slog.i(TAG, "Pinned " + pf.fileName);
}
synchronized (this) {
- mPinnedCameraFiles.add(pf);
+ pinnedApp.mFiles.add(pf);
}
// determine the ABI from either ApplicationInfo or Build
String arch = "arm";
- if (cameraInfo.primaryCpuAbi != null) {
- if (VMRuntime.is64BitAbi(cameraInfo.primaryCpuAbi)) {
+ if (appInfo.primaryCpuAbi != null) {
+ if (VMRuntime.is64BitAbi(appInfo.primaryCpuAbi)) {
arch = arch + "64";
}
} else {
@@ -273,32 +519,29 @@
}
// get the path to the odex or oat file
- String baseCodePath = cameraInfo.getBaseCodePath();
+ String baseCodePath = appInfo.getBaseCodePath();
String[] files = null;
try {
files = DexFile.getDexFileOutputPaths(baseCodePath, arch);
} catch (IOException ioe) {}
if (files == null) {
- return true;
+ return;
}
//not pinning the oat/odex is not a fatal error
for (String file : files) {
- pf = pinFile(file, MAX_CAMERA_PIN_SIZE, /*attemptPinIntrospection=*/false);
+ pf = pinFile(file, pinSizeLimit, /*attemptPinIntrospection=*/false);
if (pf != null) {
synchronized (this) {
- mPinnedCameraFiles.add(pf);
+ pinnedApp.mFiles.add(pf);
}
if (DEBUG) {
Slog.i(TAG, "Pinned " + pf.fileName);
}
}
}
-
- return true;
}
-
/** mlock length bytes of fileToPin in memory
*
* If attemptPinIntrospection is true, then treat the file to pin as a zip file and
@@ -581,24 +824,38 @@
}
}
- private synchronized ArrayList<PinnedFile> snapshotPinnedFiles() {
- int nrPinnedFiles = mPinnedFiles.size() + mPinnedCameraFiles.size();
- ArrayList<PinnedFile> pinnedFiles = new ArrayList<>(nrPinnedFiles);
- pinnedFiles.addAll(mPinnedFiles);
- pinnedFiles.addAll(mPinnedCameraFiles);
- return pinnedFiles;
- }
-
private final class BinderService extends Binder {
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
- long totalSize = 0;
- for (PinnedFile pinnedFile : snapshotPinnedFiles()) {
- pw.format("%s %s\n", pinnedFile.fileName, pinnedFile.bytesPinned);
- totalSize += pinnedFile.bytesPinned;
+ synchronized (PinnerService.this) {
+ long totalSize = 0;
+ for (PinnedFile pinnedFile : mPinnedFiles) {
+ pw.format("%s %s\n", pinnedFile.fileName, pinnedFile.bytesPinned);
+ totalSize += pinnedFile.bytesPinned;
+ }
+ pw.println();
+ for (int key : mPinnedApps.keySet()) {
+ PinnedApp app = mPinnedApps.get(key);
+ pw.print(getNameForKey(key));
+ pw.print(" uid="); pw.print(app.uid);
+ pw.print(" active="); pw.print(app.active);
+ pw.println();
+ for (PinnedFile pf : mPinnedApps.get(key).mFiles) {
+ pw.print(" "); pw.format("%s %s\n", pf.fileName, pf.bytesPinned);
+ totalSize += pf.bytesPinned;
+ }
+ }
+ pw.format("Total size: %s\n", totalSize);
+ pw.println();
+ if (!mPendingRepin.isEmpty()) {
+ pw.print("Pending repin: ");
+ for (int key : mPendingRepin.values()) {
+ pw.print(getNameForKey(key)); pw.print(' ');
+ }
+ pw.println();
+ }
}
- pw.format("Total size: %s\n", totalSize);
}
}
@@ -634,8 +891,30 @@
int length;
}
+ /**
+ * Represents an app that was pinned.
+ */
+ private final class PinnedApp {
+
+ /**
+ * The uid of the package being pinned. This stays constant while the package stays
+ * installed.
+ */
+ final int uid;
+
+ /** Whether it is currently active, i.e. there is a running process from that package. */
+ boolean active;
+
+ /** List of pinned files. */
+ final ArrayList<PinnedFile> mFiles = new ArrayList<>();
+
+ private PinnedApp(ApplicationInfo appInfo) {
+ uid = appInfo.uid;
+ active = mAmInternal.isUidActive(uid);
+ }
+ }
+
final class PinnerHandler extends Handler {
- static final int PIN_CAMERA_MSG = 4000;
static final int PIN_ONSTART_MSG = 4001;
public PinnerHandler(Looper looper) {
@@ -645,13 +924,6 @@
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
-
- case PIN_CAMERA_MSG:
- {
- handlePinCamera(msg.arg1);
- }
- break;
-
case PIN_ONSTART_MSG:
{
handlePinOnStart();
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index d025a87..92005d2 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -24,23 +24,18 @@
import static android.os.storage.OnObbStateChangeListener.ERROR_PERMISSION_DENIED;
import static android.os.storage.OnObbStateChangeListener.MOUNTED;
import static android.os.storage.OnObbStateChangeListener.UNMOUNTED;
-
import static com.android.internal.util.XmlUtils.readIntAttribute;
import static com.android.internal.util.XmlUtils.readLongAttribute;
import static com.android.internal.util.XmlUtils.readStringAttribute;
import static com.android.internal.util.XmlUtils.writeIntAttribute;
import static com.android.internal.util.XmlUtils.writeLongAttribute;
import static com.android.internal.util.XmlUtils.writeStringAttribute;
-
import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
import static org.xmlpull.v1.XmlPullParser.START_TAG;
import android.Manifest;
import android.annotation.Nullable;
import android.app.ActivityManager;
-import android.app.ActivityManagerInternal;
-import android.app.ActivityTaskManagerInternal;
-import android.app.ActivityTaskManagerInternal.ScreenObserver;
import android.app.AppOpsManager;
import android.app.IActivityManager;
import android.app.KeyguardManager;
@@ -126,9 +121,8 @@
import com.android.internal.widget.LockPatternUtils;
import com.android.server.pm.PackageManagerService;
import com.android.server.storage.AppFuseBridge;
-
-import libcore.io.IoUtils;
-import libcore.util.EmptyArray;
+import com.android.server.wm.ActivityTaskManagerInternal;
+import com.android.server.wm.ActivityTaskManagerInternal.ScreenObserver;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -164,6 +158,9 @@
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
+import libcore.io.IoUtils;
+import libcore.util.EmptyArray;
+
/**
* Service responsible for various storage media. Connects to {@code vold} to
* watch for and manage dynamically added storage, such as SD cards and USB mass
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 607db4e..ad9fa40 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -1588,10 +1588,10 @@
// Wakeup apps for the (SUBSCRIPTION_)PHONE_STATE broadcast.
intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+ // Create a version of the intent with the number always populated.
Intent intentWithPhoneNumber = new Intent(intent);
- if (!TextUtils.isEmpty(incomingNumber)) {
- intentWithPhoneNumber.putExtra(TelephonyManager.EXTRA_INCOMING_NUMBER, incomingNumber);
- }
+ intentWithPhoneNumber.putExtra(TelephonyManager.EXTRA_INCOMING_NUMBER, incomingNumber);
+
// Send broadcast twice, once for apps that have PRIVILEGED permission and once for those
// that have the runtime one
mContext.sendBroadcastAsUser(intentWithPhoneNumber, UserHandle.ALL,
diff --git a/services/core/java/com/android/server/TextServicesManagerService.java b/services/core/java/com/android/server/TextServicesManagerService.java
index 0941bc8..c043e18 100644
--- a/services/core/java/com/android/server/TextServicesManagerService.java
+++ b/services/core/java/com/android/server/TextServicesManagerService.java
@@ -63,6 +63,7 @@
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
+import java.lang.ref.WeakReference;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.HashMap;
@@ -1024,19 +1025,46 @@
private static final class ISpellCheckerServiceCallbackBinder
extends ISpellCheckerServiceCallback.Stub {
@NonNull
- private final SpellCheckerBindGroup mBindGroup;
- @NonNull
- private final SessionRequest mRequest;
+ private final Object mCallbackLock = new Object();
- ISpellCheckerServiceCallbackBinder(@NonNull final SpellCheckerBindGroup bindGroup,
- @NonNull final SessionRequest request) {
- mBindGroup = bindGroup;
- mRequest = request;
+ @GuardedBy("mCallbackLock")
+ @Nullable
+ private WeakReference<SpellCheckerBindGroup> mBindGroup;
+
+ /**
+ * Original {@link SessionRequest} that is associated with this callback.
+ *
+ * <p>Note that {@link SpellCheckerBindGroup#mOnGoingSessionRequests} guarantees that this
+ * {@link SessionRequest} object is kept alive until the request is canceled.</p>
+ */
+ @GuardedBy("mCallbackLock")
+ @Nullable
+ private WeakReference<SessionRequest> mRequest;
+
+ ISpellCheckerServiceCallbackBinder(@NonNull SpellCheckerBindGroup bindGroup,
+ @NonNull SessionRequest request) {
+ synchronized (mCallbackLock) {
+ mBindGroup = new WeakReference<>(bindGroup);
+ mRequest = new WeakReference<>(request);
+ }
}
@Override
public void onSessionCreated(@Nullable ISpellCheckerSession newSession) {
- mBindGroup.onSessionCreated(newSession, mRequest);
+ final SpellCheckerBindGroup group;
+ final SessionRequest request;
+ synchronized (mCallbackLock) {
+ if (mBindGroup == null || mRequest == null) {
+ return;
+ }
+ group = mBindGroup.get();
+ request = mRequest.get();
+ mBindGroup = null;
+ mRequest = null;
+ }
+ if (group != null && request != null) {
+ group.onSessionCreated(newSession, request);
+ }
}
}
}
diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java
index c29fc7f..cb03255 100644
--- a/services/core/java/com/android/server/UiModeManagerService.java
+++ b/services/core/java/com/android/server/UiModeManagerService.java
@@ -82,6 +82,7 @@
private boolean mCarModeEnabled = false;
private boolean mCharging = false;
+ private boolean mPowerSave = false;
private int mDefaultUiModeType;
private boolean mCarModeKeepsScreenOn;
private boolean mDeskModeKeepsScreenOn;
@@ -160,7 +161,14 @@
private final BroadcastReceiver mBatteryReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- mCharging = (intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0) != 0);
+ switch (intent.getAction()) {
+ case Intent.ACTION_BATTERY_CHANGED:
+ mCharging = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0) != 0;
+ break;
+ case PowerManager.ACTION_POWER_SAVE_MODE_CHANGING:
+ mPowerSave = intent.getBooleanExtra(PowerManager.EXTRA_POWER_SAVE_MODE, false);
+ break;
+ }
synchronized (mLock) {
if (mSystemReady) {
updateLocked(0, 0);
@@ -203,8 +211,9 @@
context.registerReceiver(mDockModeReceiver,
new IntentFilter(Intent.ACTION_DOCK_EVENT));
- context.registerReceiver(mBatteryReceiver,
- new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
+ IntentFilter batteryFilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
+ batteryFilter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGING);
+ context.registerReceiver(mBatteryReceiver, batteryFilter);
mConfiguration.setToDefaults();
@@ -457,6 +466,11 @@
uiMode |= mNightMode << 4;
}
+ if (mPowerSave && !mNightModeLocked) {
+ uiMode &= ~Configuration.UI_MODE_NIGHT_NO;
+ uiMode |= Configuration.UI_MODE_NIGHT_YES;
+ }
+
if (LOG) {
Slog.d(TAG,
"updateConfigurationLocked: mDockState=" + mDockState
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index 59093c1..e7a8221 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -21,6 +21,7 @@
import android.os.Build;
import android.os.RemoteException;
import android.system.ErrnoException;
+import android.system.Os;
import android.system.OsConstants;
import android.system.StructRlimit;
import com.android.internal.os.ZygoteConnectionConstants;
@@ -47,6 +48,10 @@
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -621,23 +626,38 @@
mFdHighWaterMark = fdThreshold;
}
+ /**
+ * Dumps open file descriptors and their full paths to a temporary file in {@code mDumpDir}.
+ */
private void dumpOpenDescriptors() {
+ // We cannot exec lsof to get more info about open file descriptors because a newly
+ // forked process will not have the permissions to readlink. Instead list all open
+ // descriptors from /proc/pid/fd and resolve them.
+ List<String> dumpInfo = new ArrayList<>();
+ String fdDirPath = String.format("/proc/%d/fd/", Process.myPid());
+ File[] fds = new File(fdDirPath).listFiles();
+ if (fds == null) {
+ dumpInfo.add("Unable to list " + fdDirPath);
+ } else {
+ for (File f : fds) {
+ String fdSymLink = f.getAbsolutePath();
+ String resolvedPath = "";
+ try {
+ resolvedPath = Os.readlink(fdSymLink);
+ } catch (ErrnoException ex) {
+ resolvedPath = ex.getMessage();
+ }
+ dumpInfo.add(fdSymLink + "\t" + resolvedPath);
+ }
+ }
+
+ // Dump the fds & paths to a temp file.
try {
File dumpFile = File.createTempFile("anr_fd_", "", mDumpDir);
- java.lang.Process proc = new ProcessBuilder()
- .command("/system/bin/lsof", "-p", String.valueOf(Process.myPid()))
- .redirectErrorStream(true)
- .redirectOutput(dumpFile)
- .start();
-
- int returnCode = proc.waitFor();
- if (returnCode != 0) {
- Slog.w(TAG, "Unable to dump open descriptors, lsof return code: "
- + returnCode);
- dumpFile.delete();
- }
- } catch (IOException | InterruptedException ex) {
- Slog.w(TAG, "Unable to dump open descriptors: " + ex);
+ Path out = Paths.get(dumpFile.getAbsolutePath());
+ Files.write(out, dumpInfo, StandardCharsets.UTF_8);
+ } catch (IOException ex) {
+ Slog.w(TAG, "Unable to write open descriptors to file: " + ex);
}
}
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index ca715b5..9c55de7 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -24,7 +24,6 @@
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
@@ -32,6 +31,7 @@
import java.util.Set;
import java.util.function.Predicate;
+import android.app.ActivityManagerInternal;
import android.app.ActivityThread;
import android.app.AppOpsManager;
import android.app.NotificationManager;
@@ -59,7 +59,6 @@
import com.android.internal.notification.SystemNotificationChannels;
import com.android.internal.os.BatteryStatsImpl;
import com.android.internal.os.TransferPipe;
-import com.android.internal.util.CollectionUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FastPrintWriter;
import com.android.server.AppStateTracker;
@@ -540,6 +539,11 @@
if (fgRequired) {
// We are now effectively running a foreground service.
+ ServiceState stracker = r.getTracker();
+ if (stracker != null) {
+ stracker.setForeground(true, mAm.mProcessStats.getMemFactorLocked(),
+ r.lastActivity);
+ }
mAm.mAppOpsService.startOperation(AppOpsManager.getToken(mAm.mAppOpsService),
AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName, true);
}
@@ -1191,13 +1195,14 @@
r.app.pid, r.appInfo.uid, "startForeground");
}
boolean alreadyStartedOp = false;
+ boolean stopProcStatsOp = false;
if (r.fgRequired) {
if (DEBUG_SERVICE || DEBUG_BACKGROUND_CHECK) {
Slog.i(TAG, "Service called startForeground() as required: " + r);
}
r.fgRequired = false;
r.fgWaiting = false;
- alreadyStartedOp = true;
+ alreadyStartedOp = stopProcStatsOp = true;
mAm.mHandler.removeMessages(
ActivityManagerService.SERVICE_FOREGROUND_TIMEOUT_MSG, r);
}
@@ -1265,6 +1270,15 @@
active.mNumActive++;
}
r.isForeground = true;
+ if (!stopProcStatsOp) {
+ ServiceState stracker = r.getTracker();
+ if (stracker != null) {
+ stracker.setForeground(true,
+ mAm.mProcessStats.getMemFactorLocked(), r.lastActivity);
+ }
+ } else {
+ stopProcStatsOp = false;
+ }
mAm.mAppOpsService.startOperation(
AppOpsManager.getToken(mAm.mAppOpsService),
AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName,
@@ -1286,6 +1300,15 @@
}
}
} finally {
+ if (stopProcStatsOp) {
+ // We got through to this point with it actively being started foreground,
+ // and never decided we wanted to keep it like that, so drop it.
+ ServiceState stracker = r.getTracker();
+ if (stracker != null) {
+ stracker.setForeground(false, mAm.mProcessStats.getMemFactorLocked(),
+ r.lastActivity);
+ }
+ }
if (alreadyStartedOp) {
// If we had previously done a start op for direct foreground start,
// we have cleared the flag so can now drop it.
@@ -1301,6 +1324,11 @@
decActiveForegroundAppLocked(smap, r);
}
r.isForeground = false;
+ ServiceState stracker = r.getTracker();
+ if (stracker != null) {
+ stracker.setForeground(false, mAm.mProcessStats.getMemFactorLocked(),
+ r.lastActivity);
+ }
mAm.mAppOpsService.finishOperation(
AppOpsManager.getToken(mAm.mAppOpsService),
AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName);
@@ -1390,7 +1418,7 @@
private boolean updateServiceClientActivitiesLocked(ProcessRecord proc,
ConnectionRecord modCr, boolean updateLru) {
if (modCr != null && modCr.binding.client != null) {
- if (modCr.binding.client.activities.size() <= 0) {
+ if (!modCr.binding.client.hasActivities()) {
// This connection is from a client without activities, so adding
// and removing is not interesting.
return false;
@@ -1408,7 +1436,7 @@
// Binding to ourself is not interesting.
continue;
}
- if (cr.binding.client.activities.size() > 0) {
+ if (cr.binding.client.hasActivities()) {
anyClientActivities = true;
break;
}
@@ -1599,7 +1627,7 @@
}
mAm.startAssociationLocked(callerApp.uid, callerApp.processName, callerApp.curProcState,
- s.appInfo.uid, s.name, s.processName);
+ s.appInfo.uid, s.appInfo.longVersionCode, s.name, s.processName);
// Once the apps have become associated, if one of them is caller is ephemeral
// the target app should now be able to see the calling app
mAm.grantEphemeralAccessLocked(callerApp.userId, service,
@@ -1607,7 +1635,8 @@
AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp);
ConnectionRecord c = new ConnectionRecord(b, activity,
- connection, flags, clientLabel, clientIntent);
+ connection, flags, clientLabel, clientIntent,
+ callerApp.uid, callerApp.processName);
IBinder binder = connection.asBinder();
ArrayList<ConnectionRecord> clist = s.connections.get(binder);
@@ -1624,6 +1653,7 @@
activity.connections.add(c);
}
b.client.connections.add(c);
+ c.startAssociationIfNeeded();
if ((c.flags&Context.BIND_ABOVE_CLIENT) != 0) {
b.client.hasAboveClient = true;
}
@@ -1869,7 +1899,7 @@
+ " type=" + resolvedType + " callingUid=" + callingUid);
userId = mAm.mUserController.handleIncomingUser(callingPid, callingUid, userId, false,
- ActivityManagerService.ALLOW_NON_FULL_IN_PROFILE, "service", null);
+ ActivityManagerInternal.ALLOW_NON_FULL_IN_PROFILE, "service", null);
ServiceMap smap = getServiceMapLocked(userId);
final ComponentName comp = service.getComponent();
@@ -2082,7 +2112,7 @@
bumpServiceExecutingLocked(r, execInFg, "bind");
r.app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);
r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind,
- r.app.repProcState);
+ r.app.getReportedProcState());
if (!rebind) {
i.requested = true;
}
@@ -2108,7 +2138,7 @@
private final boolean scheduleServiceRestartLocked(ServiceRecord r, boolean allowCancel) {
boolean canceled = false;
- if (mAm.isShuttingDownLocked()) {
+ if (mAm.mAtmInternal.isShuttingDown()) {
Slog.w(TAG, "Not scheduling restart of crashed service " + r.shortName
+ " - system is shutting down");
return false;
@@ -2438,7 +2468,7 @@
if (DEBUG_MU)
Slog.v(TAG_MU, "realStartServiceLocked, ServiceRecord.uid = " + r.appInfo.uid
+ ", ProcessRecord.uid = " + app.uid);
- r.app = app;
+ r.setProcess(app);
r.restartTime = r.lastActivity = SystemClock.uptimeMillis();
final boolean newService = app.services.add(r);
@@ -2464,7 +2494,7 @@
app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);
app.thread.scheduleCreateService(r, r.serviceInfo,
mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),
- app.repProcState);
+ app.getReportedProcState());
r.postNotification();
created = true;
} catch (DeadObjectException e) {
@@ -2480,7 +2510,7 @@
// Cleanup.
if (newService) {
app.services.remove(r);
- r.app = null;
+ r.setProcess(null);
}
// Retry.
@@ -2664,6 +2694,7 @@
// There is still a connection to the service that is
// being brought down. Mark it as dead.
cr.serviceDead = true;
+ cr.stopAssociation();
try {
cr.conn.connected(r.name, null, true);
} catch (Exception e) {
@@ -2704,6 +2735,11 @@
+ r);
r.fgRequired = false;
r.fgWaiting = false;
+ ServiceState stracker = r.getTracker();
+ if (stracker != null) {
+ stracker.setForeground(false, mAm.mProcessStats.getMemFactorLocked(),
+ r.lastActivity);
+ }
mAm.mAppOpsService.finishOperation(AppOpsManager.getToken(mAm.mAppOpsService),
AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName);
mAm.mHandler.removeMessages(
@@ -2756,6 +2792,11 @@
cancelForegroundNotificationLocked(r);
if (r.isForeground) {
decActiveForegroundAppLocked(smap, r);
+ ServiceState stracker = r.getTracker();
+ if (stracker != null) {
+ stracker.setForeground(false, mAm.mProcessStats.getMemFactorLocked(),
+ r.lastActivity);
+ }
mAm.mAppOpsService.finishOperation(
AppOpsManager.getToken(mAm.mAppOpsService),
AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName);
@@ -2836,6 +2877,7 @@
}
}
b.connections.remove(c);
+ c.stopAssociation();
if (c.activity != null && c.activity != skipAct) {
if (c.activity.connections != null) {
c.activity.connections.remove(c);
@@ -2866,7 +2908,8 @@
}
}
- mAm.stopAssociationLocked(b.client.uid, b.client.processName, s.appInfo.uid, s.name);
+ mAm.stopAssociationLocked(b.client.uid, b.client.processName, s.appInfo.uid,
+ s.appInfo.longVersionCode, s.name, s.processName);
if (b.connections.size() == 0) {
b.intent.apps.remove(b.client);
@@ -3051,13 +3094,13 @@
}
}
if (finishing) {
- if (r.app != null && !r.app.persistent) {
+ if (r.app != null && !r.app.isPersistent()) {
r.app.services.remove(r);
if (r.whitelistManager) {
updateWhitelistManagerLocked(r.app);
}
}
- r.app = null;
+ r.setProcess(null);
}
}
}
@@ -3141,7 +3184,7 @@
&& (filterByClasses == null
|| filterByClasses.contains(service.name.getClassName())));
if (sameComponent
- && (service.app == null || evenPersistent || !service.app.persistent)) {
+ && (service.app == null || evenPersistent || !service.app.isPersistent())) {
if (!doit) {
return true;
}
@@ -3149,14 +3192,14 @@
Slog.i(TAG, " Force stopping service " + service);
if (service.app != null) {
service.app.removed = killProcess;
- if (!service.app.persistent) {
+ if (!service.app.isPersistent()) {
service.app.services.remove(service);
if (service.whitelistManager) {
updateWhitelistManagerLocked(service.app);
}
}
}
- service.app = null;
+ service.setProcess(null);
service.isolatedProc = null;
if (mTmpCollectionResults == null) {
mTmpCollectionResults = new ArrayList<>();
@@ -3303,10 +3346,10 @@
synchronized (sr.stats.getBatteryStats()) {
sr.stats.stopLaunchedLocked();
}
- if (sr.app != app && sr.app != null && !sr.app.persistent) {
+ if (sr.app != app && sr.app != null && !sr.app.isPersistent()) {
sr.app.services.remove(sr);
}
- sr.app = null;
+ sr.setProcess(null);
sr.isolatedProc = null;
sr.executeNesting = 0;
sr.forceClearTracker();
@@ -3348,7 +3391,7 @@
continue;
}
// XXX turned off for now until we have more time to get a better policy.
- if (false && proc != null && !proc.persistent && proc.thread != null
+ if (false && proc != null && !proc.isPersistent() && proc.thread != null
&& proc.pid != 0 && proc.pid != ActivityManagerService.MY_PID
&& proc.setProcState >= ActivityManager.PROCESS_STATE_LAST_ACTIVITY) {
proc.kill("bound to service " + sr.name.flattenToShortString()
@@ -3366,7 +3409,7 @@
// Unless the process is persistent, this process record is going away,
// so make sure the service is cleaned out of it.
- if (!app.persistent) {
+ if (!app.isPersistent()) {
app.services.removeAt(i);
}
@@ -3476,7 +3519,7 @@
if (r.app != null && r.app.pid == ActivityManagerService.MY_PID) {
info.flags |= ActivityManager.RunningServiceInfo.FLAG_SYSTEM_PROCESS;
}
- if (r.app != null && r.app.persistent) {
+ if (r.app != null && r.app.isPersistent()) {
info.flags |= ActivityManager.RunningServiceInfo.FLAG_PERSISTENT_PROCESS;
}
diff --git a/services/core/java/com/android/server/am/ActivityDisplay.java b/services/core/java/com/android/server/am/ActivityDisplay.java
index ee93fc8..552ed16 100644
--- a/services/core/java/com/android/server/am/ActivityDisplay.java
+++ b/services/core/java/com/android/server/am/ActivityDisplay.java
@@ -30,28 +30,29 @@
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.FLAG_PRIVATE;
import static android.view.Display.REMOVE_MODE_DESTROY_CONTENT;
+import static com.android.server.am.ActivityDisplayProto.CONFIGURATION_CONTAINER;
+import static com.android.server.am.ActivityDisplayProto.ID;
+import static com.android.server.am.ActivityDisplayProto.STACKS;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_STACK;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_STACK;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.am.ActivityDisplayProto.CONFIGURATION_CONTAINER;
-import static com.android.server.am.ActivityDisplayProto.STACKS;
-import static com.android.server.am.ActivityDisplayProto.ID;
import android.annotation.Nullable;
import android.app.ActivityOptions;
-import android.app.ActivityTaskManagerInternal;
import android.app.WindowConfiguration;
import android.graphics.Point;
import android.util.IntArray;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
import android.view.Display;
+
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.ConfigurationContainer;
import com.android.server.wm.DisplayWindowController;
-
import com.android.server.wm.WindowContainerListener;
+
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -140,7 +141,7 @@
+ " to displayId=" + mDisplayId + " position=" + position);
addStackReferenceIfNeeded(stack);
positionChildAt(stack, position);
- mSupervisor.mService.mAm.updateSleepIfNeededLocked();
+ mSupervisor.mService.updateSleepIfNeededLocked();
}
void removeChild(ActivityStack stack) {
@@ -148,7 +149,7 @@
+ " from displayId=" + mDisplayId);
mStacks.remove(stack);
removeStackReferenceIfNeeded(stack);
- mSupervisor.mService.mAm.updateSleepIfNeededLocked();
+ mSupervisor.mService.updateSleepIfNeededLocked();
onStackOrderChanged();
}
@@ -166,8 +167,14 @@
mStacks.remove(stack);
final int insertPosition = getTopInsertPosition(stack, position);
mStacks.add(insertPosition, stack);
- mWindowContainerController.positionChildAt(stack.getWindowContainerController(),
- insertPosition);
+ // Since positionChildAt() is called during the creation process of pinned stacks,
+ // ActivityStack#getWindowContainerController() can be null. In this special case,
+ // since DisplayContest#positionStackAt() is called in TaskStack#onConfigurationChanged(),
+ // we don't have to call WindowContainerController#positionChildAt() here.
+ if (stack.getWindowContainerController() != null) {
+ mWindowContainerController.positionChildAt(stack.getWindowContainerController(),
+ insertPosition);
+ }
onStackOrderChanged();
}
@@ -300,7 +307,7 @@
}
}
- final ActivityManagerService service = mSupervisor.mService.mAm;
+ final ActivityTaskManagerService service = mSupervisor.mService;
if (!isWindowingModeSupported(windowingMode, service.mSupportsMultiWindow,
service.mSupportsSplitScreenMultiWindow, service.mSupportsFreeformWindowManagement,
service.mSupportsPictureInPicture, activityType)) {
@@ -332,6 +339,38 @@
this, stackId, mSupervisor, windowingMode, activityType, onTop);
}
+ ActivityStack getFocusedStack() {
+ for (int i = mStacks.size() - 1; i >= 0; --i) {
+ final ActivityStack stack = mStacks.get(i);
+ if (stack.isFocusable() && stack.shouldBeVisible(null /* starting */)) {
+ return stack;
+ }
+ }
+
+ return null;
+ }
+
+ ActivityRecord getResumedActivity() {
+ final ActivityStack focusedStack = getFocusedStack();
+ if (focusedStack == null) {
+ return null;
+ }
+ // TODO(b/111541062): Move this into ActivityStack#getResumedActivity()
+ // Check if the focused stack has the resumed activity
+ ActivityRecord resumedActivity = focusedStack.getResumedActivity();
+ if (resumedActivity == null || resumedActivity.app == null) {
+ // If there is no registered resumed activity in the stack or it is not running -
+ // try to use previously resumed one.
+ resumedActivity = focusedStack.mPausingActivity;
+ if (resumedActivity == null || resumedActivity.app == null) {
+ // If previously resumed activity doesn't work either - find the topmost running
+ // activity that can be focused.
+ resumedActivity = focusedStack.topRunningActivityLocked(true /* focusableOnly */);
+ }
+ }
+ return resumedActivity;
+ }
+
/**
* Removes stacks in the input windowing modes from the system if they are of activity type
* ACTIVITY_TYPE_STANDARD or ACTIVITY_TYPE_UNDEFINED
@@ -536,7 +575,7 @@
}
// Make sure the windowing mode we are trying to use makes sense for what is supported.
- final ActivityManagerService service = mSupervisor.mService.mAm;
+ final ActivityTaskManagerService service = mSupervisor.mService;
boolean supportsMultiWindow = service.mSupportsMultiWindow;
boolean supportsSplitScreen = service.mSupportsSplitScreenMultiWindow;
boolean supportsFreeform = service.mSupportsFreeformWindowManagement;
@@ -578,7 +617,7 @@
/**
* Get the topmost stack on the display. It may be different from focused stack, because
- * focus may be on another display.
+ * some stacks are not focusable (e.g. PiP).
*/
ActivityStack getTopStack() {
return mStacks.isEmpty() ? null : mStacks.get(mStacks.size() - 1);
@@ -714,7 +753,7 @@
boolean shouldSleep() {
return (mStacks.isEmpty() || !mAllSleepTokens.isEmpty())
- && (mSupervisor.mService.mAm.mRunningVoice == null);
+ && (mSupervisor.mService.mRunningVoice == null);
}
/**
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index ac4f6ab..e1ebbec 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -22,19 +22,16 @@
import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
import static android.Manifest.permission.MANAGE_ACTIVITY_STACKS;
import static android.Manifest.permission.REMOVE_TASKS;
-import static android.Manifest.permission.STOP_APP_SWITCHES;
+import static android.app.ActivityManager.PROCESS_STATE_CACHED_ACTIVITY;
+import static android.app.ActivityManager.PROCESS_STATE_LAST_ACTIVITY;
+import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY;
+import static android.app.ActivityManagerInternal.ALLOW_NON_FULL;
import static android.app.ActivityThread.PROC_START_SEQ_IDENT;
import static android.app.AppOpsManager.OP_NONE;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME;
import static android.content.pm.ApplicationInfo.HIDDEN_API_ENFORCEMENT_DEFAULT;
-import static android.content.pm.PackageManager.FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS;
-import static android.content.pm.PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT;
-import static android.content.pm.PackageManager.FEATURE_LEANBACK_ONLY;
-import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
import static android.content.pm.PackageManager.GET_PROVIDERS;
import static android.content.pm.PackageManager.MATCH_ANY_USER;
import static android.content.pm.PackageManager.MATCH_DEBUG_TRIAGED_MISSING;
@@ -43,10 +40,8 @@
import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-import static android.content.res.Configuration.UI_MODE_TYPE_TELEVISION;
import static android.net.NetworkPolicyManager.isProcStateAllowedWhileIdleOrPowerSaveMode;
import static android.net.NetworkPolicyManager.isProcStateAllowedWhileOnRestrictBackground;
-import static android.os.Build.VERSION_CODES.N;
import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_CRITICAL;
import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_HIGH;
import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL;
@@ -68,7 +63,6 @@
import static android.os.Process.SCHED_RESET_ON_FORK;
import static android.os.Process.SE_UID;
import static android.os.Process.SHELL_UID;
-import static android.os.Process.SIGNAL_QUIT;
import static android.os.Process.SIGNAL_USR1;
import static android.os.Process.SYSTEM_UID;
import static android.os.Process.THREAD_GROUP_BG_NONINTERACTIVE;
@@ -92,19 +86,12 @@
import static android.os.Process.setThreadScheduler;
import static android.os.Process.startWebView;
import static android.os.Process.zygoteProcess;
-import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
import static android.provider.Settings.Global.ALWAYS_FINISH_ACTIVITIES;
import static android.provider.Settings.Global.DEBUG_APP;
-import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT;
-import static android.provider.Settings.Global.DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES;
-import static android.provider.Settings.Global.DEVELOPMENT_FORCE_RTL;
-import static android.provider.Settings.Global.HIDE_ERROR_DIALOGS;
import static android.provider.Settings.Global.NETWORK_ACCESS_TIMEOUT_MS;
import static android.provider.Settings.Global.WAIT_FOR_DEBUGGER;
-import static android.provider.Settings.System.FONT_SCALE;
import static android.text.format.DateUtils.DAY_IN_MILLIS;
import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.Display.INVALID_DISPLAY;
import static com.android.internal.util.XmlUtils.readBooleanAttribute;
import static com.android.internal.util.XmlUtils.readIntAttribute;
import static com.android.internal.util.XmlUtils.readLongAttribute;
@@ -120,7 +107,6 @@
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST_LIGHT;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_CLEANUP;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_CONFIGURATION;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_IMMERSIVE;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LOCKTASK;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LRU;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_MU;
@@ -134,9 +120,7 @@
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROVIDER;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PSS;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SERVICE;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_STACK;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SWITCH;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_TASKS;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_UID_OBSERVERS;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_URI_PERMISSION;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_USAGE_STATS;
@@ -145,8 +129,6 @@
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_BROADCAST;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_CLEANUP;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_CONFIGURATION;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_FOCUS;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_IMMERSIVE;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_LOCKTASK;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_LRU;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_MU;
@@ -157,22 +139,15 @@
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_PROCESS_OBSERVERS;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_PROVIDER;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_PSS;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_RECENTS;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_SERVICE;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_STACK;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_SWITCH;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_UID_OBSERVERS;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_URI_PERMISSION;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_VISIBILITY;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
-import static com.android.server.am.MemoryStatUtil.readMemoryStatFromFilesystem;
import static com.android.server.am.MemoryStatUtil.hasMemcg;
-import static android.view.WindowManager.TRANSIT_ACTIVITY_OPEN;
-import static android.view.WindowManager.TRANSIT_NONE;
-import static android.view.WindowManager.TRANSIT_TASK_OPEN;
-import static android.view.WindowManager.TRANSIT_TASK_TO_FRONT;
+import static com.android.server.am.MemoryStatUtil.readMemoryStatFromFilesystem;
import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
import static org.xmlpull.v1.XmlPullParser.START_TAG;
@@ -185,17 +160,13 @@
import android.app.ActivityManager;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.ActivityManager.StackInfo;
-import android.app.ActivityManager.TaskSnapshot;
import android.app.ActivityManagerInternal;
-import android.app.ActivityTaskManagerInternal.ScreenObserver;
-import android.app.ActivityTaskManagerInternal.SleepToken;
import android.app.ActivityManagerProto;
import android.app.ActivityOptions;
-import android.app.ActivityTaskManager;
import android.app.ActivityThread;
-import android.app.AlertDialog;
import android.app.AppGlobals;
import android.app.AppOpsManager;
+import android.app.AppOpsManagerInternal.CheckOpsDelegate;
import android.app.ApplicationErrorReport;
import android.app.ApplicationThreadConstants;
import android.app.BroadcastOptions;
@@ -224,7 +195,6 @@
import android.app.WindowConfiguration.ActivityType;
import android.app.WindowConfiguration.WindowingMode;
import android.app.backup.IBackupManager;
-import android.app.servertransaction.ConfigurationChangeItem;
import android.app.usage.UsageEvents;
import android.app.usage.UsageStatsManagerInternal;
import android.appwidget.AppWidgetManager;
@@ -235,7 +205,6 @@
import android.content.ContentProvider;
import android.content.ContentResolver;
import android.content.Context;
-import android.content.DialogInterface;
import android.content.IContentProvider;
import android.content.IIntentReceiver;
import android.content.IIntentSender;
@@ -252,6 +221,7 @@
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageManagerInternal;
+import android.content.pm.PackageManagerInternal.CheckPermissionDelegate;
import android.content.pm.ParceledListSlice;
import android.content.pm.PathPermission;
import android.content.pm.PermissionInfo;
@@ -273,13 +243,13 @@
import android.net.Uri;
import android.os.BatteryStats;
import android.os.Binder;
+import android.os.BinderProxy;
import android.os.Build;
import android.os.Bundle;
import android.os.Debug;
import android.os.DropBoxManager;
import android.os.Environment;
import android.os.FactoryTest;
-import android.os.FileObserver;
import android.os.FileUtils;
import android.os.Handler;
import android.os.IBinder;
@@ -287,12 +257,10 @@
import android.os.IPermissionController;
import android.os.IProcessInfoService;
import android.os.IProgressListener;
-import android.os.LocaleList;
import android.os.Looper;
import android.os.Message;
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
-import android.os.PowerManager;
import android.os.PowerManager.ServiceType;
import android.os.PowerManagerInternal;
import android.os.Process;
@@ -307,7 +275,6 @@
import android.os.SystemProperties;
import android.os.Trace;
import android.os.TransactionTooLargeException;
-import android.os.UpdateLock;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.WorkSource;
@@ -316,10 +283,8 @@
import android.os.storage.StorageManagerInternal;
import android.provider.Downloads;
import android.provider.Settings;
-import android.service.voice.IVoiceInteractionSession;
import android.text.TextUtils;
import android.text.format.DateUtils;
-import android.text.format.Time;
import android.text.style.SuggestionSpan;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -346,13 +311,15 @@
import android.view.WindowManager;
import android.view.autofill.AutofillManagerInternal;
+import com.google.android.collect.Lists;
+import com.google.android.collect.Maps;
+
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.DumpHeapActivity;
import com.android.internal.app.IAppOpsCallback;
import com.android.internal.app.IAppOpsService;
-import com.android.internal.app.IVoiceInteractor;
import com.android.internal.app.ProcessMap;
import com.android.internal.app.SystemUserHomeActivity;
import com.android.internal.app.procstats.ProcessStats;
@@ -373,6 +340,8 @@
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.MemInfoReader;
import com.android.internal.util.Preconditions;
+import com.android.internal.util.function.QuadFunction;
+import com.android.internal.util.function.TriFunction;
import com.android.server.AlarmManagerInternal;
import com.android.server.AppOpsService;
import com.android.server.AttributeCache;
@@ -390,21 +359,9 @@
import com.android.server.SystemServiceManager;
import com.android.server.ThreadPriorityBooster;
import com.android.server.Watchdog;
+import com.android.server.am.ActivityManagerServiceDumpProcessesProto.UidObserverRegistrationProto;
import com.android.server.am.ActivityStack.ActivityState;
import com.android.server.am.MemoryStatUtil.MemoryStat;
-import com.android.server.am.ActivityManagerServiceProto;
-import com.android.server.am.ActivityManagerServiceDumpActivitiesProto;
-import com.android.server.am.ActivityManagerServiceDumpBroadcastsProto;
-import com.android.server.am.ActivityManagerServiceDumpProcessesProto;
-import com.android.server.am.ActivityManagerServiceDumpProcessesProto.UidObserverRegistrationProto;
-import com.android.server.am.ActivityManagerServiceDumpServicesProto;
-import com.android.server.am.GrantUriProto;
-import com.android.server.am.ImportanceTokenProto;
-import com.android.server.am.MemInfoDumpProto;
-import com.android.server.am.NeededUriGrantsProto;
-import com.android.server.am.ProcessOomProto;
-import com.android.server.am.ProcessToGcProto;
-import com.android.server.am.StickyBroadcastProto;
import com.android.server.firewall.IntentFirewall;
import com.android.server.job.JobSchedulerInternal;
import com.android.server.pm.Installer;
@@ -412,25 +369,20 @@
import com.android.server.pm.dex.DexManager;
import com.android.server.utils.PriorityDump;
import com.android.server.vr.VrManagerInternal;
+import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.WindowManagerService;
-import dalvik.system.VMRuntime;
-
-import libcore.io.IoUtils;
-import libcore.util.EmptyArray;
-
-import com.google.android.collect.Lists;
-import com.google.android.collect.Maps;
-
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
+import java.io.BufferedReader;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
+import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
@@ -457,6 +409,11 @@
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
+import java.util.function.BiFunction;
+
+import dalvik.system.VMRuntime;
+import libcore.io.IoUtils;
+import libcore.util.EmptyArray;
public class ActivityManagerService extends IActivityManager.Stub
implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
@@ -471,8 +428,6 @@
private static final String TAG_BROADCAST = TAG + POSTFIX_BROADCAST;
private static final String TAG_CLEANUP = TAG + POSTFIX_CLEANUP;
private static final String TAG_CONFIGURATION = TAG + POSTFIX_CONFIGURATION;
- private static final String TAG_FOCUS = TAG + POSTFIX_FOCUS;
- private static final String TAG_IMMERSIVE = TAG + POSTFIX_IMMERSIVE;
private static final String TAG_LOCKTASK = TAG + POSTFIX_LOCKTASK;
private static final String TAG_LRU = TAG + POSTFIX_LRU;
private static final String TAG_MU = TAG + POSTFIX_MU;
@@ -483,13 +438,10 @@
private static final String TAG_PROCESSES = TAG + POSTFIX_PROCESSES;
private static final String TAG_PROVIDER = TAG + POSTFIX_PROVIDER;
private static final String TAG_PSS = TAG + POSTFIX_PSS;
- private static final String TAG_RECENTS = TAG + POSTFIX_RECENTS;
private static final String TAG_SERVICE = TAG + POSTFIX_SERVICE;
- private static final String TAG_STACK = TAG + POSTFIX_STACK;
private static final String TAG_SWITCH = TAG + POSTFIX_SWITCH;
private static final String TAG_UID_OBSERVERS = TAG + POSTFIX_UID_OBSERVERS;
private static final String TAG_URI_PERMISSION = TAG + POSTFIX_URI_PERMISSION;
- private static final String TAG_VISIBILITY = TAG + POSTFIX_VISIBILITY;
// Mock "pretend we're idle now" broadcast action to the job scheduler; declared
// here so that while the job scheduler can depend on AMS, the other way around
@@ -511,13 +463,11 @@
static final String SYSTEM_DEBUGGABLE = "ro.debuggable";
+ private static final String ANR_TRACE_DIR = "/data/anr";
+
// Maximum number of receivers an app can register.
private static final int MAX_RECEIVERS_ALLOWED_PER_APP = 1000;
- // Amount of time after a call to stopAppSwitches() during which we will
- // prevent further untrusted switches from happening.
- static final long APP_SWITCH_DELAY_TIME = 5*1000;
-
// How long we wait for a launched process to attach to the activity manager
// before we decide it's never going to come up for real.
static final int PROC_START_TIMEOUT = 10*1000;
@@ -561,11 +511,6 @@
/** If a UID observer takes more than this long, send a WTF. */
private static final int SLOW_UID_OBSERVER_THRESHOLD_MS = 20;
- // Access modes for handleIncomingUser.
- static final int ALLOW_NON_FULL = 0;
- static final int ALLOW_NON_FULL_IN_PROFILE = 1;
- static final int ALLOW_FULL_ONLY = 2;
-
// Necessary ApplicationInfo flags to mark an app as persistent
private static final int PERSISTENT_MASK =
ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PERSISTENT;
@@ -577,8 +522,8 @@
// Used to indicate that an app transition should be animated.
static final boolean ANIMATE = true;
- // Determines whether to take full screen screenshots
- static final boolean TAKE_FULLSCREEN_SCREENSHOTS = true;
+ // If set, we will push process association information in to procstats.
+ static final boolean TRACK_PROCSTATS_ASSOCIATIONS = true;
/**
* Default value for {@link Settings.Global#NETWORK_ACCESS_TIMEOUT_MS}.
@@ -623,19 +568,11 @@
public final IntentFirewall mIntentFirewall;
- // Whether we should show our dialogs (ANR, crash, etc) or just perform their
- // default action automatically. Important for devices without direct input
- // devices.
- private boolean mShowDialogs = true;
-
- // VR Vr2d Display Id.
- int mVr2dDisplayId = INVALID_DISPLAY;
+ public OomAdjProfiler mOomAdjProfiler = new OomAdjProfiler();
// Whether we should use SCHED_FIFO for UI and RenderThreads.
private boolean mUseFifoUiScheduling = false;
- private static final String SYSUI_COMPONENT_NAME = "com.android.systemui/.SystemUIService";
-
BroadcastQueue mFgBroadcastQueue;
BroadcastQueue mBgBroadcastQueue;
// Convenient for easy iteration over the queues. Foreground is first
@@ -654,37 +591,12 @@
}
/**
- * The last resumed activity. This is identical to the current resumed activity most
- * of the time but could be different when we're pausing one activity before we resume
- * another activity.
- */
- ActivityRecord mLastResumedActivity;
-
- /**
- * The activity that is currently being traced as the active resumed activity.
- *
- * @see #updateResumedAppTrace
- */
- private @Nullable ActivityRecord mTracedResumedActivity;
-
- /**
- * If non-null, we are tracking the time the user spends in the currently focused app.
- */
- private AppTimeTracker mCurAppTimeTracker;
-
- /**
* The package name of the DeviceOwner. This package is not permitted to have its data cleared.
*/
String mDeviceOwnerName;
final UserController mUserController;
- /**
- * Packages that are being allowed to perform unrestricted app switches. Mapping is
- * User -> Type -> uid.
- */
- final SparseArray<ArrayMap<String, Integer>> mAllowAppSwitchUids = new SparseArray<>();
-
final AppErrors mAppErrors;
final AppWarnings mAppWarnings;
@@ -717,7 +629,7 @@
boolean asProto) {
if (asProto) return;
doDump(fd, pw, new String[]{"activities"}, asProto);
- doDump(fd, pw, new String[]{"service", SYSUI_COMPONENT_NAME}, asProto);
+ doDump(fd, pw, new String[]{"service", "all-platform-critical"}, asProto);
}
@Override
@@ -731,15 +643,6 @@
}
};
- public boolean canShowErrorDialogs() {
- return mShowDialogs && !mSleeping && !mShuttingDown
- && !mActivityTaskManager.mKeyguardController.isKeyguardOrAodShowing(DEFAULT_DISPLAY)
- && !mUserController.hasUserRestriction(UserManager.DISALLOW_SYSTEM_ERROR_DIALOGS,
- mUserController.getCurrentUserId())
- && !(UserManager.isDeviceInDemoMode(mContext)
- && mUserController.getCurrentUser().isDemo());
- }
-
private static ThreadPriorityBooster sThreadPriorityBooster = new ThreadPriorityBooster(
THREAD_PRIORITY_FOREGROUND, LockGuard.INDEX_ACTIVITY);
@@ -762,7 +665,22 @@
* returned by the package manager), and the keys are ApplicationRecord
* objects.
*/
- final ProcessMap<ProcessRecord> mProcessNames = new ProcessMap<ProcessRecord>();
+ final MyProcessMap mProcessNames = new MyProcessMap();
+ final class MyProcessMap extends ProcessMap<ProcessRecord> {
+ @Override
+ public ProcessRecord put(String name, int uid, ProcessRecord value) {
+ final ProcessRecord r = super.put(name, uid, value);
+ mAtmInternal.onProcessAdded(r.getWindowProcessController());
+ return r;
+ }
+
+ @Override
+ public ProcessRecord remove(String name, int uid) {
+ final ProcessRecord r = super.remove(name, uid);
+ mAtmInternal.onProcessRemoved(name, uid);
+ return r;
+ }
+ }
/**
* Tracking long-term execution of processes to look for abuse and other
@@ -773,7 +691,7 @@
/**
* The currently running isolated processes.
*/
- final SparseArray<ProcessRecord> mIsolatedProcesses = new SparseArray<ProcessRecord>();
+ final SparseArray<ProcessRecord> mIsolatedProcesses = new SparseArray<>();
/**
* Counter for assigning isolated process uids, to avoid frequently reusing the
@@ -900,23 +818,6 @@
boolean mFullPssPending = false;
/**
- * This is the process holding what we currently consider to be
- * the "home" activity.
- */
- ProcessRecord mHomeProcess;
-
- /**
- * This is the process holding the activity the user last visited that
- * is in a different process from the one they are currently in.
- */
- ProcessRecord mPreviousProcess;
-
- /**
- * The time at which the previous process was last visible.
- */
- long mPreviousProcessVisibleTime;
-
- /**
* Track all uids that have actively running processes.
*/
final SparseArray<UidRecord> mActiveUids = new SparseArray<>();
@@ -1149,32 +1050,6 @@
CoreSettingsObserver mCoreSettingsObserver;
- FontScaleSettingObserver mFontScaleSettingObserver;
-
- private final class FontScaleSettingObserver extends ContentObserver {
- private final Uri mFontScaleUri = Settings.System.getUriFor(FONT_SCALE);
- private final Uri mHideErrorDialogsUri = Settings.Global.getUriFor(HIDE_ERROR_DIALOGS);
-
- public FontScaleSettingObserver() {
- super(mHandler);
- ContentResolver resolver = mContext.getContentResolver();
- resolver.registerContentObserver(mFontScaleUri, false, this, UserHandle.USER_ALL);
- resolver.registerContentObserver(mHideErrorDialogsUri, false, this,
- UserHandle.USER_ALL);
- }
-
- @Override
- public void onChange(boolean selfChange, Uri uri, @UserIdInt int userId) {
- if (mFontScaleUri.equals(uri)) {
- updateFontScaleIfNeeded(userId);
- } else if (mHideErrorDialogsUri.equals(uri)) {
- synchronized (ActivityManagerService.this) {
- updateShouldShowDialogsLocked(getGlobalConfiguration());
- }
- }
- }
- }
-
DevelopmentSettingsObserver mDevelopmentSettingsObserver;
private final class DevelopmentSettingsObserver extends ContentObserver {
@@ -1329,72 +1204,21 @@
final Context mUiContext;
/**
- * The time at which we will allow normal application switches again,
- * after a call to {@link #stopAppSwitches()}.
- */
- long mAppSwitchesAllowedTime;
-
- /**
- * This is set to true after the first switch after mAppSwitchesAllowedTime
- * is set; any switches after that will clear the time.
- */
- boolean mDidAppSwitch;
-
- /**
* Last time (in uptime) at which we checked for power usage.
*/
long mLastPowerCheckUptime;
/**
- * Set while we are wanting to sleep, to prevent any
- * activities from being started/resumed.
- *
- * TODO(b/33594039): Clarify the actual state transitions represented by mSleeping.
- *
- * Currently mSleeping is set to true when transitioning into the sleep state, and remains true
- * while in the sleep state until there is a pending transition out of sleep, in which case
- * mSleeping is set to false, and remains false while awake.
- *
- * Whether mSleeping can quickly toggled between true/false without the device actually
- * display changing states is undefined.
- */
- private boolean mSleeping = false;
-
- /**
- * The process state used for processes that are running the top activities.
- * This changes between TOP and TOP_SLEEPING to following mSleeping.
- */
- int mTopProcessState = ActivityManager.PROCESS_STATE_TOP;
-
- /**
- * Set while we are running a voice interaction. This overrides
- * sleeping while it is active.
- */
- IVoiceInteractionSession mRunningVoice;
-
- /**
* For some direct access we need to power manager.
*/
PowerManagerInternal mLocalPowerManager;
/**
- * We want to hold a wake lock while running a voice interaction session, since
- * this may happen with the screen off and we need to keep the CPU running to
- * be able to continue to interact with the user.
- */
- PowerManager.WakeLock mVoiceWakeLock;
-
- /**
* State of external calls telling us if the device is awake or asleep.
*/
private int mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
/**
- * Set if we are shutting down the system, similar to sleeping.
- */
- boolean mShuttingDown = false;
-
- /**
* Current sequence id for oom_adj computation traversal.
*/
int mAdjSeq = 0;
@@ -1491,29 +1315,7 @@
String mOrigDebugApp = null;
boolean mOrigWaitForDebugger = false;
boolean mAlwaysFinishActivities = false;
- boolean mForceResizableActivities;
- /**
- * Flag that indicates if multi-window is enabled.
- *
- * For any particular form of multi-window to be enabled, generic multi-window must be enabled
- * in {@link com.android.internal.R.bool#config_supportsMultiWindow} config or
- * {@link Settings.Global#DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES} development option set.
- * At least one of the forms of multi-window must be enabled in order for this flag to be
- * initialized to 'true'.
- *
- * @see #mSupportsSplitScreenMultiWindow
- * @see #mSupportsFreeformWindowManagement
- * @see #mSupportsPictureInPicture
- * @see #mSupportsMultiDisplay
- */
- boolean mSupportsMultiWindow;
- boolean mSupportsSplitScreenMultiWindow;
- boolean mSupportsFreeformWindowManagement;
- boolean mSupportsPictureInPicture;
- boolean mSupportsMultiDisplay;
- boolean mSupportsLeanbackOnly;
- IActivityController mController = null;
- boolean mControllerIsAMonkey = false;
+
String mProfileApp = null;
ProcessRecord mProfileProc = null;
ProfilerInfo mProfilerInfo = null;
@@ -1637,8 +1439,6 @@
}
}
- final List<ScreenObserver> mScreenObservers = new ArrayList<>();
-
final RemoteCallbackList<IProcessObserver> mProcessObservers = new RemoteCallbackList<>();
ProcessChangeItem[] mActiveProcessChanges = new ProcessChangeItem[5];
@@ -1680,12 +1480,6 @@
long mLastWriteTime = 0;
/**
- * Used to retain an update lock when the foreground activity is in
- * immersive mode.
- */
- final UpdateLock mUpdateLock = new UpdateLock("immersive");
-
- /**
* Set to true after the system has finished booting.
*/
boolean mBooted = false;
@@ -1697,6 +1491,7 @@
WindowManagerService mWindowManager;
ActivityTaskManagerService mActivityTaskManager;
+ ActivityTaskManagerInternal mAtmInternal;
final ActivityThread mSystemThread;
private final class AppDeathRecipient implements IBinder.DeathRecipient {
@@ -1733,8 +1528,6 @@
static final int WAIT_FOR_DEBUGGER_UI_MSG = 6;
static final int SERVICE_TIMEOUT_MSG = 12;
static final int UPDATE_TIME_ZONE = 13;
- static final int SHOW_UID_ERROR_UI_MSG = 14;
- static final int SHOW_FINGERPRINT_ERROR_UI_MSG = 15;
static final int PROC_START_TIMEOUT_MSG = 20;
static final int KILL_APPLICATION_MSG = 22;
static final int FINALIZE_PENDING_INTENT_MSG = 23;
@@ -1748,23 +1541,18 @@
static final int DISPATCH_PROCESSES_CHANGED_UI_MSG = 31;
static final int DISPATCH_PROCESS_DIED_UI_MSG = 32;
static final int REPORT_MEM_USAGE_MSG = 33;
- static final int IMMERSIVE_MODE_LOCK_MSG = 37;
static final int PERSIST_URI_GRANTS_MSG = 38;
static final int UPDATE_TIME_PREFERENCE_MSG = 41;
static final int FINISH_BOOTING_MSG = 45;
static final int SEND_LOCALE_TO_MOUNT_DAEMON_MSG = 47;
- static final int DISMISS_DIALOG_UI_MSG = 48;
static final int NOTIFY_CLEARTEXT_NETWORK_MSG = 49;
static final int POST_DUMP_HEAP_NOTIFICATION_MSG = 50;
static final int DELETE_DUMPHEAP_MSG = 51;
static final int DISPATCH_UIDS_CHANGED_UI_MSG = 53;
- static final int REPORT_TIME_TRACKER_MSG = 54;
static final int SHUTDOWN_UI_AUTOMATION_CONNECTION_MSG = 56;
static final int CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG = 57;
static final int IDLE_UIDS_MSG = 58;
static final int HANDLE_TRUST_STORAGE_UPDATE_MSG = 63;
- static final int DISPATCH_SCREEN_AWAKE_MSG = 64;
- static final int DISPATCH_SCREEN_KEYGUARD_MSG = 65;
static final int SERVICE_FOREGROUND_TIMEOUT_MSG = 66;
static final int DISPATCH_PENDING_INTENT_CANCEL_MSG = 67;
static final int PUSH_TEMP_WHITELIST_UI_MSG = 68;
@@ -1790,11 +1578,6 @@
*/
private boolean mUserIsMonkey;
- /** The dimensions of the thumbnails in the Recents UI. */
- int mThumbnailWidth;
- int mThumbnailHeight;
- float mFullscreenThumbnailScale;
-
final ServiceThread mHandlerThread;
final MainHandler mHandler;
final Handler mUiHandler;
@@ -1880,7 +1663,7 @@
return;
}
AppErrorResult res = (AppErrorResult) data.get("result");
- if (mShowDialogs && !mSleeping && !mShuttingDown) {
+ if (mAtmInternal.showStrictModeViolationDialog()) {
Dialog d = new StrictModeViolationDialog(mUiContext,
ActivityManagerService.this, res, proc);
d.show();
@@ -1919,30 +1702,6 @@
}
}
} break;
- case SHOW_UID_ERROR_UI_MSG: {
- if (mShowDialogs) {
- AlertDialog d = new BaseErrorDialog(mUiContext);
- d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ERROR);
- d.setCancelable(false);
- d.setTitle(mUiContext.getText(R.string.android_system_label));
- d.setMessage(mUiContext.getText(R.string.system_error_wipe_data));
- d.setButton(DialogInterface.BUTTON_POSITIVE, mUiContext.getText(R.string.ok),
- obtainMessage(DISMISS_DIALOG_UI_MSG, d));
- d.show();
- }
- } break;
- case SHOW_FINGERPRINT_ERROR_UI_MSG: {
- if (mShowDialogs) {
- AlertDialog d = new BaseErrorDialog(mUiContext);
- d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ERROR);
- d.setCancelable(false);
- d.setTitle(mUiContext.getText(R.string.android_system_label));
- d.setMessage(mUiContext.getText(R.string.system_error_manufacturer));
- d.setButton(DialogInterface.BUTTON_POSITIVE, mUiContext.getText(R.string.ok),
- obtainMessage(DISMISS_DIALOG_UI_MSG, d));
- d.show();
- }
- } break;
case SHOW_COMPAT_MODE_DIALOG_UI_MSG: {
synchronized (ActivityManagerService.this) {
ActivityRecord ar = (ActivityRecord) msg.obj;
@@ -1971,11 +1730,6 @@
}
break;
}
- case DISMISS_DIALOG_UI_MSG: {
- final Dialog d = (Dialog) msg.obj;
- d.dismiss();
- break;
- }
case DISPATCH_PROCESSES_CHANGED_UI_MSG: {
dispatchProcessesChanged();
break;
@@ -2038,6 +1792,9 @@
}
}
callbacks.finishBroadcast();
+ // We have to clean up the RemoteCallbackList here, because otherwise it will
+ // needlessly hold the enclosed callbacks until the remote process dies.
+ callbacks.kill();
} break;
case UPDATE_TIME_ZONE: {
synchronized (ActivityManagerService.this) {
@@ -2128,13 +1885,13 @@
}
ActivityRecord root = (ActivityRecord)msg.obj;
- ProcessRecord process = root.app;
+ final WindowProcessController process = root.app;
if (process == null) {
return;
}
try {
- Context context = mContext.createPackageContext(process.info.packageName, 0);
+ Context context = mContext.createPackageContext(process.mInfo.packageName, 0);
String text = mContext.getString(R.string.heavy_weight_notification,
context.getApplicationInfo().loadLabel(context.getPackageManager()));
Notification notification =
@@ -2198,20 +1955,6 @@
thread.start();
break;
}
- case IMMERSIVE_MODE_LOCK_MSG: {
- final boolean nextState = (msg.arg1 != 0);
- if (mUpdateLock.isHeld() != nextState) {
- if (DEBUG_IMMERSIVE) Slog.d(TAG_IMMERSIVE,
- "Applying new update lock state '" + nextState
- + "' for " + (ActivityRecord)msg.obj);
- if (nextState) {
- mUpdateLock.acquire();
- } else {
- mUpdateLock.release();
- }
- }
- break;
- }
case PERSIST_URI_GRANTS_MSG: {
writeGrantedUriPermissions();
break;
@@ -2241,7 +1984,7 @@
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
if (msg.arg2 != 0) {
- enableScreenAfterBoot();
+ mAtmInternal.enableScreenAfterBoot(mBooted);
}
break;
}
@@ -2361,10 +2104,6 @@
mMemWatchDumpUid = -1;
}
} break;
- case REPORT_TIME_TRACKER_MSG: {
- AppTimeTracker tracker = (AppTimeTracker)msg.obj;
- tracker.deliverResult(mContext);
- } break;
case SHUTDOWN_UI_AUTOMATION_CONNECTION_MSG: {
IUiAutomationConnection connection = (IUiAutomationConnection) msg.obj;
try {
@@ -2379,18 +2118,6 @@
case IDLE_UIDS_MSG: {
idleUids();
} break;
- case DISPATCH_SCREEN_AWAKE_MSG: {
- final boolean isAwake = msg.arg1 != 0;
- for (int i = mScreenObservers.size() - 1; i >= 0; i--) {
- mScreenObservers.get(i).onAwakeStateChanged(isAwake);
- }
- } break;
- case DISPATCH_SCREEN_KEYGUARD_MSG: {
- final boolean isShowing = msg.arg1 != 0;
- for (int i = mScreenObservers.size() - 1; i >= 0; i--) {
- mScreenObservers.get(i).onKeyguardStateChanged(isShowing);
- }
- } break;
case HANDLE_TRUST_STORAGE_UPDATE_MSG: {
synchronized (ActivityManagerService.this) {
for (int i = mLruProcesses.size() - 1 ; i >= 0 ; i--) {
@@ -2544,8 +2271,9 @@
synchronized (this) {
ProcessRecord app = newProcessRecordLocked(info, info.processName, false, 0);
- app.persistent = true;
+ app.setPersistent(true);
app.pid = MY_PID;
+ app.getWindowProcessController().setPid(MY_PID);
app.maxAdj = ProcessList.SYSTEM_ADJ;
app.makeActive(mSystemThread.getApplicationThread(), mProcessStats);
synchronized (mPidsSelfLocked) {
@@ -2581,14 +2309,6 @@
}
}
- public void setActivityTaskManager(ActivityTaskManagerService atm) {
- synchronized (this) {
- mActivityTaskManager = atm;
- mActivityTaskManager.setActivityManagerService(this);
- mStackSupervisor = mActivityTaskManager.mStackSupervisor;
- }
- }
-
public void setUsageStatsManager(UsageStatsManagerInternal usageStatsManager) {
mUsageStatsService = usageStatsManager;
}
@@ -2689,10 +2409,17 @@
public static final class Lifecycle extends SystemService {
private final ActivityManagerService mService;
+ private static ActivityTaskManagerService sAtm;
public Lifecycle(Context context) {
super(context);
- mService = new ActivityManagerService(context);
+ mService = new ActivityManagerService(context, sAtm);
+ }
+
+ public static ActivityManagerService startService(
+ SystemServiceManager ssm, ActivityTaskManagerService atm) {
+ sAtm = atm;
+ return ssm.startService(ActivityManagerService.Lifecycle.class).getService();
}
@Override
@@ -2849,7 +2576,7 @@
// Note: This method is invoked on the main thread but may need to attach various
// handlers to other threads. So take care to be explicit about the looper.
- public ActivityManagerService(Context systemContext) {
+ public ActivityManagerService(Context systemContext, ActivityTaskManagerService atm) {
LockGuard.installLock(this, LockGuard.INDEX_ACTIVITY);
mInjector = new Injector();
mContext = systemContext;
@@ -2908,6 +2635,7 @@
mOnBattery = DEBUG_POWER ? true
: mBatteryStatsService.getActiveStatistics().getIsOnBattery();
mBatteryStatsService.getActiveStatistics().setCallback(this);
+ mOomAdjProfiler.batteryPowerChanged(mOnBattery);
mProcessStats = new ProcessStatsService(this, new File(systemDir, "procstats"));
@@ -2928,6 +2656,11 @@
mCompatModePackages = new CompatModePackages(this, systemDir, mHandler);
mIntentFirewall = new IntentFirewall(new IntentFirewallInterface(), mHandler);
+ mActivityTaskManager = atm;
+ mActivityTaskManager.setActivityManagerService(this);
+ mAtmInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
+ mStackSupervisor = mActivityTaskManager.mStackSupervisor;
+
mProcessCpuThread = new Thread("CpuTracker") {
@Override
public void run() {
@@ -2996,6 +2729,7 @@
mAppOpsService.publish(mContext);
Slog.d("AppOps", "AppOpsService published");
LocalServices.addService(ActivityManagerInternal.class, new LocalService());
+ mActivityTaskManager.onActivityManagerInternalAdded();
// Wait for the synchronized block started in mProcessCpuThread,
// so that any other access to mProcessCpuTracker from main thread
// will be blocked during mProcessCpuTracker initialization.
@@ -3008,18 +2742,10 @@
}
}
- void onUserStoppedLocked(int userId) {
- mActivityTaskManager.getRecentTasks().unloadUserDataFromMemoryLocked(userId);
- mAllowAppSwitchUids.remove(userId);
- }
-
public void initPowerManagement() {
- mStackSupervisor.initPowerManagement();
+ mActivityTaskManager.onInitPowerManagement();
mBatteryStatsService.initPowerManagement();
mLocalPowerManager = LocalServices.getService(PowerManagerInternal.class);
- PowerManager pm = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
- mVoiceWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*voice*");
- mVoiceWakeLock.setReferenceCounted(false);
}
private ArraySet<String> getBackgroundLaunchBroadcasts() {
@@ -3198,12 +2924,14 @@
synchronized(mPidsSelfLocked) {
mOnBattery = DEBUG_POWER ? true : onBattery;
}
+ mOomAdjProfiler.batteryPowerChanged(onBattery);
}
}
@Override
public void batteryStatsReset() {
BinderCallsStatsService.reset();
+ mOomAdjProfiler.reset();
}
@Override
@@ -3270,96 +2998,6 @@
}
}
- /**
- * Update AMS states when an activity is resumed. This should only be called by
- * {@link ActivityStack#onActivityStateChanged(ActivityRecord, ActivityState, String)} when an
- * activity is resumed.
- */
- @GuardedBy("this")
- void setResumedActivityUncheckLocked(ActivityRecord r, String reason) {
- final TaskRecord task = r.getTask();
- if (task.isActivityTypeStandard()) {
- if (mCurAppTimeTracker != r.appTimeTracker) {
- // We are switching app tracking. Complete the current one.
- if (mCurAppTimeTracker != null) {
- mCurAppTimeTracker.stop();
- mHandler.obtainMessage(
- REPORT_TIME_TRACKER_MSG, mCurAppTimeTracker).sendToTarget();
- mStackSupervisor.clearOtherAppTimeTrackers(r.appTimeTracker);
- mCurAppTimeTracker = null;
- }
- if (r.appTimeTracker != null) {
- mCurAppTimeTracker = r.appTimeTracker;
- startTimeTrackingFocusedActivityLocked();
- }
- } else {
- startTimeTrackingFocusedActivityLocked();
- }
- } else {
- r.appTimeTracker = null;
- }
- // TODO: VI Maybe r.task.voiceInteractor || r.voiceInteractor != null
- // TODO: Probably not, because we don't want to resume voice on switching
- // back to this activity
- if (task.voiceInteractor != null) {
- startRunningVoiceLocked(task.voiceSession, r.info.applicationInfo.uid);
- } else {
- finishRunningVoiceLocked();
-
- if (mLastResumedActivity != null) {
- final IVoiceInteractionSession session;
-
- final TaskRecord lastResumedActivityTask = mLastResumedActivity.getTask();
- if (lastResumedActivityTask != null
- && lastResumedActivityTask.voiceSession != null) {
- session = lastResumedActivityTask.voiceSession;
- } else {
- session = mLastResumedActivity.voiceSession;
- }
-
- if (session != null) {
- // We had been in a voice interaction session, but now focused has
- // move to something different. Just finish the session, we can't
- // return to it and retain the proper state and synchronization with
- // the voice interaction service.
- mActivityTaskManager.finishVoiceTask(session);
- }
- }
- }
-
- if (mLastResumedActivity != null && r.userId != mLastResumedActivity.userId) {
- mUserController.sendForegroundProfileChanged(r.userId);
- }
- updateResumedAppTrace(r);
- mLastResumedActivity = r;
-
- mWindowManager.setFocusedApp(r.appToken, true);
-
- applyUpdateLockStateLocked(r);
- mActivityTaskManager.applyUpdateVrModeLocked(r);
-
- EventLogTags.writeAmSetResumedActivity(
- r == null ? -1 : r.userId,
- r == null ? "NULL" : r.shortComponentName,
- reason);
- }
-
- private void updateResumedAppTrace(@Nullable ActivityRecord resumed) {
- if (mTracedResumedActivity != null) {
- Trace.asyncTraceEnd(TRACE_TAG_ACTIVITY_MANAGER,
- constructResumedTraceName(mTracedResumedActivity.packageName), 0);
- }
- if (resumed != null) {
- Trace.asyncTraceBegin(TRACE_TAG_ACTIVITY_MANAGER,
- constructResumedTraceName(resumed.packageName), 0);
- }
- mTracedResumedActivity = resumed;
- }
-
- private String constructResumedTraceName(String packageName) {
- return "focused app: " + packageName;
- }
-
@Override
public void setFocusedStack(int stackId) {
mActivityTaskManager.setFocusedStack(stackId);
@@ -3379,16 +3017,6 @@
mActivityTaskManager.unregisterTaskStackListener(listener);
}
- final void applyUpdateLockStateLocked(ActivityRecord r) {
- // Modifications to the UpdateLock state are done on our handler, outside
- // the activity manager's locks. The new state is determined based on the
- // state *now* of the relevant activity record. The object is passed to
- // the handler solely for logging detail, not to be consulted/modified.
- final boolean nextState = r != null && r.immersive;
- mHandler.sendMessage(
- mHandler.obtainMessage(IMMERSIVE_MODE_LOCK_MSG, (nextState) ? 1 : 0, 0, r));
- }
-
final void showAskCompatModeDialogLocked(ActivityRecord r) {
Message msg = Message.obtain();
msg.what = SHOW_COMPAT_MODE_DIALOG_UI_MSG;
@@ -3415,7 +3043,7 @@
String what, Object obj, ProcessRecord srcApp) {
app.lastActivityTime = now;
- if (app.activities.size() > 0 || app.recentTasks.size() > 0) {
+ if (app.hasActivitiesOrRecentTasks()) {
// Don't want to touch dependent processes that are hosting activities.
return index;
}
@@ -3462,7 +3090,7 @@
int lrui = mLruProcesses.lastIndexOf(app);
if (lrui >= 0) {
if (!app.killed) {
- if (app.persistent) {
+ if (app.isPersistent()) {
Slog.w(TAG, "Removing persistent process that hasn't been killed: " + app);
} else {
Slog.wtfStack(TAG, "Removing process that hasn't been killed: " + app);
@@ -3486,8 +3114,8 @@
final void updateLruProcessLocked(ProcessRecord app, boolean activityChange,
ProcessRecord client) {
- final boolean hasActivity = app.activities.size() > 0 || app.hasClientActivities
- || app.treatLikeActivity || app.recentTasks.size() > 0;
+ final boolean hasActivity = app.hasActivitiesOrRecentTasks() || app.hasClientActivities
+ || app.treatLikeActivity;
final boolean hasService = false; // not impl yet. app.services.size() > 0;
if (!activityChange && hasActivity) {
// The process has activities, so we are only allowing activity-based adjustments
@@ -3519,7 +3147,7 @@
int lrui = mLruProcesses.lastIndexOf(app);
- if (app.persistent && lrui >= 0) {
+ if (app.isPersistent() && lrui >= 0) {
// We don't care about the position of persistent processes, as long as
// they are in the list.
if (DEBUG_LRU) Slog.d(TAG_LRU, "Not moving, persistent: " + app);
@@ -3591,7 +3219,7 @@
int nextIndex;
if (hasActivity) {
final int N = mLruProcesses.size();
- if ((app.activities.size() == 0 || app.recentTasks.size() > 0)
+ if ((!app.hasActivities() || app.hasRecentTasks())
&& mLruProcessActivityStart < (N - 1)) {
// Process doesn't have activities, but has clients with
// activities... move it up, but one below the top (the top
@@ -3666,14 +3294,14 @@
if (cr.binding != null && !cr.serviceDead && cr.binding.service != null
&& cr.binding.service.app != null
&& cr.binding.service.app.lruSeq != mLruSeq
- && !cr.binding.service.app.persistent) {
+ && !cr.binding.service.app.isPersistent()) {
nextIndex = updateLruProcessInternalLocked(cr.binding.service.app, now, nextIndex,
"service connection", cr, app);
}
}
for (int j=app.conProviders.size()-1; j>=0; j--) {
ContentProviderRecord cpr = app.conProviders.get(j).provider;
- if (cpr.proc != null && cpr.proc.lruSeq != mLruSeq && !cpr.proc.persistent) {
+ if (cpr.proc != null && cpr.proc.lruSeq != mLruSeq && !cpr.proc.isPersistent()) {
nextIndex = updateLruProcessInternalLocked(cpr.proc, now, nextIndex,
"provider reference", cpr, app);
}
@@ -3703,7 +3331,7 @@
&& proc.lastCachedPss >= 4000) {
// Turn this condition on to cause killing to happen regularly, for testing.
if (proc.baseProcessTracker != null) {
- proc.baseProcessTracker.reportCachedKill(proc.pkgList, proc.lastCachedPss);
+ proc.baseProcessTracker.reportCachedKill(proc.pkgList.mPkgList, proc.lastCachedPss);
}
proc.kill(Long.toString(proc.lastCachedPss) + "k from cached", true);
} else if (proc != null && !keepIfLarge
@@ -3712,7 +3340,7 @@
if (DEBUG_PSS) Slog.d(TAG_PSS, "May not keep " + proc + ": pss=" + proc.lastCachedPss);
if (proc.lastCachedPss >= mProcessList.getCachedRestoreThresholdKb()) {
if (proc.baseProcessTracker != null) {
- proc.baseProcessTracker.reportCachedKill(proc.pkgList, proc.lastCachedPss);
+ proc.baseProcessTracker.reportCachedKill(proc.pkgList.mPkgList, proc.lastCachedPss);
}
proc.kill(Long.toString(proc.lastCachedPss) + "k from cached", true);
}
@@ -4018,7 +3646,7 @@
}
if (app.info.isPrivilegedApp() &&
- DexManager.isPackageSelectedToRunOob(app.pkgList.keySet())) {
+ DexManager.isPackageSelectedToRunOob(app.pkgList.mPkgList.keySet())) {
runtimeFlags |= Zygote.ONLY_USE_SYSTEM_OAT_FILES;
}
@@ -4060,7 +3688,7 @@
}
app.gids = gids;
- app.requiredAbi = requiredAbi;
+ app.setRequiredAbi(requiredAbi);
app.instructionSet = instructionSet;
// the per-user SELinux context must be set
@@ -4251,7 +3879,7 @@
// Ignore
}
- if (app.persistent) {
+ if (app.isPersistent()) {
Watchdog.getInstance().processStarted(app.processName, pid);
}
@@ -4310,7 +3938,7 @@
"updateUsageStats: comp=" + component + "res=" + resumed);
final BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
StatsLog.write(StatsLog.ACTIVITY_FOREGROUND_STATE_CHANGED,
- component.app.uid, component.realActivity.getPackageName(),
+ component.app.mUid, component.realActivity.getPackageName(),
component.realActivity.getShortClassName(), resumed ?
StatsLog.ACTIVITY_FOREGROUND_STATE_CHANGED__STATE__FOREGROUND :
StatsLog.ACTIVITY_FOREGROUND_STATE_CHANGED__STATE__BACKGROUND);
@@ -4321,7 +3949,7 @@
}
synchronized (stats) {
- stats.noteActivityResumedLocked(component.app.uid);
+ stats.noteActivityResumedLocked(component.app.mUid);
}
} else {
if (mUsageStatsService != null) {
@@ -4329,7 +3957,7 @@
UsageEvents.Event.MOVE_TO_BACKGROUND);
}
synchronized (stats) {
- stats.noteActivityPausedLocked(component.app.uid);
+ stats.noteActivityPausedLocked(component.app.mUid);
}
}
}
@@ -4413,7 +4041,7 @@
return mCompatModePackages.compatibilityInfoForPackageLocked(ai);
}
- void enforceNotIsolatedCaller(String caller) {
+ private void enforceNotIsolatedCaller(String caller) {
if (UserHandle.isIsolated(Binder.getCallingUid())) {
throw new SecurityException("Isolated process not allowed to call " + caller);
}
@@ -4789,44 +4417,6 @@
mActivityTaskManager.cancelRecentsAnimation(restoreHomeStackPosition);
}
- @GuardedBy("this")
- void onLocalVoiceInteractionStartedLocked(IBinder activity,
- IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor) {
- ActivityRecord activityToCallback = ActivityRecord.forTokenLocked(activity);
- if (activityToCallback == null) return;
- activityToCallback.setVoiceSessionLocked(voiceSession);
-
- // Inform the activity
- try {
- activityToCallback.app.thread.scheduleLocalVoiceInteractionStarted(activity,
- voiceInteractor);
- long token = Binder.clearCallingIdentity();
- try {
- startRunningVoiceLocked(voiceSession, activityToCallback.appInfo.uid);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- // TODO: VI Should we cache the activity so that it's easier to find later
- // rather than scan through all the stacks and activities?
- } catch (RemoteException re) {
- activityToCallback.clearVoiceSessionLocked();
- // TODO: VI Should this terminate the voice session?
- }
- }
-
- @Override
- public void setVoiceKeepAwake(IVoiceInteractionSession session, boolean keepAwake) {
- synchronized (this) {
- if (mRunningVoice != null && mRunningVoice.asBinder() == session.asBinder()) {
- if (keepAwake) {
- mVoiceWakeLock.acquire();
- } else {
- mVoiceWakeLock.release();
- }
- }
- }
- }
-
/**
* This is the internal entry point for handling Activity.finish().
*
@@ -4866,14 +4456,7 @@
return;
}
- ArrayList<ActivityRecord> activities = new ArrayList<>(proc.activities);
- for (int i = 0; i < activities.size(); i++) {
- ActivityRecord r = activities.get(i);
- if (!r.finishing && r.isInStackLocked()) {
- r.getStack().finishActivityLocked(r, Activity.RESULT_CANCELED,
- null, "finish-heavy", true);
- }
- }
+ proc.getWindowProcessController().finishActivities();
mHandler.sendMessage(mHandler.obtainMessage(CANCEL_HEAVY_NOTIFICATION_MSG,
proc.userId, 0));
@@ -4922,11 +4505,10 @@
}
// Remove this application's activities from active lists.
- boolean hasVisibleActivities = mStackSupervisor.handleAppDiedLocked(app);
+ boolean hasVisibleActivities = mStackSupervisor.handleAppDiedLocked(app.getWindowProcessController());
app.clearRecentTasks();
-
- app.activities.clear();
+ app.clearActivities();
if (app.instr != null) {
Slog.w(TAG, "Crash of app " + app.processName
@@ -4999,7 +4581,7 @@
for (int i=mLruProcesses.size()-1; i>=0; i--) {
ProcessRecord rec = mLruProcesses.get(i);
if (rec.thread != null
- && rec.setProcState >= ActivityManager.PROCESS_STATE_CACHED_ACTIVITY) {
+ && rec.setProcState >= PROCESS_STATE_CACHED_ACTIVITY) {
haveBg = true;
break;
}
@@ -5169,7 +4751,7 @@
}
}
- final File tracesDir = new File("/data/anr");
+ final File tracesDir = new File(ANR_TRACE_DIR);
// Each set of ANR traces is written to a separate file and dumpstate will process
// all such files and add them to a captured bug report if they're recent enough.
maybePruneOldTraces(tracesDir);
@@ -5251,7 +4833,7 @@
return SystemClock.elapsedRealtime() - timeStart;
}
- private static void dumpStackTraces(String tracesFile, ArrayList<Integer> firstPids,
+ public static void dumpStackTraces(String tracesFile, ArrayList<Integer> firstPids,
ArrayList<Integer> nativePids, ArrayList<Integer> extraPids) {
// We don't need any sort of inotify based monitoring when we're dumping traces via
@@ -5327,90 +4909,6 @@
}
}
- final void logAppTooSlow(ProcessRecord app, long startTime, String msg) {
- if (true || Build.IS_USER) {
- return;
- }
-
- StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
- StrictMode.allowThreadDiskWrites();
- try {
- File tracesDir = new File("/data/anr");
- File tracesFile = null;
- try {
- tracesFile = File.createTempFile("app_slow", null, tracesDir);
-
- StringBuilder sb = new StringBuilder();
- Time tobj = new Time();
- tobj.set(System.currentTimeMillis());
- sb.append(tobj.format("%Y-%m-%d %H:%M:%S"));
- sb.append(": ");
- TimeUtils.formatDuration(SystemClock.uptimeMillis()-startTime, sb);
- sb.append(" since ");
- sb.append(msg);
- FileOutputStream fos = new FileOutputStream(tracesFile);
- fos.write(sb.toString().getBytes());
- if (app == null) {
- fos.write("\n*** No application process!".getBytes());
- }
- fos.close();
- FileUtils.setPermissions(tracesFile.getPath(), 0666, -1, -1); // -rw-rw-rw-
- } catch (IOException e) {
- Slog.w(TAG, "Unable to prepare slow app traces file: " + tracesFile, e);
- return;
- }
-
- if (app != null && app.pid > 0) {
- ArrayList<Integer> firstPids = new ArrayList<Integer>();
- firstPids.add(app.pid);
- dumpStackTraces(tracesFile.getAbsolutePath(), firstPids, null, null);
- }
-
- File lastTracesFile = null;
- File curTracesFile = null;
- for (int i=9; i>=0; i--) {
- String name = String.format(Locale.US, "slow%02d.txt", i);
- curTracesFile = new File(tracesDir, name);
- if (curTracesFile.exists()) {
- if (lastTracesFile != null) {
- curTracesFile.renameTo(lastTracesFile);
- } else {
- curTracesFile.delete();
- }
- }
- lastTracesFile = curTracesFile;
- }
- tracesFile.renameTo(curTracesFile);
- } finally {
- StrictMode.setThreadPolicy(oldPolicy);
- }
- }
-
- @GuardedBy("this")
- final void showLaunchWarningLocked(final ActivityRecord cur, final ActivityRecord next) {
- if (!mLaunchWarningShown) {
- mLaunchWarningShown = true;
- mUiHandler.post(new Runnable() {
- @Override
- public void run() {
- synchronized (ActivityManagerService.this) {
- final Dialog d = new LaunchWarningWindow(mContext, cur, next);
- d.show();
- mUiHandler.postDelayed(new Runnable() {
- @Override
- public void run() {
- synchronized (ActivityManagerService.this) {
- d.dismiss();
- mLaunchWarningShown = false;
- }
- }
- }, 4000);
- }
- }
- });
- }
- }
-
@Override
public boolean clearApplicationUserData(final String packageName, boolean keepState,
final IPackageDataObserver observer, int userId) {
@@ -5611,7 +5109,7 @@
final int NA = apps.size();
for (int ia = 0; ia < NA; ia++) {
final ProcessRecord app = apps.valueAt(ia);
- if (app.persistent) {
+ if (app.isPersistent()) {
// We don't kill persistent processes.
continue;
}
@@ -5648,7 +5146,7 @@
* @param maxProcState the process state at or below which to preserve
* processes, or {@code -1} to ignore the process state
*/
- private void killAllBackgroundProcessesExcept(int minTargetSdk, int maxProcState) {
+ void killAllBackgroundProcessesExcept(int minTargetSdk, int maxProcState) {
if (checkCallingPermission(android.Manifest.permission.KILL_BACKGROUND_PROCESSES)
!= PackageManager.PERMISSION_GRANTED) {
final String msg = "Permission Denial: killAllBackgroundProcessesExcept() from pid="
@@ -5866,7 +5364,7 @@
proc.baseProcessTracker.addPss(infos[i].getTotalPss(),
infos[i].getTotalUss(), infos[i].getTotalRss(), false,
ProcessStats.ADD_PSS_EXTERNAL_SLOW, endTime-startTime,
- proc.pkgList);
+ proc.pkgList.mPkgList);
}
}
}
@@ -5896,7 +5394,7 @@
if (proc.thread != null && proc.setAdj == oomAdj) {
// Record this for posterity if the process has been stable.
proc.baseProcessTracker.addPss(pss[i], tmpUss[0], tmpUss[2], false,
- ProcessStats.ADD_PSS_EXTERNAL, endTime-startTime, proc.pkgList);
+ ProcessStats.ADD_PSS_EXTERNAL, endTime-startTime, proc.pkgList.mPkgList);
}
}
}
@@ -5969,7 +5467,7 @@
final int NA = apps.size();
for (int ia=0; ia<NA; ia++) {
ProcessRecord app = apps.valueAt(ia);
- if (app.persistent && !evenPersistent) {
+ if (app.isPersistent() && !evenPersistent) {
// we don't kill persistent processes
continue;
}
@@ -6306,7 +5804,7 @@
// We shouldn't already have a process under this name, but just in case we
// need to clean up whatever may be there now.
ProcessRecord old = removeProcessNameLocked(proc.processName, proc.uid);
- if (old == proc && proc.persistent) {
+ if (old == proc && proc.isPersistent()) {
// We are re-adding a persistent process. Whatevs! Just leave it there.
Slog.w(TAG, "Re-adding persistent process " + proc);
} else if (old != null) {
@@ -6373,7 +5871,7 @@
}
}
boolean willRestart = false;
- if (app.persistent && !app.isolated) {
+ if (app.isPersistent() && !app.isolated) {
if (!callerWillRestart) {
willRestart = true;
} else {
@@ -6526,7 +6024,7 @@
app.makeActive(thread, mProcessStats);
app.curAdj = app.setAdj = app.verifiedAdj = ProcessList.INVALID_ADJ;
- app.curSchedGroup = app.setSchedGroup = ProcessList.SCHED_GROUP_DEFAULT;
+ app.setCurrentSchedulingGroup(app.setSchedGroup = ProcessList.SCHED_GROUP_DEFAULT);
app.forcingToImportant = null;
updateProcessForegroundLocked(app, false, false);
app.hasShownUi = false;
@@ -6705,7 +6203,7 @@
app.instr.mWatcher,
app.instr.mUiAutomationConnection, testMode,
mBinderTransactionTrackingEnabled, enableTrackAllocation,
- isRestrictedBackupMode || !normalMode, app.persistent,
+ isRestrictedBackupMode || !normalMode, app.isPersistent(),
new Configuration(getGlobalConfiguration()), app.compat,
getCommonServicesLocked(app.isolated),
mCoreSettingsObserver.getCoreSettingsLocked(),
@@ -6714,7 +6212,7 @@
thread.bindApplication(processName, appInfo, providers, null, profilerInfo,
null, null, null, testMode,
mBinderTransactionTrackingEnabled, enableTrackAllocation,
- isRestrictedBackupMode || !normalMode, app.persistent,
+ isRestrictedBackupMode || !normalMode, app.isPersistent(),
new Configuration(getGlobalConfiguration()), app.compat,
getCommonServicesLocked(app.isolated),
mCoreSettingsObserver.getCoreSettingsLocked(),
@@ -6830,16 +6328,6 @@
finishBooting ? 1 : 0, enableScreen ? 1 : 0));
}
- void enableScreenAfterBoot() {
- EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_ENABLE_SCREEN,
- SystemClock.uptimeMillis());
- mWindowManager.enableScreenAfterBoot();
-
- synchronized (this) {
- updateEventDispatchingLocked();
- }
- }
-
@Override
public void showBootMessage(final CharSequence msg, final boolean always) {
if (Binder.getCallingUid() != myUid()) {
@@ -6987,7 +6475,7 @@
}
if (enableScreen) {
- enableScreenAfterBoot();
+ mAtmInternal.enableScreenAfterBoot(mBooted);
}
}
@@ -7609,7 +7097,7 @@
}
}
- int checkComponentPermission(String permission, int pid, int uid,
+ static int checkComponentPermission(String permission, int pid, int uid,
int owningUid, boolean exported) {
if (pid == MY_PID) {
return PackageManager.PERMISSION_GRANTED;
@@ -7697,15 +7185,6 @@
}
/**
- * This can be called with or without the global lock held.
- */
- void enforceCallerIsRecentsOrHasPermission(String permission, String func) {
- if (!mActivityTaskManager.getRecentTasks().isCallerRecents(Binder.getCallingUid())) {
- enforceCallingPermission(permission, func);
- }
- }
-
- /**
* Determine if UID is holding permissions required to access {@link Uri} in
* the given {@link ProviderInfo}. Final permission checking is always done
* in {@link ContentProvider}.
@@ -9162,38 +8641,6 @@
mActivityTaskManager.cancelTaskWindowTransition(taskId);
}
- boolean isGetTasksAllowed(String caller, int callingPid, int callingUid) {
- if (mActivityTaskManager.getRecentTasks().isCallerRecents(callingUid)) {
- // Always allow the recents component to get tasks
- return true;
- }
-
- boolean allowed = checkPermission(android.Manifest.permission.REAL_GET_TASKS,
- callingPid, callingUid) == PackageManager.PERMISSION_GRANTED;
- if (!allowed) {
- if (checkPermission(android.Manifest.permission.GET_TASKS,
- callingPid, callingUid) == PackageManager.PERMISSION_GRANTED) {
- // Temporary compatibility: some existing apps on the system image may
- // still be requesting the old permission and not switched to the new
- // one; if so, we'll still allow them full access. This means we need
- // to see if they are holding the old permission and are a system app.
- try {
- if (AppGlobals.getPackageManager().isUidPrivileged(callingUid)) {
- allowed = true;
- if (DEBUG_TASKS) Slog.w(TAG, caller + ": caller " + callingUid
- + " is using old GET_TASKS but privileged; allowing");
- }
- } catch (RemoteException e) {
- }
- }
- }
- if (!allowed) {
- if (DEBUG_TASKS) Slog.w(TAG, caller + ": caller " + callingUid
- + " does not hold REAL_GET_TASKS; limiting output");
- }
- return allowed;
- }
-
@Override
public void setTaskResizeable(int taskId, int resizeableMode) {
mActivityTaskManager.setTaskResizeable(taskId, resizeableMode);
@@ -9563,7 +9010,8 @@
}
ContentProviderConnection incProviderCountLocked(ProcessRecord r,
- final ContentProviderRecord cpr, IBinder externalProcessToken, boolean stable) {
+ final ContentProviderRecord cpr, IBinder externalProcessToken, int callingUid,
+ String callingTag, boolean stable) {
if (r != null) {
for (int i=0; i<r.conProviders.size(); i++) {
ContentProviderConnection conn = r.conProviders.get(i);
@@ -9584,6 +9032,7 @@
}
}
ContentProviderConnection conn = new ContentProviderConnection(cpr, r);
+ conn.startAssociationIfNeeded();
if (stable) {
conn.stableCount = 1;
conn.numStableIncs = 1;
@@ -9594,10 +9043,10 @@
cpr.connections.add(conn);
r.conProviders.add(conn);
startAssociationLocked(r.uid, r.processName, r.curProcState,
- cpr.uid, cpr.name, cpr.info.processName);
+ cpr.uid, cpr.appInfo.longVersionCode, cpr.name, cpr.info.processName);
return conn;
}
- cpr.addExternalProcessHandleLocked(externalProcessToken);
+ cpr.addExternalProcessHandleLocked(externalProcessToken, callingUid, callingTag);
return null;
}
@@ -9616,9 +9065,10 @@
conn.unstableCount--;
}
if (conn.stableCount == 0 && conn.unstableCount == 0) {
+ conn.stopAssociation();
cpr.connections.remove(conn);
conn.client.conProviders.remove(conn);
- if (conn.client.setProcState < ActivityManager.PROCESS_STATE_LAST_ACTIVITY) {
+ if (conn.client.setProcState < PROCESS_STATE_LAST_ACTIVITY) {
// The client is more important than last activity -- note the time this
// is happening, so we keep the old provider process around a bit as last
// activity to avoid thrashing it.
@@ -9626,7 +9076,8 @@
cpr.proc.lastProviderTime = SystemClock.uptimeMillis();
}
}
- stopAssociationLocked(conn.client.uid, conn.client.processName, cpr.uid, cpr.name);
+ stopAssociationLocked(conn.client.uid, conn.client.processName, cpr.uid,
+ cpr.appInfo.longVersionCode, cpr.name, cpr.info.processName);
return true;
}
return false;
@@ -9672,7 +9123,8 @@
}
private ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
- String name, IBinder token, boolean stable, int userId) {
+ String name, IBinder token, int callingUid, String callingTag, boolean stable,
+ int userId) {
ContentProviderRecord cpr;
ContentProviderConnection conn = null;
ProviderInfo cpi = null;
@@ -9765,7 +9217,7 @@
// In this case the provider instance already exists, so we can
// return it right away.
- conn = incProviderCountLocked(r, cpr, token, stable);
+ conn = incProviderCountLocked(r, cpr, token, callingUid, callingTag, stable);
if (conn != null && (conn.stableCount+conn.unstableCount) == 1) {
if (cpr.proc != null && r.setAdj <= ProcessList.PERCEPTIBLE_APP_ADJ) {
// If this is a perceptible app accessing the provider,
@@ -10009,7 +9461,7 @@
}
mProviderMap.putProviderByName(name, cpr);
- conn = incProviderCountLocked(r, cpr, token, stable);
+ conn = incProviderCountLocked(r, cpr, token, callingUid, callingTag, stable);
if (conn != null) {
conn.waiting = true;
}
@@ -10120,21 +9572,23 @@
}
// The incoming user check is now handled in checkContentProviderPermissionLocked() to deal
// with cross-user grant.
- return getContentProviderImpl(caller, name, null, stable, userId);
+ return getContentProviderImpl(caller, name, null, Binder.getCallingUid(), null, stable,
+ userId);
}
public ContentProviderHolder getContentProviderExternal(
- String name, int userId, IBinder token) {
+ String name, int userId, IBinder token, String tag) {
enforceCallingPermission(android.Manifest.permission.ACCESS_CONTENT_PROVIDERS_EXTERNALLY,
"Do not have permission in call getContentProviderExternal()");
userId = mUserController.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
userId, false, ALLOW_FULL_ONLY, "getContentProvider", null);
- return getContentProviderExternalUnchecked(name, token, userId);
+ return getContentProviderExternalUnchecked(name, token, Binder.getCallingUid(),
+ tag != null ? tag : "*external*", userId);
}
private ContentProviderHolder getContentProviderExternalUnchecked(String name,
- IBinder token, int userId) {
- return getContentProviderImpl(null, name, token, true, userId);
+ IBinder token, int callingUid, String callingTag, int userId) {
+ return getContentProviderImpl(null, name, token, callingUid, callingTag, true, userId);
}
/**
@@ -10256,7 +9710,7 @@
}
synchronized (dst) {
dst.provider = src.provider;
- dst.proc = r;
+ dst.setProcess(r);
dst.notifyAll();
}
updateOomAdjLocked(r, true);
@@ -10424,7 +9878,7 @@
mConstants.start(mContext.getContentResolver());
mCoreSettingsObserver = new CoreSettingsObserver(this);
- mFontScaleSettingObserver = new FontScaleSettingObserver();
+ mActivityTaskManager.installSystemProviders();
mDevelopmentSettingsObserver = new DevelopmentSettingsObserver();
GlobalSettingsToPropertiesMapper.start(mContext.getContentResolver());
@@ -10526,7 +9980,8 @@
}
ContentProviderHolder holder = null;
try {
- holder = getContentProviderExternalUnchecked(name, null, userId);
+ holder = getContentProviderExternalUnchecked(name, null, callingUid,
+ "*getmimetype*", userId);
if (holder != null) {
return holder.provider.getType(uri);
}
@@ -10618,9 +10073,9 @@
&& userId == UserHandle.USER_SYSTEM
&& (info.flags & PERSISTENT_MASK) == PERSISTENT_MASK) {
// The system process is initialized to SCHED_GROUP_DEFAULT in init.rc.
- r.curSchedGroup = ProcessList.SCHED_GROUP_DEFAULT;
+ r.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_DEFAULT);
r.setSchedGroup = ProcessList.SCHED_GROUP_DEFAULT;
- r.persistent = true;
+ r.setPersistent(true);
r.maxAdj = ProcessList.PERSISTENT_PROC_ADJ;
}
if (isolated && isolatedUid != 0) {
@@ -10719,7 +10174,7 @@
}
if ((info.flags & PERSISTENT_MASK) == PERSISTENT_MASK) {
- app.persistent = true;
+ app.setPersistent(true);
app.maxAdj = ProcessList.PERSISTENT_PROC_ADJ;
}
if (app.thread == null && mPersistentStartingProcesses.indexOf(app) < 0) {
@@ -10741,7 +10196,8 @@
final int userId = UserHandle.getCallingUserId();
final Uri uri = Uri.parse(uriString);
String name = uri.getAuthority();
- ContentProviderHolder cph = getContentProviderExternalUnchecked(name, null, userId);
+ ContentProviderHolder cph = getContentProviderExternalUnchecked(name, null,
+ Binder.getCallingUid(), "*opencontent*", userId);
ParcelFileDescriptor pfd = null;
if (cph != null) {
// We record the binder invoker's uid in thread-local storage before
@@ -10770,20 +10226,6 @@
return pfd;
}
- // Actually is sleeping or shutting down or whatever else in the future
- // is an inactive state.
- boolean isSleepingOrShuttingDownLocked() {
- return isSleepingLocked() || mShuttingDown;
- }
-
- boolean isShuttingDownLocked() {
- return mShuttingDown;
- }
-
- boolean isSleepingLocked() {
- return mSleeping;
- }
-
void reportGlobalUsageEventLocked(int event) {
mUsageStatsService.reportEvent("android", mUserController.getCurrentUserId(), event);
int[] profiles = mUserController.getCurrentProfileIds();
@@ -10810,60 +10252,13 @@
// Also update state in a special way for running foreground services UI.
mServices.updateScreenStateLocked(isAwake);
reportCurWakefulnessUsageEventLocked();
- mHandler.obtainMessage(DISPATCH_SCREEN_AWAKE_MSG, isAwake ? 1 : 0, 0)
- .sendToTarget();
+ mActivityTaskManager.onScreenAwakeChanged(isAwake);
+ mOomAdjProfiler.onWakefulnessChanged(wakefulness);
}
updateOomAdjLocked();
}
}
- @GuardedBy("this")
- void finishRunningVoiceLocked() {
- if (mRunningVoice != null) {
- mRunningVoice = null;
- mVoiceWakeLock.release();
- updateSleepIfNeededLocked();
- }
- }
-
- void startTimeTrackingFocusedActivityLocked() {
- final ActivityRecord resumedActivity = mStackSupervisor.getResumedActivityLocked();
- if (!mSleeping && mCurAppTimeTracker != null && resumedActivity != null) {
- mCurAppTimeTracker.start(resumedActivity.packageName);
- }
- }
-
- @GuardedBy("this")
- void updateSleepIfNeededLocked() {
- final boolean shouldSleep = !mStackSupervisor.hasAwakeDisplay();
- final boolean wasSleeping = mSleeping;
-
- if (!shouldSleep) {
- // If wasSleeping is true, we need to wake up activity manager state from when
- // we started sleeping. In either case, we need to apply the sleep tokens, which
- // will wake up stacks or put them to sleep as appropriate.
- if (wasSleeping) {
- mSleeping = false;
- startTimeTrackingFocusedActivityLocked();
- mTopProcessState = ActivityManager.PROCESS_STATE_TOP;
- mStackSupervisor.comeOutOfSleepIfNeededLocked();
- }
- mStackSupervisor.applySleepTokensLocked(true /* applyToStacks */);
- if (wasSleeping) {
- updateOomAdjLocked();
- }
- } else if (!mSleeping && shouldSleep) {
- mSleeping = true;
- if (mCurAppTimeTracker != null) {
- mCurAppTimeTracker.stop();
- }
- mTopProcessState = ActivityManager.PROCESS_STATE_TOP_SLEEPING;
- mStackSupervisor.goingToSleepLocked();
- updateResumedAppTrace(null /* resumed */);
- updateOomAdjLocked();
- }
- }
-
@Override
public void notifyCleartextNetwork(int uid, byte[] firstPacket) {
mHandler.obtainMessage(NOTIFY_CLEARTEXT_NETWORK_MSG, uid, 0, firstPacket).sendToTarget();
@@ -10877,14 +10272,7 @@
+ android.Manifest.permission.SHUTDOWN);
}
- boolean timedout = false;
-
- synchronized(this) {
- mShuttingDown = true;
- mStackSupervisor.prepareForShutdownLocked();
- updateEventDispatchingLocked();
- timedout = mStackSupervisor.shutdownLocked(timeout);
- }
+ final boolean timedout = mAtmInternal.shuttingDown(mBooted, timeout);
mAppOpsService.shutdown();
if (mUsageStatsService != null) {
@@ -10899,24 +10287,6 @@
return timedout;
}
- @GuardedBy("this")
- void startRunningVoiceLocked(IVoiceInteractionSession session, int targetUid) {
- Slog.d(TAG, "<<< startRunningVoiceLocked()");
- mVoiceWakeLock.setWorkSource(new WorkSource(targetUid));
- if (mRunningVoice == null || mRunningVoice.asBinder() != session.asBinder()) {
- boolean wasRunningVoice = mRunningVoice != null;
- mRunningVoice = session;
- if (!wasRunningVoice) {
- mVoiceWakeLock.acquire();
- updateSleepIfNeededLocked();
- }
- }
- }
-
- private void updateEventDispatchingLocked() {
- mWindowManager.setEventDispatching(mBooted && !mShuttingDown);
- }
-
@Override
public void setLockScreenShown(boolean keyguardShowing, boolean aodShowing,
int secondaryDisplayShowing) {
@@ -10975,69 +10345,12 @@
@Override
public void stopAppSwitches() {
- enforceCallerIsRecentsOrHasPermission(STOP_APP_SWITCHES, "stopAppSwitches");
- synchronized(this) {
- mAppSwitchesAllowedTime = SystemClock.uptimeMillis()
- + APP_SWITCH_DELAY_TIME;
- mDidAppSwitch = false;
- mActivityTaskManager.getActivityStartController().schedulePendingActivityLaunches(APP_SWITCH_DELAY_TIME);
- }
+ mActivityTaskManager.stopAppSwitches();
}
+ @Override
public void resumeAppSwitches() {
- enforceCallerIsRecentsOrHasPermission(STOP_APP_SWITCHES, "resumeAppSwitches");
- synchronized(this) {
- // Note that we don't execute any pending app switches... we will
- // let those wait until either the timeout, or the next start
- // activity request.
- mAppSwitchesAllowedTime = 0;
- }
- }
-
- boolean checkAllowAppSwitchUid(int uid) {
- ArrayMap<String, Integer> types = mAllowAppSwitchUids.get(UserHandle.getUserId(uid));
- if (types != null) {
- for (int i = types.size() - 1; i >= 0; i--) {
- if (types.valueAt(i).intValue() == uid) {
- return true;
- }
- }
- }
- return false;
- }
-
- boolean checkAppSwitchAllowedLocked(int sourcePid, int sourceUid,
- int callingPid, int callingUid, String name) {
- if (mAppSwitchesAllowedTime < SystemClock.uptimeMillis()) {
- return true;
- }
-
- if (mActivityTaskManager.getRecentTasks().isCallerRecents(sourceUid)) {
- return true;
- }
-
- int perm = checkComponentPermission(STOP_APP_SWITCHES, sourcePid, sourceUid, -1, true);
- if (perm == PackageManager.PERMISSION_GRANTED) {
- return true;
- }
- if (checkAllowAppSwitchUid(sourceUid)) {
- return true;
- }
-
- // If the actual IPC caller is different from the logical source, then
- // also see if they are allowed to control app switches.
- if (callingUid != -1 && callingUid != sourceUid) {
- perm = checkComponentPermission(STOP_APP_SWITCHES, callingPid, callingUid, -1, true);
- if (perm == PackageManager.PERMISSION_GRANTED) {
- return true;
- }
- if (checkAllowAppSwitchUid(callingUid)) {
- return true;
- }
- }
-
- Slog.w(TAG, name + " request from " + sourceUid + " stopped");
- return false;
+ mActivityTaskManager.resumeAppSwitches();
}
public void setDebugApp(String packageName, boolean waitForDebugger,
@@ -11191,13 +10504,7 @@
@Override
public void setActivityController(IActivityController controller, boolean imAMonkey) {
- enforceCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER,
- "setActivityController()");
- synchronized (this) {
- mController = controller;
- mControllerIsAMonkey = imAMonkey;
- Watchdog.getInstance().setActivityController(controller);
- }
+ mActivityTaskManager.setActivityController(controller, imAMonkey);
}
@Override
@@ -11222,7 +10529,7 @@
public boolean isUserAMonkey() {
synchronized (this) {
// If there is a controller also implies the user is a monkey.
- return (mUserIsMonkey || (mController != null && mControllerIsAMonkey));
+ return mUserIsMonkey || mActivityTaskManager.isControllerAMonkey();
}
}
@@ -11334,7 +10641,10 @@
public static long getInputDispatchingTimeoutLocked(ActivityRecord r) {
- return r != null ? getInputDispatchingTimeoutLocked(r.app) : KEY_DISPATCHING_TIMEOUT;
+ if (r == null || !r.hasProcess()) {
+ return KEY_DISPATCHING_TIMEOUT;
+ }
+ return getInputDispatchingTimeoutLocked((ProcessRecord) r.app.mOwner);
}
public static long getInputDispatchingTimeoutLocked(ProcessRecord r) {
@@ -11529,17 +10839,6 @@
return false;
}
- /**
- * Check that we have the features required for VR-related API calls, and throw an exception if
- * not.
- */
- void enforceSystemHasVrFeature() {
- if (!mContext.getPackageManager().hasSystemFeature(
- PackageManager.FEATURE_VR_MODE_HIGH_PERFORMANCE)) {
- throw new UnsupportedOperationException("VR mode not supported on this device!");
- }
- }
-
@Override
public void setRenderThread(int tid) {
synchronized (this) {
@@ -11562,7 +10861,7 @@
Slog.d("UI_FIFO", "Set RenderThread tid " + tid + " for pid " + pid);
}
// promote to FIFO now
- if (proc.curSchedGroup == ProcessList.SCHED_GROUP_TOP_APP) {
+ if (proc.getCurrentSchedulingGroup() == ProcessList.SCHED_GROUP_TOP_APP) {
if (DEBUG_OOM_ADJ) Slog.d("UI_FIFO", "Promoting " + tid + "out of band");
if (mUseFifoUiScheduling) {
setThreadScheduler(proc.renderThreadTid,
@@ -11594,7 +10893,7 @@
@Override
public boolean isVrModePackageEnabled(ComponentName packageName) {
- enforceSystemHasVrFeature();
+ mActivityTaskManager.enforceSystemHasVrFeature();
final VrManagerInternal vrService = LocalServices.getService(VrManagerInternal.class);
@@ -12039,7 +11338,7 @@
proc.notCachedSinceIdle = true;
proc.initialIdlePss = 0;
proc.nextPssTime = ProcessList.computeNextPssTime(proc.setProcState, null,
- mTestPssMode, isSleepingLocked(), now);
+ mTestPssMode, mAtmInternal.isSleeping(), now);
}
}
}
@@ -12067,78 +11366,22 @@
private void retrieveSettings() {
final ContentResolver resolver = mContext.getContentResolver();
- final boolean freeformWindowManagement =
- mContext.getPackageManager().hasSystemFeature(FEATURE_FREEFORM_WINDOW_MANAGEMENT)
- || Settings.Global.getInt(
- resolver, DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT, 0) != 0;
+ mActivityTaskManager.retrieveSettings(resolver);
- final boolean supportsMultiWindow = ActivityTaskManager.supportsMultiWindow(mContext);
- final boolean supportsPictureInPicture = supportsMultiWindow &&
- mContext.getPackageManager().hasSystemFeature(FEATURE_PICTURE_IN_PICTURE);
- final boolean supportsSplitScreenMultiWindow =
- ActivityTaskManager.supportsSplitScreenMultiWindow(mContext);
- final boolean supportsMultiDisplay = mContext.getPackageManager()
- .hasSystemFeature(FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS);
final String debugApp = Settings.Global.getString(resolver, DEBUG_APP);
final boolean waitForDebugger = Settings.Global.getInt(resolver, WAIT_FOR_DEBUGGER, 0) != 0;
final boolean alwaysFinishActivities =
Settings.Global.getInt(resolver, ALWAYS_FINISH_ACTIVITIES, 0) != 0;
- final boolean forceRtl = Settings.Global.getInt(resolver, DEVELOPMENT_FORCE_RTL, 0) != 0;
- final boolean forceResizable = Settings.Global.getInt(
- resolver, DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES, 0) != 0;
final long waitForNetworkTimeoutMs = Settings.Global.getLong(resolver,
NETWORK_ACCESS_TIMEOUT_MS, NETWORK_ACCESS_TIMEOUT_DEFAULT_MS);
- final boolean supportsLeanbackOnly =
- mContext.getPackageManager().hasSystemFeature(FEATURE_LEANBACK_ONLY);
mHiddenApiBlacklist.registerObserver();
- // Transfer any global setting for forcing RTL layout, into a System Property
- SystemProperties.set(DEVELOPMENT_FORCE_RTL, forceRtl ? "1":"0");
-
- final Configuration configuration = new Configuration();
- Settings.System.getConfiguration(resolver, configuration);
- if (forceRtl) {
- // This will take care of setting the correct layout direction flags
- configuration.setLayoutDirection(configuration.locale);
- }
-
synchronized (this) {
mDebugApp = mOrigDebugApp = debugApp;
mWaitForDebugger = mOrigWaitForDebugger = waitForDebugger;
mAlwaysFinishActivities = alwaysFinishActivities;
- mSupportsLeanbackOnly = supportsLeanbackOnly;
- mForceResizableActivities = forceResizable;
- final boolean multiWindowFormEnabled = freeformWindowManagement
- || supportsSplitScreenMultiWindow
- || supportsPictureInPicture
- || supportsMultiDisplay;
- if ((supportsMultiWindow || forceResizable) && multiWindowFormEnabled) {
- mSupportsMultiWindow = true;
- mSupportsFreeformWindowManagement = freeformWindowManagement;
- mSupportsSplitScreenMultiWindow = supportsSplitScreenMultiWindow;
- mSupportsPictureInPicture = supportsPictureInPicture;
- mSupportsMultiDisplay = supportsMultiDisplay;
- } else {
- mSupportsMultiWindow = false;
- mSupportsFreeformWindowManagement = false;
- mSupportsSplitScreenMultiWindow = false;
- mSupportsPictureInPicture = false;
- mSupportsMultiDisplay = false;
- }
- mWindowManager.setForceResizableTasks(mForceResizableActivities);
- mWindowManager.setSupportsPictureInPicture(mSupportsPictureInPicture);
- // This happens before any activities are started, so we can change global configuration
- // in-place.
- updateConfigurationLocked(configuration, null, true);
- final Configuration globalConfig = getGlobalConfiguration();
- if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, "Initial config: " + globalConfig);
-
// Load resources only after the current configuration has been set.
final Resources res = mContext.getResources();
- mThumbnailWidth = res.getDimensionPixelSize(
- com.android.internal.R.dimen.thumbnail_width);
- mThumbnailHeight = res.getDimensionPixelSize(
- com.android.internal.R.dimen.thumbnail_height);
mAppErrors.loadAppsNotReportingCrashesFromConfigLocked(res.getString(
com.android.internal.R.string.config_appsNotReportingCrashes));
mUserController.mUserSwitchUiEnabled = !res.getBoolean(
@@ -12146,14 +11389,6 @@
mUserController.mMaxRunningUsers = res.getInteger(
com.android.internal.R.integer.config_multiuserMaxRunningUsers);
- if ((globalConfig.uiMode & UI_MODE_TYPE_TELEVISION) == UI_MODE_TYPE_TELEVISION) {
- mFullscreenThumbnailScale = (float) res
- .getInteger(com.android.internal.R.integer.thumbnail_width_tv) /
- (float) globalConfig.screenWidthDp;
- } else {
- mFullscreenThumbnailScale = res.getFraction(
- com.android.internal.R.fraction.thumbnail_fullscreen_scale, 1, 1);
- }
mWaitForNetworkTimeoutMs = waitForNetworkTimeoutMs;
}
}
@@ -12303,19 +11538,7 @@
}
startHomeActivityLocked(currentUserId, "systemReady");
- try {
- if (AppGlobals.getPackageManager().hasSystemUidErrors()) {
- Slog.e(TAG, "UIDs on the system are inconsistent, you need to wipe your"
- + " data partition or your device will be unstable.");
- mUiHandler.obtainMessage(SHOW_UID_ERROR_UI_MSG).sendToTarget();
- }
- } catch (RemoteException e) {
- }
-
- if (!Build.isBuildConsistent()) {
- Slog.e(TAG, "Build fingerprint is not consistent, warning user");
- mUiHandler.obtainMessage(SHOW_FINGERPRINT_ERROR_UI_MSG).sendToTarget();
- }
+ mAtmInternal.showSystemReadyErrorDialogsIfNeeded();
long ident = Binder.clearCallingIdentity();
try {
@@ -12356,6 +11579,7 @@
public void onLimitReached(int uid) {
Slog.wtf(TAG, "Uid " + uid + " sent too many Binders to uid "
+ Process.myUid());
+ BinderProxy.dumpProxyDebugInfo();
if (uid == Process.SYSTEM_UID) {
Slog.i(TAG, "Skipping kill (uid is SYSTEM)");
} else {
@@ -12450,13 +11674,13 @@
public void handleApplicationStrictModeViolation(
IBinder app,
- int violationMask,
+ int penaltyMask,
StrictMode.ViolationInfo info) {
// We're okay if the ProcessRecord is missing; it probably means that
// we're reporting a violation from the system process itself.
final ProcessRecord r = findAppProcess(app, "StrictMode");
- if ((violationMask & StrictMode.PENALTY_DROPBOX) != 0) {
+ if ((penaltyMask & StrictMode.PENALTY_DROPBOX) != 0) {
Integer stackFingerprint = info.hashCode();
boolean logIt = true;
synchronized (mAlreadyLoggedViolatedStacks) {
@@ -12480,7 +11704,7 @@
}
}
- if ((violationMask & StrictMode.PENALTY_DIALOG) != 0) {
+ if ((penaltyMask & StrictMode.PENALTY_DIALOG) != 0) {
AppErrorResult result = new AppErrorResult();
synchronized (this) {
final long origId = Binder.clearCallingIdentity();
@@ -12490,7 +11714,6 @@
HashMap<String, Object> data = new HashMap<String, Object>();
data.put("result", result);
data.put("app", r);
- data.put("violationMask", violationMask);
data.put("info", info);
msg.obj = data;
mUiHandler.sendMessage(msg);
@@ -12594,7 +11817,7 @@
final boolean isFatal = Build.IS_ENG || Settings.Global
.getInt(mContext.getContentResolver(), Settings.Global.WTF_IS_FATAL, 0) != 0;
- final boolean isSystem = (r == null) || r.persistent;
+ final boolean isSystem = (r == null) || r.isPersistent();
if (isFatal && !isSystem) {
mAppErrors.crashApplication(r, crashInfo);
@@ -12757,8 +11980,8 @@
if (activity != null) {
sb.append("Activity: ").append(activity.shortComponentName).append("\n");
}
- if (parent != null && parent.app != null && parent.app.pid != process.pid) {
- sb.append("Parent-Process: ").append(parent.app.processName).append("\n");
+ if (parent != null && parent.app != null && parent.app.getPid() != process.pid) {
+ sb.append("Parent-Process: ").append(parent.app.mName).append("\n");
}
if (parent != null && parent != activity) {
sb.append("Parent-Activity: ").append(parent.shortComponentName).append("\n");
@@ -12860,25 +12083,27 @@
if (!allUsers && app.userId != userId) {
continue;
}
- if ((app.thread != null) && (app.crashing || app.notResponding)) {
+ final boolean crashing = app.isCrashing();
+ final boolean notResponding = app.isNotResponding();
+ if ((app.thread != null) && (crashing || notResponding)) {
// This one's in trouble, so we'll generate a report for it
// crashes are higher priority (in case there's a crash *and* an anr)
ActivityManager.ProcessErrorStateInfo report = null;
- if (app.crashing) {
+ if (crashing) {
report = app.crashingReport;
- } else if (app.notResponding) {
+ } else if (notResponding) {
report = app.notRespondingReport;
}
if (report != null) {
if (errList == null) {
- errList = new ArrayList<ActivityManager.ProcessErrorStateInfo>(1);
+ errList = new ArrayList<>(1);
}
errList.add(report);
} else {
Slog.w(TAG, "Missing app error report, app = " + app.processName +
- " crashing = " + app.crashing +
- " notResponding = " + app.notResponding);
+ " crashing = " + crashing +
+ " notResponding = " + notResponding);
}
}
}
@@ -12900,7 +12125,7 @@
return imp;
}
- private void fillInProcMemInfo(ProcessRecord app,
+ private void fillInProcMemInfoLocked(ProcessRecord app,
ActivityManager.RunningAppProcessInfo outInfo,
int clientTargetSdk) {
outInfo.pid = app.pid;
@@ -12908,10 +12133,10 @@
if (mHeavyWeightProcess == app) {
outInfo.flags |= ActivityManager.RunningAppProcessInfo.FLAG_CANT_SAVE_STATE;
}
- if (app.persistent) {
+ if (app.isPersistent()) {
outInfo.flags |= ActivityManager.RunningAppProcessInfo.FLAG_PERSISTENT;
}
- if (app.activities.size() > 0) {
+ if (app.hasActivities()) {
outInfo.flags |= ActivityManager.RunningAppProcessInfo.FLAG_HAS_ACTIVITIES;
}
outInfo.lastTrimLevel = app.trimMemoryLevel;
@@ -12920,6 +12145,8 @@
outInfo.importance = procStateToImportance(procState, adj, outInfo, clientTargetSdk);
outInfo.importanceReasonCode = app.adjTypeCode;
outInfo.processState = app.curProcState;
+ outInfo.isFocused = (app == getTopAppLocked());
+ outInfo.lastActivityTime = app.lastActivityTime;
}
@Override
@@ -12934,7 +12161,7 @@
final boolean allUsers = ActivityManager.checkUidPermission(INTERACT_ACROSS_USERS_FULL,
callingUid) == PackageManager.PERMISSION_GRANTED;
final int userId = UserHandle.getUserId(callingUid);
- final boolean allUids = isGetTasksAllowed(
+ final boolean allUids = mAtmInternal.isGetTasksAllowed(
"getRunningAppProcesses", Binder.getCallingPid(), callingUid);
synchronized (this) {
@@ -12945,12 +12172,12 @@
|| (!allUids && app.uid != callingUid)) {
continue;
}
- if ((app.thread != null) && (!app.crashing && !app.notResponding)) {
+ if ((app.thread != null) && (!app.isCrashing() && !app.isNotResponding())) {
// Generate process state info for running application
ActivityManager.RunningAppProcessInfo currApp =
new ActivityManager.RunningAppProcessInfo(app.processName,
app.pid, app.getPackageList());
- fillInProcMemInfo(app, currApp, clientTargetSdk);
+ fillInProcMemInfoLocked(app, currApp, clientTargetSdk);
if (app.adjSource instanceof ProcessRecord) {
currApp.importanceReasonPid = ((ProcessRecord)app.adjSource).pid;
currApp.importanceReasonImportance =
@@ -12958,7 +12185,7 @@
app.adjSourceProcState);
} else if (app.adjSource instanceof ActivityRecord) {
ActivityRecord r = (ActivityRecord)app.adjSource;
- if (r.app != null) currApp.importanceReasonPid = r.app.pid;
+ if (r.app != null) currApp.importanceReasonPid = r.app.getPid();
}
if (app.adjTarget instanceof ComponentName) {
currApp.importanceReasonComponent = (ComponentName)app.adjTarget;
@@ -13019,7 +12246,7 @@
proc = mPidsSelfLocked.get(Binder.getCallingPid());
}
if (proc != null) {
- fillInProcMemInfo(proc, outState, clientTargetSdk);
+ fillInProcMemInfoLocked(proc, outState, clientTargetSdk);
}
}
}
@@ -13040,14 +12267,6 @@
this, in, out, err, args, callback, resultReceiver);
}
- SleepToken acquireSleepToken(String tag, int displayId) {
- synchronized (this) {
- final SleepToken token = mStackSupervisor.createSleepTokenLocked(tag, displayId);
- updateSleepIfNeededLocked();
- return token;
- }
- }
-
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
PriorityDump.dump(mPriorityDumper, fd, pw, args);
@@ -13191,6 +12410,10 @@
synchronized (this) {
dumpLastANRLocked(pw);
}
+ } else if ("lastanr-traces".equals(cmd)) {
+ synchronized (this) {
+ dumpLastANRTracesLocked(pw);
+ }
} else if ("starter".equals(cmd)) {
synchronized (this) {
dumpActivityStarterLocked(pw, dumpPackage);
@@ -13207,8 +12430,10 @@
}
} else if ("binder-proxies".equals(cmd)) {
if (opti >= args.length) {
+ dumpBinderProxyInterfaceCounts(pw,
+ "Top proxy interface names held by SYSTEM");
dumpBinderProxiesCounts(pw, BinderInternal.nGetBinderProxyPerUidCounts(),
- "Counts of Binder Proxies held by SYSTEM");
+ "Number of proxies per uid held by SYSTEM");
} else {
String uid = args[opti];
opti++;
@@ -13521,6 +12746,11 @@
pw.println("-------------------------------------------------------------------------------");
}
dumpProcessesLocked(fd, pw, args, opti, dumpAll, dumpPackage, dumpAppId);
+ pw.println();
+ if (dumpAll) {
+ pw.println("-------------------------------------------------------------------------------");
+ }
+ mOomAdjProfiler.dump(pw);
}
}
Binder.restoreCallingIdentity(origId);
@@ -13540,6 +12770,36 @@
}
}
+ private void dumpLastANRTracesLocked(PrintWriter pw) {
+ pw.println("ACTIVITY MANAGER LAST ANR TRACES (dumpsys activity lastanr-traces)");
+
+ final File[] files = new File(ANR_TRACE_DIR).listFiles();
+ if (ArrayUtils.isEmpty(files)) {
+ pw.println(" <no ANR has occurred since boot>");
+ return;
+ }
+ // Find the latest file.
+ File latest = null;
+ for (File f : files) {
+ if ((latest == null) || (latest.lastModified() < f.lastModified())) {
+ latest = f;
+ }
+ }
+ pw.print("File: ");
+ pw.print(latest.getName());
+ pw.println();
+ try (BufferedReader in = new BufferedReader(new FileReader(latest))) {
+ String line;
+ while ((line = in.readLine()) != null) {
+ pw.println(line);
+ }
+ } catch (IOException e) {
+ pw.print("Unable to read: ");
+ pw.print(e);
+ pw.println();
+ }
+ }
+
private void dumpActivityContainersLocked(PrintWriter pw) {
pw.println("ACTIVITY MANAGER STARTER (dumpsys activity containers)");
mStackSupervisor.dumpChildrenNames(pw, " ");
@@ -13566,8 +12826,8 @@
boolean needSep = printedAnything;
boolean printed = ActivityStackSupervisor.printThisActivity(pw,
- mStackSupervisor.getResumedActivityLocked(),
- dumpPackage, needSep, " ResumedActivity: ");
+ mStackSupervisor.getTopResumedActivity(), dumpPackage, needSep,
+ " ResumedActivity: ");
if (printed) {
printedAnything = true;
needSep = false;
@@ -13711,6 +12971,15 @@
return printed;
}
+ void dumpBinderProxyInterfaceCounts(PrintWriter pw, String header) {
+ final BinderProxy.InterfaceCount[] proxyCounts = BinderProxy.getSortedInterfaceCounts(50);
+
+ pw.println(header);
+ for (int i = 0; i < proxyCounts.length; i++) {
+ pw.println(" #" + (i + 1) + ": " + proxyCounts[i]);
+ }
+ }
+
boolean dumpBinderProxiesCounts(PrintWriter pw, SparseIntArray counts, String header) {
if(counts != null) {
pw.println(header);
@@ -13761,11 +13030,11 @@
pw.println(" All known processes:");
needSep = true;
}
- pw.print(r.persistent ? " *PERS*" : " *APP*");
+ pw.print(r.isPersistent() ? " *PERS*" : " *APP*");
pw.print(" UID "); pw.print(procs.keyAt(ia));
pw.print(" "); pw.println(r);
r.dump(pw, " ");
- if (r.persistent) {
+ if (r.isPersistent()) {
numPers++;
}
}
@@ -13917,27 +13186,27 @@
needSep = false;
mUserController.dump(pw, dumpAll);
}
- if (mHomeProcess != null && (dumpPackage == null
- || mHomeProcess.pkgList.containsKey(dumpPackage))) {
+ if (mActivityTaskManager.mHomeProcess != null && (dumpPackage == null
+ || mActivityTaskManager.mHomeProcess.mPkgList.contains(dumpPackage))) {
if (needSep) {
pw.println();
needSep = false;
}
- pw.println(" mHomeProcess: " + mHomeProcess);
+ pw.println(" mHomeProcess: " + mActivityTaskManager.mHomeProcess);
}
- if (mPreviousProcess != null && (dumpPackage == null
- || mPreviousProcess.pkgList.containsKey(dumpPackage))) {
+ if (mActivityTaskManager.mPreviousProcess != null && (dumpPackage == null
+ || mActivityTaskManager.mPreviousProcess.mPkgList.contains(dumpPackage))) {
if (needSep) {
pw.println();
needSep = false;
}
- pw.println(" mPreviousProcess: " + mPreviousProcess);
+ pw.println(" mPreviousProcess: " + mActivityTaskManager.mPreviousProcess);
}
- if (dumpAll && (mPreviousProcess == null || dumpPackage == null
- || mPreviousProcess.pkgList.containsKey(dumpPackage))) {
+ if (dumpAll && (mActivityTaskManager.mPreviousProcess == null || dumpPackage == null
+ || mActivityTaskManager.mPreviousProcess.mPkgList.contains(dumpPackage))) {
StringBuilder sb = new StringBuilder(128);
sb.append(" mPreviousProcessVisibleTime: ");
- TimeUtils.formatDuration(mPreviousProcessVisibleTime, sb);
+ TimeUtils.formatDuration(mActivityTaskManager.mPreviousProcessVisibleTime, sb);
pw.println(sb);
}
if (mHeavyWeightProcess != null && (dumpPackage == null
@@ -13962,7 +13231,8 @@
}
if (dumpAll) {
if (dumpPackage == null) {
- pw.println(" mConfigWillChange: " + getFocusedStack().mConfigWillChange);
+ pw.println(" mConfigWillChange: "
+ + mActivityTaskManager.getTopDisplayFocusedStack().mConfigWillChange);
}
if (mCompatModePackages.getPackages().size() > 0) {
boolean printed = false;
@@ -14038,14 +13308,7 @@
if (dumpPackage == null) {
pw.println(" mWakefulness="
+ PowerManagerInternal.wakefulnessToString(mWakefulness));
- pw.println(" mSleepTokens=" + mStackSupervisor.mSleepTokens);
- pw.println(" mSleeping=" + mSleeping);
- pw.println(" mShuttingDown=" + mShuttingDown + " mTestPssMode=" + mTestPssMode);
- if (mRunningVoice != null) {
- pw.println(" mRunningVoice=" + mRunningVoice);
- pw.println(" mVoiceWakeLock" + mVoiceWakeLock);
- }
- mActivityTaskManager.dumpVrControllerLocked(pw);
+ mActivityTaskManager.dumpSleepStates(pw, mTestPssMode);
}
if (mDebugApp != null || mOrigDebugApp != null || mDebugTransient
|| mOrigWaitForDebugger) {
@@ -14060,8 +13323,8 @@
+ " mOrigWaitForDebugger=" + mOrigWaitForDebugger);
}
}
- if (mCurAppTimeTracker != null) {
- mCurAppTimeTracker.dumpWithHeader(pw, " ", true);
+ if (mActivityTaskManager.mCurAppTimeTracker != null) {
+ mActivityTaskManager.mCurAppTimeTracker.dumpWithHeader(pw, " ", true);
}
if (mMemWatchProcesses.getMap().size() > 0) {
pw.println(" Mem watch processes:");
@@ -14127,10 +13390,10 @@
pw.println(" mNativeDebuggingApp=" + mNativeDebuggingApp);
}
}
- if (mAllowAppSwitchUids.size() > 0) {
+ if (mActivityTaskManager.mAllowAppSwitchUids.size() > 0) {
boolean printed = false;
- for (int i = 0; i < mAllowAppSwitchUids.size(); i++) {
- ArrayMap<String, Integer> types = mAllowAppSwitchUids.valueAt(i);
+ for (int i = 0; i < mActivityTaskManager.mAllowAppSwitchUids.size(); i++) {
+ ArrayMap<String, Integer> types = mActivityTaskManager.mAllowAppSwitchUids.valueAt(i);
for (int j = 0; j < types.size(); j++) {
if (dumpPackage == null ||
UserHandle.getAppId(types.valueAt(j).intValue()) == dumpAppId) {
@@ -14143,7 +13406,7 @@
printed = true;
}
pw.print(" User ");
- pw.print(mAllowAppSwitchUids.keyAt(i));
+ pw.print(mActivityTaskManager.mAllowAppSwitchUids.keyAt(i));
pw.print(": Type ");
pw.print(types.keyAt(j));
pw.print(" = ");
@@ -14157,9 +13420,9 @@
if (mAlwaysFinishActivities) {
pw.println(" mAlwaysFinishActivities=" + mAlwaysFinishActivities);
}
- if (mController != null) {
- pw.println(" mController=" + mController
- + " mControllerIsAMonkey=" + mControllerIsAMonkey);
+ if (mActivityTaskManager.mController != null) {
+ pw.println(" mController=" + mActivityTaskManager.mController
+ + " mControllerIsAMonkey=" + mActivityTaskManager.mControllerIsAMonkey);
}
if (dumpAll) {
pw.println(" Total persistent processes: " + numPers);
@@ -14233,7 +13496,7 @@
continue;
}
r.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.PROCS);
- if (r.persistent) {
+ if (r.isPersistent()) {
numPers++;
}
}
@@ -14340,18 +13603,18 @@
if (dumpPackage == null) {
mUserController.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.USER_CONTROLLER);
getGlobalConfiguration().writeToProto(proto, ActivityManagerServiceDumpProcessesProto.GLOBAL_CONFIGURATION);
- proto.write(ActivityManagerServiceDumpProcessesProto.CONFIG_WILL_CHANGE, getFocusedStack().mConfigWillChange);
+ proto.write(ActivityManagerServiceDumpProcessesProto.CONFIG_WILL_CHANGE, mActivityTaskManager.getTopDisplayFocusedStack().mConfigWillChange);
}
- if (mHomeProcess != null && (dumpPackage == null
- || mHomeProcess.pkgList.containsKey(dumpPackage))) {
- mHomeProcess.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.HOME_PROC);
+ if (mActivityTaskManager.mHomeProcess != null && (dumpPackage == null
+ || mActivityTaskManager.mHomeProcess.mPkgList.contains(dumpPackage))) {
+ ((ProcessRecord) mActivityTaskManager.mHomeProcess.mOwner).writeToProto(proto, ActivityManagerServiceDumpProcessesProto.HOME_PROC);
}
- if (mPreviousProcess != null && (dumpPackage == null
- || mPreviousProcess.pkgList.containsKey(dumpPackage))) {
- mPreviousProcess.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.PREVIOUS_PROC);
- proto.write(ActivityManagerServiceDumpProcessesProto.PREVIOUS_PROC_VISIBLE_TIME_MS, mPreviousProcessVisibleTime);
+ if (mActivityTaskManager.mPreviousProcess != null && (dumpPackage == null
+ || mActivityTaskManager.mPreviousProcess.mPkgList.contains(dumpPackage))) {
+ ((ProcessRecord) mActivityTaskManager.mPreviousProcess.mOwner).writeToProto(proto, ActivityManagerServiceDumpProcessesProto.PREVIOUS_PROC);
+ proto.write(ActivityManagerServiceDumpProcessesProto.PREVIOUS_PROC_VISIBLE_TIME_MS, mActivityTaskManager.mPreviousProcessVisibleTime);
}
if (mHeavyWeightProcess != null && (dumpPackage == null
@@ -14398,23 +13661,10 @@
final long sleepToken = proto.start(ActivityManagerServiceDumpProcessesProto.SLEEP_STATUS);
proto.write(ActivityManagerServiceDumpProcessesProto.SleepStatus.WAKEFULNESS,
PowerManagerInternal.wakefulnessToProtoEnum(mWakefulness));
- for (SleepToken st : mStackSupervisor.mSleepTokens) {
- proto.write(ActivityManagerServiceDumpProcessesProto.SleepStatus.SLEEP_TOKENS, st.toString());
- }
- proto.write(ActivityManagerServiceDumpProcessesProto.SleepStatus.SLEEPING, mSleeping);
- proto.write(ActivityManagerServiceDumpProcessesProto.SleepStatus.SHUTTING_DOWN, mShuttingDown);
proto.write(ActivityManagerServiceDumpProcessesProto.SleepStatus.TEST_PSS_MODE, mTestPssMode);
proto.end(sleepToken);
- if (mRunningVoice != null) {
- final long vrToken = proto.start(ActivityManagerServiceDumpProcessesProto.RUNNING_VOICE);
- proto.write(ActivityManagerServiceDumpProcessesProto.Voice.SESSION, mRunningVoice.toString());
- mVoiceWakeLock.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.Voice.WAKELOCK);
- proto.end(vrToken);
- }
-
- mActivityTaskManager.writeVrControllerToProto(
- proto, ActivityManagerServiceDumpProcessesProto.VR_CONTROLLER);
+ mActivityTaskManager.writeSleepStateToProto(proto);
}
if (mDebugApp != null || mOrigDebugApp != null || mDebugTransient
@@ -14430,8 +13680,9 @@
}
}
- if (mCurAppTimeTracker != null) {
- mCurAppTimeTracker.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.CURRENT_TRACKER, true);
+ if (mActivityTaskManager.mCurAppTimeTracker != null) {
+ mActivityTaskManager.mCurAppTimeTracker.writeToProto(
+ proto, ActivityManagerServiceDumpProcessesProto.CURRENT_TRACKER, true);
}
if (mMemWatchProcesses.getMap().size() > 0) {
@@ -14490,10 +13741,10 @@
if (dumpPackage == null) {
proto.write(ActivityManagerServiceDumpProcessesProto.ALWAYS_FINISH_ACTIVITIES, mAlwaysFinishActivities);
- if (mController != null) {
+ if (mActivityTaskManager.mController != null) {
final long token = proto.start(ActivityManagerServiceDumpProcessesProto.CONTROLLER);
- proto.write(ActivityManagerServiceDumpProcessesProto.Controller.CONTROLLER, mController.toString());
- proto.write(ActivityManagerServiceDumpProcessesProto.Controller.IS_A_MONKEY, mControllerIsAMonkey);
+ proto.write(ActivityManagerServiceDumpProcessesProto.Controller.CONTROLLER, mActivityTaskManager.mController.toString());
+ proto.write(ActivityManagerServiceDumpProcessesProto.Controller.IS_A_MONKEY, mActivityTaskManager.mControllerIsAMonkey);
proto.end(token);
}
proto.write(ActivityManagerServiceDumpProcessesProto.TOTAL_PERSISTENT_PROCS, numPers);
@@ -14622,8 +13873,8 @@
dumpProcessesToGc(pw, needSep, null);
pw.println();
- pw.println(" mHomeProcess: " + mHomeProcess);
- pw.println(" mPreviousProcess: " + mPreviousProcess);
+ pw.println(" mHomeProcess: " + mActivityTaskManager.mHomeProcess);
+ pw.println(" mPreviousProcess: " + mActivityTaskManager.mPreviousProcess);
if (mHeavyWeightProcess != null) {
pw.println(" mHeavyWeightProcess: " + mHeavyWeightProcess);
}
@@ -14795,20 +14046,20 @@
pw.print(prefix); pw.print("ACTIVITY "); pw.print(r.shortComponentName);
pw.print(" "); pw.print(Integer.toHexString(System.identityHashCode(r)));
pw.print(" pid=");
- if (r.app != null) pw.println(r.app.pid);
+ if (r.hasProcess()) pw.println(r.app.getPid());
else pw.println("(not running)");
if (dumpAll) {
r.dump(pw, innerPrefix);
}
}
- if (r.app != null && r.app.thread != null) {
+ if (r.attachedToProcess()) {
// flush anything that is already in the PrintWriter since the thread is going
// to write to the file descriptor directly
pw.flush();
try {
TransferPipe tp = new TransferPipe();
try {
- r.app.thread.dumpActivity(tp.getWriteFd(),
+ r.app.getThread().dumpActivity(tp.getWriteFd(),
r.appToken, innerPrefix, args);
tp.go(fd);
} finally {
@@ -15163,9 +14414,9 @@
continue;
}
pw.println(String.format("%s%s #%2d: %s",
- prefix, (r.persistent ? persistentLabel : normalLabel),
+ prefix, (r.isPersistent() ? persistentLabel : normalLabel),
i, r.toString()));
- if (r.persistent) {
+ if (r.isPersistent()) {
numPers++;
}
}
@@ -15218,7 +14469,7 @@
ProcessRecord r = list.get(i).first;
long token = proto.start(fieldId);
String oomAdj = ProcessList.makeOomAdjString(r.setAdj);
- proto.write(ProcessOomProto.PERSISTENT, r.persistent);
+ proto.write(ProcessOomProto.PERSISTENT, r.isPersistent());
proto.write(ProcessOomProto.NUM, (origList.size()-1)-list.get(i).second);
proto.write(ProcessOomProto.OOM_ADJ, oomAdj);
int schedGroup = ProcessOomProto.SCHED_GROUP_UNKNOWN;
@@ -15241,7 +14492,7 @@
}
if (r.foregroundActivities) {
proto.write(ProcessOomProto.ACTIVITIES, true);
- } else if (r.foregroundServices) {
+ } else if (r.hasForegroundServices()) {
proto.write(ProcessOomProto.SERVICES, true);
}
proto.write(ProcessOomProto.STATE, ProcessList.makeProcStateProtoEnum(r.curProcState));
@@ -15338,14 +14589,14 @@
char foreground;
if (r.foregroundActivities) {
foreground = 'A';
- } else if (r.foregroundServices) {
+ } else if (r.hasForegroundServices()) {
foreground = 'S';
} else {
foreground = ' ';
}
String procState = ProcessList.makeProcStateString(r.curProcState);
pw.print(prefix);
- pw.print(r.persistent ? persistentLabel : normalLabel);
+ pw.print(r.isPersistent() ? persistentLabel : normalLabel);
pw.print(" #");
int num = (origList.size()-1)-list.get(i).second;
if (num < 10) pw.print(' ');
@@ -15960,7 +15211,7 @@
thread = r.thread;
pid = r.pid;
oomAdj = r.getSetAdjWithServices();
- hasActivities = r.activities.size() > 0;
+ hasActivities = r.hasActivities();
}
if (thread != null) {
if (!opts.isCheckinRequest && opts.dumpDetails) {
@@ -16027,7 +15278,7 @@
if (r.thread != null && oomAdj == r.getSetAdjWithServices()) {
// Record this for posterity if the process has been stable.
r.baseProcessTracker.addPss(myTotalPss, myTotalUss, myTotalRss, true,
- reportType, endTime-startTime, r.pkgList);
+ reportType, endTime-startTime, r.pkgList.mPkgList);
}
}
@@ -16463,7 +15714,7 @@
thread = r.thread;
pid = r.pid;
oomAdj = r.getSetAdjWithServices();
- hasActivities = r.activities.size() > 0;
+ hasActivities = r.hasActivities();
}
if (thread == null) {
continue;
@@ -16525,7 +15776,7 @@
if (r.thread != null && oomAdj == r.getSetAdjWithServices()) {
// Record this for posterity if the process has been stable.
r.baseProcessTracker.addPss(myTotalPss, myTotalUss, myTotalRss, true,
- reportType, endTime-startTime, r.pkgList);
+ reportType, endTime-startTime, r.pkgList.mPkgList);
}
}
@@ -16795,6 +16046,7 @@
}
updateCpuStatsNow();
long[] memtrackTmp = new long[1];
+ long[] swaptrackTmp = new long[2];
final List<ProcessCpuTracker.Stats> stats;
// Get a list of Stats that have vsize > 0
synchronized (mProcessCpuTracker) {
@@ -16805,12 +16057,13 @@
final int statsCount = stats.size();
for (int i = 0; i < statsCount; i++) {
ProcessCpuTracker.Stats st = stats.get(i);
- long pss = Debug.getPss(st.pid, null, memtrackTmp);
+ long pss = Debug.getPss(st.pid, swaptrackTmp, memtrackTmp);
if (pss > 0) {
if (infoMap.indexOfKey(st.pid) < 0) {
ProcessMemInfo mi = new ProcessMemInfo(st.name, st.pid,
ProcessList.NATIVE_ADJ, -1, "native", null);
mi.pss = pss;
+ mi.swapPss = swaptrackTmp[1];
mi.memtrack = memtrackTmp[0];
memInfos.add(mi);
}
@@ -16818,14 +16071,17 @@
}
long totalPss = 0;
+ long totalSwapPss = 0;
long totalMemtrack = 0;
for (int i=0, N=memInfos.size(); i<N; i++) {
ProcessMemInfo mi = memInfos.get(i);
if (mi.pss == 0) {
- mi.pss = Debug.getPss(mi.pid, null, memtrackTmp);
+ mi.pss = Debug.getPss(mi.pid, swaptrackTmp, memtrackTmp);
+ mi.swapPss = swaptrackTmp[1];
mi.memtrack = memtrackTmp[0];
}
totalPss += mi.pss;
+ totalSwapPss += mi.swapPss;
totalMemtrack += mi.memtrack;
}
Collections.sort(memInfos, new Comparator<ProcessMemInfo>() {
@@ -16987,7 +16243,7 @@
memInfoBuilder.append("\n");
memInfoBuilder.append(" Lost RAM: ");
memInfoBuilder.append(stringifyKBSize(memInfo.getTotalSizeKb()
- - totalPss - memInfo.getFreeSizeKb() - memInfo.getCachedSizeKb()
+ - (totalPss - totalSwapPss) - memInfo.getFreeSizeKb() - memInfo.getCachedSizeKb()
- memInfo.getKernelUsedSizeKb() - memInfo.getZramTotalSizeKb()));
memInfoBuilder.append("\n");
Slog.i(TAG, "Low on memory:");
@@ -17091,7 +16347,7 @@
ProcessRecord capp = conn.client;
conn.dead = true;
if (conn.stableCount > 0) {
- if (!capp.persistent && capp.thread != null
+ if (!capp.isPersistent() && capp.thread != null
&& capp.pid != 0
&& capp.pid != MY_PID) {
capp.kill("depends on provider "
@@ -17108,7 +16364,8 @@
// clean up this connection, we'll just remove it.
cpr.connections.remove(i);
if (conn.client.conProviders.remove(conn)) {
- stopAssociationLocked(capp.uid, capp.processName, cpr.uid, cpr.name);
+ stopAssociationLocked(capp.uid, capp.processName, cpr.uid,
+ cpr.appInfo.longVersionCode, cpr.name, cpr.info.processName);
}
}
}
@@ -17153,8 +16410,8 @@
app.waitDialog = null;
}
- app.crashing = false;
- app.notResponding = false;
+ app.setCrashing(false);
+ app.setNotResponding(false);
app.resetPackageList(mProcessStats);
app.unlinkDeathRecipient();
@@ -17184,7 +16441,7 @@
}
cpr.provider = null;
- cpr.proc = null;
+ cpr.setProcess(null);
}
app.pubProviders.clear();
@@ -17199,7 +16456,8 @@
ContentProviderConnection conn = app.conProviders.get(i);
conn.provider.connections.remove(conn);
stopAssociationLocked(app.uid, app.processName, conn.provider.uid,
- conn.provider.name);
+ conn.provider.appInfo.longVersionCode, conn.provider.name,
+ conn.provider.info.processName);
}
app.conProviders.clear();
}
@@ -17263,7 +16521,7 @@
return false;
}
- if (!app.persistent || app.isolated) {
+ if (!app.isPersistent() || app.isolated) {
if (DEBUG_PROCESSES || DEBUG_CLEANUP) Slog.v(TAG_CLEANUP,
"Removing non-persistent process during cleanup: " + app);
if (!replacingPid) {
@@ -17287,12 +16545,7 @@
TAG_CLEANUP, "Clean-up removing on hold: " + app);
mProcessesOnHold.remove(app);
- if (app == mHomeProcess) {
- mHomeProcess = null;
- }
- if (app == mPreviousProcess) {
- mPreviousProcess = null;
- }
+ mAtmInternal.onCleanUpApplicationRecord(app.getWindowProcessController());
if (restart && !app.isolated) {
// We have components that still need to be running in the
@@ -17360,8 +16613,8 @@
final int callingUid = Binder.getCallingUid();
final boolean canInteractAcrossUsers = (ActivityManager.checkUidPermission(
INTERACT_ACROSS_USERS_FULL, callingUid) == PERMISSION_GRANTED);
- final boolean allowed = isGetTasksAllowed("getServices", Binder.getCallingPid(),
- callingUid);
+ final boolean allowed = mAtmInternal.isGetTasksAllowed("getServices",
+ Binder.getCallingPid(), callingUid);
synchronized (this) {
return mServices.getRunningServiceInfoLocked(maxNum, flags, callingUid,
allowed, canInteractAcrossUsers);
@@ -18337,7 +17590,7 @@
isCallerSystem = true;
break;
default:
- isCallerSystem = (callerApp != null) && callerApp.persistent;
+ isCallerSystem = (callerApp != null) && callerApp.isPersistent();
break;
}
@@ -19276,6 +18529,8 @@
// Can't call out of the system process with a lock held, so post a message.
if (app.instr.mUiAutomationConnection != null) {
+ mAppOpsService.setAppOpsServiceDelegate(null);
+ getPackageManagerInternalLocked().setCheckPermissionDelegate(null);
mHandler.obtainMessage(SHUTDOWN_UI_AUTOMATION_CONNECTION_MSG,
app.instr.mUiAutomationConnection).sendToTarget();
}
@@ -19333,22 +18588,14 @@
return config;
}
- ActivityStack getFocusedStack() {
- return mStackSupervisor.getFocusedStack();
- }
-
@Override
public StackInfo getFocusedStackInfo() throws RemoteException {
return mActivityTaskManager.getFocusedStackInfo();
}
+ @Override
public Configuration getConfiguration() {
- Configuration ci;
- synchronized(this) {
- ci = new Configuration(getGlobalConfiguration());
- ci.userSetLocale = false;
- }
- return ci;
+ return mActivityTaskManager.getConfiguration();
}
@Override
@@ -19366,34 +18613,7 @@
int userId = UserHandle.getCallingUserId();
- synchronized(this) {
- updatePersistentConfigurationLocked(values, userId);
- }
- }
-
- private void updatePersistentConfigurationLocked(Configuration values, @UserIdInt int userId) {
- final long origId = Binder.clearCallingIdentity();
- try {
- updateConfigurationLocked(values, null, false, true, userId, false /* deferResume */);
- } finally {
- Binder.restoreCallingIdentity(origId);
- }
- }
-
- private void updateFontScaleIfNeeded(@UserIdInt int userId) {
- final float scaleFactor = Settings.System.getFloatForUser(mContext.getContentResolver(),
- FONT_SCALE, 1.0f, userId);
-
- synchronized (this) {
- if (getGlobalConfiguration().fontScale == scaleFactor) {
- return;
- }
-
- final Configuration configuration
- = mWindowManager.computeNewConfiguration(DEFAULT_DISPLAY);
- configuration.fontScale = scaleFactor;
- updatePersistentConfigurationLocked(configuration, userId);
- }
+ mActivityTaskManager.updatePersistentConfiguration(values, userId);
}
private void enforceWriteSettingsPermission(String func) {
@@ -19420,379 +18640,6 @@
return mActivityTaskManager.updateConfiguration(values);
}
- void updateUserConfigurationLocked() {
- final Configuration configuration = new Configuration(getGlobalConfiguration());
- final int currentUserId = mUserController.getCurrentUserId();
- Settings.System.adjustConfigurationForUser(mContext.getContentResolver(), configuration,
- currentUserId, Settings.System.canWrite(mContext));
- updateConfigurationLocked(configuration, null /* starting */, false /* initLocale */,
- false /* persistent */, currentUserId, false /* deferResume */);
- }
-
- boolean updateConfigurationLocked(Configuration values, ActivityRecord starting,
- boolean initLocale) {
- return updateConfigurationLocked(values, starting, initLocale, false /* deferResume */);
- }
-
- boolean updateConfigurationLocked(Configuration values, ActivityRecord starting,
- boolean initLocale, boolean deferResume) {
- // pass UserHandle.USER_NULL as userId because we don't persist configuration for any user
- return updateConfigurationLocked(values, starting, initLocale, false /* persistent */,
- UserHandle.USER_NULL, deferResume);
- }
-
- // To cache the list of supported system locales
- private String[] mSupportedSystemLocales = null;
-
- private boolean updateConfigurationLocked(Configuration values, ActivityRecord starting,
- boolean initLocale, boolean persistent, int userId, boolean deferResume) {
- return updateConfigurationLocked(values, starting, initLocale, persistent, userId,
- deferResume, null /* result */);
- }
-
- /**
- * Do either or both things: (1) change the current configuration, and (2)
- * make sure the given activity is running with the (now) current
- * configuration. Returns true if the activity has been left running, or
- * false if <var>starting</var> is being destroyed to match the new
- * configuration.
- *
- * @param userId is only used when persistent parameter is set to true to persist configuration
- * for that particular user
- */
- boolean updateConfigurationLocked(Configuration values, ActivityRecord starting,
- boolean initLocale, boolean persistent, int userId, boolean deferResume,
- ActivityTaskManagerService.UpdateConfigurationResult result) {
- int changes = 0;
- boolean kept = true;
-
- if (mWindowManager != null) {
- mWindowManager.deferSurfaceLayout();
- }
- try {
- if (values != null) {
- changes = updateGlobalConfigurationLocked(values, initLocale, persistent, userId,
- deferResume);
- }
-
- kept = ensureConfigAndVisibilityAfterUpdate(starting, changes);
- } finally {
- if (mWindowManager != null) {
- mWindowManager.continueSurfaceLayout();
- }
- }
-
- if (result != null) {
- result.changes = changes;
- result.activityRelaunched = !kept;
- }
- return kept;
- }
-
- /**
- * Returns true if this configuration change is interesting enough to send an
- * {@link Intent#ACTION_SPLIT_CONFIGURATION_CHANGED} broadcast.
- */
- private static boolean isSplitConfigurationChange(int configDiff) {
- return (configDiff & (ActivityInfo.CONFIG_LOCALE | ActivityInfo.CONFIG_DENSITY)) != 0;
- }
-
- /** Update default (global) configuration and notify listeners about changes. */
- private int updateGlobalConfigurationLocked(@NonNull Configuration values, boolean initLocale,
- boolean persistent, int userId, boolean deferResume) {
- mActivityTaskManager.mTempConfig.setTo(getGlobalConfiguration());
- final int changes = mActivityTaskManager.mTempConfig.updateFrom(values);
- if (changes == 0) {
- // Since calling to Activity.setRequestedOrientation leads to freezing the window with
- // setting WindowManagerService.mWaitingForConfig to true, it is important that we call
- // performDisplayOverrideConfigUpdate in order to send the new display configuration
- // (even if there are no actual changes) to unfreeze the window.
- performDisplayOverrideConfigUpdate(values, deferResume, DEFAULT_DISPLAY);
- return 0;
- }
-
- if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.i(TAG_CONFIGURATION,
- "Updating global configuration to: " + values);
-
- EventLog.writeEvent(EventLogTags.CONFIGURATION_CHANGED, changes);
- StatsLog.write(StatsLog.RESOURCE_CONFIGURATION_CHANGED,
- values.colorMode,
- values.densityDpi,
- values.fontScale,
- values.hardKeyboardHidden,
- values.keyboard,
- values.keyboardHidden,
- values.mcc,
- values.mnc,
- values.navigation,
- values.navigationHidden,
- values.orientation,
- values.screenHeightDp,
- values.screenLayout,
- values.screenWidthDp,
- values.smallestScreenWidthDp,
- values.touchscreen,
- values.uiMode);
-
-
- if (!initLocale && !values.getLocales().isEmpty() && values.userSetLocale) {
- final LocaleList locales = values.getLocales();
- int bestLocaleIndex = 0;
- if (locales.size() > 1) {
- if (mSupportedSystemLocales == null) {
- mSupportedSystemLocales = Resources.getSystem().getAssets().getLocales();
- }
- bestLocaleIndex = Math.max(0, locales.getFirstMatchIndex(mSupportedSystemLocales));
- }
- SystemProperties.set("persist.sys.locale",
- locales.get(bestLocaleIndex).toLanguageTag());
- LocaleList.setDefault(locales, bestLocaleIndex);
- mHandler.sendMessage(mHandler.obtainMessage(SEND_LOCALE_TO_MOUNT_DAEMON_MSG,
- locales.get(bestLocaleIndex)));
- }
-
- mActivityTaskManager.mConfigurationSeq = Math.max(++mActivityTaskManager.mConfigurationSeq, 1);
- mActivityTaskManager.mTempConfig.seq = mActivityTaskManager.mConfigurationSeq;
-
- // Update stored global config and notify everyone about the change.
- mStackSupervisor.onConfigurationChanged(mActivityTaskManager.mTempConfig);
-
- Slog.i(TAG, "Config changes=" + Integer.toHexString(changes) + " " + mActivityTaskManager.mTempConfig);
- // TODO(multi-display): Update UsageEvents#Event to include displayId.
- mUsageStatsService.reportConfigurationChange(mActivityTaskManager.mTempConfig,
- mUserController.getCurrentUserId());
-
- // TODO: If our config changes, should we auto dismiss any currently showing dialogs?
- updateShouldShowDialogsLocked(mActivityTaskManager.mTempConfig);
-
- AttributeCache ac = AttributeCache.instance();
- if (ac != null) {
- ac.updateConfiguration(mActivityTaskManager.mTempConfig);
- }
-
- // Make sure all resources in our process are updated right now, so that anyone who is going
- // to retrieve resource values after we return will be sure to get the new ones. This is
- // especially important during boot, where the first config change needs to guarantee all
- // resources have that config before following boot code is executed.
- mSystemThread.applyConfigurationToResources(mActivityTaskManager.mTempConfig);
-
- // We need another copy of global config because we're scheduling some calls instead of
- // running them in place. We need to be sure that object we send will be handled unchanged.
- final Configuration configCopy = new Configuration(mActivityTaskManager.mTempConfig);
- if (persistent && Settings.System.hasInterestingConfigurationChanges(changes)) {
- Message msg = mHandler.obtainMessage(UPDATE_CONFIGURATION_MSG);
- msg.obj = configCopy;
- msg.arg1 = userId;
- mHandler.sendMessage(msg);
- }
-
- for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
- ProcessRecord app = mLruProcesses.get(i);
- try {
- if (app.thread != null) {
- if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, "Sending to proc "
- + app.processName + " new config " + configCopy);
- mActivityTaskManager.getLifecycleManager().scheduleTransaction(app.thread,
- ConfigurationChangeItem.obtain(configCopy));
- }
- } catch (Exception e) {
- Slog.e(TAG_CONFIGURATION, "Failed to schedule configuration change", e);
- }
- }
-
- Intent intent = new Intent(Intent.ACTION_CONFIGURATION_CHANGED);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_REPLACE_PENDING
- | Intent.FLAG_RECEIVER_FOREGROUND
- | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
- broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null,
- OP_NONE, null, false, false, MY_PID, SYSTEM_UID,
- UserHandle.USER_ALL);
- if ((changes & ActivityInfo.CONFIG_LOCALE) != 0) {
- intent = new Intent(Intent.ACTION_LOCALE_CHANGED);
- intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND
- | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND
- | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
- if (initLocale || !mProcessesReady) {
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
- }
- broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null,
- OP_NONE, null, false, false, MY_PID, SYSTEM_UID,
- UserHandle.USER_ALL);
- }
-
- // Send a broadcast to PackageInstallers if the configuration change is interesting
- // for the purposes of installing additional splits.
- if (!initLocale && isSplitConfigurationChange(changes)) {
- intent = new Intent(Intent.ACTION_SPLIT_CONFIGURATION_CHANGED);
- intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING
- | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
-
- // Typically only app stores will have this permission.
- String[] permissions = new String[] { android.Manifest.permission.INSTALL_PACKAGES };
- broadcastIntentLocked(null, null, intent, null, null, 0, null, null, permissions,
- OP_NONE, null, false, false, MY_PID, SYSTEM_UID, UserHandle.USER_ALL);
- }
-
- // Override configuration of the default display duplicates global config, so we need to
- // update it also. This will also notify WindowManager about changes.
- performDisplayOverrideConfigUpdate(mStackSupervisor.getConfiguration(), deferResume,
- DEFAULT_DISPLAY);
-
- return changes;
- }
-
- boolean updateDisplayOverrideConfigurationLocked(Configuration values, ActivityRecord starting,
- boolean deferResume, int displayId) {
- return updateDisplayOverrideConfigurationLocked(values, starting, deferResume /* deferResume */,
- displayId, null /* result */);
- }
-
- /**
- * Updates override configuration specific for the selected display. If no config is provided,
- * new one will be computed in WM based on current display info.
- */
- boolean updateDisplayOverrideConfigurationLocked(Configuration values,
- ActivityRecord starting, boolean deferResume, int displayId,
- ActivityTaskManagerService.UpdateConfigurationResult result) {
- int changes = 0;
- boolean kept = true;
-
- if (mWindowManager != null) {
- mWindowManager.deferSurfaceLayout();
- }
- try {
- if (values != null) {
- if (displayId == DEFAULT_DISPLAY) {
- // Override configuration of the default display duplicates global config, so
- // we're calling global config update instead for default display. It will also
- // apply the correct override config.
- changes = updateGlobalConfigurationLocked(values, false /* initLocale */,
- false /* persistent */, UserHandle.USER_NULL /* userId */, deferResume);
- } else {
- changes = performDisplayOverrideConfigUpdate(values, deferResume, displayId);
- }
- }
-
- kept = ensureConfigAndVisibilityAfterUpdate(starting, changes);
- } finally {
- if (mWindowManager != null) {
- mWindowManager.continueSurfaceLayout();
- }
- }
-
- if (result != null) {
- result.changes = changes;
- result.activityRelaunched = !kept;
- }
- return kept;
- }
-
- private int performDisplayOverrideConfigUpdate(Configuration values, boolean deferResume,
- int displayId) {
- mActivityTaskManager.mTempConfig.setTo(mStackSupervisor.getDisplayOverrideConfiguration(displayId));
- final int changes = mActivityTaskManager.mTempConfig.updateFrom(values);
- if (changes != 0) {
- Slog.i(TAG, "Override config changes=" + Integer.toHexString(changes) + " "
- + mActivityTaskManager.mTempConfig + " for displayId=" + displayId);
- mStackSupervisor.setDisplayOverrideConfiguration(mActivityTaskManager.mTempConfig, displayId);
-
- final boolean isDensityChange = (changes & ActivityInfo.CONFIG_DENSITY) != 0;
- if (isDensityChange && displayId == DEFAULT_DISPLAY) {
- mAppWarnings.onDensityChanged();
-
- killAllBackgroundProcessesExcept(N,
- ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
- }
- }
-
- // Update the configuration with WM first and check if any of the stacks need to be resized
- // due to the configuration change. If so, resize the stacks now and do any relaunches if
- // necessary. This way we don't need to relaunch again afterwards in
- // ensureActivityConfiguration().
- if (mWindowManager != null) {
- final int[] resizedStacks =
- mWindowManager.setNewDisplayOverrideConfiguration(mActivityTaskManager.mTempConfig, displayId);
- if (resizedStacks != null) {
- for (int stackId : resizedStacks) {
- resizeStackWithBoundsFromWindowManager(stackId, deferResume);
- }
- }
- }
-
- return changes;
- }
-
- /** Applies latest configuration and/or visibility updates if needed. */
- private boolean ensureConfigAndVisibilityAfterUpdate(ActivityRecord starting, int changes) {
- boolean kept = true;
- final ActivityStack mainStack = mStackSupervisor.getFocusedStack();
- // mainStack is null during startup.
- if (mainStack != null) {
- if (changes != 0 && starting == null) {
- // If the configuration changed, and the caller is not already
- // in the process of starting an activity, then find the top
- // activity to check if its configuration needs to change.
- starting = mainStack.topRunningActivityLocked();
- }
-
- if (starting != null) {
- kept = starting.ensureActivityConfiguration(changes,
- false /* preserveWindow */);
- // And we need to make sure at this point that all other activities
- // are made visible with the correct configuration.
- mStackSupervisor.ensureActivitiesVisibleLocked(starting, changes,
- !PRESERVE_WINDOWS);
- }
- }
-
- return kept;
- }
-
- /** Helper method that requests bounds from WM and applies them to stack. */
- private void resizeStackWithBoundsFromWindowManager(int stackId, boolean deferResume) {
- final Rect newStackBounds = new Rect();
- final ActivityStack stack = mStackSupervisor.getStack(stackId);
-
- // TODO(b/71548119): Revert CL introducing below once cause of mismatch is found.
- if (stack == null) {
- final StringWriter writer = new StringWriter();
- final PrintWriter printWriter = new PrintWriter(writer);
- mStackSupervisor.dumpDisplays(printWriter);
- printWriter.flush();
-
- Log.wtf(TAG, "stack not found:" + stackId + " displays:" + writer);
- }
-
- stack.getBoundsForNewConfiguration(newStackBounds);
- mStackSupervisor.resizeStackLocked(
- stack, !newStackBounds.isEmpty() ? newStackBounds : null /* bounds */,
- null /* tempTaskBounds */, null /* tempTaskInsetBounds */,
- false /* preserveWindows */, false /* allowResizeInDockedMode */, deferResume);
- }
-
- /**
- * Decide based on the configuration whether we should show the ANR,
- * crash, etc dialogs. The idea is that if there is no affordance to
- * press the on-screen buttons, or the user experience would be more
- * greatly impacted than the crash itself, we shouldn't show the dialog.
- *
- * A thought: SystemUI might also want to get told about this, the Power
- * dialog / global actions also might want different behaviors.
- */
- private void updateShouldShowDialogsLocked(Configuration config) {
- final boolean inputMethodExists = !(config.keyboard == Configuration.KEYBOARD_NOKEYS
- && config.touchscreen == Configuration.TOUCHSCREEN_NOTOUCH
- && config.navigation == Configuration.NAVIGATION_NONAV);
- int modeType = config.uiMode & Configuration.UI_MODE_TYPE_MASK;
- final boolean uiModeSupportsDialogs = (modeType != Configuration.UI_MODE_TYPE_CAR
- && !(modeType == Configuration.UI_MODE_TYPE_WATCH && Build.IS_USER)
- && modeType != Configuration.UI_MODE_TYPE_TELEVISION
- && modeType != Configuration.UI_MODE_TYPE_VR_HEADSET);
- final boolean hideDialogsSet = Settings.Global.getInt(mContext.getContentResolver(),
- HIDE_ERROR_DIALOGS, 0) != 0;
- mShowDialogs = inputMethodExists && uiModeSupportsDialogs && !hideDialogsSet;
- }
-
@Override
public int getLaunchedFromUid(IBinder activityToken) {
return mActivityTaskManager.getLaunchedFromUid(activityToken);
@@ -19832,7 +18679,8 @@
}
Association startAssociationLocked(int sourceUid, String sourceProcess, int sourceState,
- int targetUid, ComponentName targetComponent, String targetProcess) {
+ int targetUid, long targetVersionCode, ComponentName targetComponent,
+ String targetProcess) {
if (!mTrackingAssociations) {
return null;
}
@@ -19868,7 +18716,7 @@
}
void stopAssociationLocked(int sourceUid, String sourceProcess, int targetUid,
- ComponentName targetComponent) {
+ long targetVersionCode, ComponentName targetComponent, String targetProcess) {
if (!mTrackingAssociations) {
return;
}
@@ -19928,6 +18776,129 @@
}
}
+ private final ComputeOomAdjWindowCallback mTmpComputeOomAdjWindowCallback =
+ new ComputeOomAdjWindowCallback();
+
+ private final class ComputeOomAdjWindowCallback
+ implements WindowProcessController.ComputeOomAdjCallback {
+
+ ProcessRecord app;
+ int adj;
+ boolean foregroundActivities;
+ int procState;
+ int schedGroup;
+ int appUid;
+ int logUid;
+ int processStateCurTop;
+
+ void initialize(ProcessRecord app, int adj, boolean foregroundActivities,
+ int procState, int schedGroup, int appUid, int logUid, int processStateCurTop) {
+ this.app = app;
+ this.adj = adj;
+ this.foregroundActivities = foregroundActivities;
+ this.procState = procState;
+ this.schedGroup = schedGroup;
+ this.appUid = appUid;
+ this.logUid = logUid;
+ this.processStateCurTop = processStateCurTop;
+ }
+
+ @Override
+ public void onVisibleActivity() {
+ // App has a visible activity; only upgrade adjustment.
+ if (adj > ProcessList.VISIBLE_APP_ADJ) {
+ adj = ProcessList.VISIBLE_APP_ADJ;
+ app.adjType = "vis-activity";
+ if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+ reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise adj to vis-activity: " + app);
+ }
+ }
+ if (procState > processStateCurTop) {
+ procState = processStateCurTop;
+ app.adjType = "vis-activity";
+ if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+ reportOomAdjMessageLocked(TAG_OOM_ADJ,
+ "Raise procstate to vis-activity (top): " + app);
+ }
+ }
+ if (schedGroup < ProcessList.SCHED_GROUP_DEFAULT) {
+ schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
+ }
+ app.cached = false;
+ app.empty = false;
+ foregroundActivities = true;
+ }
+
+ @Override
+ public void onPausedActivity() {
+ if (adj > ProcessList.PERCEPTIBLE_APP_ADJ) {
+ adj = ProcessList.PERCEPTIBLE_APP_ADJ;
+ app.adjType = "pause-activity";
+ if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+ reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise adj to pause-activity: " + app);
+ }
+ }
+ if (procState > processStateCurTop) {
+ procState = processStateCurTop;
+ app.adjType = "pause-activity";
+ if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+ reportOomAdjMessageLocked(TAG_OOM_ADJ,
+ "Raise procstate to pause-activity (top): " + app);
+ }
+ }
+ if (schedGroup < ProcessList.SCHED_GROUP_DEFAULT) {
+ schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
+ }
+ app.cached = false;
+ app.empty = false;
+ foregroundActivities = true;
+ }
+
+ @Override
+ public void onStoppingActivity(boolean finishing) {
+ if (adj > ProcessList.PERCEPTIBLE_APP_ADJ) {
+ adj = ProcessList.PERCEPTIBLE_APP_ADJ;
+ app.adjType = "stop-activity";
+ if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+ reportOomAdjMessageLocked(TAG_OOM_ADJ,
+ "Raise adj to stop-activity: " + app);
+ }
+ }
+
+ // For the process state, we will at this point consider the process to be cached. It
+ // will be cached either as an activity or empty depending on whether the activity is
+ // finishing. We do this so that we can treat the process as cached for purposes of
+ // memory trimming (determining current memory level, trim command to send to process)
+ // since there can be an arbitrary number of stopping processes and they should soon all
+ // go into the cached state.
+ if (!finishing) {
+ if (procState > PROCESS_STATE_LAST_ACTIVITY) {
+ procState = PROCESS_STATE_LAST_ACTIVITY;
+ app.adjType = "stop-activity";
+ if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+ reportOomAdjMessageLocked(TAG_OOM_ADJ,
+ "Raise procstate to stop-activity: " + app);
+ }
+ }
+ }
+ app.cached = false;
+ app.empty = false;
+ foregroundActivities = true;
+ }
+
+ @Override
+ public void onOtherActivity() {
+ if (procState > PROCESS_STATE_CACHED_ACTIVITY) {
+ procState = PROCESS_STATE_CACHED_ACTIVITY;
+ app.adjType = "cch-act";
+ if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+ reportOomAdjMessageLocked(TAG_OOM_ADJ,
+ "Raise procstate to cached activity: " + app);
+ }
+ }
+ }
+ }
+
private final boolean computeOomAdjLocked(ProcessRecord app, int cachedAdj, ProcessRecord TOP_APP,
boolean doingAll, long now) {
if (mAdjSeq == app.adjSeq) {
@@ -19938,13 +18909,14 @@
// The process is being computed, so there is a cycle. We cannot
// rely on this process's state.
app.containsCycle = true;
+
return false;
}
}
if (app.thread == null) {
app.adjSeq = mAdjSeq;
- app.curSchedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
+ app.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_BACKGROUND);
app.curProcState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;
app.curAdj=app.curRawAdj=ProcessList.CACHED_APP_MAX_ADJ;
app.completedAdjSeq = app.adjSeq;
@@ -19957,11 +18929,12 @@
app.empty = false;
app.cached = false;
- final int activitiesSize = app.activities.size();
+ final WindowProcessController wpc = app.getWindowProcessController();
final int appUid = app.info.uid;
final int logUid = mCurOomAdjUid;
int prevAppAdj = app.curAdj;
+ int prevProcState = app.curProcState;
if (app.maxAdj <= ProcessList.FOREGROUND_APP_ADJ) {
// The max adjustment doesn't allow this app to be anything
@@ -19973,7 +18946,7 @@
app.adjSeq = mAdjSeq;
app.curRawAdj = app.maxAdj;
app.foregroundActivities = false;
- app.curSchedGroup = ProcessList.SCHED_GROUP_DEFAULT;
+ app.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_DEFAULT);
app.curProcState = ActivityManager.PROCESS_STATE_PERSISTENT;
// System processes can do UI, and when they do we want to have
// them trim their memory after the user leaves the UI. To
@@ -19982,29 +18955,24 @@
app.systemNoUi = true;
if (app == TOP_APP) {
app.systemNoUi = false;
- app.curSchedGroup = ProcessList.SCHED_GROUP_TOP_APP;
+ app.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_TOP_APP);
app.adjType = "pers-top-activity";
} else if (app.hasTopUi) {
// sched group/proc state adjustment is below
app.systemNoUi = false;
app.adjType = "pers-top-ui";
- } else if (activitiesSize > 0) {
- for (int j = 0; j < activitiesSize; j++) {
- final ActivityRecord r = app.activities.get(j);
- if (r.visible) {
- app.systemNoUi = false;
- }
- }
+ } else if (wpc.hasVisibleActivities()) {
+ app.systemNoUi = false;
}
if (!app.systemNoUi) {
if (mWakefulness == PowerManagerInternal.WAKEFULNESS_AWAKE) {
// screen on, promote UI
app.curProcState = ActivityManager.PROCESS_STATE_PERSISTENT_UI;
- app.curSchedGroup = ProcessList.SCHED_GROUP_TOP_APP;
+ app.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_TOP_APP);
} else {
// screen off, restrict UI scheduling
app.curProcState = ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
- app.curSchedGroup = ProcessList.SCHED_GROUP_RESTRICTED;
+ app.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_RESTRICTED);
}
}
app.curAdj = app.maxAdj;
@@ -20015,7 +18983,7 @@
app.systemNoUi = false;
- final int PROCESS_STATE_CUR_TOP = mTopProcessState;
+ final int PROCESS_STATE_CUR_TOP = mAtmInternal.getTopProcessState();
// Determine the importance of the process, starting with most
// important to least, and assign an appropriate OOM adjustment.
@@ -20103,120 +19071,23 @@
}
// Examine all activities if not already foreground.
- if (!foregroundActivities && activitiesSize > 0) {
- int minLayer = ProcessList.VISIBLE_APP_LAYER_MAX;
- for (int j = 0; j < activitiesSize; j++) {
- final ActivityRecord r = app.activities.get(j);
- if (r.app != app) {
- Log.e(TAG, "Found activity " + r + " in proc activity list using " + r.app
- + " instead of expected " + app);
- if (r.app == null || (r.app.uid == app.uid)) {
- // Only fix things up when they look sane
- r.setProcess(app);
- } else {
- continue;
- }
- }
- if (r.visible) {
- // App has a visible activity; only upgrade adjustment.
- if (adj > ProcessList.VISIBLE_APP_ADJ) {
- adj = ProcessList.VISIBLE_APP_ADJ;
- app.adjType = "vis-activity";
- if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
- reportOomAdjMessageLocked(TAG_OOM_ADJ,
- "Raise adj to vis-activity: " + app);
- }
- }
- if (procState > PROCESS_STATE_CUR_TOP) {
- procState = PROCESS_STATE_CUR_TOP;
- app.adjType = "vis-activity";
- if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
- reportOomAdjMessageLocked(TAG_OOM_ADJ,
- "Raise procstate to vis-activity (top): " + app);
- }
- }
- if (schedGroup < ProcessList.SCHED_GROUP_DEFAULT) {
- schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
- }
- app.cached = false;
- app.empty = false;
- foregroundActivities = true;
- final TaskRecord task = r.getTask();
- if (task != null && minLayer > 0) {
- final int layer = task.mLayerRank;
- if (layer >= 0 && minLayer > layer) {
- minLayer = layer;
- }
- }
- break;
- } else if (r.isState(ActivityState.PAUSING, ActivityState.PAUSED)) {
- if (adj > ProcessList.PERCEPTIBLE_APP_ADJ) {
- adj = ProcessList.PERCEPTIBLE_APP_ADJ;
- app.adjType = "pause-activity";
- if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
- reportOomAdjMessageLocked(TAG_OOM_ADJ,
- "Raise adj to pause-activity: " + app);
- }
- }
- if (procState > PROCESS_STATE_CUR_TOP) {
- procState = PROCESS_STATE_CUR_TOP;
- app.adjType = "pause-activity";
- if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
- reportOomAdjMessageLocked(TAG_OOM_ADJ,
- "Raise procstate to pause-activity (top): " + app);
- }
- }
- if (schedGroup < ProcessList.SCHED_GROUP_DEFAULT) {
- schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
- }
- app.cached = false;
- app.empty = false;
- foregroundActivities = true;
- } else if (r.isState(ActivityState.STOPPING)) {
- if (adj > ProcessList.PERCEPTIBLE_APP_ADJ) {
- adj = ProcessList.PERCEPTIBLE_APP_ADJ;
- app.adjType = "stop-activity";
- if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
- reportOomAdjMessageLocked(TAG_OOM_ADJ,
- "Raise adj to stop-activity: " + app);
- }
- }
- // For the process state, we will at this point consider the
- // process to be cached. It will be cached either as an activity
- // or empty depending on whether the activity is finishing. We do
- // this so that we can treat the process as cached for purposes of
- // memory trimming (determing current memory level, trim command to
- // send to process) since there can be an arbitrary number of stopping
- // processes and they should soon all go into the cached state.
- if (!r.finishing) {
- if (procState > ActivityManager.PROCESS_STATE_LAST_ACTIVITY) {
- procState = ActivityManager.PROCESS_STATE_LAST_ACTIVITY;
- app.adjType = "stop-activity";
- if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
- reportOomAdjMessageLocked(TAG_OOM_ADJ,
- "Raise procstate to stop-activity: " + app);
- }
- }
- }
- app.cached = false;
- app.empty = false;
- foregroundActivities = true;
- } else {
- if (procState > ActivityManager.PROCESS_STATE_CACHED_ACTIVITY) {
- procState = ActivityManager.PROCESS_STATE_CACHED_ACTIVITY;
- app.adjType = "cch-act";
- if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
- reportOomAdjMessageLocked(TAG_OOM_ADJ,
- "Raise procstate to cached activity: " + app);
- }
- }
- }
- }
+ if (!foregroundActivities && wpc.hasActivities()) {
+ mTmpComputeOomAdjWindowCallback.initialize(app, adj, foregroundActivities, procState,
+ schedGroup, appUid, logUid, PROCESS_STATE_CUR_TOP);
+ final int minLayer = wpc.computeOomAdjFromActivities(
+ ProcessList.VISIBLE_APP_LAYER_MAX, mTmpComputeOomAdjWindowCallback);
+
+ adj = mTmpComputeOomAdjWindowCallback.adj;
+ foregroundActivities = mTmpComputeOomAdjWindowCallback.foregroundActivities;
+ procState = mTmpComputeOomAdjWindowCallback.procState;
+ schedGroup = mTmpComputeOomAdjWindowCallback.schedGroup;
+
if (adj == ProcessList.VISIBLE_APP_ADJ) {
adj += minLayer;
}
}
- if (procState > ActivityManager.PROCESS_STATE_CACHED_RECENT && app.recentTasks.size() > 0) {
+
+ if (procState > ActivityManager.PROCESS_STATE_CACHED_RECENT && app.hasRecentTasks()) {
procState = ActivityManager.PROCESS_STATE_CACHED_RECENT;
app.adjType = "cch-rec";
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
@@ -20226,7 +19097,7 @@
if (adj > ProcessList.PERCEPTIBLE_APP_ADJ
|| procState > ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) {
- if (app.foregroundServices) {
+ if (app.hasForegroundServices()) {
// The user is aware of this app, so make it visible.
adj = ProcessList.PERCEPTIBLE_APP_ADJ;
procState = ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
@@ -20287,7 +19158,7 @@
}
}
- if (app == mHomeProcess) {
+ if (wpc == mActivityTaskManager.mHomeProcess) {
if (adj > ProcessList.HOME_APP_ADJ) {
// This process is hosting what we currently consider to be the
// home app, so we don't want to let it go into the background.
@@ -20308,7 +19179,7 @@
}
}
- if (app == mPreviousProcess && app.activities.size() > 0) {
+ if (wpc == mActivityTaskManager.mPreviousProcess && app.hasActivities()) {
if (adj > ProcessList.PREVIOUS_APP_ADJ) {
// This was the previous process that showed UI to the user.
// We want to try to keep it around more aggressively, to give
@@ -20321,8 +19192,8 @@
reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise adj to prev: " + app);
}
}
- if (procState > ActivityManager.PROCESS_STATE_LAST_ACTIVITY) {
- procState = ActivityManager.PROCESS_STATE_LAST_ACTIVITY;
+ if (procState > PROCESS_STATE_LAST_ACTIVITY) {
+ procState = PROCESS_STATE_LAST_ACTIVITY;
app.adjType = "previous";
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise procstate to prev: " + app);
@@ -20385,7 +19256,7 @@
"Raise procstate to started service: " + app);
}
}
- if (app.hasShownUi && app != mHomeProcess) {
+ if (app.hasShownUi && wpc != mActivityTaskManager.mHomeProcess) {
// If this process has shown some UI, let it immediately
// go to the LRU list because it may be pretty heavy with
// UI stuff. We'll tag it with a label just to help
@@ -20432,23 +19303,29 @@
// all connected clients.
ConnectionRecord cr = clist.get(i);
if (cr.binding.client == app) {
- // Binding to ourself is not interesting.
+ // Binding to oneself is not interesting.
continue;
}
+ boolean trackedProcState = false;
if ((cr.flags&Context.BIND_WAIVE_PRIORITY) == 0) {
ProcessRecord client = cr.binding.client;
computeOomAdjLocked(client, cachedAdj, TOP_APP, doingAll, now);
if (client.containsCycle) {
- // We've detected a cycle. We should ignore this connection and allow
- // this process to retry computeOomAdjLocked later in case a later-checked
- // connection from a client would raise its priority legitimately.
+ // We've detected a cycle. We should retry computeOomAdjLocked later in
+ // case a later-checked connection from a client would raise its
+ // priority legitimately.
app.containsCycle = true;
- continue;
+ // If the client has not been completely evaluated, skip using its
+ // priority. Else use the conservative value for now and look for a
+ // better state in the next iteration.
+ if (client.completedAdjSeq < mAdjSeq) {
+ continue;
+ }
}
int clientAdj = client.curRawAdj;
int clientProcState = client.curProcState;
- if (clientProcState >= ActivityManager.PROCESS_STATE_CACHED_ACTIVITY) {
+ if (clientProcState >= PROCESS_STATE_CACHED_ACTIVITY) {
// If the other app is cached for any reason, for purposes here
// we are going to consider it empty. The specific cached state
// doesn't propagate except under certain conditions.
@@ -20458,7 +19335,7 @@
if ((cr.flags&Context.BIND_ALLOW_OOM_MANAGEMENT) != 0) {
// Not doing bind OOM management, so treat
// this guy more like a started service.
- if (app.hasShownUi && app != mHomeProcess) {
+ if (app.hasShownUi && wpc != mActivityTaskManager.mHomeProcess) {
// If this process has shown some UI, let it immediately
// go to the LRU list because it may be pretty heavy with
// UI stuff. We'll tag it with a label just to help
@@ -20491,7 +19368,7 @@
// about letting this process get into the LRU
// list to be killed and restarted if needed for
// memory.
- if (app.hasShownUi && app != mHomeProcess
+ if (app.hasShownUi && wpc != mActivityTaskManager.mHomeProcess
&& clientAdj > ProcessList.PERCEPTIBLE_APP_ADJ) {
if (adj >= ProcessList.CACHED_APP_MIN_ADJ) {
adjType = "cch-bound-ui-services";
@@ -20507,6 +19384,8 @@
newAdj = ProcessList.PERSISTENT_SERVICE_ADJ;
schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
procState = ActivityManager.PROCESS_STATE_PERSISTENT;
+ cr.trackProcState(procState, mAdjSeq, now);
+ trackedProcState = true;
}
} else if ((cr.flags&Context.BIND_NOT_VISIBLE) != 0
&& clientAdj < ProcessList.PERCEPTIBLE_APP_ADJ
@@ -20535,9 +19414,10 @@
// This will treat important bound services identically to
// the top app, which may behave differently than generic
// foreground work.
- if (client.curSchedGroup > schedGroup) {
+ final int curSchedGroup = client.getCurrentSchedulingGroup();
+ if (curSchedGroup > schedGroup) {
if ((cr.flags&Context.BIND_IMPORTANT) != 0) {
- schedGroup = client.curSchedGroup;
+ schedGroup = curSchedGroup;
} else {
schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
}
@@ -20591,6 +19471,9 @@
ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND;
}
}
+ if (!trackedProcState) {
+ cr.trackProcState(clientProcState, mAdjSeq, now);
+ }
if (procState > clientProcState) {
procState = clientProcState;
if (adjType == null) {
@@ -20667,22 +19550,27 @@
}
computeOomAdjLocked(client, cachedAdj, TOP_APP, doingAll, now);
if (client.containsCycle) {
- // We've detected a cycle. We should ignore this connection and allow
- // this process to retry computeOomAdjLocked later in case a later-checked
- // connection from a client would raise its priority legitimately.
+ // We've detected a cycle. We should retry computeOomAdjLocked later in
+ // case a later-checked connection from a client would raise its
+ // priority legitimately.
app.containsCycle = true;
- continue;
+ // If the client has not been completely evaluated, skip using its
+ // priority. Else use the conservative value for now and look for a
+ // better state in the next iteration.
+ if (client.completedAdjSeq < mAdjSeq) {
+ continue;
+ }
}
int clientAdj = client.curRawAdj;
int clientProcState = client.curProcState;
- if (clientProcState >= ActivityManager.PROCESS_STATE_CACHED_ACTIVITY) {
+ if (clientProcState >= PROCESS_STATE_CACHED_ACTIVITY) {
// If the other app is cached for any reason, for purposes here
// we are going to consider it empty.
clientProcState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;
}
String adjType = null;
if (adj > clientAdj) {
- if (app.hasShownUi && app != mHomeProcess
+ if (app.hasShownUi && wpc != mActivityTaskManager.mHomeProcess
&& clientAdj > ProcessList.PERCEPTIBLE_APP_ADJ) {
adjType = "cch-ui-provider";
} else {
@@ -20720,10 +19608,11 @@
}
}
}
+ conn.trackProcState(clientProcState, mAdjSeq, now);
if (procState > clientProcState) {
procState = clientProcState;
}
- if (client.curSchedGroup > schedGroup) {
+ if (client.getCurrentSchedulingGroup() > schedGroup) {
schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
}
if (adjType != null) {
@@ -20776,8 +19665,8 @@
"Raise adj to recent provider: " + app);
}
}
- if (procState > ActivityManager.PROCESS_STATE_LAST_ACTIVITY) {
- procState = ActivityManager.PROCESS_STATE_LAST_ACTIVITY;
+ if (procState > PROCESS_STATE_LAST_ACTIVITY) {
+ procState = PROCESS_STATE_LAST_ACTIVITY;
app.adjType = "recent-provider";
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ,
@@ -20839,7 +19728,7 @@
} else if (app.treatLikeActivity) {
// This is a cached process, but somebody wants us to treat it like it has
// an activity, okay!
- procState = ActivityManager.PROCESS_STATE_CACHED_ACTIVITY;
+ procState = PROCESS_STATE_CACHED_ACTIVITY;
app.adjType = "cch-as-act";
}
}
@@ -20898,13 +19787,13 @@
// worry about this for max adj above, since max adj will always be used to
// keep it out of the cached vaues.
app.curAdj = app.modifyRawOomAdj(adj);
- app.curSchedGroup = schedGroup;
+ app.setCurrentSchedulingGroup(schedGroup);
app.curProcState = procState;
app.foregroundActivities = foregroundActivities;
app.completedAdjSeq = mAdjSeq;
- // if curAdj is less than prevAppAdj, then this process was promoted
- return app.curAdj < prevAppAdj;
+ // if curAdj or curProcState improved, then this process was promoted
+ return app.curAdj < prevAppAdj || app.curProcState < prevProcState;
}
/**
@@ -20915,7 +19804,8 @@
EventLogTags.writeAmPss(proc.pid, proc.uid, proc.processName, pss * 1024, uss * 1024,
swapPss * 1024, rss * 1024, statType, procState, pssDuration);
proc.lastPssTime = now;
- proc.baseProcessTracker.addPss(pss, uss, rss, true, statType, pssDuration, proc.pkgList);
+ proc.baseProcessTracker.addPss(
+ pss, uss, rss, true, statType, pssDuration, proc.pkgList.mPkgList);
if (DEBUG_PSS) Slog.d(TAG_PSS,
"pss of " + proc.toShortString() + ": " + pss + " lastPss=" + proc.lastPss
+ " state=" + ProcessList.makeProcStateString(procState));
@@ -21056,7 +19946,7 @@
app.pssStatType = always ? ProcessStats.ADD_PSS_INTERNAL_ALL_POLL
: ProcessStats.ADD_PSS_INTERNAL_ALL_MEM;
app.nextPssTime = ProcessList.computeNextPssTime(app.curProcState,
- app.procStateMemTracker, mTestPssMode, isSleepingLocked(), now);
+ app.procStateMemTracker, mTestPssMode, mAtmInternal.isSleeping(), now);
mPendingPssProcesses.add(app);
}
}
@@ -21106,7 +19996,7 @@
}
}
return !processingBroadcasts
- && (isSleepingLocked() || mStackSupervisor.allResumedActivitiesIdle());
+ && (mAtmInternal.isSleeping() || mStackSupervisor.allResumedActivitiesIdle());
}
/**
@@ -21266,7 +20156,7 @@
}
app.kill("excessive cpu " + cputimeUsed + " during " + uptimeSince
+ " dur=" + checkDur + " limit=" + cpuLimit, true);
- app.baseProcessTracker.reportExcessiveCpu(app.pkgList);
+ app.baseProcessTracker.reportExcessiveCpu(app.pkgList.mPkgList);
}
}
app.lastCpuTime = app.curCpuTime;
@@ -21295,12 +20185,13 @@
app.verifiedAdj = ProcessList.INVALID_ADJ;
}
- if (app.setSchedGroup != app.curSchedGroup) {
+ final int curSchedGroup = app.getCurrentSchedulingGroup();
+ if (app.setSchedGroup != curSchedGroup) {
int oldSchedGroup = app.setSchedGroup;
- app.setSchedGroup = app.curSchedGroup;
+ app.setSchedGroup = curSchedGroup;
if (DEBUG_SWITCH || DEBUG_OOM_ADJ || mCurOomAdjUid == app.uid) {
String msg = "Setting sched group of " + app.processName
- + " to " + app.curSchedGroup + ": " + app.adjType;
+ + " to " + curSchedGroup + ": " + app.adjType;
reportOomAdjMessageLocked(TAG_OOM_ADJ, msg);
}
if (app.waitingToKill != null && app.curReceivers.isEmpty()
@@ -21309,7 +20200,7 @@
success = false;
} else {
int processGroup;
- switch (app.curSchedGroup) {
+ switch (curSchedGroup) {
case ProcessList.SCHED_GROUP_BACKGROUND:
processGroup = THREAD_GROUP_BG_NONINTERACTIVE;
break;
@@ -21327,10 +20218,11 @@
long oldId = Binder.clearCallingIdentity();
try {
setProcessGroup(app.pid, processGroup);
- if (app.curSchedGroup == ProcessList.SCHED_GROUP_TOP_APP) {
+ if (curSchedGroup == ProcessList.SCHED_GROUP_TOP_APP) {
// do nothing if we already switched to RT
if (oldSchedGroup != ProcessList.SCHED_GROUP_TOP_APP) {
- mActivityTaskManager.onTopProcChangedLocked(app);
+ mActivityTaskManager.onTopProcChangedLocked(
+ app.getWindowProcessController());
if (mUseFifoUiScheduling) {
// Switch UI pipeline for app to SCHED_FIFO
app.savedPriority = Process.getThreadPriority(app.pid);
@@ -21361,8 +20253,9 @@
}
}
} else if (oldSchedGroup == ProcessList.SCHED_GROUP_TOP_APP &&
- app.curSchedGroup != ProcessList.SCHED_GROUP_TOP_APP) {
- mActivityTaskManager.onTopProcChangedLocked(app);
+ curSchedGroup != ProcessList.SCHED_GROUP_TOP_APP) {
+ mActivityTaskManager.onTopProcChangedLocked(
+ app.getWindowProcessController());
if (mUseFifoUiScheduling) {
try {
// Reset UI pipeline to SCHED_OTHER
@@ -21391,7 +20284,7 @@
} catch (Exception e) {
if (false) {
Slog.w(TAG, "Failed setting process group of " + app.pid
- + " to " + app.curSchedGroup);
+ + " to " + app.getCurrentSchedulingGroup());
Slog.w(TAG, "at location", e);
}
} finally {
@@ -21403,16 +20296,16 @@
app.repForegroundActivities = app.foregroundActivities;
changes |= ProcessChangeItem.CHANGE_ACTIVITIES;
}
- if (app.repProcState != app.curProcState) {
- app.repProcState = app.curProcState;
+ if (app.getReportedProcState() != app.curProcState) {
+ app.setReportedProcState(app.curProcState);
if (app.thread != null) {
try {
if (false) {
//RuntimeException h = new RuntimeException("here");
- Slog.i(TAG, "Sending new process state " + app.repProcState
+ Slog.i(TAG, "Sending new process state " + app.getReportedProcState()
+ " to " + app /*, h*/);
}
- app.thread.setProcessState(app.repProcState);
+ app.thread.setProcessState(app.getReportedProcState());
} catch (RemoteException e) {
}
}
@@ -21437,7 +20330,7 @@
}
app.lastStateTime = now;
app.nextPssTime = ProcessList.computeNextPssTime(app.curProcState,
- app.procStateMemTracker, mTestPssMode, isSleepingLocked(), now);
+ app.procStateMemTracker, mTestPssMode, mAtmInternal.isSleeping(), now);
if (DEBUG_PSS) Slog.d(TAG_PSS, "Process state change from "
+ ProcessList.makeProcStateString(app.setProcState) + " to "
+ ProcessList.makeProcStateString(app.curProcState) + " next pss in "
@@ -21448,7 +20341,7 @@
mTestPssMode)))) {
if (requestPssLocked(app, app.setProcState)) {
app.nextPssTime = ProcessList.computeNextPssTime(app.curProcState,
- app.procStateMemTracker, mTestPssMode, isSleepingLocked(), now);
+ app.procStateMemTracker, mTestPssMode, mAtmInternal.isSleeping(), now);
}
} else if (false && DEBUG_PSS) Slog.d(TAG_PSS,
"Not requesting pss of " + app + ": next=" + (app.nextPssTime-now));
@@ -21699,10 +20592,9 @@
}
private final void setProcessTrackerStateLocked(ProcessRecord proc, int memFactor, long now) {
- if (proc.thread != null) {
- if (proc.baseProcessTracker != null) {
- proc.baseProcessTracker.setState(proc.repProcState, memFactor, now, proc.pkgList);
- }
+ if (proc.thread != null && proc.baseProcessTracker != null) {
+ proc.baseProcessTracker.setState(
+ proc.getReportedProcState(), memFactor, now, proc.pkgList.mPkgList);
}
}
@@ -21720,8 +20612,8 @@
@GuardedBy("this")
final void updateProcessForegroundLocked(ProcessRecord proc, boolean isForeground,
boolean oomAdj) {
- if (isForeground != proc.foregroundServices) {
- proc.foregroundServices = isForeground;
+ if (isForeground != proc.hasForegroundServices()) {
+ proc.setHasForegroundServices(isForeground);
ArrayList<ProcessRecord> curProcs = mForegroundPackages.get(proc.info.packageName,
proc.info.uid);
if (isForeground) {
@@ -21752,9 +20644,10 @@
}
}
- private final ActivityRecord resumedAppLocked() {
- final ActivityRecord act =
- mStackSupervisor != null ? mStackSupervisor.getResumedActivityLocked() : null;
+ // TODO(b/111541062): This method is only used for updating OOM adjustments. We need to update
+ // the logic there and in mBatteryStatsService to make them aware of multiple resumed activities
+ private ActivityRecord resumedAppLocked() {
+ final ActivityRecord act = mStackSupervisor.getTopResumedActivity();
String pkg;
int uid;
if (act != null) {
@@ -21767,16 +20660,23 @@
// Has the UID or resumed package name changed?
if (uid != mCurResumedUid || (pkg != mCurResumedPackage
&& (pkg == null || !pkg.equals(mCurResumedPackage)))) {
- if (mCurResumedPackage != null) {
- mBatteryStatsService.noteEvent(BatteryStats.HistoryItem.EVENT_TOP_FINISH,
- mCurResumedPackage, mCurResumedUid);
+
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ if (mCurResumedPackage != null) {
+ mBatteryStatsService.noteEvent(BatteryStats.HistoryItem.EVENT_TOP_FINISH,
+ mCurResumedPackage, mCurResumedUid);
+ }
+ mCurResumedPackage = pkg;
+ mCurResumedUid = uid;
+ if (mCurResumedPackage != null) {
+ mBatteryStatsService.noteEvent(BatteryStats.HistoryItem.EVENT_TOP_START,
+ mCurResumedPackage, mCurResumedUid);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
}
- mCurResumedPackage = pkg;
- mCurResumedUid = uid;
- if (mCurResumedPackage != null) {
- mBatteryStatsService.noteEvent(BatteryStats.HistoryItem.EVENT_TOP_START,
- mCurResumedPackage, mCurResumedUid);
- }
+
}
return act;
}
@@ -21791,7 +20691,8 @@
@GuardedBy("this")
final boolean updateOomAdjLocked(ProcessRecord app, boolean oomAdjAll) {
final ActivityRecord TOP_ACT = resumedAppLocked();
- final ProcessRecord TOP_APP = TOP_ACT != null ? TOP_ACT.app : null;
+ final ProcessRecord TOP_APP = TOP_ACT != null && TOP_ACT.hasProcess()
+ ? (ProcessRecord) TOP_ACT.app.mOwner : null;
final boolean wasCached = app.cached;
mAdjSeq++;
@@ -21814,20 +20715,24 @@
}
@GuardedBy("this")
- final void updateOomAdjLocked() {
+ ProcessRecord getTopAppLocked() {
final ActivityRecord TOP_ACT = resumedAppLocked();
- final ProcessRecord TOP_APP = TOP_ACT != null ? TOP_ACT.app : null;
+ if (TOP_ACT != null && TOP_ACT.hasProcess()) {
+ return (ProcessRecord) TOP_ACT.app.mOwner;
+ } else {
+ return null;
+ }
+ }
+
+ @GuardedBy("this")
+ final void updateOomAdjLocked() {
+ mOomAdjProfiler.oomAdjStarted();
+ final ProcessRecord TOP_APP = getTopAppLocked();
final long now = SystemClock.uptimeMillis();
final long nowElapsed = SystemClock.elapsedRealtime();
final long oldTime = now - ProcessList.MAX_EMPTY_TIME;
final int N = mLruProcesses.size();
- if (false) {
- RuntimeException e = new RuntimeException();
- e.fillInStackTrace();
- Slog.i(TAG, "updateOomAdj: top=" + TOP_ACT, e);
- }
-
// Reset state in all uid records.
for (int i=mActiveUids.size()-1; i>=0; i--) {
final UidRecord uidRec = mActiveUids.valueAt(i);
@@ -21903,7 +20808,7 @@
// to the process, do that now.
if (app.curAdj >= ProcessList.UNKNOWN_ADJ) {
switch (app.curProcState) {
- case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY:
+ case PROCESS_STATE_CACHED_ACTIVITY:
case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT:
case ActivityManager.PROCESS_STATE_CACHED_RECENT:
// This process is a cached process holding activities...
@@ -21961,7 +20866,7 @@
// - Continue retrying until no process was promoted.
// - Iterate from least important to most important.
int cycleCount = 0;
- while (retryCycles) {
+ while (retryCycles && cycleCount < 10) {
cycleCount++;
retryCycles = false;
@@ -21976,12 +20881,14 @@
for (int i=0; i<N; i++) {
ProcessRecord app = mLruProcesses.get(i);
if (!app.killedByAm && app.thread != null && app.containsCycle == true) {
+
if (computeOomAdjLocked(app, ProcessList.UNKNOWN_ADJ, TOP_APP, true, now)) {
retryCycles = true;
}
}
}
}
+
for (int i=N-1; i>=0; i--) {
ProcessRecord app = mLruProcesses.get(i);
if (!app.killedByAm && app.thread != null) {
@@ -21989,7 +20896,7 @@
// Count the number of process types.
switch (app.curProcState) {
- case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY:
+ case PROCESS_STATE_CACHED_ACTIVITY:
case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT:
mNumCachedHiddenProcs++;
numCached++;
@@ -22032,7 +20939,7 @@
if (uidRec.curProcState > app.curProcState) {
uidRec.curProcState = app.curProcState;
}
- if (app.foregroundServices) {
+ if (app.hasForegroundServices()) {
uidRec.foregroundServices = true;
}
}
@@ -22045,6 +20952,8 @@
}
}
+ mProcessStats.updateTrackingAssociationsLocked(mAdjSeq, now);
+
incrementProcStateSeqAndNotifyAppsLocked();
mNumServiceProcs = mNewNumServiceProcs;
@@ -22086,7 +20995,8 @@
}
mLastMemoryLevel = memFactor;
mLastNumProcesses = mLruProcesses.size();
- boolean allChanged = mProcessStats.setMemFactorLocked(memFactor, !isSleepingLocked(), now);
+ boolean allChanged = mProcessStats.setMemFactorLocked(
+ memFactor, mAtmInternal != null ? !mAtmInternal.isSleeping() : true, now);
final int trackerMemFactor = mProcessStats.getMemFactorLocked();
if (memFactor != ProcessStats.ADJ_MEM_FACTOR_NORMAL) {
if (mLowRamStartTime == 0) {
@@ -22107,8 +21017,8 @@
}
int factor = numTrimming/3;
int minFactor = 2;
- if (mHomeProcess != null) minFactor++;
- if (mPreviousProcess != null) minFactor++;
+ if (mActivityTaskManager.mHomeProcess != null) minFactor++;
+ if (mActivityTaskManager.mPreviousProcess != null) minFactor++;
if (factor < minFactor) factor = minFactor;
int curLevel = ComponentCallbacks2.TRIM_MEMORY_COMPLETE;
for (int i=N-1; i>=0; i--) {
@@ -22126,19 +21036,6 @@
app.thread.scheduleTrimMemory(curLevel);
} catch (RemoteException e) {
}
- if (false) {
- // For now we won't do this; our memory trimming seems
- // to be good enough at this point that destroying
- // activities causes more harm than good.
- if (curLevel >= ComponentCallbacks2.TRIM_MEMORY_COMPLETE
- && app != mHomeProcess && app != mPreviousProcess) {
- // Need to do this on its own message because the stack may not
- // be in a consistent state at this point.
- // For these apps we will also finish their activities
- // to help them free memory.
- mStackSupervisor.scheduleDestroyAllActivities(app, "trim");
- }
- }
}
app.trimMemoryLevel = curLevel;
step++;
@@ -22333,6 +21230,7 @@
Slog.d(TAG_OOM_ADJ, "Did OOM ADJ in " + duration + "ms");
}
}
+ mOomAdjProfiler.oomAdjEnded();
}
@Override
@@ -22689,7 +21587,7 @@
// has been removed.
for (int i=mRemovedProcesses.size()-1; i>=0; i--) {
final ProcessRecord app = mRemovedProcesses.get(i);
- if (app.activities.size() == 0 && app.recentTasks.size() == 0
+ if (!app.hasActivitiesOrRecentTasks()
&& app.curReceivers.isEmpty() && app.services.size() == 0) {
Slog.i(
TAG, "Exiting empty application process "
@@ -22708,7 +21606,7 @@
cleanUpApplicationRecordLocked(app, false, true, -1, false /*replacingPid*/);
mRemovedProcesses.remove(i);
- if (app.persistent) {
+ if (app.isPersistent()) {
addAppLocked(app.info, null, false, null /* ABI override */);
}
}
@@ -22734,7 +21632,7 @@
for (int i = mLruProcesses.size() - 1 ; i >= 0 ; i--) {
ProcessRecord r = mLruProcesses.get(i);
- if (r.thread != null && r.persistent) {
+ if (r.thread != null && r.isPersistent()) {
sendSignal(r.pid, sig);
}
}
@@ -23230,15 +22128,6 @@
}
@Override
- public void onUserRemoved(int userId) {
- synchronized (ActivityManagerService.this) {
- ActivityManagerService.this.onUserStoppedLocked(userId);
- }
- mBatteryStatsService.onUserRemoved(userId);
- mUserController.onUserRemoved(userId);
- }
-
- @Override
public void killForegroundAppsForUser(int userHandle) {
synchronized (ActivityManagerService.this) {
final ArrayList<ProcessRecord> procs = new ArrayList<>();
@@ -23248,7 +22137,7 @@
final int NA = apps.size();
for (int ia = 0; ia < NA; ia++) {
final ProcessRecord app = apps.valueAt(ia);
- if (app.persistent) {
+ if (app.isPersistent()) {
// We don't kill persistent processes.
continue;
}
@@ -23297,17 +22186,6 @@
}
@Override
- public void updatePersistentConfigurationForUser(@NonNull Configuration values,
- int userId) {
- Preconditions.checkNotNull(values, "Configuration must not be null");
- Preconditions.checkArgumentNonnegative(userId, "userId " + userId + " not supported");
- synchronized (ActivityManagerService.this) {
- updateConfigurationLocked(values, null, false, true, userId,
- false /* deferResume */);
- }
- }
-
- @Override
public int getUidProcessState(int uid) {
return getUidState(uid);
}
@@ -23425,27 +22303,6 @@
}
@Override
- public void setAllowAppSwitches(@NonNull String type, int uid, int userId) {
- synchronized (ActivityManagerService.this) {
- if (mUserController.isUserRunning(userId, ActivityManager.FLAG_OR_STOPPED)) {
- ArrayMap<String, Integer> types = mAllowAppSwitchUids.get(userId);
- if (types == null) {
- if (uid < 0) {
- return;
- }
- types = new ArrayMap<>();
- mAllowAppSwitchUids.put(userId, types);
- }
- if (uid < 0) {
- types.remove(type);
- } else {
- types.put(type, uid);
- }
- }
- }
- }
-
- @Override
public boolean isRuntimeRestarted() {
return mSystemServiceManager.isRuntimeRestarted();
}
@@ -23508,6 +22365,140 @@
}
return processMemoryStates;
}
+
+ @Override
+ public int handleIncomingUser(int callingPid, int callingUid, int userId,
+ boolean allowAll, int allowMode, String name, String callerPackage) {
+ return mUserController.handleIncomingUser(callingPid, callingUid, userId, allowAll,
+ allowMode, name, callerPackage);
+ }
+
+ @Override
+ public void enforceCallingPermission(String permission, String func) {
+ ActivityManagerService.this.enforceCallingPermission(permission, func);
+ }
+
+ @Override
+ public int getCurrentUserId() {
+ return mUserController.getCurrentUserIdLU();
+ }
+
+ @Override
+ public boolean isUserRunning(int userId, int flags) {
+ // Holding am lock isn't required to call into user controller.
+ return mUserController.isUserRunning(userId, flags);
+ }
+
+ @Override
+ public void trimApplications() {
+ ActivityManagerService.this.trimApplications();
+ }
+
+ public int getPackageScreenCompatMode(ApplicationInfo ai) {
+ synchronized (ActivityManagerService.this) {
+ return mCompatModePackages.computeCompatModeLocked(ai);
+ }
+ }
+
+ public void setPackageScreenCompatMode(ApplicationInfo ai, int mode) {
+ synchronized (ActivityManagerService.this) {
+ mCompatModePackages.setPackageScreenCompatModeLocked(ai, mode);
+ }
+ }
+
+ public void closeSystemDialogs(String reason) {
+ ActivityManagerService.this.closeSystemDialogs(reason);
+ }
+
+ public void killProcessesForRemovedTask(ArrayList<Object> procsToKill) {
+ synchronized (ActivityManagerService.this) {
+ for (int i = 0; i < procsToKill.size(); i++) {
+ final WindowProcessController wpc =
+ (WindowProcessController) procsToKill.get(i);
+ final ProcessRecord pr = (ProcessRecord) wpc.mOwner;
+ if (pr.setSchedGroup == ProcessList.SCHED_GROUP_BACKGROUND
+ && pr.curReceivers.isEmpty()) {
+ pr.kill("remove task", true);
+ } else {
+ // We delay killing processes that are not in the background or running a
+ // receiver.
+ pr.waitingToKill = "remove task";
+ }
+ }
+ }
+ }
+
+ @Override
+ public boolean hasRunningActivity(int uid, @Nullable String packageName) {
+ if (packageName == null) return false;
+
+ synchronized (ActivityManagerService.this) {
+ for (int i = 0; i < mLruProcesses.size(); i++) {
+ final ProcessRecord pr = mLruProcesses.get(i);
+ if (pr.uid != uid) {
+ continue;
+ }
+ if (pr.getWindowProcessController().hasRunningActivity(packageName)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public void updateOomAdj() {
+ synchronized (ActivityManagerService.this) {
+ ActivityManagerService.this.updateOomAdjLocked();
+ }
+ }
+
+ @Override
+ public void sendForegroundProfileChanged(int userId) {
+ mUserController.sendForegroundProfileChanged(userId);
+ }
+
+ @Override
+ public boolean shouldConfirmCredentials(int userId) {
+ return mUserController.shouldConfirmCredentials(userId);
+ }
+
+ @Override
+ public int[] getCurrentProfileIds() {
+ return mUserController.getCurrentProfileIds();
+ }
+
+ @Override
+ public UserInfo getCurrentUser() {
+ return mUserController.getCurrentUser();
+ }
+
+ @Override
+ public void ensureNotSpecialUser(int userId) {
+ mUserController.ensureNotSpecialUser(userId);
+ }
+
+ @Override
+ public boolean isCurrentProfile(int userId) {
+ return mUserController.isCurrentProfile(userId);
+ }
+
+ @Override
+ public boolean hasStartedUserState(int userId) {
+ return mUserController.hasStartedUserState(userId);
+ }
+
+ @Override
+ public void finishUserSwitch(Object uss) {
+ mUserController.finishUserSwitch((UserState) uss);
+ }
+
+ @Override
+ public Intent getHomeIntent() {
+ synchronized (ActivityManagerService.this) {
+ return ActivityManagerService.this.getHomeIntent();
+ }
+ }
}
/**
@@ -23758,4 +22749,143 @@
return mNmi != null;
}
}
+
+ @Override
+ public void startDelegateShellPermissionIdentity(int delegateUid) {
+ if (UserHandle.getCallingAppId() != Process.SHELL_UID
+ && UserHandle.getCallingAppId() != Process.ROOT_UID) {
+ throw new SecurityException("Only the shell can delegate its permissions");
+ }
+
+ // We allow delegation only to one instrumentation started from the shell
+ synchronized (ActivityManagerService.this) {
+ // If there is a delegate it should be the same instance for app ops and permissions.
+ if (mAppOpsService.getAppOpsServiceDelegate()
+ != getPackageManagerInternalLocked().getCheckPermissionDelegate()) {
+ throw new IllegalStateException("Bad shell delegate state");
+ }
+
+ // If the delegate is already set up for the target UID, nothing to do.
+ if (mAppOpsService.getAppOpsServiceDelegate() != null) {
+ if (!(mAppOpsService.getAppOpsServiceDelegate() instanceof ShellDelegate)) {
+ throw new IllegalStateException("Bad shell delegate state");
+ }
+ if (((ShellDelegate) mAppOpsService.getAppOpsServiceDelegate())
+ .getDelegateUid() != delegateUid) {
+ throw new SecurityException("Shell can delegate permissions only "
+ + "to one instrumentation at a time");
+ }
+ return;
+ }
+
+ final int instrCount = mActiveInstrumentation.size();
+ for (int i = 0; i < instrCount; i++) {
+ final ActiveInstrumentation instr = mActiveInstrumentation.get(i);
+ if (instr.mTargetInfo.uid != delegateUid) {
+ continue;
+ }
+ // If instrumentation started from the shell the connection is not null
+ if (instr.mUiAutomationConnection == null) {
+ throw new SecurityException("Shell can delegate its permissions" +
+ " only to an instrumentation started from the shell");
+ }
+
+ // Hook them up...
+ final ShellDelegate shellDelegate = new ShellDelegate(
+ instr.mTargetInfo.packageName, delegateUid);
+ mAppOpsService.setAppOpsServiceDelegate(shellDelegate);
+ getPackageManagerInternalLocked().setCheckPermissionDelegate(shellDelegate);
+ return;
+ }
+ }
+ }
+
+ @Override
+ public void stopDelegateShellPermissionIdentity() {
+ if (UserHandle.getCallingAppId() != Process.SHELL_UID
+ && UserHandle.getCallingAppId() != Process.ROOT_UID) {
+ throw new SecurityException("Only the shell can delegate its permissions");
+ }
+ synchronized (ActivityManagerService.this) {
+ mAppOpsService.setAppOpsServiceDelegate(null);
+ getPackageManagerInternalLocked().setCheckPermissionDelegate(null);
+ }
+ }
+
+ private class ShellDelegate implements CheckOpsDelegate, CheckPermissionDelegate {
+ private final String mTargetPackageName;
+ private final int mTargetUid;
+
+ ShellDelegate(String targetPacakgeName, int targetUid) {
+ mTargetPackageName = targetPacakgeName;
+ mTargetUid = targetUid;
+ }
+
+ int getDelegateUid() {
+ return mTargetUid;
+ }
+
+ @Override
+ public int checkOperation(int code, int uid, String packageName,
+ TriFunction<Integer, Integer, String, Integer> superImpl) {
+ if (uid == mTargetUid) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ return superImpl.apply(code, Process.SHELL_UID,
+ "com.android.shell");
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+ return superImpl.apply(code, uid, packageName);
+ }
+
+ @Override
+ public int checkAudioOperation(int code, int usage, int uid, String packageName,
+ QuadFunction<Integer, Integer, Integer, String, Integer> superImpl) {
+ if (uid == mTargetUid) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ return superImpl.apply(code, usage, Process.SHELL_UID,
+ "com.android.shell");
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+ return superImpl.apply(code, usage, uid, packageName);
+ }
+
+ @Override
+ public int noteOperation(int code, int uid, String packageName,
+ TriFunction<Integer, Integer, String, Integer> superImpl) {
+ if (uid == mTargetUid) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ return mAppOpsService.noteProxyOperation(code, Process.SHELL_UID,
+ "com.android.shell", uid, packageName);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+ return superImpl.apply(code, uid, packageName);
+ }
+
+ @Override
+ public int checkPermission(String permName, String pkgName, int userId,
+ TriFunction<String, String, Integer, Integer> superImpl) {
+ if (mTargetPackageName.equals(pkgName)) {
+ return superImpl.apply(permName, "com.android.shell", userId);
+ }
+ return superImpl.apply(permName, pkgName, userId);
+ }
+
+ @Override
+ public int checkUidPermission(String permName, int uid,
+ BiFunction<String, Integer, Integer> superImpl) {
+ if (uid == mTargetUid) {
+ return superImpl.apply(permName, Process.SHELL_UID);
+ }
+ return superImpl.apply(permName, uid);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/am/ActivityMetricsLogger.java b/services/core/java/com/android/server/am/ActivityMetricsLogger.java
index 1611a38..d3e3af3 100644
--- a/services/core/java/com/android/server/am/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/am/ActivityMetricsLogger.java
@@ -2,7 +2,7 @@
import static android.app.ActivityManager.START_SUCCESS;
import static android.app.ActivityManager.START_TASK_TO_FRONT;
-import static android.app.ActivityTaskManagerInternal.APP_TRANSITION_TIMEOUT;
+import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_TIMEOUT;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
@@ -133,7 +133,7 @@
private final class WindowingModeTransitionInfoSnapshot {
final private ApplicationInfo applicationInfo;
- final private ProcessRecord processRecord;
+ final private WindowProcessController processRecord;
final private String packageName;
final private String launchedActivityName;
final private String launchedActivityLaunchedFromPackage;
@@ -154,7 +154,7 @@
launchedActivityLaunchToken = info.launchedActivity.info.launchToken;
launchedActivityAppRecordRequiredAbi = info.launchedActivity.app == null
? null
- : info.launchedActivity.app.requiredAbi;
+ : info.launchedActivity.app.getRequiredAbi();
reason = info.reason;
startingWindowDelayMs = info.startingWindowDelayMs;
bindApplicationDelayMs = info.bindApplicationDelayMs;
@@ -184,7 +184,7 @@
mLastLogTimeSecs = now;
mWindowState = WINDOW_STATE_INVALID;
- ActivityStack stack = mSupervisor.getFocusedStack();
+ ActivityStack stack = mSupervisor.getTopDisplayFocusedStack();
if (stack == null) {
return;
}
@@ -238,7 +238,7 @@
* @param launchedActivity the activity that is being launched
*/
void notifyActivityLaunched(int resultCode, ActivityRecord launchedActivity) {
- final ProcessRecord processRecord = findProcessForActivity(launchedActivity);
+ final WindowProcessController processRecord = findProcessForActivity(launchedActivity);
final boolean processRunning = processRecord != null;
// We consider this a "process switch" if the process of the activity that gets launched
@@ -246,25 +246,11 @@
// of caches might be purged so the time until it produces the first frame is very
// interesting.
final boolean processSwitch = processRecord == null
- || !hasStartedActivity(processRecord, launchedActivity);
+ || !processRecord.hasStartedActivity(launchedActivity);
notifyActivityLaunched(resultCode, launchedActivity, processRunning, processSwitch);
}
- private boolean hasStartedActivity(ProcessRecord record, ActivityRecord launchedActivity) {
- final ArrayList<ActivityRecord> activities = record.activities;
- for (int i = activities.size() - 1; i >= 0; i--) {
- final ActivityRecord activity = activities.get(i);
- if (launchedActivity == activity) {
- continue;
- }
- if (!activity.stopped) {
- return true;
- }
- }
- return false;
- }
-
/**
* Notifies the tracker the the activity is actually launching.
*
@@ -645,7 +631,7 @@
return;
}
- final int pid = info.processRecord.pid;
+ final int pid = info.processRecord.getPid();
final int uid = info.applicationInfo.uid;
final MemoryStat memoryStat = readMemoryStatFromFilesystem(uid, pid);
if (memoryStat == null) {
@@ -665,9 +651,9 @@
memoryStat.swapInBytes);
}
- private ProcessRecord findProcessForActivity(ActivityRecord launchedActivity) {
+ private WindowProcessController findProcessForActivity(ActivityRecord launchedActivity) {
return launchedActivity != null
- ? mSupervisor.mService.mAm.mProcessNames.get(
+ ? mSupervisor.mService.mProcessNames.get(
launchedActivity.processName, launchedActivity.appInfo.uid)
: null;
}
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 0e427d6..b17aada 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -288,7 +288,7 @@
AppTimeTracker appTimeTracker; // set if we are tracking the time in this app/task/activity
HashSet<ConnectionRecord> connections; // All ConnectionRecord we hold
UriPermissionOwner uriPermissions; // current special URI access perms.
- ProcessRecord app; // if non-null, hosting application
+ WindowProcessController app; // if non-null, hosting application
private ActivityState mState; // current state we are in
Bundle icicle; // last saved activity state
PersistableBundle persistentState; // last persistently saved activity state
@@ -622,7 +622,7 @@
}
private void scheduleActivityMovedToDisplay(int displayId, Configuration config) {
- if (app == null || app.thread == null) {
+ if (!attachedToProcess()) {
if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.w(TAG,
"Can't report activity moved to display - client not running, activityRecord="
+ this + ", displayId=" + displayId);
@@ -633,7 +633,7 @@
"Reporting activity moved to display" + ", activityRecord=" + this
+ ", displayId=" + displayId + ", config=" + config);
- service.getLifecycleManager().scheduleTransaction(app.thread, appToken,
+ service.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
MoveToDisplayItem.obtain(displayId, config));
} catch (RemoteException e) {
// If process died, whatever.
@@ -641,7 +641,7 @@
}
private void scheduleConfigurationChanged(Configuration config) {
- if (app == null || app.thread == null) {
+ if (!attachedToProcess()) {
if (DEBUG_CONFIGURATION) Slog.w(TAG,
"Can't report activity configuration update - client not running"
+ ", activityRecord=" + this);
@@ -651,7 +651,7 @@
if (DEBUG_CONFIGURATION) Slog.v(TAG, "Sending new config to " + this + ", config: "
+ config);
- service.getLifecycleManager().scheduleTransaction(app.thread, appToken,
+ service.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
ActivityConfigurationChangeItem.obtain(config));
} catch (RemoteException e) {
// If process died, whatever.
@@ -659,7 +659,7 @@
}
void updateMultiWindowMode() {
- if (task == null || task.getStack() == null || app == null || app.thread == null) {
+ if (task == null || task.getStack() == null || !attachedToProcess()) {
return;
}
@@ -678,7 +678,7 @@
private void scheduleMultiWindowModeChanged(Configuration overrideConfig) {
try {
- service.getLifecycleManager().scheduleTransaction(app.thread, appToken,
+ service.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
MultiWindowModeChangeItem.obtain(mLastReportedMultiWindowMode, overrideConfig));
} catch (Exception e) {
// If process died, I don't care.
@@ -686,7 +686,7 @@
}
void updatePictureInPictureMode(Rect targetStackBounds, boolean forceUpdate) {
- if (task == null || task.getStack() == null || app == null || app.thread == null) {
+ if (task == null || task.getStack() == null || !attachedToProcess()) {
return;
}
@@ -705,7 +705,7 @@
private void schedulePictureInPictureModeChanged(Configuration overrideConfig) {
try {
- service.getLifecycleManager().scheduleTransaction(app.thread, appToken,
+ service.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
PipModeChangeItem.obtain(mLastReportedPictureInPictureMode,
overrideConfig));
} catch (Exception e) {
@@ -984,7 +984,7 @@
}
}
- void setProcess(ProcessRecord proc) {
+ void setProcess(WindowProcessController proc) {
app = proc;
final ActivityRecord root = task != null ? task.getRootActivity() : null;
if (root == this) {
@@ -992,6 +992,14 @@
}
}
+ boolean hasProcess() {
+ return app != null;
+ }
+
+ boolean attachedToProcess() {
+ return hasProcess() && app.hasThread();
+ }
+
AppWindowContainerController getWindowContainerController() {
return mWindowContainerController;
}
@@ -1220,7 +1228,7 @@
* @return whether this activity supports PiP multi-window and can be put in the pinned stack.
*/
boolean supportsPictureInPicture() {
- return service.mAm.mSupportsPictureInPicture && isActivityTypeStandardOrUndefined()
+ return service.mSupportsPictureInPicture && isActivityTypeStandardOrUndefined()
&& info.supportsPictureInPicture();
}
@@ -1233,7 +1241,7 @@
// An activity can not be docked even if it is considered resizeable because it only
// supports picture-in-picture mode but has a non-resizeable resizeMode
return super.supportsSplitScreenWindowingMode()
- && service.mAm.mSupportsSplitScreenMultiWindow && supportsResizeableMultiWindow();
+ && service.mSupportsSplitScreenMultiWindow && supportsResizeableMultiWindow();
}
/**
@@ -1241,16 +1249,16 @@
* stack.
*/
boolean supportsFreeform() {
- return service.mAm.mSupportsFreeformWindowManagement && supportsResizeableMultiWindow();
+ return service.mSupportsFreeformWindowManagement && supportsResizeableMultiWindow();
}
/**
* @return whether this activity supports non-PiP multi-window.
*/
private boolean supportsResizeableMultiWindow() {
- return service.mAm.mSupportsMultiWindow && !isActivityTypeHome()
+ return service.mSupportsMultiWindow && !isActivityTypeHome()
&& (ActivityInfo.isResizeableMode(info.resizeMode)
- || service.mAm.mForceResizableActivities);
+ || service.mForceResizableActivities);
}
/**
@@ -1335,13 +1343,8 @@
* @return Whether AppOps allows this package to enter picture-in-picture.
*/
private boolean checkEnterPictureInPictureAppOpsState() {
- try {
- return service.mAm.getAppOpsService().checkOperation(
- OP_PICTURE_IN_PICTURE, appInfo.uid, packageName) == MODE_ALLOWED;
- } catch (RemoteException e) {
- // Local call
- }
- return false;
+ return service.getAppOpsService().checkOperation(
+ OP_PICTURE_IN_PICTURE, appInfo.uid, packageName) == MODE_ALLOWED;
}
boolean isAlwaysFocusable() {
@@ -1366,7 +1369,7 @@
clearOptionsLocked();
}
- if (service.mAm != null) {
+ if (service != null) {
service.getTaskChangeNotificationController().notifyTaskStackChanged();
}
}
@@ -1416,7 +1419,7 @@
final boolean isSleeping() {
final ActivityStack stack = getStack();
- return stack != null ? stack.shouldSleepActivities() : service.mAm.isSleepingLocked();
+ return stack != null ? stack.shouldSleepActivities() : service.isSleepingLocked();
}
/**
@@ -1435,13 +1438,13 @@
// - It is currently resumed or paused. i.e. it is currently visible to the user and we want
// the user to see the visual effects caused by the intent delivery now.
// - The device is sleeping and it is the top activity behind the lock screen (b/6700897).
- if ((mState == RESUMED || mState == PAUSED
- || isTopActivityWhileSleeping) && app != null && app.thread != null) {
+ if ((mState == RESUMED || mState == PAUSED || isTopActivityWhileSleeping)
+ && attachedToProcess()) {
try {
ArrayList<ReferrerIntent> ar = new ArrayList<>(1);
ar.add(rintent);
service.getLifecycleManager().scheduleTransaction(
- app.thread, appToken, NewIntentItem.obtain(ar, mState == PAUSED));
+ app.getThread(), appToken, NewIntentItem.obtain(ar, mState == PAUSED));
unsent = false;
} catch (RemoteException e) {
Slog.w(TAG, "Exception thrown sending new intent to " + this, e);
@@ -1744,7 +1747,7 @@
}
setVisible(true);
sleeping = false;
- app.pendingUiClean = true;
+ app.setPendingUiClean(true);
if (reportToClient) {
makeClientVisible();
} else {
@@ -1764,13 +1767,13 @@
void makeClientVisible() {
mClientVisibilityDeferred = false;
try {
- service.getLifecycleManager().scheduleTransaction(app.thread, appToken,
+ service.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
WindowVisibilityItem.obtain(true /* showWindow */));
if (shouldPauseWhenBecomingVisible()) {
// An activity must be in the {@link PAUSING} state for the system to validate
// the move to {@link PAUSED}.
setState(PAUSING, "makeVisibleIfNeeded");
- service.getLifecycleManager().scheduleTransaction(app.thread, appToken,
+ service.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
PauseActivityItem.obtain(finishing, false /* userLeaving */,
configChangeFlags, false /* dontReport */));
}
@@ -1789,7 +1792,7 @@
// considers the resumed activity, as normal means will bring the activity from STOPPED
// to RESUMED. Adding PAUSING in this scenario will lead to double lifecycles.
if (!isState(STOPPED, STOPPING) || getStack().mTranslucentActivityWaiting != null
- || mStackSupervisor.getResumedActivityLocked() == this) {
+ || isResumedActivityOnDisplay()) {
return false;
}
@@ -1816,7 +1819,7 @@
stopFreezingScreenLocked(false);
try {
if (returningOptions != null) {
- app.thread.scheduleOnNewActivityOptions(appToken, returningOptions.toBundle());
+ app.getThread().scheduleOnNewActivityOptions(appToken, returningOptions.toBundle());
}
} catch(RemoteException e) {
}
@@ -1850,9 +1853,9 @@
stopped = false;
if (isActivityTypeHome()) {
- ProcessRecord app = task.mActivities.get(0).app;
- if (app != null && app != service.mAm.mHomeProcess) {
- service.mAm.mHomeProcess = app;
+ WindowProcessController app = task.mActivities.get(0).app;
+ if (hasProcess() && app != service.mHomeProcess) {
+ service.mHomeProcess = app;
}
}
@@ -1873,8 +1876,8 @@
// Mark the point when the activity is resuming
// TODO: To be more accurate, the mark should be before the onCreate,
// not after the onResume. But for subsequent starts, onResume is fine.
- if (app != null) {
- cpuTimeAtResume = service.mAm.mProcessCpuTracker.getCpuTimeForPid(app.pid);
+ if (hasProcess()) {
+ cpuTimeAtResume = app.getCpuTime();
} else {
cpuTimeAtResume = 0; // Couldn't get the cpu time of process
}
@@ -1970,15 +1973,15 @@
// IApplicationToken
- public boolean mayFreezeScreenLocked(ProcessRecord app) {
+ public boolean mayFreezeScreenLocked(WindowProcessController app) {
// Only freeze the screen if this activity is currently attached to
// an application, and that application is not blocked or unresponding.
// In any other case, we can't count on getting the screen unfrozen,
// so it is best to leave as-is.
- return app != null && !app.crashing && !app.notResponding;
+ return hasProcess() && !app.isCrashing() && !app.isNotResponding();
}
- public void startFreezingScreenLocked(ProcessRecord app, int configChanges) {
+ public void startFreezingScreenLocked(WindowProcessController app, int configChanges) {
if (mayFreezeScreenLocked(app)) {
mWindowContainerController.startFreezingScreen(configChanges);
}
@@ -2007,7 +2010,7 @@
EventLog.writeEvent(AM_ACTIVITY_FULLY_DRAWN_TIME,
userId, System.identityHashCode(this), shortComponentName,
thisTime, totalTime);
- StringBuilder sb = service.mAm.mStringBuilder;
+ StringBuilder sb = service.mStringBuilder;
sb.setLength(0);
sb.append("Fully drawn ");
sb.append(shortComponentName);
@@ -2044,7 +2047,7 @@
EventLog.writeEvent(AM_ACTIVITY_LAUNCH_TIME,
userId, System.identityHashCode(this), shortComponentName,
thisTime, totalTime);
- StringBuilder sb = service.mAm.mStringBuilder;
+ StringBuilder sb = service.mStringBuilder;
sb.setLength(0);
sb.append("Displayed ");
sb.append(shortComponentName);
@@ -2135,17 +2138,17 @@
@Override
public boolean keyDispatchingTimedOut(String reason, int windowPid) {
ActivityRecord anrActivity;
- ProcessRecord anrApp;
+ WindowProcessController anrApp;
boolean windowFromSameProcessAsActivity;
synchronized (service.mGlobalLock) {
anrActivity = getWaitingHistoryRecordLocked();
anrApp = app;
windowFromSameProcessAsActivity =
- app == null || app.pid == windowPid || windowPid == -1;
+ !hasProcess() || app.getPid() == windowPid || windowPid == -1;
}
if (windowFromSameProcessAsActivity) {
return service.mAm.inputDispatchingTimedOut(
- anrApp, anrActivity, this, false, reason);
+ (ProcessRecord) anrApp.mOwner, anrActivity, this, false, reason);
} else {
// In this case another process added windows using this activity token. So, we call the
// generic service input dispatch timed out method so that the right process is blamed.
@@ -2159,7 +2162,7 @@
// another activity to start or has stopped, then the key dispatching
// timeout should not be caused by this.
if (mStackSupervisor.mActivitiesWaitingForVisibleActivity.contains(this) || stopped) {
- final ActivityStack stack = mStackSupervisor.getFocusedStack();
+ final ActivityStack stack = mStackSupervisor.getTopDisplayFocusedStack();
// Try to use the one which is closest to top.
ActivityRecord r = stack.getResumedActivity();
if (r == null) {
@@ -2183,7 +2186,7 @@
return (info.flags & FLAG_SHOW_FOR_ALL_USERS) != 0
|| (mStackSupervisor.isCurrentProfileLocked(userId)
- && service.mAm.mUserController.isUserRunning(userId, 0 /* flags */));
+ && service.mAmInternal.isUserRunning(userId, 0 /* flags */));
}
/**
@@ -2202,9 +2205,9 @@
if (!force && sleeping == _sleeping) {
return;
}
- if (app != null && app.thread != null) {
+ if (attachedToProcess()) {
try {
- app.thread.scheduleSleeping(appToken, _sleeping);
+ app.getThread().scheduleSleeping(appToken, _sleeping);
if (_sleeping && !mStackSupervisor.mGoingToSleepActivities.contains(this)) {
mStackSupervisor.mGoingToSleepActivities.add(this);
}
@@ -2253,7 +2256,7 @@
}
final boolean isDestroyable() {
- if (finishing || app == null) {
+ if (finishing || !hasProcess()) {
// This would be redundant.
return false;
}
@@ -2347,7 +2350,7 @@
displayId, displayConfig, mayFreezeScreenLocked(app));
if (config != null) {
frozenBeforeDestroy = true;
- if (!service.mAm.updateDisplayOverrideConfigurationLocked(config, this,
+ if (!service.updateDisplayOverrideConfigurationLocked(config, this,
false /* deferResume */, displayId)) {
mStackSupervisor.resumeFocusedStackTopActivityLocked();
}
@@ -2562,8 +2565,7 @@
// Update last reported values.
final Configuration newMergedOverrideConfig = getMergedOverrideConfiguration();
- setLastReportedConfiguration(service.mAm.getGlobalConfiguration(),
- newMergedOverrideConfig);
+ setLastReportedConfiguration(service.getGlobalConfiguration(), newMergedOverrideConfig);
if (mState == INITIALIZING) {
// No need to relaunch or schedule new config for activity that hasn't been launched
@@ -2593,7 +2595,7 @@
// If the activity isn't currently running, just leave the new configuration and it will
// pick that up next time it starts.
- if (app == null || app.thread == null) {
+ if (!attachedToProcess()) {
if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
"Configuration doesn't matter not running " + this);
stopFreezingScreenLocked(false);
@@ -2614,7 +2616,7 @@
startFreezingScreenLocked(app, globalChanges);
forceNewConfig = false;
preserveWindow &= isResizeOnlyChange(changes);
- if (app == null || app.thread == null) {
+ if (!attachedToProcess()) {
if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
"Config is destroying non-running " + this);
stack.destroyActivityLocked(this, true, "config");
@@ -2765,7 +2767,7 @@
mStackSupervisor.activityRelaunchingLocked(this);
final ClientTransactionItem callbackItem = ActivityRelaunchItem.obtain(pendingResults,
pendingNewIntents, configChangeFlags,
- new MergedConfiguration(service.mAm.getGlobalConfiguration(),
+ new MergedConfiguration(service.getGlobalConfiguration(),
getMergedOverrideConfiguration()),
preserveWindow);
final ActivityLifecycleItem lifecycleItem;
@@ -2774,7 +2776,7 @@
} else {
lifecycleItem = PauseActivityItem.obtain();
}
- final ClientTransaction transaction = ClientTransaction.obtain(app.thread, appToken);
+ final ClientTransaction transaction = ClientTransaction.obtain(app.getThread(), appToken);
transaction.addCallback(callbackItem);
transaction.setLifecycleStateRequest(lifecycleItem);
service.getLifecycleManager().scheduleTransaction(transaction);
@@ -2804,11 +2806,11 @@
}
private boolean isProcessRunning() {
- ProcessRecord proc = app;
+ WindowProcessController proc = app;
if (proc == null) {
- proc = service.mAm.mProcessNames.get(processName, info.applicationInfo.uid);
+ proc = service.mProcessNames.get(processName, info.applicationInfo.uid);
}
- return proc != null && proc.thread != null;
+ return proc != null && proc.hasThread();
}
/**
@@ -2939,7 +2941,7 @@
}
final ActivityRecord r = new ActivityRecord(service, null /* caller */,
0 /* launchedFromPid */, launchedFromUid, launchedFromPackage, intent, resolvedType,
- aInfo, service.mAm.getConfiguration(), null /* resultTo */, null /* resultWho */,
+ aInfo, service.getConfiguration(), null /* resultTo */, null /* resultWho */,
0 /* reqCode */, componentSpecified, false /* rootVoiceInteraction */,
stackSupervisor, null /* options */, null /* sourceRecord */);
@@ -3001,6 +3003,15 @@
return mStackSupervisor.topRunningActivityLocked() == this;
}
+ /**
+ * @return {@code true} if this is the resumed activity on its current display, {@code false}
+ * otherwise.
+ */
+ boolean isResumedActivityOnDisplay() {
+ final ActivityDisplay display = getDisplay();
+ return display != null && this == display.getResumedActivity();
+ }
+
void registerRemoteAnimations(RemoteAnimationDefinition definition) {
mWindowContainerController.registerRemoteAnimations(definition);
}
@@ -3037,8 +3048,8 @@
proto.write(STATE, mState.toString());
proto.write(VISIBLE, visible);
proto.write(FRONT_OF_TASK, frontOfTask);
- if (app != null) {
- proto.write(PROC_ID, app.pid);
+ if (hasProcess()) {
+ proto.write(PROC_ID, app.getPid());
}
proto.write(TRANSLUCENT, !fullscreen);
proto.end(token);
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 6118131..d08784f 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -367,9 +367,9 @@
static final int TRANSLUCENT_TIMEOUT_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 6;
private static class ScheduleDestroyArgs {
- final ProcessRecord mOwner;
+ final WindowProcessController mOwner;
final String mReason;
- ScheduleDestroyArgs(ProcessRecord owner, String reason) {
+ ScheduleDestroyArgs(WindowProcessController owner, String reason) {
mOwner = owner;
mReason = reason;
}
@@ -392,8 +392,8 @@
// so we need to be conservative and assume it isn't.
Slog.w(TAG, "Activity pause timeout for " + r);
synchronized (mService.mGlobalLock) {
- if (r.app != null) {
- mService.mAm.logAppTooSlow(r.app, r.pauseTime, "pausing " + r);
+ if (r.hasProcess()) {
+ mService.logAppTooSlow(r.app, r.pauseTime, "pausing " + r);
}
activityPausedLocked(r.appToken, true);
}
@@ -402,7 +402,7 @@
ActivityRecord r = (ActivityRecord)msg.obj;
synchronized (mService.mGlobalLock) {
if (r.continueLaunchTickingLocked()) {
- mService.mAm.logAppTooSlow(r.app, r.launchTickTime, "launching " + r);
+ mService.logAppTooSlow(r.app, r.launchTickTime, "launching " + r);
}
}
} break;
@@ -457,7 +457,7 @@
mHandler = new ActivityStackHandler(supervisor.mLooper);
mWindowManager = mService.mWindowManager;
mStackId = stackId;
- mCurrentUser = mService.mAm.mUserController.getCurrentUserId();
+ mCurrentUser = mService.mAmInternal.getCurrentUserId();
mTmpRect2.setEmpty();
// Set display id before setting activity and window type to make sure it won't affect
// stacks on a wrong display.
@@ -495,7 +495,7 @@
if (DEBUG_STACK) Slog.v(TAG_STACK, "set resumed activity to:" + record + " reason:"
+ reason);
setResumedActivity(record, reason + " - onActivityStateChanged");
- mService.mAm.setResumedActivityUncheckLocked(record, reason);
+ mService.setResumedActivityUncheckLocked(record, reason);
mStackSupervisor.mRecentTasks.add(record.getTask());
}
}
@@ -503,11 +503,21 @@
@Override
public void onConfigurationChanged(Configuration newParentConfig) {
final int prevWindowingMode = getWindowingMode();
+ final boolean prevIsAlwaysOnTop = isAlwaysOnTop();
super.onConfigurationChanged(newParentConfig);
final ActivityDisplay display = getDisplay();
- if (display != null && prevWindowingMode != getWindowingMode()) {
+ if (display == null) {
+ return;
+ }
+ if (prevWindowingMode != getWindowingMode()) {
display.onStackWindowingModeChanged(this);
}
+ if (prevIsAlwaysOnTop != isAlwaysOnTop()) {
+ // Since always on top is only on when the stack is freeform or pinned, the state
+ // can be toggled when the windowing mode changes. We must make sure the stack is
+ // placed properly when always on top state changes.
+ display.positionChildAtTop(this);
+ }
}
@Override
@@ -837,7 +847,7 @@
}
}
- private ActivityRecord topRunningActivityLocked(boolean focusableOnly) {
+ ActivityRecord topRunningActivityLocked(boolean focusableOnly) {
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
ActivityRecord r = mTaskHistory.get(taskNdx).topRunningActivityLocked();
if (r != null && (!focusableOnly || r.isFocusable())) {
@@ -1042,12 +1052,14 @@
if (!isActivityTypeHome() && returnsToHomeStack()) {
// Make sure the home stack is behind this stack since that is where we should return to
// when this stack is no longer visible.
+ // TODO(b/111541062): Move home stack on the current display
mStackSupervisor.moveHomeStackToFront(reason + " returnToHome");
}
display.positionChildAtTop(this);
mStackSupervisor.setFocusStackUnchecked(reason, this);
if (task != null) {
+ // This also moves the entire hierarchy branch to top, including parents
insertTaskAtTop(task, null);
return;
}
@@ -1073,6 +1085,8 @@
getDisplay().positionChildAtBottom(this);
mStackSupervisor.setFocusStackUnchecked(reason, getDisplay().getTopStack());
if (task != null) {
+ // TODO(b/111541062): We probably don't want to change display z-order to bottom just
+ // because one of its stacks moved to bottom.
insertTaskAtBottom(task);
return;
}
@@ -1453,15 +1467,15 @@
mService.mAm.updateCpuStats();
- if (prev.app != null && prev.app.thread != null) {
+ if (prev.attachedToProcess()) {
if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Enqueueing pending pause: " + prev);
try {
EventLogTags.writeAmPauseActivity(prev.userId, System.identityHashCode(prev),
prev.shortComponentName, "userLeaving=" + userLeaving);
mService.mAm.updateUsageStats(prev, false);
- mService.getLifecycleManager().scheduleTransaction(prev.app.thread, prev.appToken,
- PauseActivityItem.obtain(prev.finishing, userLeaving,
+ mService.getLifecycleManager().scheduleTransaction(prev.app.getThread(),
+ prev.appToken, PauseActivityItem.obtain(prev.finishing, userLeaving,
prev.configChangeFlags, pauseImmediately));
} catch (Exception e) {
// Ignore exception, if process died other code will cleanup.
@@ -1478,7 +1492,7 @@
// If we are not going to sleep, we want to ensure the device is
// awake until the next activity is started.
- if (!uiSleeping && !mService.mAm.isSleepingOrShuttingDownLocked()) {
+ if (!uiSleeping && !mService.isSleepingOrShuttingDownLocked()) {
mStackSupervisor.acquireLaunchWakelock();
}
@@ -1563,7 +1577,7 @@
if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Executing finish of activity: " + prev);
prev = finishCurrentActivityLocked(prev, FINISH_AFTER_VISIBLE, false,
"completedPausedLocked");
- } else if (prev.app != null) {
+ } else if (prev.hasProcess()) {
if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Enqueue pending stop if needed: " + prev
+ " wasStopping=" + wasStopping + " visible=" + prev.visible);
if (mStackSupervisor.mActivitiesWaitingForVisibleActivity.remove(prev)) {
@@ -1601,7 +1615,7 @@
}
if (resumeNext) {
- final ActivityStack topStack = mStackSupervisor.getFocusedStack();
+ final ActivityStack topStack = mStackSupervisor.getTopDisplayFocusedStack();
if (!topStack.shouldSleepOrShutDownActivities()) {
mStackSupervisor.resumeFocusedStackTopActivityLocked(topStack, prev, null);
} else {
@@ -1620,10 +1634,9 @@
if (prev != null) {
prev.resumeKeyDispatchingLocked();
- if (prev.app != null && prev.cpuTimeAtResume > 0
+ if (prev.hasProcess() && prev.cpuTimeAtResume > 0
&& mService.mAm.mBatteryStatsService.isOnBattery()) {
- long diff = mService.mAm.mProcessCpuTracker.getCpuTimeForPid(prev.app.pid)
- - prev.cpuTimeAtResume;
+ long diff = prev.app.getCpuTime() - prev.cpuTimeAtResume;
if (diff > 0) {
BatteryStatsImpl bsi = mService.mAm.mBatteryStatsService.getActiveStatistics();
synchronized (bsi) {
@@ -1654,6 +1667,16 @@
void addToStopping(ActivityRecord r, boolean scheduleIdle, boolean idleDelayed) {
if (!mStackSupervisor.mStoppingActivities.contains(r)) {
mStackSupervisor.mStoppingActivities.add(r);
+
+ // Some activity is waiting for another activity to become visible before it's being
+ // stopped, which means that we also want to wait with stopping this one to avoid
+ // flickers.
+ if (!mStackSupervisor.mActivitiesWaitingForVisibleActivity.isEmpty()
+ && !mStackSupervisor.mActivitiesWaitingForVisibleActivity.contains(r)) {
+ if (DEBUG_SWITCH) Slog.i(TAG_SWITCH, "adding to waiting visible activity=" + r
+ + " existing=" + mStackSupervisor.mActivitiesWaitingForVisibleActivity);
+ mStackSupervisor.mActivitiesWaitingForVisibleActivity.add(r);
+ }
}
// If we already have a few activities waiting to stop, then give up
@@ -1715,7 +1738,17 @@
}
boolean isTopStackOnDisplay() {
- return getDisplay().isTopStack(this);
+ final ActivityDisplay display = getDisplay();
+ return display != null && display.isTopStack(this);
+ }
+
+ /**
+ * @return {@code true} if this is the focused stack on its current display, {@code false}
+ * otherwise.
+ */
+ boolean isFocusedStackOnDisplay() {
+ final ActivityDisplay display = getDisplay();
+ return display != null && this == display.getFocusedStack();
}
boolean isTopActivityVisible() {
@@ -1732,9 +1765,6 @@
if (!isAttached() || mForceHidden) {
return false;
}
- if (mStackSupervisor.isFocusedStack(this)) {
- return true;
- }
final ActivityRecord top = topRunningActivityLocked();
if (top == null && isInStackLocked(starting) == null && !isTopStackOnDisplay()) {
@@ -1864,7 +1894,7 @@
boolean aboveTop = top != null;
final boolean stackShouldBeVisible = shouldBeVisible(starting);
boolean behindFullscreenActivity = !stackShouldBeVisible;
- boolean resumeNextActivity = mStackSupervisor.isFocusedStack(this)
+ boolean resumeNextActivity = mStackSupervisor.isTopDisplayFocusedStack(this)
&& (isInStackLocked(starting) == null);
final boolean isTopNotPinnedStack =
isAttached() && getDisplay().isTopNotPinnedStack(this);
@@ -1904,7 +1934,7 @@
true /* ignoreStopState */);
}
- if (r.app == null || r.app.thread == null) {
+ if (!r.attachedToProcess()) {
if (makeVisibleAndRestartIfNeeded(starting, configChanges, isTop,
resumeNextActivity, r)) {
if (activityNdx >= activities.size()) {
@@ -2136,11 +2166,11 @@
switch (r.getState()) {
case STOPPING:
case STOPPED:
- if (r.app != null && r.app.thread != null) {
+ if (r.attachedToProcess()) {
if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY,
"Scheduling invisibility: " + r);
- mService.getLifecycleManager().scheduleTransaction(r.app.thread, r.appToken,
- WindowVisibilityItem.obtain(false /* showWindow */));
+ mService.getLifecycleManager().scheduleTransaction(r.app.getThread(),
+ r.appToken, WindowVisibilityItem.obtain(false /* showWindow */));
}
// Reset the flag indicating that an app can enter picture-in-picture once the
@@ -2217,9 +2247,9 @@
if (waitingActivity != null) {
mWindowManager.setWindowOpaque(waitingActivity.appToken, false);
- if (waitingActivity.app != null && waitingActivity.app.thread != null) {
+ if (waitingActivity.attachedToProcess()) {
try {
- waitingActivity.app.thread.scheduleTranslucentConversionComplete(
+ waitingActivity.app.getThread().scheduleTranslucentConversionComplete(
waitingActivity.appToken, r != null);
} catch (RemoteException e) {
}
@@ -2388,7 +2418,7 @@
// Make sure that the user who owns this activity is started. If not,
// we will just leave it as is because someone should be bringing
// another user's activities to the top of the stack.
- if (!mService.mAm.mUserController.hasStartedUserState(next.userId)) {
+ if (!mService.mAmInternal.hasStartedUserState(next.userId)) {
Slog.w(TAG, "Skipping resume of top activity " + next
+ ": user " + next.userId + " is stopped");
if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
@@ -2416,7 +2446,7 @@
boolean lastResumedCanPip = false;
ActivityRecord lastResumed = null;
- final ActivityStack lastFocusedStack = mStackSupervisor.getLastStack();
+ final ActivityStack lastFocusedStack = mStackSupervisor.getTopDisplayLastFocusedStack();
if (lastFocusedStack != null && lastFocusedStack != this) {
// So, why aren't we using prev here??? See the param comment on the method. prev doesn't
// represent the last resumed activity. However, the last focus stack does if it isn't null.
@@ -2451,8 +2481,9 @@
// at the top of the LRU list, since we know we will be needing it
// very soon and it would be a waste to let it get killed if it
// happens to be sitting towards the end.
- if (next.app != null && next.app.thread != null) {
- mService.mAm.updateLruProcessLocked(next.app, true, null);
+ if (next.attachedToProcess()) {
+ next.app.updateProcessInfo(false /* updateServiceConnectionActivities */,
+ true /* updateLru */, true /* activityChange */, false /* updateOomAdj */);
}
if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
if (lastResumed != null) {
@@ -2576,8 +2607,8 @@
mStackSupervisor.mNoAnimActivities.clear();
- ActivityStack lastStack = mStackSupervisor.getLastStack();
- if (next.app != null && next.app.thread != null) {
+ ActivityStack lastStack = mStackSupervisor.getTopDisplayLastFocusedStack();
+ if (next.attachedToProcess()) {
if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Resume running: " + next
+ " stopped=" + next.stopped + " visible=" + next.visible);
@@ -2618,15 +2649,15 @@
next.setState(RESUMED, "resumeTopActivityInnerLocked");
- mService.mAm.updateLruProcessLocked(next.app, true, null);
+ next.app.updateProcessInfo(false /* updateServiceConnectionActivities */,
+ true /* updateLru */, true /* activityChange */, true /* updateOomAdj */);
updateLRUListLocked(next);
- mService.mAm.updateOomAdjLocked();
// Have the window manager re-evaluate the orientation of
// the screen based on the new activity order.
boolean notUpdated = true;
- if (mStackSupervisor.isFocusedStack(this)) {
+ if (mStackSupervisor.isTopDisplayFocusedStack(this)) {
// We have special rotation behavior when here is some active activity that
// requests specific orientation or Keyguard is locked. Make sure all activity
// visibilities are set correctly as well as the transition is updated if needed
@@ -2662,8 +2693,8 @@
}
try {
- final ClientTransaction transaction = ClientTransaction.obtain(next.app.thread,
- next.appToken);
+ final ClientTransaction transaction =
+ ClientTransaction.obtain(next.app.getThread(), next.appToken);
// Deliver all pending results.
ArrayList<ResultInfo> a = next.results;
if (a != null) {
@@ -2691,11 +2722,10 @@
next.sleeping = false;
mService.mAm.getAppWarningsLocked().onResumeActivity(next);
mService.mAm.showAskCompatModeDialogLocked(next);
- next.app.pendingUiClean = true;
- next.app.forceProcessStateUpTo(mService.mAm.mTopProcessState);
+ next.app.setPendingUiCleanAndForceProcessStateUpTo(mService.mTopProcessState);
next.clearOptionsLocked();
transaction.setLifecycleStateRequest(
- ResumeActivityItem.obtain(next.app.repProcState,
+ ResumeActivityItem.obtain(next.app.getReportedProcState(),
mService.isNextTransitionForward()));
mService.getLifecycleManager().scheduleTransaction(transaction);
@@ -2765,7 +2795,7 @@
// stack is not covering the entire screen or is on a secondary display (with no home
// stack).
return mStackSupervisor.resumeFocusedStackTopActivityLocked(
- mStackSupervisor.getFocusedStack(), prev, null);
+ mStackSupervisor.getTopDisplayFocusedStack(), prev, null);
}
// Let's just start up the Launcher...
@@ -3345,12 +3375,12 @@
if (DEBUG_RESULTS) Slog.v(TAG, "Send activity result to " + r
+ " : who=" + resultWho + " req=" + requestCode
+ " res=" + resultCode + " data=" + data);
- if (mResumedActivity == r && r.app != null && r.app.thread != null) {
+ if (mResumedActivity == r && r.attachedToProcess()) {
try {
ArrayList<ResultInfo> list = new ArrayList<ResultInfo>();
list.add(new ResultInfo(resultWho, requestCode,
resultCode, data));
- mService.getLifecycleManager().scheduleTransaction(r.app.thread, r.appToken,
+ mService.getLifecycleManager().scheduleTransaction(r.app.getThread(), r.appToken,
ActivityResultItem.obtain(list));
return;
} catch (Exception e) {
@@ -3378,7 +3408,7 @@
}
private void adjustFocusedActivityStack(ActivityRecord r, String reason) {
- if (!mStackSupervisor.isFocusedStack(this) ||
+ if (!mStackSupervisor.isTopDisplayFocusedStack(this) ||
((mResumedActivity != r) && (mResumedActivity != null))) {
return;
}
@@ -3464,7 +3494,7 @@
}
}
- if (r.app != null && r.app.thread != null) {
+ if (r.attachedToProcess()) {
adjustFocusedActivityStack(r, "stopActivity");
r.resumeKeyDispatchingLocked();
try {
@@ -3479,7 +3509,7 @@
}
EventLogTags.writeAmStopActivity(
r.userId, System.identityHashCode(r), r.shortComponentName);
- mService.getLifecycleManager().scheduleTransaction(r.app.thread, r.appToken,
+ mService.getLifecycleManager().scheduleTransaction(r.app.getThread(), r.appToken,
StopActivityItem.obtain(r.visible, r.configChangeFlags));
if (shouldSleepOrShutDownActivities()) {
r.setSleeping(true);
@@ -3535,7 +3565,7 @@
}
}
}
- mService.mAm.updateOomAdjLocked();
+ mService.updateOomAdj();
}
/**
@@ -3546,7 +3576,7 @@
* @return The task that was finished in this stack, {@code null} if top running activity does
* not belong to the crashed app.
*/
- final TaskRecord finishTopCrashedActivityLocked(ProcessRecord app, String reason) {
+ final TaskRecord finishTopCrashedActivityLocked(WindowProcessController app, String reason) {
ActivityRecord r = topRunningActivityLocked();
TaskRecord finishedTask = null;
if (r == null || r.app != app) {
@@ -3578,7 +3608,7 @@
if (activityNdx >= 0) {
r = mTaskHistory.get(taskNdx).mActivities.get(activityNdx);
if (r.isState(RESUMED, PAUSING, PAUSED)) {
- if (!r.isActivityTypeHome() || mService.mAm.mHomeProcess != r.app) {
+ if (!r.isActivityTypeHome() || mService.mHomeProcess != r.app) {
Slog.w(TAG, " Force finishing activity "
+ r.intent.getComponent().flattenToShortString());
finishActivityLocked(r, Activity.RESULT_CANCELED, null, reason, false);
@@ -3606,17 +3636,16 @@
// Check if any of the activities are using voice
for (int activityNdx = tr.mActivities.size() - 1; activityNdx >= 0; --activityNdx) {
ActivityRecord r = tr.mActivities.get(activityNdx);
- if (r.voiceSession != null
- && r.voiceSession.asBinder() == sessionBinder) {
+ if (r.voiceSession != null && r.voiceSession.asBinder() == sessionBinder) {
// Inform of cancellation
r.clearVoiceSessionLocked();
try {
- r.app.thread.scheduleLocalVoiceInteractionStarted((IBinder) r.appToken,
- null);
+ r.app.getThread().scheduleLocalVoiceInteractionStarted(
+ r.appToken, null);
} catch (RemoteException re) {
// Ok
}
- mService.mAm.finishRunningVoiceLocked();
+ mService.finishRunningVoiceLocked();
break;
}
}
@@ -3624,7 +3653,7 @@
}
if (didOne) {
- mService.mAm.updateOomAdjLocked();
+ mService.updateOomAdj();
}
}
@@ -3813,7 +3842,7 @@
"Moving to STOPPING: "+ r + " (finish requested)");
r.setState(STOPPING, "finishCurrentActivityLocked");
if (oomAdj) {
- mService.mAm.updateOomAdjLocked();
+ mService.updateOomAdj();
}
return r;
}
@@ -3827,7 +3856,7 @@
r.setState(FINISHING, "finishCurrentActivityLocked");
final boolean finishingActivityInNonFocusedStack
- = r.getStack() != mStackSupervisor.getFocusedStack()
+ = r.getStack() != mStackSupervisor.getTopDisplayFocusedStack()
&& prevState == PAUSED && mode == FINISH_AFTER_VISIBLE;
if (mode == FINISH_IMMEDIATELY
@@ -3956,7 +3985,9 @@
}
}
- IActivityController controller = mService.mAm.mController;
+ // TODO: There is a dup. of this block of code in ActivityTaskManagerService.finishActivity
+ // We should consolidate.
+ IActivityController controller = mService.mController;
if (controller != null) {
ActivityRecord next = topRunningActivityLocked(srec.appToken, 0);
if (next != null) {
@@ -3965,7 +3996,7 @@
try {
resumeOK = controller.activityResuming(next.packageName);
} catch (RemoteException e) {
- mService.mAm.mController = null;
+ mService.mController = null;
Watchdog.getInstance().setActivityController(null);
}
@@ -4000,7 +4031,7 @@
// TODO(b/64750076): Check if calling pid should really be -1.
final int res = mService.getActivityStartController()
.obtainStarter(destIntent, "navigateUpTo")
- .setCaller(srec.app.thread)
+ .setCaller(srec.app.getThread())
.setActivityInfo(aInfo)
.setResultTo(parent.appToken)
.setCallingPid(-1)
@@ -4167,13 +4198,13 @@
}
}
- final void scheduleDestroyActivities(ProcessRecord owner, String reason) {
+ final void scheduleDestroyActivities(WindowProcessController owner, String reason) {
Message msg = mHandler.obtainMessage(DESTROY_ACTIVITIES_MSG);
msg.obj = new ScheduleDestroyArgs(owner, reason);
mHandler.sendMessage(msg);
}
- private void destroyActivitiesLocked(ProcessRecord owner, String reason) {
+ private void destroyActivitiesLocked(WindowProcessController owner, String reason) {
boolean lastIsOpaque = false;
boolean activityRemoved = false;
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
@@ -4218,7 +4249,7 @@
return false;
}
- final int releaseSomeActivitiesLocked(ProcessRecord app, ArraySet<TaskRecord> tasks,
+ final int releaseSomeActivitiesLocked(WindowProcessController app, ArraySet<TaskRecord> tasks,
String reason) {
// Iterate over tasks starting at the back (oldest) first.
if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Trying to release some activities in " + app);
@@ -4272,7 +4303,7 @@
final boolean destroyActivityLocked(ActivityRecord r, boolean removeFromApp, String reason) {
if (DEBUG_SWITCH || DEBUG_CLEANUP) Slog.v(TAG_SWITCH,
"Removing activity from " + reason + ": token=" + r
- + ", app=" + (r.app != null ? r.app.processName : "(null)"));
+ + ", app=" + (r.hasProcess() ? r.app.mName : "(null)"));
if (r.isState(DESTROYING, DESTROYED)) {
if (DEBUG_STATES) Slog.v(TAG_STATES, "activity " + r + " already destroying."
@@ -4288,23 +4319,23 @@
cleanUpActivityLocked(r, false, false);
- final boolean hadApp = r.app != null;
+ final boolean hadApp = r.hasProcess();
if (hadApp) {
if (removeFromApp) {
- r.app.activities.remove(r);
- if (mService.mAm.mHeavyWeightProcess == r.app && r.app.activities.size() <= 0) {
+ r.app.removeActivity(r);
+ if (mService.mAm.mHeavyWeightProcess != null
+ && mService.mAm.mHeavyWeightProcess.getWindowProcessController() == r.app
+ && !r.app.hasActivities()) {
mService.mAm.mHeavyWeightProcess = null;
mService.mAm.mHandler.sendEmptyMessage(
ActivityManagerService.CANCEL_HEAVY_NOTIFICATION_MSG);
}
- if (r.app.activities.isEmpty()) {
+ if (!r.app.hasActivities()) {
// Update any services we are bound to that might care about whether
// their client may have activities.
- mService.mAm.mServices.updateServiceConnectionActivitiesLocked(r.app);
// No longer have activities, so update LRU list and oom adj.
- mService.mAm.updateLruProcessLocked(r.app, false, null);
- mService.mAm.updateOomAdjLocked();
+ r.app.updateProcessInfo(true, true, false, true);
}
}
@@ -4312,7 +4343,7 @@
try {
if (DEBUG_SWITCH) Slog.i(TAG_SWITCH, "Destroying: " + r);
- mService.getLifecycleManager().scheduleTransaction(r.app.thread, r.appToken,
+ mService.getLifecycleManager().scheduleTransaction(r.app.getThread(), r.appToken,
DestroyActivityItem.obtain(r.finishing, r.configChangeFlags));
} catch (Exception e) {
// We can just ignore exceptions here... if the process
@@ -4403,7 +4434,7 @@
}
private void removeHistoryRecordsForAppLocked(ArrayList<ActivityRecord> list,
- ProcessRecord app, String listName) {
+ WindowProcessController app, String listName) {
int i = list.size();
if (DEBUG_CLEANUP) Slog.v(TAG_CLEANUP,
"Removing app " + app + " from list " + listName + " with " + i + " entries");
@@ -4419,7 +4450,7 @@
}
}
- private boolean removeHistoryRecordsForAppLocked(ProcessRecord app) {
+ private boolean removeHistoryRecordsForAppLocked(WindowProcessController app) {
removeHistoryRecordsForAppLocked(mLRUActivities, app, "mLRUActivities");
removeHistoryRecordsForAppLocked(mStackSupervisor.mStoppingActivities, app,
"mStoppingActivities");
@@ -4662,7 +4693,7 @@
// If we have a watcher, preflight the move before committing to it. First check
// for *other* available tasks, but if none are available, then try again allowing the
// current task to be selected.
- if (isTopStackOnDisplay() && mService.mAm.mController != null) {
+ if (isTopStackOnDisplay() && mService.mController != null) {
ActivityRecord next = topRunningActivityLocked(null, taskId);
if (next == null) {
next = topRunningActivityLocked(null, 0);
@@ -4671,9 +4702,9 @@
// ask watcher if this is allowed
boolean moveOK = true;
try {
- moveOK = mService.mAm.mController.activityResuming(next.packageName);
+ moveOK = mService.mController.activityResuming(next.packageName);
} catch (RemoteException e) {
- mService.mAm.mController = null;
+ mService.mController = null;
Watchdog.getInstance().setActivityController(null);
}
if (!moveOK) {
@@ -4893,7 +4924,7 @@
|| (packageName == null && r.userId == userId);
if ((userId == UserHandle.USER_ALL || r.userId == userId)
&& (sameComponent || r.getTask() == lastTask)
- && (r.app == null || evenPersistent || !r.app.persistent)) {
+ && (r.app == null || evenPersistent || !r.app.isPersistent())) {
if (!doit) {
if (r.finishing) {
// If this activity is just finishing, then it is not
@@ -4913,8 +4944,8 @@
didSomething = true;
Slog.i(TAG, " Force finishing activity " + r);
if (sameComponent) {
- if (r.app != null) {
- r.app.removed = true;
+ if (r.hasProcess()) {
+ r.app.setRemoved(true);
}
r.app = null;
}
@@ -4934,7 +4965,7 @@
*/
void getRunningTasks(List<TaskRecord> tasksOut, @ActivityType int ignoreActivityType,
@WindowingMode int ignoreWindowingMode, int callingUid, boolean allowed) {
- boolean focusedStack = mStackSupervisor.getFocusedStack() == this;
+ boolean focusedStack = mStackSupervisor.getTopDisplayFocusedStack() == this;
boolean topTask = true;
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
final TaskRecord task = mTaskHistory.get(taskNdx);
@@ -4985,7 +5016,7 @@
* @param app The app of the activity that died.
* @return result from removeHistoryRecordsForAppLocked.
*/
- boolean handleAppDiedLocked(ProcessRecord app) {
+ boolean handleAppDiedLocked(WindowProcessController app) {
if (mPausingActivity != null && mPausingActivity.app == app) {
if (DEBUG_PAUSE || DEBUG_CLEANUP) Slog.v(TAG_PAUSE,
"App died while pausing: " + mPausingActivity);
@@ -4999,7 +5030,7 @@
return removeHistoryRecordsForAppLocked(app);
}
- void handleAppCrashLocked(ProcessRecord app) {
+ void handleAppCrashLocked(WindowProcessController app) {
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
@@ -5141,7 +5172,7 @@
// We only need to adjust focused stack if this stack is in focus and we are not in the
// process of moving the task to the top of the stack that will be focused.
if (isOnHomeDisplay() && mode != REMOVE_TASK_MODE_MOVING_TO_TOP
- && mStackSupervisor.isFocusedStack(this)) {
+ && mStackSupervisor.isTopDisplayFocusedStack(this)) {
String myReason = reason + " leftTaskHistoryEmpty";
if (!inMultiWindowMode() || !adjustFocusToNextFocusableStack(myReason)) {
mStackSupervisor.moveHomeStackToFront(myReason);
@@ -5277,6 +5308,20 @@
}
}
+ public void setAlwaysOnTop(boolean alwaysOnTop) {
+ if (isAlwaysOnTop() == alwaysOnTop) {
+ return;
+ }
+ super.setAlwaysOnTop(alwaysOnTop);
+ final ActivityDisplay display = getDisplay();
+ // positionChildAtTop() must be called even when always on top gets turned off because we
+ // need to make sure that the stack is moved from among always on top windows to below other
+ // always on top windows. Since the position the stack should be inserted into is calculated
+ // properly in {@link ActivityDisplay#getTopInsertPosition()} in both cases, we can just
+ // request that the stack is put at top here.
+ display.positionChildAtTop(this);
+ }
+
void moveToFrontAndResumeStateIfNeeded(ActivityRecord r, boolean moveToFront, boolean setResume,
boolean setPause, String reason) {
if (!moveToFront) {
@@ -5331,16 +5376,16 @@
// Do not sleep activities in this stack if we're marked as focused and the keyguard
// is in the process of going away.
- if (mStackSupervisor.getFocusedStack() == this
+ if (mStackSupervisor.getTopDisplayFocusedStack() == this
&& mStackSupervisor.getKeyguardController().isKeyguardGoingAway()) {
return false;
}
- return display != null ? display.isSleeping() : mService.mAm.isSleepingLocked();
+ return display != null ? display.isSleeping() : mService.isSleepingLocked();
}
boolean shouldSleepOrShutDownActivities() {
- return shouldSleepActivities() || mService.mAm.isShuttingDownLocked();
+ return shouldSleepActivities() || mService.mShuttingDown;
}
public void writeToProto(ProtoOutputStream proto, long fieldId) {
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index a77d734..4e39033 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -95,6 +95,7 @@
import static com.android.server.am.ActivityStackSupervisorProto.FOCUSED_STACK_ID;
import static com.android.server.am.ActivityStackSupervisorProto.IS_HOME_RECENTS_COMPONENT;
import static com.android.server.am.ActivityStackSupervisorProto.KEYGUARD_CONTROLLER;
+import static com.android.server.am.ActivityStackSupervisorProto.PENDING_ACTIVITIES;
import static com.android.server.am.ActivityStackSupervisorProto.RESUMED_ACTIVITY;
import static android.view.WindowManager.TRANSIT_DOCK_TASK_FROM_RECENTS;
@@ -109,7 +110,9 @@
import android.app.ActivityManager;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.ActivityManager.StackInfo;
-import android.app.ActivityTaskManagerInternal.SleepToken;
+import android.app.ActivityManagerInternal;
+import com.android.internal.util.function.pooled.PooledLambda;
+import com.android.server.wm.ActivityTaskManagerInternal.SleepToken;
import android.app.ActivityOptions;
import android.app.AppOpsManager;
import android.app.ProfilerInfo;
@@ -334,9 +337,6 @@
* Display.DEFAULT_DISPLAY. */
ActivityStack mHomeStack;
- /** The stack currently receiving input or launching the next activity. */
- ActivityStack mFocusedStack;
-
/** If this is the same as mFocusedStack then the activity on the top of the focused stack has
* been resumed. If stacks are changing position this will hold the old stack until the new
* stack becomes resumed after which it will be set to mFocusedStack. */
@@ -488,7 +488,7 @@
// No restrictions for the default display.
return true;
}
- if (!mService.mAm.mSupportsMultiDisplay) {
+ if (!mService.mSupportsMultiDisplay) {
// Can't launch on secondary displays if feature is not supported.
return false;
}
@@ -580,21 +580,21 @@
final ActivityRecord sourceRecord;
final int startFlags;
final ActivityStack stack;
- final ProcessRecord callerApp;
+ final WindowProcessController callerApp;
PendingActivityLaunch(ActivityRecord _r, ActivityRecord _sourceRecord,
- int _startFlags, ActivityStack _stack, ProcessRecord _callerApp) {
+ int _startFlags, ActivityStack _stack, WindowProcessController app) {
r = _r;
sourceRecord = _sourceRecord;
startFlags = _startFlags;
stack = _stack;
- callerApp = _callerApp;
+ callerApp = app;
}
void sendErrorResult(String message) {
try {
- if (callerApp.thread != null) {
- callerApp.thread.scheduleCrash(message);
+ if (callerApp.hasThread()) {
+ callerApp.getThread().scheduleCrash(message);
}
} catch (RemoteException e) {
Slog.e(TAG, "Exception scheduling crash of failed "
@@ -622,9 +622,9 @@
mInitialized = true;
mRunningTasks = createRunningTasks();
mActivityMetricsLogger = new ActivityMetricsLogger(this, mService.mContext, mHandler.getLooper());
- mKeyguardController = new KeyguardController(mService.mAm, this);
+ mKeyguardController = new KeyguardController(mService, this);
- mLaunchParamsController = new LaunchParamsController(mService.mAm);
+ mLaunchParamsController = new LaunchParamsController(mService);
mLaunchParamsController.registerDefaultModifiers(this);
}
@@ -680,12 +680,62 @@
calculateDefaultMinimalSizeOfResizeableTasks(activityDisplay);
}
- mHomeStack = mFocusedStack = mLastFocusedStack = getDefaultDisplay().getOrCreateStack(
+ final ActivityDisplay defaultDisplay = getDefaultDisplay();
+ mHomeStack = mLastFocusedStack = defaultDisplay.getOrCreateStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
}
- ActivityStack getFocusedStack() {
- return mFocusedStack;
+ ActivityStack getTopDisplayFocusedStack() {
+ mWindowManager.getDisplaysInFocusOrder(mTmpOrderedDisplayIds);
+
+ for (int i = mTmpOrderedDisplayIds.size() - 1; i >= 0; --i) {
+ final int displayId = mTmpOrderedDisplayIds.get(i);
+ final ActivityDisplay display = mActivityDisplays.get(displayId);
+
+ // If WindowManagerService has encountered the display before we have, ignore as there
+ // will be no stacks present and therefore no activities.
+ if (display == null) {
+ continue;
+ }
+ final ActivityStack focusedStack = display.getFocusedStack();
+ if (focusedStack != null) {
+ return focusedStack;
+ }
+ }
+ return null;
+ }
+
+ ActivityRecord getTopResumedActivity() {
+ if (mWindowManager == null) {
+ return null;
+ }
+
+ final ActivityStack focusedStack = getTopDisplayFocusedStack();
+ if (focusedStack == null) {
+ return null;
+ }
+ final ActivityRecord resumedActivity = focusedStack.getResumedActivity();
+ if (resumedActivity != null && resumedActivity.app != null) {
+ return resumedActivity;
+ }
+ // The top focused stack might not have a resumed activity yet - look on all displays in
+ // focus order.
+ mWindowManager.getDisplaysInFocusOrder(mTmpOrderedDisplayIds);
+ for (int i = mTmpOrderedDisplayIds.size() - 1; i >= 0; --i) {
+ final int displayId = mTmpOrderedDisplayIds.get(i);
+ final ActivityDisplay display = mActivityDisplays.get(displayId);
+
+ // If WindowManagerService has encountered the display before we have, ignore as there
+ // will be no stacks present and therefore no activities.
+ if (display == null) {
+ continue;
+ }
+ final ActivityRecord resumedActivityOnDisplay = display.getResumedActivity();
+ if (resumedActivityOnDisplay != null) {
+ return resumedActivityOnDisplay;
+ }
+ }
+ return null;
}
boolean isFocusable(ConfigurationContainer container, boolean alwaysFocusable) {
@@ -696,12 +746,12 @@
return container.getWindowConfiguration().canReceiveKeys() || alwaysFocusable;
}
- ActivityStack getLastStack() {
+ ActivityStack getTopDisplayLastFocusedStack() {
return mLastFocusedStack;
}
- boolean isFocusedStack(ActivityStack stack) {
- return stack != null && stack == mFocusedStack;
+ boolean isTopDisplayFocusedStack(ActivityStack stack) {
+ return stack != null && stack == getTopDisplayFocusedStack();
}
/** NOTE: Should only be called from {@link ActivityStack#moveToFront} */
@@ -716,12 +766,12 @@
}
}
- if (focusCandidate != mFocusedStack) {
- mLastFocusedStack = mFocusedStack;
- mFocusedStack = focusCandidate;
-
+ final ActivityStack currentFocusedStack = getTopDisplayFocusedStack();
+ if (currentFocusedStack != focusCandidate) {
+ mLastFocusedStack = currentFocusedStack;
+ // TODO(b/111541062): Update event log to include focus movements on all displays
EventLogTags.writeAmFocusedStack(
- mCurrentUser, mFocusedStack == null ? -1 : mFocusedStack.getStackId(),
+ mCurrentUser, focusCandidate == null ? -1 : focusCandidate.getStackId(),
mLastFocusedStack == null ? -1 : mLastFocusedStack.getStackId(), reason);
}
@@ -910,7 +960,7 @@
// result to an activity belonging to userId. Example case: a document
// picker for personal files, opened by a work app, should still get locked.
if (taskTopActivityIsUser(task, userId)) {
- mService.mAm.mActivityTaskManager.getTaskChangeNotificationController().notifyTaskProfileLocked(
+ mService.getTaskChangeNotificationController().notifyTaskProfileLocked(
task.taskId, userId);
}
}
@@ -959,21 +1009,6 @@
return candidateTaskId;
}
- ActivityRecord getResumedActivityLocked() {
- ActivityStack stack = mFocusedStack;
- if (stack == null) {
- return null;
- }
- ActivityRecord resumedActivity = stack.getResumedActivity();
- if (resumedActivity == null || resumedActivity.app == null) {
- resumedActivity = stack.mPausingActivity;
- if (resumedActivity == null || resumedActivity.app == null) {
- resumedActivity = stack.topRunningActivityLocked();
- }
- }
- return resumedActivity;
- }
-
boolean attachApplicationLocked(ProcessRecord app) throws RemoteException {
final String processName = app.processName;
boolean didSomething = false;
@@ -981,7 +1016,7 @@
final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = display.getChildAt(stackNdx);
- if (!isFocusedStack(stack)) {
+ if (!isTopDisplayFocusedStack(stack)) {
continue;
}
stack.getAllRunningVisibleActivitiesLocked(mTmpActivityList);
@@ -1016,7 +1051,7 @@
final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = display.getChildAt(stackNdx);
- if (!isFocusedStack(stack) || stack.numActivities() == 0) {
+ if (!isTopDisplayFocusedStack(stack) || stack.numActivities() == 0) {
continue;
}
final ActivityRecord resumedActivity = stack.getResumedActivity();
@@ -1037,7 +1072,7 @@
final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = display.getChildAt(stackNdx);
- if (isFocusedStack(stack)) {
+ if (isTopDisplayFocusedStack(stack)) {
final ActivityRecord r = stack.getResumedActivity();
if (r != null && !r.isState(RESUMED)) {
return false;
@@ -1046,10 +1081,11 @@
}
}
// TODO: Not sure if this should check if all Paused are complete too.
+ final ActivityStack focusedStack = getTopDisplayFocusedStack();
if (DEBUG_STACK) Slog.d(TAG_STACK,
- "allResumedActivitiesComplete: mLastFocusedStack changing from=" +
- mLastFocusedStack + " to=" + mFocusedStack);
- mLastFocusedStack = mFocusedStack;
+ "allResumedActivitiesComplete: mLastFocusedStack changing from="
+ + mLastFocusedStack + " to=" + focusedStack);
+ mLastFocusedStack = focusedStack;
return true;
}
@@ -1085,7 +1121,7 @@
final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = display.getChildAt(stackNdx);
- if (!isFocusedStack(stack) && stack.getResumedActivity() != null) {
+ if (!isTopDisplayFocusedStack(stack) && stack.getResumedActivity() != null) {
if (DEBUG_STATES) Slog.d(TAG_STATES, "pauseBackStacks: stack=" + stack +
" mResumedActivity=" + stack.getResumedActivity());
someActivityPaused |= stack.startPausingLocked(userLeaving, false, resuming,
@@ -1165,7 +1201,7 @@
}
}
if (changed) {
- mService.mAm.notifyAll();
+ mService.mGlobalLock.notifyAll();
}
}
@@ -1195,7 +1231,7 @@
}
if (changed) {
- mService.mAm.notifyAll();
+ mService.mGlobalLock.notifyAll();
}
}
@@ -1216,7 +1252,7 @@
}
}
if (changed) {
- mService.mAm.notifyAll();
+ mService.mGlobalLock.notifyAll();
}
}
@@ -1233,7 +1269,7 @@
* @return The top running activity. {@code null} if none is available.
*/
ActivityRecord topRunningActivityLocked(boolean considerKeyguardState) {
- final ActivityStack focusedStack = mFocusedStack;
+ final ActivityStack focusedStack = getTopDisplayFocusedStack();
ActivityRecord r = focusedStack.topRunningActivityLocked();
if (r != null && isValidTopRunningActivity(r, considerKeyguardState)) {
return r;
@@ -1399,12 +1435,13 @@
beginDeferResume();
try {
- r.startFreezingScreenLocked(app, 0);
+ final WindowProcessController proc = app.getWindowProcessController();
+ r.startFreezingScreenLocked(proc, 0);
// schedule launch ticks to collect information about slow apps.
r.startLaunchTickingLocked();
- r.setProcess(app);
+ r.setProcess(proc);
if (getKeyguardController().isKeyguardLocked()) {
r.notifyUnknownVisibilityLaunched();
@@ -1448,12 +1485,8 @@
if (DEBUG_ALL) Slog.v(TAG, "Launching: " + r);
- int idx = app.activities.indexOf(r);
- if (idx < 0) {
- app.activities.add(r);
- }
- mService.mAm.updateLruProcessLocked(app, true, null);
- mService.mAm.updateOomAdjLocked();
+ proc.addActivityIfNeeded(r);
+ proc.updateProcessInfo(false, true, true, true);
final LockTaskController lockTaskController = mService.getLockTaskController();
if (task.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE
@@ -1483,7 +1516,7 @@
System.identityHashCode(r), task.taskId, r.shortComponentName);
if (r.isActivityTypeHome()) {
// Home process is the root process of the task.
- mService.mAm.mHomeProcess = task.mActivities.get(0).app;
+ mService.mHomeProcess = task.mActivities.get(0).app;
}
mService.mAm.notifyPackageUse(r.intent.getComponent().getPackageName(),
PackageManager.NOTIFY_PACKAGE_USE_ACTIVITY);
@@ -1513,13 +1546,13 @@
app.hasShownUi = true;
app.pendingUiClean = true;
- app.forceProcessStateUpTo(mService.mAm.mTopProcessState);
+ app.forceProcessStateUpTo(mService.mTopProcessState);
// Because we could be starting an Activity in the system process this may not go
// across a Binder interface which would create a new Configuration. Consequently
// we have to always create a new Configuration here.
final MergedConfiguration mergedConfiguration = new MergedConfiguration(
- mService.mAm.getGlobalConfiguration(), r.getMergedOverrideConfiguration());
+ mService.getGlobalConfiguration(), r.getMergedOverrideConfiguration());
r.setLastReportedConfiguration(mergedConfiguration);
logIfTransactionTooLarge(r.intent, r.icicle);
@@ -1534,9 +1567,9 @@
// and override configs.
mergedConfiguration.getGlobalConfiguration(),
mergedConfiguration.getOverrideConfiguration(), r.compat,
- r.launchedFromPackage, task.voiceInteractor, app.repProcState, r.icicle,
- r.persistentState, results, newIntents, mService.isNextTransitionForward(),
- profilerInfo));
+ r.launchedFromPackage, task.voiceInteractor, app.getReportedProcState(),
+ r.icicle, r.persistentState, results, newIntents,
+ mService.isNextTransitionForward(), profilerInfo));
// Set desired final state.
final ActivityLifecycleItem lifecycleItem;
@@ -1587,7 +1620,7 @@
// This is the first time we failed -- restart process and
// retry.
r.launchFailed = true;
- app.activities.remove(r);
+ proc.removeActivity(r);
throw e;
}
} finally {
@@ -1618,14 +1651,14 @@
// launching the initial activity (that is, home), so that it can have
// a chance to initialize itself while in the background, making the
// switch back to it faster and look better.
- if (isFocusedStack(stack)) {
+ if (isTopDisplayFocusedStack(stack)) {
mService.getActivityStartController().startSetupActivity();
}
// Update any services we are bound to that might care about whether
// their client may have activities.
if (r.app != null) {
- mService.mAm.mServices.updateServiceConnectionActivitiesLocked(r.app);
+ r.app.updateServiceConnectionActivities();
}
return true;
@@ -1639,6 +1672,8 @@
* @param markFrozenIfConfigChanged Whether to set {@link ActivityRecord#frozenBeforeDestroy} to
* {@code true} if config changed.
* @param deferResume Whether to defer resume while updating config.
+ * @return 'true' if starting activity was kept or wasn't provided, 'false' if it was relaunched
+ * because of configuration update.
*/
boolean ensureVisibilityAndConfig(ActivityRecord starting, int displayId,
boolean markFrozenIfConfigChanged, boolean deferResume) {
@@ -1649,6 +1684,11 @@
ensureActivitiesVisibleLocked(null /* starting */, 0 /* configChanges */,
false /* preserveWindows */, false /* notifyClients */);
+ if (displayId == INVALID_DISPLAY) {
+ // The caller didn't provide a valid display id, skip updating config.
+ return true;
+ }
+
// Force-update the orientation from the WindowManager, since we need the true configuration
// to send to the client now.
final Configuration config = mWindowManager.updateOrientationFromAppTokens(
@@ -1661,7 +1701,7 @@
}
// Update the configuration of the activities on the display.
- return mService.mAm.updateDisplayOverrideConfigurationLocked(config, starting, deferResume,
+ return mService.updateDisplayOverrideConfigurationLocked(config, starting, deferResume,
displayId);
}
@@ -1718,12 +1758,27 @@
boolean sendHint = forceSend;
if (!sendHint) {
- // If not forced, send power hint when the activity's process is different than the
- // current resumed activity.
- final ActivityRecord resumedActivity = getResumedActivityLocked();
- sendHint = resumedActivity == null
- || resumedActivity.app == null
- || !resumedActivity.app.equals(targetActivity.app);
+ // Send power hint if we don't know what we're launching yet
+ sendHint = targetActivity == null || targetActivity.app == null;
+ }
+
+ if (!sendHint) { // targetActivity != null
+ // Send power hint when the activity's process is different than the current resumed
+ // activity on all displays, or if there are no resumed activities in the system.
+ boolean noResumedActivities = true;
+ boolean allFocusedProcessesDiffer = true;
+ for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); ++displayNdx) {
+ final ActivityDisplay activityDisplay = mActivityDisplays.valueAt(displayNdx);
+ final ActivityRecord resumedActivity = activityDisplay.getResumedActivity();
+ final WindowProcessController resumedActivityProcess =
+ resumedActivity == null ? null : resumedActivity.app;
+
+ noResumedActivities &= resumedActivityProcess == null;
+ if (resumedActivityProcess != null) {
+ allFocusedProcessesDiffer &= !resumedActivityProcess.equals(targetActivity.app);
+ }
+ }
+ sendHint = noResumedActivities || allFocusedProcessesDiffer;
}
if (sendHint && mService.mAm.mLocalPowerManager != null) {
@@ -1746,7 +1801,7 @@
ProcessRecord callerApp, ActivityRecord resultRecord, ActivityStack resultStack) {
final boolean isCallerRecents = mService.getRecentTasks() != null
&& mService.getRecentTasks().isCallerRecents(callingUid);
- final int startAnyPerm = mService.mAm.checkPermission(START_ANY_ACTIVITY, callingPid,
+ final int startAnyPerm = mService.checkPermission(START_ANY_ACTIVITY, callingPid,
callingUid);
if (startAnyPerm == PERMISSION_GRANTED || (isCallerRecents && launchingInTask)) {
// If the caller has START_ANY_ACTIVITY, ignore all checks below. In addition, if the
@@ -1825,7 +1880,7 @@
// Check if the caller has enough privileges to embed activities and launch to private
// displays.
- final int startAnyPerm = mService.mAm.checkPermission(INTERNAL_SYSTEM_WINDOW, callingPid,
+ final int startAnyPerm = mService.checkPermission(INTERNAL_SYSTEM_WINDOW, callingPid,
callingUid);
if (startAnyPerm == PERMISSION_GRANTED) {
if (DEBUG_TASKS) Slog.d(TAG, "Launch on display check:"
@@ -1847,7 +1902,7 @@
return false;
}
// Check if the caller is allowed to embed activities from other apps.
- if (mService.mAm.checkPermission(ACTIVITY_EMBEDDING, callingPid, callingUid)
+ if (mService.checkPermission(ACTIVITY_EMBEDDING, callingPid, callingUid)
== PERMISSION_DENIED && !uidPresentOnDisplay) {
if (DEBUG_TASKS) Slog.d(TAG, "Launch on display check:"
+ " disallow activity embedding without permission.");
@@ -1920,8 +1975,8 @@
return ACTIVITY_RESTRICTION_NONE;
}
- if (mService.mAm.mAppOpsService.noteOperation(opCode, callingUid,
- callingPackage) != AppOpsManager.MODE_ALLOWED) {
+ if (mService.getAppOpsService().noteOperation(opCode, callingUid, callingPackage)
+ != AppOpsManager.MODE_ALLOWED) {
if (!ignoreTargetSecurity) {
return ACTIVITY_RESTRICTION_APPOP;
}
@@ -1954,7 +2009,7 @@
return ACTIVITY_RESTRICTION_NONE;
}
- if (mService.mAm.checkPermission(permission, callingPid, callingUid) == PERMISSION_DENIED) {
+ if (mService.checkPermission(permission, callingPid, callingUid) == PERMISSION_DENIED) {
return ACTIVITY_RESTRICTION_PERMISSION;
}
@@ -1963,8 +2018,8 @@
return ACTIVITY_RESTRICTION_NONE;
}
- if (mService.mAm.mAppOpsService.noteOperation(opCode, callingUid,
- callingPackage) != AppOpsManager.MODE_ALLOWED) {
+ if (mService.getAppOpsService().noteOperation(opCode, callingUid, callingPackage)
+ != AppOpsManager.MODE_ALLOWED) {
return ACTIVITY_RESTRICTION_APPOP;
}
@@ -2042,7 +2097,7 @@
r.idle = true;
//Slog.i(TAG, "IDLE: mBooted=" + mBooted + ", fromTimeout=" + fromTimeout);
- if (isFocusedStack(r.getStack()) || fromTimeout) {
+ if (isTopDisplayFocusedStack(r.getStack()) || fromTimeout) {
booting = checkFinishBootingLocked();
}
}
@@ -2106,12 +2161,12 @@
// Complete user switch
if (startingUsers != null) {
for (int i = 0; i < startingUsers.size(); i++) {
- mService.mAm.mUserController.finishUserSwitch(startingUsers.get(i));
+ mService.mAmInternal.finishUserSwitch(startingUsers.get(i));
}
}
}
- mService.mAm.trimApplications();
+ mService.mAmInternal.trimApplications();
//dump();
//mWindowManager.dump();
@@ -2122,7 +2177,7 @@
return r;
}
- boolean handleAppDiedLocked(ProcessRecord app) {
+ boolean handleAppDiedLocked(WindowProcessController app) {
boolean hasVisibleActivities = false;
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
@@ -2185,12 +2240,12 @@
// First, found out what is currently the foreground app, so that
// we don't blow away the previous app if this activity is being
// hosted by the process that is actually still the foreground.
- ProcessRecord fgApp = null;
+ WindowProcessController fgApp = null;
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = display.getChildAt(stackNdx);
- if (isFocusedStack(stack)) {
+ if (isTopDisplayFocusedStack(stack)) {
final ActivityRecord resumedActivity = stack.getResumedActivity();
if (resumedActivity != null) {
fgApp = resumedActivity.app;
@@ -2204,11 +2259,11 @@
// Now set this one as the previous process, only if that really
// makes sense to.
- if (r.app != null && fgApp != null && r.app != fgApp
- && r.lastVisibleTime > mService.mAm.mPreviousProcessVisibleTime
- && r.app != mService.mAm.mHomeProcess) {
- mService.mAm.mPreviousProcess = r.app;
- mService.mAm.mPreviousProcessVisibleTime = r.lastVisibleTime;
+ if (r.hasProcess() && fgApp != null && r.app != fgApp
+ && r.lastVisibleTime > mService.mPreviousProcessVisibleTime
+ && r.app != mService.mHomeProcess) {
+ mService.mPreviousProcess = r.app;
+ mService.mPreviousProcessVisibleTime = r.lastVisibleTime;
}
}
@@ -2223,16 +2278,17 @@
return false;
}
- if (targetStack != null && isFocusedStack(targetStack)) {
+ if (targetStack != null && isTopDisplayFocusedStack(targetStack)) {
return targetStack.resumeTopActivityUncheckedLocked(target, targetOptions);
}
- final ActivityRecord r = mFocusedStack.topRunningActivityLocked();
+ final ActivityStack focusedStack = getTopDisplayFocusedStack();
+ final ActivityRecord r = focusedStack.topRunningActivityLocked();
if (r == null || !r.isState(RESUMED)) {
- mFocusedStack.resumeTopActivityUncheckedLocked(null, null);
+ focusedStack.resumeTopActivityUncheckedLocked(null, null);
} else if (r.isState(RESUMED)) {
// Kick off any lingering app transitions form the MoveTaskToFront operation.
- mFocusedStack.executeAppTransition(targetOptions);
+ focusedStack.executeAppTransition(targetOptions);
}
return false;
@@ -2254,9 +2310,9 @@
* @param reason Reason to perform this action.
* @return The task that was finished in this stack, {@code null} if haven't found any.
*/
- TaskRecord finishTopCrashedActivitiesLocked(ProcessRecord app, String reason) {
+ TaskRecord finishTopCrashedActivitiesLocked(WindowProcessController app, String reason) {
TaskRecord finishedTask = null;
- ActivityStack focusedStack = getFocusedStack();
+ ActivityStack focusedStack = getTopDisplayFocusedStack();
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
// It is possible that request to finish activity might also remove its task and stack,
@@ -2347,9 +2403,9 @@
if (options == null || options.getLaunchBounds() == null) {
return false;
}
- return (mService.mAm.mSupportsPictureInPicture
+ return (mService.mSupportsPictureInPicture
&& options.getLaunchWindowingMode() == WINDOWING_MODE_PINNED)
- || mService.mAm.mSupportsFreeformWindowManagement;
+ || mService.mSupportsFreeformWindowManagement;
}
LaunchParamsController getLaunchParamsController() {
@@ -3104,36 +3160,34 @@
// Determine if the process(es) for this task should be killed.
final String pkg = component.getPackageName();
- ArrayList<ProcessRecord> procsToKill = new ArrayList<>();
- ArrayMap<String, SparseArray<ProcessRecord>> pmap = mService.mAm.mProcessNames.getMap();
+ ArrayList<Object> procsToKill = new ArrayList<>();
+ ArrayMap<String, SparseArray<WindowProcessController>> pmap =
+ mService.mProcessNames.getMap();
for (int i = 0; i < pmap.size(); i++) {
- SparseArray<ProcessRecord> uids = pmap.valueAt(i);
+ SparseArray<WindowProcessController> uids = pmap.valueAt(i);
for (int j = 0; j < uids.size(); j++) {
- ProcessRecord proc = uids.valueAt(j);
- if (proc.userId != tr.userId) {
+ WindowProcessController proc = uids.valueAt(j);
+ if (proc.mUserId != tr.userId) {
// Don't kill process for a different user.
continue;
}
- if (proc == mService.mAm.mHomeProcess) {
+ if (proc == mService.mHomeProcess) {
// Don't kill the home process along with tasks from the same package.
continue;
}
- if (!proc.pkgList.containsKey(pkg)) {
+ if (!proc.mPkgList.contains(pkg)) {
// Don't kill process that is not associated with this task.
continue;
}
- for (int k = 0; k < proc.activities.size(); k++) {
- TaskRecord otherTask = proc.activities.get(k).getTask();
- if (tr.taskId != otherTask.taskId && otherTask.inRecents) {
- // Don't kill process(es) that has an activity in a different task that is
- // also in recents.
- return;
- }
+ if (!proc.shouldKillProcessForRemovedTask(tr)) {
+ // Don't kill process(es) that has an activity in a different task that is also
+ // in recents.
+ return;
}
- if (proc.foregroundServices) {
+ if (proc.hasForegroundServices()) {
// Don't kill process(es) with foreground service.
return;
}
@@ -3143,17 +3197,12 @@
}
}
- // Kill the running processes.
- for (int i = 0; i < procsToKill.size(); i++) {
- ProcessRecord pr = procsToKill.get(i);
- if (pr.setSchedGroup == ProcessList.SCHED_GROUP_BACKGROUND
- && pr.curReceivers.isEmpty()) {
- pr.kill("remove task", true);
- } else {
- // We delay killing processes that are not in the background or running a receiver.
- pr.waitingToKill = "remove task";
- }
- }
+ // Kill the running processes. Post on handle since we don't want to hold the service lock
+ // while calling into AM.
+ final Runnable r = PooledLambda.obtainRunnable(
+ ActivityManagerInternal::killProcessesForRemovedTask, mService.mAmInternal,
+ procsToKill);
+ mService.mH.post(r);
}
/**
@@ -3258,21 +3307,21 @@
// Ensure that we aren't trying to move into a multi-window stack without multi-window
// support
- if (inMultiWindowMode && !mService.mAm.mSupportsMultiWindow) {
+ if (inMultiWindowMode && !mService.mSupportsMultiWindow) {
throw new IllegalArgumentException("Device doesn't support multi-window, can not"
+ " reparent task=" + task + " to stack=" + stack);
}
// Ensure that we're not moving a task to a dynamic stack if device doesn't support
// multi-display.
- if (stack.mDisplayId != DEFAULT_DISPLAY && !mService.mAm.mSupportsMultiDisplay) {
+ if (stack.mDisplayId != DEFAULT_DISPLAY && !mService.mSupportsMultiDisplay) {
throw new IllegalArgumentException("Device doesn't support multi-display, can not"
+ " reparent task=" + task + " to stackId=" + stackId);
}
// Ensure that we aren't trying to move into a freeform stack without freeform support
if (stack.getWindowingMode() == WINDOWING_MODE_FREEFORM
- && !mService.mAm.mSupportsFreeformWindowManagement) {
+ && !mService.mSupportsFreeformWindowManagement) {
throw new IllegalArgumentException("Device doesn't support freeform, can not reparent"
+ " task=" + task);
}
@@ -3305,7 +3354,7 @@
return false;
}
- if (!mService.mAm.mForceResizableActivities && !r.supportsPictureInPicture()) {
+ if (!mService.mForceResizableActivities && !r.supportsPictureInPicture()) {
Slog.w(TAG,
"moveTopStackActivityToPinnedStackLocked: Picture-In-Picture not supported for "
+ " r=" + r);
@@ -3405,7 +3454,7 @@
return false;
}
- if (stack == mFocusedStack && stack.topRunningActivityLocked() == r) {
+ if (r == getTopResumedActivity()) {
if (DEBUG_FOCUS) Slog.d(TAG_FOCUS,
"moveActivityStackToFront: already on top, r=" + r);
return false;
@@ -3518,7 +3567,7 @@
long timeRemaining = endTime - System.currentTimeMillis();
if (timeRemaining > 0) {
try {
- mService.mAm.wait(timeRemaining);
+ mService.mGlobalLock.wait(timeRemaining);
} catch (InterruptedException e) {
}
} else {
@@ -3565,8 +3614,8 @@
stack.goToSleepIfPossible(false /* shuttingDown */);
} else {
stack.awakeFromSleepingLocked();
- if (isFocusedStack(stack) && !getKeyguardController().isKeyguardOrAodShowing(
- display.mDisplayId)) {
+ if (isTopDisplayFocusedStack(stack) && !getKeyguardController()
+ .isKeyguardOrAodShowing(display.mDisplayId)) {
// If the keyguard is unlocked - resume immediately.
// It is possible that the display will not be awake at the time we
// process the keyguard going away, which can happen before the sleep token
@@ -3600,7 +3649,7 @@
}
void checkReadyForSleepLocked(boolean allowDelay) {
- if (!mService.mAm.isSleepingOrShuttingDownLocked()) {
+ if (!mService.isSleepingOrShuttingDownLocked()) {
// Do not care.
return;
}
@@ -3617,8 +3666,8 @@
if (mGoingToSleep.isHeld()) {
mGoingToSleep.release();
}
- if (mService.mAm.mShuttingDown) {
- mService.mAm.notifyAll();
+ if (mService.mShuttingDown) {
+ mService.mGlobalLock.notifyAll();
}
}
@@ -3645,7 +3694,7 @@
mStoppingActivities.remove(r);
final ActivityStack stack = r.getStack();
- if (isFocusedStack(stack)) {
+ if (isTopDisplayFocusedStack(stack)) {
mService.mAm.updateUsageStats(r, true);
}
if (allResumedActivitiesComplete()) {
@@ -3656,7 +3705,7 @@
return false;
}
- void handleAppCrashLocked(ProcessRecord app) {
+ void handleAppCrashLocked(WindowProcessController app) {
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
@@ -3758,7 +3807,7 @@
}
}
- void scheduleDestroyAllActivities(ProcessRecord app, String reason) {
+ void scheduleDestroyAllActivities(WindowProcessController app, String reason) {
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
@@ -3768,44 +3817,9 @@
}
}
- void releaseSomeActivitiesLocked(ProcessRecord app, String reason) {
- // Examine all activities currently running in the process.
- TaskRecord firstTask = null;
+ void releaseSomeActivitiesLocked(WindowProcessController app, String reason) {
// Tasks is non-null only if two or more tasks are found.
- ArraySet<TaskRecord> tasks = null;
- if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Trying to release some activities in " + app);
- for (int i = 0; i < app.activities.size(); i++) {
- ActivityRecord r = app.activities.get(i);
- // First, if we find an activity that is in the process of being destroyed,
- // then we just aren't going to do anything for now; we want things to settle
- // down before we try to prune more activities.
- if (r.finishing || r.isState(DESTROYING, DESTROYED)) {
- if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Abort release; already destroying: " + r);
- return;
- }
- // Don't consider any activies that are currently not in a state where they
- // can be destroyed.
- if (r.visible || !r.stopped || !r.haveState
- || r.isState(RESUMED, PAUSING, PAUSED, STOPPING)) {
- if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Not releasing in-use activity: " + r);
- continue;
- }
-
- final TaskRecord task = r.getTask();
- if (task != null) {
- if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Collecting release task " + task
- + " from " + r);
- if (firstTask == null) {
- firstTask = task;
- } else if (firstTask != task) {
- if (tasks == null) {
- tasks = new ArraySet<>();
- tasks.add(firstTask);
- }
- tasks.add(task);
- }
- }
- }
+ ArraySet<TaskRecord> tasks = app.getReleaseSomeActivitiesTasks();
if (tasks == null) {
if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Didn't find two or more tasks to release");
return;
@@ -3828,11 +3842,11 @@
}
boolean switchUserLocked(int userId, UserState uss) {
- final int focusStackId = mFocusedStack.getStackId();
+ final int focusStackId = getTopDisplayFocusedStack().getStackId();
// We dismiss the docked stack whenever we switch users.
final ActivityStack dockedStack = getDefaultDisplay().getSplitScreenPrimaryStack();
if (dockedStack != null) {
- moveTasksToFullscreenStackLocked(dockedStack, mFocusedStack == dockedStack);
+ moveTasksToFullscreenStackLocked(dockedStack, dockedStack.isFocusedStackOnDisplay());
}
// Also dismiss the pinned stack whenever we switch users. Removing the pinned stack will
// also cause all tasks to be moved to the fullscreen stack at a position that is
@@ -3873,7 +3887,7 @@
/** Checks whether the userid is a profile of the current user. */
boolean isCurrentProfileLocked(int userId) {
if (userId == mCurrentUser) return true;
- return mService.mAm.mUserController.isCurrentProfile(userId);
+ return mService.mAmInternal.isCurrentProfile(userId);
}
/**
@@ -3920,7 +3934,7 @@
final ActivityStack stack = s.getStack();
final boolean shouldSleepOrShutDown = stack != null
? stack.shouldSleepOrShutDownActivities()
- : mService.mAm.isSleepingOrShuttingDownLocked();
+ : mService.isSleepingOrShuttingDownLocked();
if (!waitingVisible || shouldSleepOrShutDown) {
if (!processPausingActivities && s.isState(PAUSING)) {
// Defer processing pausing activities in this iteration and reschedule
@@ -3935,6 +3949,10 @@
stops = new ArrayList<>();
}
stops.add(s);
+
+ // Make sure to remove it in all cases in case we entered this block with
+ // shouldSleepOrShutDown
+ mActivitiesWaitingForVisibleActivity.remove(s);
mStoppingActivities.remove(activityNdx);
}
}
@@ -3950,7 +3968,7 @@
final ActivityStack stack = display.getChildAt(stackNdx);
final ActivityRecord r = stack.topRunningActivityLocked();
final ActivityState state = r == null ? DESTROYED : r.getState();
- if (isFocusedStack(stack)) {
+ if (isTopDisplayFocusedStack(stack)) {
if (r == null) Slog.e(TAG,
"validateTop...: null top activity, stack=" + stack);
else {
@@ -3984,7 +4002,7 @@
}
public void dump(PrintWriter pw, String prefix) {
- pw.print(prefix); pw.print("mFocusedStack=" + mFocusedStack);
+ pw.print(prefix); pw.print("mFocusedStack=" + getTopDisplayFocusedStack());
pw.print(" mLastFocusedStack="); pw.println(mLastFocusedStack);
pw.print(prefix);
pw.println("mCurTaskIdForUser=" + mCurTaskIdForUser);
@@ -4010,13 +4028,15 @@
final long token = proto.start(fieldId);
super.writeToProto(proto, CONFIGURATION_CONTAINER, false /* trim */);
for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); ++displayNdx) {
- ActivityDisplay activityDisplay = mActivityDisplays.valueAt(displayNdx);
+ final ActivityDisplay activityDisplay = mActivityDisplays.valueAt(displayNdx);
activityDisplay.writeToProto(proto, DISPLAYS);
}
getKeyguardController().writeToProto(proto, KEYGUARD_CONTROLLER);
- if (mFocusedStack != null) {
- proto.write(FOCUSED_STACK_ID, mFocusedStack.mStackId);
- ActivityRecord focusedActivity = getResumedActivityLocked();
+ // TODO(b/111541062): Update tests to look for resumed activities on all displays
+ final ActivityStack focusedStack = getTopDisplayFocusedStack();
+ if (focusedStack != null) {
+ proto.write(FOCUSED_STACK_ID, focusedStack.mStackId);
+ final ActivityRecord focusedActivity = focusedStack.getDisplay().getResumedActivity();
if (focusedActivity != null) {
focusedActivity.writeIdentifierToProto(proto, RESUMED_ACTIVITY);
}
@@ -4025,6 +4045,7 @@
}
proto.write(IS_HOME_RECENTS_COMPONENT,
mRecentTasks.isRecentsComponentHomeActivity(mCurrentUser));
+ mService.getActivityStartController().writeToProto(proto, PENDING_ACTIVITIES);
proto.end(token);
}
@@ -4049,7 +4070,7 @@
ArrayList<ActivityRecord> getDumpActivitiesLocked(String name, boolean dumpVisibleStacksOnly,
boolean dumpFocusedStackOnly) {
if (dumpFocusedStackOnly) {
- return mFocusedStack.getDumpActivitiesLocked(name);
+ return getTopDisplayFocusedStack().getDumpActivitiesLocked(name);
} else {
ArrayList<ActivityRecord> activities = new ArrayList<>();
int numDisplays = mActivityDisplays.size();
@@ -4131,6 +4152,8 @@
}
needSep = printed;
}
+ printThisActivity(pw, activityDisplay.getResumedActivity(), dumpPackage, needSep,
+ " ResumedActivity:");
}
printed |= dumpHistoryList(fd, pw, mFinishingActivities, " ", "Fin", false, !dumpAll,
@@ -4198,16 +4221,16 @@
pw.print(innerPrefix); pw.println(r.app);
}
}
- if (client && r.app != null && r.app.thread != null) {
+ if (client && r.attachedToProcess()) {
// flush anything that is already in the PrintWriter since the thread is going
// to write to the file descriptor directly
pw.flush();
try {
TransferPipe tp = new TransferPipe();
try {
- r.app.thread.dumpActivity(tp.getWriteFd(), r.appToken, innerPrefix, args);
- // Short timeout, since blocking here can
- // deadlock with the application.
+ r.app.getThread().dumpActivity(
+ tp.getWriteFd(), r.appToken, innerPrefix, args);
+ // Short timeout, since blocking here can deadlock with the application.
tp.go(fd, 2000);
} finally {
tp.kill();
@@ -4361,7 +4384,7 @@
int displayState = activityDisplay.mDisplay.getState();
if (displayState == Display.STATE_OFF && activityDisplay.mOffToken == null) {
activityDisplay.mOffToken =
- mService.mAm.acquireSleepToken("Display-off", displayId);
+ mService.acquireSleepToken("Display-off", displayId);
} else if (displayState == Display.STATE_ON
&& activityDisplay.mOffToken != null) {
activityDisplay.mOffToken.release();
@@ -4394,7 +4417,7 @@
if (display != null) {
display.mAllSleepTokens.remove(token);
if (display.mAllSleepTokens.isEmpty()) {
- mService.mAm.updateSleepIfNeededLocked();
+ mService.updateSleepIfNeededLocked();
}
}
}
@@ -4408,7 +4431,7 @@
}
display.mAllSleepTokens.clear();
- mService.mAm.updateSleepIfNeededLocked();
+ mService.updateSleepIfNeededLocked();
}
private StackInfo getStackInfo(ActivityStack stack) {
@@ -4567,7 +4590,7 @@
for (int i = task.mActivities.size() - 1; i >= 0; i--) {
final ActivityRecord r = task.mActivities.get(i);
- if (r.app != null && r.app.thread != null) {
+ if (r.attachedToProcess()) {
mMultiWindowModeChangedActivities.add(r);
}
}
@@ -4590,7 +4613,7 @@
void scheduleUpdatePictureInPictureModeIfNeeded(TaskRecord task, Rect targetStackBounds) {
for (int i = task.mActivities.size() - 1; i >= 0; i--) {
final ActivityRecord r = task.mActivities.get(i);
- if (r.app != null && r.app.thread != null) {
+ if (r.attachedToProcess()) {
mPipModeChangedActivities.add(r);
// If we are scheduling pip change, then remove this activity from multi-window
// change list as the processing of pip change will make sure multi-window changed
@@ -4609,16 +4632,19 @@
mHandler.removeMessages(REPORT_PIP_MODE_CHANGED_MSG);
for (int i = task.mActivities.size() - 1; i >= 0; i--) {
final ActivityRecord r = task.mActivities.get(i);
- if (r.app != null && r.app.thread != null) {
+ if (r.attachedToProcess()) {
r.updatePictureInPictureMode(targetStackBounds, forceUpdate);
}
}
}
void setDockedStackMinimized(boolean minimized) {
+ // Get currently focused stack before setting mIsDockMinimized. We do this because if
+ // split-screen is active, primary stack will not be focusable (see #isFocusable) while
+ // still occluding other stacks. This will cause getTopDisplayFocusedStack() to return null.
+ final ActivityStack current = getTopDisplayFocusedStack();
mIsDockMinimized = minimized;
if (mIsDockMinimized) {
- final ActivityStack current = getFocusedStack();
if (current.inSplitScreenPrimaryWindowingMode()) {
// The primary split-screen stack can't be focused while it is minimize, so move
// focus to something else.
@@ -4705,7 +4731,7 @@
} break;
case SLEEP_TIMEOUT_MSG: {
synchronized (mService.mGlobalLock) {
- if (mService.mAm.isSleepingOrShuttingDownLocked()) {
+ if (mService.isSleepingOrShuttingDownLocked()) {
Slog.w(TAG, "Sleep timeout! Sleeping now.");
checkReadyForSleepLocked(false /* allowDelay */);
}
@@ -4821,7 +4847,7 @@
// If the user must confirm credentials (e.g. when first launching a work app and the
// Work Challenge is present) let startActivityInPackage handle the intercepting.
- if (!mService.mAm.mUserController.shouldConfirmCredentials(task.userId)
+ if (!mService.mAmInternal.shouldConfirmCredentials(task.userId)
&& task.getRootActivity() != null) {
final ActivityRecord targetActivity = task.getTopActivity();
@@ -4882,6 +4908,7 @@
*/
List<IBinder> getTopVisibleActivities() {
final ArrayList<IBinder> topActivityTokens = new ArrayList<>();
+ final ActivityStack topFocusedStack = getTopDisplayFocusedStack();
// Traverse all displays.
for (int i = mActivityDisplays.size() - 1; i >= 0; i--) {
final ActivityDisplay display = mActivityDisplays.valueAt(i);
@@ -4892,7 +4919,7 @@
if (stack.shouldBeVisible(null /* starting */)) {
final ActivityRecord top = stack.getTopActivity();
if (top != null) {
- if (stack == mFocusedStack) {
+ if (stack == topFocusedStack) {
topActivityTokens.add(0, top.appToken);
} else {
topActivityTokens.add(top.appToken);
diff --git a/services/core/java/com/android/server/am/ActivityStartController.java b/services/core/java/com/android/server/am/ActivityStartController.java
index fa001df..2cba720 100644
--- a/services/core/java/com/android/server/am/ActivityStartController.java
+++ b/services/core/java/com/android/server/am/ActivityStartController.java
@@ -21,7 +21,7 @@
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.am.ActivityManagerService.ALLOW_FULL_ONLY;
+import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY;
import android.app.IApplicationThread;
import android.content.ComponentName;
@@ -40,6 +40,7 @@
import android.os.UserHandle;
import android.provider.Settings;
import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
import android.view.RemoteAnimationAdapter;
import com.android.internal.annotations.VisibleForTesting;
@@ -237,10 +238,10 @@
int checkTargetUser(int targetUserId, boolean validateIncomingUser,
int realCallingPid, int realCallingUid, String reason) {
if (validateIncomingUser) {
- return mService.mAm.mUserController.handleIncomingUser(realCallingPid, realCallingUid,
- targetUserId, false, ALLOW_FULL_ONLY, reason, null);
+ return mService.handleIncomingUser(
+ realCallingPid, realCallingUid, targetUserId, reason);
} else {
- mService.mAm.mUserController.ensureNotSpecialUser(targetUserId);
+ mService.mAmInternal.ensureNotSpecialUser(targetUserId);
return targetUserId;
}
}
@@ -404,7 +405,7 @@
"pendingActivityLaunch");
try {
starter.startResolvedActivity(pal.r, pal.sourceRecord, null, null, pal.startFlags,
- resume, null, null, null /* outRecords */);
+ resume, pal.r.pendingOptions, null, null /* outRecords */);
} catch (Exception e) {
Slog.e(TAG, "Exception during pending activity launch pal=" + pal, e);
pal.sendErrorResult(e.getMessage());
@@ -472,4 +473,10 @@
pw.println("(nothing)");
}
}
+
+ public void writeToProto(ProtoOutputStream proto, long fieldId) {
+ for (PendingActivityLaunch activity: mPendingActivityLaunches) {
+ activity.r.writeIdentifierToProto(proto, fieldId);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/am/ActivityStartInterceptor.java b/services/core/java/com/android/server/am/ActivityStartInterceptor.java
index 171c0bb..ca12716 100644
--- a/services/core/java/com/android/server/am/ActivityStartInterceptor.java
+++ b/services/core/java/com/android/server/am/ActivityStartInterceptor.java
@@ -69,7 +69,6 @@
private final ActivityTaskManagerService mService;
private final ActivityStackSupervisor mSupervisor;
private final Context mServiceContext;
- private final UserController mUserController;
// UserManager cannot be final as it's not ready when this class is instantiated during boot
private UserManager mUserManager;
@@ -101,16 +100,15 @@
ActivityStartInterceptor(
ActivityTaskManagerService service, ActivityStackSupervisor supervisor) {
- this(service, supervisor, service.mContext, service.mAm.mUserController);
+ this(service, supervisor, service.mContext);
}
@VisibleForTesting
ActivityStartInterceptor(ActivityTaskManagerService service, ActivityStackSupervisor supervisor,
- Context context, UserController userController) {
+ Context context) {
mService = service;
mSupervisor = supervisor;
mServiceContext = context;
- mUserController = userController;
}
/**
@@ -298,7 +296,7 @@
* @return The intercepting intent if needed.
*/
private Intent interceptWithConfirmCredentialsIfNeeded(ActivityInfo aInfo, int userId) {
- if (!mUserController.shouldConfirmCredentials(userId)) {
+ if (!mService.mAmInternal.shouldConfirmCredentials(userId)) {
return null;
}
// TODO(b/28935539): should allow certain activities to bypass work challenge
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index f50bb37..0572ca9 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -720,24 +720,25 @@
abort |= !mService.mAm.mIntentFirewall.checkStartActivity(intent, callingUid,
callingPid, resolvedType, aInfo.applicationInfo);
+ final WindowProcessController callerWpc =
+ callerApp != null ? callerApp.getWindowProcessController() : null;
// Merge the two options bundles, while realCallerOptions takes precedence.
ActivityOptions checkedOptions = options != null
- ? options.getOptions(intent, aInfo, callerApp, mSupervisor)
- : null;
+ ? options.getOptions(intent, aInfo, callerWpc, mSupervisor) : null;
if (allowPendingRemoteAnimationRegistryLookup) {
checkedOptions = mService.getActivityStartController()
.getPendingRemoteAnimationRegistry()
.overrideOptionsIfNeeded(callingPackage, checkedOptions);
}
- if (mService.mAm.mController != null) {
+ if (mService.mController != null) {
try {
// The Intent we give to the watcher has the extra data
// stripped off, since it can contain private information.
Intent watchIntent = intent.cloneFilter();
- abort |= !mService.mAm.mController.activityStarting(watchIntent,
+ abort |= !mService.mController.activityStarting(watchIntent,
aInfo.applicationInfo.packageName);
} catch (RemoteException e) {
- mService.mAm.mController = null;
+ mService.mController = null;
}
}
@@ -801,10 +802,10 @@
null /*profilerInfo*/);
if (DEBUG_PERMISSIONS_REVIEW) {
+ final ActivityStack focusedStack = mSupervisor.getTopDisplayFocusedStack();
Slog.i(TAG, "START u" + userId + " {" + intent.toShortString(true, true,
true, false) + "} from uid " + callingUid + " on display "
- + (mSupervisor.mFocusedStack == null
- ? DEFAULT_DISPLAY : mSupervisor.mFocusedStack.mDisplayId));
+ + (focusedStack == null ? DEFAULT_DISPLAY : focusedStack.mDisplayId));
}
}
}
@@ -825,7 +826,7 @@
ActivityRecord r = new ActivityRecord(mService, callerApp, callingPid,
callingUid,
- callingPackage, intent, resolvedType, aInfo, mService.mAm.getGlobalConfiguration(),
+ callingPackage, intent, resolvedType, aInfo, mService.getGlobalConfiguration(),
resultRecord, resultWho, requestCode, componentSpecified, voiceSession != null,
mSupervisor, checkedOptions, sourceRecord);
if (outActivity != null) {
@@ -838,32 +839,22 @@
r.appTimeTracker = sourceRecord.appTimeTracker;
}
- final ActivityStack stack = mSupervisor.mFocusedStack;
+ final ActivityStack stack = mSupervisor.getTopDisplayFocusedStack();
// If we are starting an activity that is not from the same uid as the currently resumed
// one, check whether app switches are allowed.
if (voiceSession == null && (stack.getResumedActivity() == null
|| stack.getResumedActivity().info.applicationInfo.uid != realCallingUid)) {
- if (!mService.mAm.checkAppSwitchAllowedLocked(callingPid, callingUid,
+ if (!mService.checkAppSwitchAllowedLocked(callingPid, callingUid,
realCallingPid, realCallingUid, "Activity start")) {
mController.addPendingActivityLaunch(new PendingActivityLaunch(r,
- sourceRecord, startFlags, stack, callerApp));
+ sourceRecord, startFlags, stack, callerWpc));
ActivityOptions.abort(checkedOptions);
return ActivityManager.START_SWITCHES_CANCELED;
}
}
- if (mService.mAm.mDidAppSwitch) {
- // This is the second allowed switch since we stopped switches,
- // so now just generally allow switches. Use case: user presses
- // home (switches disabled, switch to home, mDidAppSwitch now true);
- // user taps a home icon (coming from home so allowed, we hit here
- // and now allow anyone to switch again).
- mService.mAm.mAppSwitchesAllowedTime = 0;
- } else {
- mService.mAm.mDidAppSwitch = true;
- }
-
+ mService.onStartActivitySetDidAppSwitch();
mController.doPendingActivityLaunches(false);
return startActivity(r, sourceRecord, voiceSession, voiceInteractor, startFlags,
@@ -1022,9 +1013,9 @@
ActivityInfo aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags, profilerInfo);
synchronized (mService.mGlobalLock) {
- final ActivityStack stack = mSupervisor.mFocusedStack;
+ final ActivityStack stack = mSupervisor.getTopDisplayFocusedStack();
stack.mConfigWillChange = globalConfig != null
- && mService.mAm.getGlobalConfiguration().diff(globalConfig) != 0;
+ && mService.getGlobalConfiguration().diff(globalConfig) != 0;
if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
"Starting activity when config will change = " + stack.mConfigWillChange);
@@ -1067,13 +1058,8 @@
}
newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_INTENT,
new IntentSender(target));
- if (heavy.activities.size() > 0) {
- ActivityRecord hist = heavy.activities.get(0);
- newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_CUR_APP,
- hist.packageName);
- newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_CUR_TASK,
- hist.getTask().taskId);
- }
+ heavy.getWindowProcessController().updateIntentForHeavyWeightActivity(
+ newIntent);
newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_NEW_APP,
aInfo.packageName);
newIntent.setFlags(intent.getFlags());
@@ -1110,12 +1096,12 @@
// do so now. This allows a clean switch, as we are waiting
// for the current activity to pause (so we will not destroy
// it), and have not yet started the next activity.
- mService.mAm.enforceCallingPermission(android.Manifest.permission.CHANGE_CONFIGURATION,
+ mService.mAmInternal.enforceCallingPermission(android.Manifest.permission.CHANGE_CONFIGURATION,
"updateConfiguration()");
stack.mConfigWillChange = false;
if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
"Updating to new configuration after starting activity.");
- mService.mAm.updateConfigurationLocked(globalConfig, null, false);
+ mService.updateConfigurationLocked(globalConfig, null, false);
}
if (outResult != null) {
@@ -1368,13 +1354,13 @@
// If the activity being launched is the same as the one currently at the top, then
// we need to check if it should only be launched once.
- final ActivityStack topStack = mSupervisor.mFocusedStack;
+ final ActivityStack topStack = mSupervisor.getTopDisplayFocusedStack();
final ActivityRecord topFocused = topStack.getTopActivity();
final ActivityRecord top = topStack.topRunningNonDelayedActivityLocked(mNotTop);
final boolean dontStart = top != null && mStartActivity.resultTo == null
&& top.realActivity.equals(mStartActivity.realActivity)
&& top.userId == mStartActivity.userId
- && top.app != null && top.app.thread != null
+ && top.attachedToProcess()
&& ((mLaunchFlags & FLAG_ACTIVITY_SINGLE_TOP) != 0
|| isLaunchModeOneOf(LAUNCH_SINGLE_TOP, LAUNCH_SINGLE_TASK));
if (dontStart) {
@@ -1461,7 +1447,8 @@
// will not update the focused stack. If starting the new activity now allows the
// task stack to be focusable, then ensure that we now update the focused stack
// accordingly.
- if (mTargetStack.isFocusable() && !mSupervisor.isFocusedStack(mTargetStack)) {
+ if (mTargetStack.isFocusable()
+ && !mSupervisor.isTopDisplayFocusedStack(mTargetStack)) {
mTargetStack.moveToFront("startActivityUnchecked");
}
mSupervisor.resumeFocusedStackTopActivityLocked(mTargetStack, mStartActivity,
@@ -1623,8 +1610,8 @@
if ((startFlags & START_FLAG_ONLY_IF_NEEDED) != 0) {
ActivityRecord checkedCaller = sourceRecord;
if (checkedCaller == null) {
- checkedCaller = mSupervisor.mFocusedStack.topRunningNonDelayedActivityLocked(
- mNotTop);
+ checkedCaller = mSupervisor.getTopDisplayFocusedStack()
+ .topRunningNonDelayedActivityLocked(mNotTop);
}
if (!checkedCaller.realActivity.equals(r.realActivity)) {
// Caller is not the same as launcher, so always needed.
@@ -1821,7 +1808,7 @@
}
// Get the virtual display id from ActivityManagerService.
- int displayId = mService.mAm.mVr2dDisplayId;
+ int displayId = mService.mVr2dDisplayId;
if (displayId != INVALID_DISPLAY) {
if (DEBUG_STACK) {
Slog.d(TAG, "getSourceDisplayId :" + displayId);
@@ -1857,7 +1844,7 @@
// except... well, with SINGLE_TASK_LAUNCH it's not entirely clear. We'd like to have
// the same behavior as if a new instance was being started, which means not bringing it
// to the front if the caller is not itself in the front.
- final ActivityStack focusStack = mSupervisor.getFocusedStack();
+ final ActivityStack focusStack = mSupervisor.getTopDisplayFocusedStack();
ActivityRecord curTop = (focusStack == null)
? null : focusStack.topRunningNonDelayedActivityLocked(mNotTop);
@@ -2318,23 +2305,23 @@
}
final ActivityStack currentStack = task != null ? task.getStack() : null;
+ final ActivityStack focusedStack = mSupervisor.getTopDisplayFocusedStack();
if (currentStack != null) {
- if (mSupervisor.mFocusedStack != currentStack) {
+ if (focusedStack != currentStack) {
if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
"computeStackFocus: Setting " + "focused stack to r=" + r
+ " task=" + task);
} else {
if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
- "computeStackFocus: Focused stack already="
- + mSupervisor.mFocusedStack);
+ "computeStackFocus: Focused stack already=" + focusedStack);
}
return currentStack;
}
if (canLaunchIntoFocusedStack(r, newTask)) {
if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
- "computeStackFocus: Have a focused stack=" + mSupervisor.mFocusedStack);
- return mSupervisor.mFocusedStack;
+ "computeStackFocus: Have a focused stack=" + focusedStack);
+ return focusedStack;
}
if (mPreferredDisplayId != DEFAULT_DISPLAY) {
@@ -2370,7 +2357,7 @@
/** Check if provided activity record can launch in currently focused stack. */
// TODO: This method can probably be consolidated into getLaunchStack() below.
private boolean canLaunchIntoFocusedStack(ActivityRecord r, boolean newTask) {
- final ActivityStack focusedStack = mSupervisor.mFocusedStack;
+ final ActivityStack focusedStack = mSupervisor.getTopDisplayFocusedStack();
final boolean canUseFocusedStack;
if (focusedStack.isActivityTypeAssistant()) {
canUseFocusedStack = r.isActivityTypeAssistant();
@@ -2420,18 +2407,19 @@
}
// Otherwise handle adjacent launch.
+ final ActivityStack focusedStack = mSupervisor.getTopDisplayFocusedStack();
// The parent activity doesn't want to launch the activity on top of itself, but
// instead tries to put it onto other side in side-by-side mode.
- final ActivityStack parentStack = task != null ? task.getStack(): mSupervisor.mFocusedStack;
+ final ActivityStack parentStack = task != null ? task.getStack(): focusedStack;
- if (parentStack != mSupervisor.mFocusedStack) {
+ if (parentStack != focusedStack) {
// If task's parent stack is not focused - use it during adjacent launch.
return parentStack;
} else {
- if (mSupervisor.mFocusedStack != null && task == mSupervisor.mFocusedStack.topTask()) {
+ if (focusedStack != null && task == focusedStack.topTask()) {
// If task is already on top of focused stack - use it. We don't want to move the
// existing focused task to adjacent stack, just deliver new intent in this case.
- return mSupervisor.mFocusedStack;
+ return focusedStack;
}
if (parentStack != null && parentStack.inSplitScreenPrimaryWindowingMode()) {
diff --git a/services/core/java/com/android/server/am/ActivityTaskManagerService.java b/services/core/java/com/android/server/am/ActivityTaskManagerService.java
index 90097fd..10e0182 100644
--- a/services/core/java/com/android/server/am/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityTaskManagerService.java
@@ -24,14 +24,22 @@
import static android.Manifest.permission.READ_FRAME_BUFFER;
import static android.Manifest.permission.REMOVE_TASKS;
import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
+import static android.Manifest.permission.STOP_APP_SWITCHES;
import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
-import static android.app.ActivityTaskManagerInternal.ASSIST_KEY_CONTENT;
-import static android.app.ActivityTaskManagerInternal.ASSIST_KEY_DATA;
-import static android.app.ActivityTaskManagerInternal.ASSIST_KEY_RECEIVER_EXTRAS;
-import static android.app.ActivityTaskManagerInternal.ASSIST_KEY_STRUCTURE;
+import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
+import static android.provider.Settings.Global.HIDE_ERROR_DIALOGS;
+import static android.provider.Settings.System.FONT_SCALE;
+import static com.android.server.am.ActivityManagerService.dumpStackTraces;
+import static com.android.server.am.ActivityTaskManagerService.H.REPORT_TIME_TRACKER_MSG;
+import static com.android.server.am.ActivityTaskManagerService.UiHandler.DISMISS_DIALOG_UI_MSG;
+import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_CONTENT;
+import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_DATA;
+import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_RECEIVER_EXTRAS;
+import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_STRUCTURE;
import static android.app.ActivityTaskManager.INVALID_STACK_ID;
import static android.app.ActivityTaskManager.RESIZE_MODE_PRESERVE_WINDOW;
import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
+import static android.app.AppOpsManager.OP_NONE;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
@@ -42,8 +50,17 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static android.content.pm.PackageManager.FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS;
+import static android.content.pm.PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT;
+import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.content.res.Configuration.UI_MODE_TYPE_TELEVISION;
+import static android.os.Build.VERSION_CODES.N;
import static android.os.Process.SYSTEM_UID;
+import static android.provider.Settings.Global.ALWAYS_FINISH_ACTIVITIES;
+import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT;
+import static android.provider.Settings.Global.DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES;
+import static android.provider.Settings.Global.DEVELOPMENT_FORCE_RTL;
import static android.service.voice.VoiceInteractionSession.SHOW_SOURCE_APPLICATION;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
@@ -60,7 +77,9 @@
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_OOM_ADJ;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_STACK;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SWITCH;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_TASKS;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_VISIBILITY;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_CONFIGURATION;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_FOCUS;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_IMMERSIVE;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_LOCKTASK;
@@ -69,10 +88,13 @@
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_VISIBILITY;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.am.ActivityManagerService.ALLOW_FULL_ONLY;
+import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY;
import static com.android.server.am.ActivityManagerService.ANIMATE;
-import static com.android.server.am.ActivityManagerService.DISPATCH_SCREEN_KEYGUARD_MSG;
+import static com.android.server.am.ActivityManagerService.MY_PID;
+import static com.android.server.am.ActivityManagerService.SEND_LOCALE_TO_MOUNT_DAEMON_MSG;
import static com.android.server.am.ActivityManagerService.STOCK_PM_FLAGS;
+import static com.android.server.am.ActivityManagerService.UPDATE_CONFIGURATION_MSG;
+import static com.android.server.am.ActivityManagerService.checkComponentPermission;
import static com.android.server.am.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
import static com.android.server.am.ActivityStackSupervisor.DEFER_RESUME;
import static com.android.server.am.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_ONLY;
@@ -88,14 +110,34 @@
import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_ORIGINAL_POSITION;
import android.Manifest;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.Activity;
import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
import android.app.ActivityOptions;
import android.app.ActivityTaskManager;
-import android.app.ActivityTaskManagerInternal;
+import android.app.ActivityThread;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.database.ContentObserver;
+import android.os.IUserManager;
+import android.os.PowerManager;
+import android.os.ServiceManager;
+import android.os.Trace;
+import android.os.UserManager;
+import android.os.WorkSource;
+import android.view.WindowManager;
+import com.android.internal.R;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.app.IAppOpsService;
+import com.android.server.AppOpsService;
+import com.android.server.pm.UserManagerService;
+import com.android.server.wm.ActivityTaskManagerInternal;
import android.app.AppGlobals;
+import android.app.IActivityController;
import android.app.IActivityTaskManager;
import android.app.IApplicationThread;
import android.app.IAssistDataReceiver;
@@ -108,24 +150,30 @@
import android.app.admin.DevicePolicyCache;
import android.app.assist.AssistContent;
import android.app.assist.AssistStructure;
+import android.app.servertransaction.ConfigurationChangeItem;
import android.app.usage.UsageEvents;
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.IIntentSender;
import android.content.Intent;
import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice;
import android.content.pm.ResolveInfo;
import android.content.res.Configuration;
+import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Point;
import android.graphics.Rect;
import android.metrics.LogMaker;
import android.net.Uri;
import android.os.Binder;
+import android.os.Build;
import android.os.Bundle;
+import android.os.FileUtils;
import android.os.Handler;
import android.os.IBinder;
import android.os.LocaleList;
@@ -134,29 +182,42 @@
import android.os.PersistableBundle;
import android.os.Process;
import android.os.RemoteException;
-import android.os.TransactionTooLargeException;
+import android.os.StrictMode;
+import android.os.SystemClock;
+import android.os.SystemProperties;
+import android.os.UpdateLock;
import android.os.UserHandle;
import android.provider.Settings;
import android.service.voice.IVoiceInteractionSession;
import android.service.voice.VoiceInteractionManagerInternal;
import android.telecom.TelecomManager;
import android.text.TextUtils;
+import android.text.format.Time;
+import android.util.ArrayMap;
+import android.util.EventLog;
+import android.util.Log;
import android.util.Slog;
+import android.util.SparseArray;
import android.util.SparseIntArray;
+import android.util.StatsLog;
+import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
import android.view.IRecentsAnimationRunner;
import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationDefinition;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.AssistUtils;
import com.android.internal.app.IVoiceInteractor;
+import com.android.internal.app.ProcessMap;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.os.logging.MetricsLoggerWrapper;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.policy.IKeyguardDismissCallback;
import com.android.internal.policy.KeyguardDismissCallback;
import com.android.internal.util.Preconditions;
+import com.android.server.AttributeCache;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.Watchdog;
@@ -165,9 +226,13 @@
import com.android.server.wm.WindowManagerService;
import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
import java.io.PrintWriter;
+import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;
+import java.util.Locale;
/**
* System service for managing activities and their containers (task, stacks, displays,... ).
@@ -182,14 +247,36 @@
private static final String TAG_FOCUS = TAG + POSTFIX_FOCUS;
private static final String TAG_VISIBILITY = TAG + POSTFIX_VISIBILITY;
private static final String TAG_LOCKTASK = TAG + POSTFIX_LOCKTASK;
+ private static final String TAG_CONFIGURATION = TAG + POSTFIX_CONFIGURATION;
Context mContext;
+ /**
+ * This Context is themable and meant for UI display (AlertDialogs, etc.). The theme can
+ * change at runtime. Use mContext for non-UI purposes.
+ */
+ final Context mUiContext;
H mH;
+ UiHandler mUiHandler;
ActivityManagerService mAm;
+ ActivityManagerInternal mAmInternal;
/* Global service lock used by the package the owns this service. */
Object mGlobalLock;
ActivityStackSupervisor mStackSupervisor;
WindowManagerService mWindowManager;
+ private UserManagerService mUserManager;
+ private AppOpsService mAppOpsService;
+ /** All processes currently running that might have a window organized by name. */
+ final ProcessMap<WindowProcessController> mProcessNames = new ProcessMap<>();
+ /** This is the process holding what we currently consider to be the "home" activity. */
+ WindowProcessController mHomeProcess;
+ /**
+ * This is the process holding the activity the user last visited that is in a different process
+ * from the one they are currently in.
+ */
+ WindowProcessController mPreviousProcess;
+ /** The time at which the previous process was last visible. */
+ long mPreviousProcessVisibleTime;
+
/** List of intents that were used to start the most recent tasks. */
private RecentTasks mRecentTasks;
/** State of external calls telling us if the device is awake or asleep. */
@@ -245,17 +332,167 @@
}
/** Current sequencing integer of the configuration, for skipping old configurations. */
- int mConfigurationSeq;
+ private int mConfigurationSeq;
+ // To cache the list of supported system locales
+ private String[] mSupportedSystemLocales = null;
/**
* Temp object used when global and/or display override configuration is updated. It is also
* sent to outer world instead of {@link #getGlobalConfiguration} because we don't trust
* anyone...
*/
- Configuration mTempConfig = new Configuration();
+ private Configuration mTempConfig = new Configuration();
+
+ /** Temporary to avoid allocations. */
+ final StringBuilder mStringBuilder = new StringBuilder(256);
+
+ // Amount of time after a call to stopAppSwitches() during which we will
+ // prevent further untrusted switches from happening.
+ private static final long APP_SWITCH_DELAY_TIME = 5 * 1000;
+
+ /**
+ * The time at which we will allow normal application switches again,
+ * after a call to {@link #stopAppSwitches()}.
+ */
+ private long mAppSwitchesAllowedTime;
+ /**
+ * This is set to true after the first switch after mAppSwitchesAllowedTime
+ * is set; any switches after that will clear the time.
+ */
+ private boolean mDidAppSwitch;
+
+ IActivityController mController = null;
+ boolean mControllerIsAMonkey = false;
+
+ /**
+ * Used to retain an update lock when the foreground activity is in
+ * immersive mode.
+ */
+ private final UpdateLock mUpdateLock = new UpdateLock("immersive");
+
+ /**
+ * Packages that are being allowed to perform unrestricted app switches. Mapping is
+ * User -> Type -> uid.
+ */
+ final SparseArray<ArrayMap<String, Integer>> mAllowAppSwitchUids = new SparseArray<>();
+
+ /** The dimensions of the thumbnails in the Recents UI. */
+ private int mThumbnailWidth;
+ private int mThumbnailHeight;
+ private float mFullscreenThumbnailScale;
+
+ /**
+ * Flag that indicates if multi-window is enabled.
+ *
+ * For any particular form of multi-window to be enabled, generic multi-window must be enabled
+ * in {@link com.android.internal.R.bool#config_supportsMultiWindow} config or
+ * {@link Settings.Global#DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES} development option set.
+ * At least one of the forms of multi-window must be enabled in order for this flag to be
+ * initialized to 'true'.
+ *
+ * @see #mSupportsSplitScreenMultiWindow
+ * @see #mSupportsFreeformWindowManagement
+ * @see #mSupportsPictureInPicture
+ * @see #mSupportsMultiDisplay
+ */
+ boolean mSupportsMultiWindow;
+ boolean mSupportsSplitScreenMultiWindow;
+ boolean mSupportsFreeformWindowManagement;
+ boolean mSupportsPictureInPicture;
+ boolean mSupportsMultiDisplay;
+ boolean mForceResizableActivities;
+
+ final List<ActivityTaskManagerInternal.ScreenObserver> mScreenObservers = new ArrayList<>();
+
+ // VR Vr2d Display Id.
+ int mVr2dDisplayId = INVALID_DISPLAY;
+
+ /**
+ * Set while we are wanting to sleep, to prevent any
+ * activities from being started/resumed.
+ *
+ * TODO(b/33594039): Clarify the actual state transitions represented by mSleeping.
+ *
+ * Currently mSleeping is set to true when transitioning into the sleep state, and remains true
+ * while in the sleep state until there is a pending transition out of sleep, in which case
+ * mSleeping is set to false, and remains false while awake.
+ *
+ * Whether mSleeping can quickly toggled between true/false without the device actually
+ * display changing states is undefined.
+ */
+ private boolean mSleeping = false;
+
+ /**
+ * The process state used for processes that are running the top activities.
+ * This changes between TOP and TOP_SLEEPING to following mSleeping.
+ */
+ int mTopProcessState = ActivityManager.PROCESS_STATE_TOP;
+
+ // Whether we should show our dialogs (ANR, crash, etc) or just perform their default action
+ // automatically. Important for devices without direct input devices.
+ private boolean mShowDialogs = true;
+
+ /** Set if we are shutting down the system, similar to sleeping. */
+ boolean mShuttingDown = false;
+
+ /**
+ * We want to hold a wake lock while running a voice interaction session, since
+ * this may happen with the screen off and we need to keep the CPU running to
+ * be able to continue to interact with the user.
+ */
+ PowerManager.WakeLock mVoiceWakeLock;
+
+ /**
+ * Set while we are running a voice interaction. This overrides sleeping while it is active.
+ */
+ IVoiceInteractionSession mRunningVoice;
+
+ /**
+ * The last resumed activity. This is identical to the current resumed activity most
+ * of the time but could be different when we're pausing one activity before we resume
+ * another activity.
+ */
+ ActivityRecord mLastResumedActivity;
+
+ /**
+ * The activity that is currently being traced as the active resumed activity.
+ *
+ * @see #updateResumedAppTrace
+ */
+ private @Nullable ActivityRecord mTracedResumedActivity;
+
+ /** If non-null, we are tracking the time the user spends in the currently focused app. */
+ AppTimeTracker mCurAppTimeTracker;
+
+ private FontScaleSettingObserver mFontScaleSettingObserver;
+
+ private final class FontScaleSettingObserver extends ContentObserver {
+ private final Uri mFontScaleUri = Settings.System.getUriFor(FONT_SCALE);
+ private final Uri mHideErrorDialogsUri = Settings.Global.getUriFor(HIDE_ERROR_DIALOGS);
+
+ public FontScaleSettingObserver() {
+ super(mH);
+ final ContentResolver resolver = mContext.getContentResolver();
+ resolver.registerContentObserver(mFontScaleUri, false, this, UserHandle.USER_ALL);
+ resolver.registerContentObserver(mHideErrorDialogsUri, false, this,
+ UserHandle.USER_ALL);
+ }
+
+ @Override
+ public void onChange(boolean selfChange, Uri uri, @UserIdInt int userId) {
+ if (mFontScaleUri.equals(uri)) {
+ updateFontScaleIfNeeded(userId);
+ } else if (mHideErrorDialogsUri.equals(uri)) {
+ synchronized (mGlobalLock) {
+ updateShouldShowDialogsLocked(getGlobalConfiguration());
+ }
+ }
+ }
+ }
ActivityTaskManagerService(Context context) {
mContext = context;
+ mUiContext = ActivityThread.currentActivityThread().getSystemUiContext();
mLifecycleManager = new ClientLifecycleManager();
}
@@ -265,11 +502,97 @@
mRecentTasks.onSystemReadyLocked();
}
+ void onInitPowerManagement() {
+ mStackSupervisor.initPowerManagement();
+ final PowerManager pm = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
+ mVoiceWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*voice*");
+ mVoiceWakeLock.setReferenceCounted(false);
+ }
+
+ void installSystemProviders() {
+ mFontScaleSettingObserver = new FontScaleSettingObserver();
+ }
+
+ void retrieveSettings(ContentResolver resolver) {
+ final boolean freeformWindowManagement =
+ mContext.getPackageManager().hasSystemFeature(FEATURE_FREEFORM_WINDOW_MANAGEMENT)
+ || Settings.Global.getInt(
+ resolver, DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT, 0) != 0;
+
+ final boolean supportsMultiWindow = ActivityTaskManager.supportsMultiWindow(mContext);
+ final boolean supportsPictureInPicture = supportsMultiWindow &&
+ mContext.getPackageManager().hasSystemFeature(FEATURE_PICTURE_IN_PICTURE);
+ final boolean supportsSplitScreenMultiWindow =
+ ActivityTaskManager.supportsSplitScreenMultiWindow(mContext);
+ final boolean supportsMultiDisplay = mContext.getPackageManager()
+ .hasSystemFeature(FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS);
+ final boolean alwaysFinishActivities =
+ Settings.Global.getInt(resolver, ALWAYS_FINISH_ACTIVITIES, 0) != 0;
+ final boolean forceRtl = Settings.Global.getInt(resolver, DEVELOPMENT_FORCE_RTL, 0) != 0;
+ final boolean forceResizable = Settings.Global.getInt(
+ resolver, DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES, 0) != 0;
+
+ // Transfer any global setting for forcing RTL layout, into a System Property
+ SystemProperties.set(DEVELOPMENT_FORCE_RTL, forceRtl ? "1":"0");
+
+ final Configuration configuration = new Configuration();
+ Settings.System.getConfiguration(resolver, configuration);
+ if (forceRtl) {
+ // This will take care of setting the correct layout direction flags
+ configuration.setLayoutDirection(configuration.locale);
+ }
+
+ synchronized (mGlobalLock) {
+ mForceResizableActivities = forceResizable;
+ final boolean multiWindowFormEnabled = freeformWindowManagement
+ || supportsSplitScreenMultiWindow
+ || supportsPictureInPicture
+ || supportsMultiDisplay;
+ if ((supportsMultiWindow || forceResizable) && multiWindowFormEnabled) {
+ mSupportsMultiWindow = true;
+ mSupportsFreeformWindowManagement = freeformWindowManagement;
+ mSupportsSplitScreenMultiWindow = supportsSplitScreenMultiWindow;
+ mSupportsPictureInPicture = supportsPictureInPicture;
+ mSupportsMultiDisplay = supportsMultiDisplay;
+ } else {
+ mSupportsMultiWindow = false;
+ mSupportsFreeformWindowManagement = false;
+ mSupportsSplitScreenMultiWindow = false;
+ mSupportsPictureInPicture = false;
+ mSupportsMultiDisplay = false;
+ }
+ mWindowManager.setForceResizableTasks(mForceResizableActivities);
+ mWindowManager.setSupportsPictureInPicture(mSupportsPictureInPicture);
+ // This happens before any activities are started, so we can change global configuration
+ // in-place.
+ updateConfigurationLocked(configuration, null, true);
+ final Configuration globalConfig = getGlobalConfiguration();
+ if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, "Initial config: " + globalConfig);
+
+ // Load resources only after the current configuration has been set.
+ final Resources res = mContext.getResources();
+ mThumbnailWidth = res.getDimensionPixelSize(
+ com.android.internal.R.dimen.thumbnail_width);
+ mThumbnailHeight = res.getDimensionPixelSize(
+ com.android.internal.R.dimen.thumbnail_height);
+
+ if ((globalConfig.uiMode & UI_MODE_TYPE_TELEVISION) == UI_MODE_TYPE_TELEVISION) {
+ mFullscreenThumbnailScale = (float) res
+ .getInteger(com.android.internal.R.integer.thumbnail_width_tv) /
+ (float) globalConfig.screenWidthDp;
+ } else {
+ mFullscreenThumbnailScale = res.getFraction(
+ com.android.internal.R.fraction.thumbnail_fullscreen_scale, 1, 1);
+ }
+ }
+ }
+
// TODO: Will be converted to WM lock once transition is complete.
void setActivityManagerService(ActivityManagerService am) {
mAm = am;
mGlobalLock = mAm;
mH = new H(mAm.mHandlerThread.getLooper());
+ mUiHandler = new UiHandler();
mTempConfig.setToDefaults();
mTempConfig.setLocales(LocaleList.getDefault());
@@ -278,15 +601,19 @@
mStackSupervisor.onConfigurationChanged(mTempConfig);
mTaskChangeNotificationController =
- new TaskChangeNotificationController(mAm, mStackSupervisor, mH);
+ new TaskChangeNotificationController(mGlobalLock, mStackSupervisor, mH);
mLockTaskController = new LockTaskController(mContext, mStackSupervisor, mH);
mActivityStartController = new ActivityStartController(this);
mRecentTasks = createRecentTasks();
mStackSupervisor.setRecentTasks(mRecentTasks);
- mVrController = new VrController(mAm);
+ mVrController = new VrController(mGlobalLock);
mKeyguardController = mStackSupervisor.getKeyguardController();
}
+ void onActivityManagerInternalAdded() {
+ mAmInternal = LocalServices.getService(ActivityManagerInternal.class);
+ }
+
protected ActivityStackSupervisor createStackSupervisor() {
final ActivityStackSupervisor supervisor = new ActivityStackSupervisor(this, mH.getLooper());
supervisor.initialize();
@@ -298,6 +625,26 @@
mLockTaskController.setWindowManager(wm);
}
+ UserManagerService getUserManager() {
+ if (mUserManager == null) {
+ IBinder b = ServiceManager.getService(Context.USER_SERVICE);
+ mUserManager = (UserManagerService) IUserManager.Stub.asInterface(b);
+ }
+ return mUserManager;
+ }
+
+ AppOpsService getAppOpsService() {
+ if (mAppOpsService == null) {
+ IBinder b = ServiceManager.getService(Context.APP_OPS_SERVICE);
+ mAppOpsService = (AppOpsService) IAppOpsService.Stub.asInterface(b);
+ }
+ return mAppOpsService;
+ }
+
+ boolean hasUserRestriction(String restriction, int userId) {
+ return getUserManager().hasUserRestriction(restriction, userId);
+ }
+
protected RecentTasks createRecentTasks() {
return new RecentTasks(this, mStackSupervisor);
}
@@ -359,9 +706,8 @@
Intent[] intents, String[] resolvedTypes, IBinder resultTo, Bundle bOptions,
int userId) {
final String reason = "startActivities";
- mAm.enforceNotIsolatedCaller(reason);
- userId = mAm.mUserController.handleIncomingUser(Binder.getCallingPid(),
- Binder.getCallingUid(), userId, false, ALLOW_FULL_ONLY, reason, null);
+ enforceNotIsolatedCaller(reason);
+ userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId, reason);
// TODO: Switch to user app stacks here.
return getActivityStartController().startActivities(caller, -1, callingPackage, intents,
resolvedTypes, resultTo, SafeActivityOptions.fromBundle(bOptions), userId, reason);
@@ -380,7 +726,7 @@
Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId,
boolean validateIncomingUser) {
- mAm.enforceNotIsolatedCaller("startActivityAsUser");
+ enforceNotIsolatedCaller("startActivityAsUser");
userId = getActivityStartController().checkTargetUser(userId, validateIncomingUser,
Binder.getCallingPid(), Binder.getCallingUid(), "startActivityAsUser");
@@ -404,9 +750,8 @@
@Override
public int startActivityIntentSender(IApplicationThread caller, IIntentSender target,
IBinder whitelistToken, Intent fillInIntent, String resolvedType, IBinder resultTo,
- String resultWho, int requestCode, int flagsMask, int flagsValues, Bundle bOptions)
- throws TransactionTooLargeException {
- mAm.enforceNotIsolatedCaller("startActivityIntentSender");
+ String resultWho, int requestCode, int flagsMask, int flagsValues, Bundle bOptions) {
+ enforceNotIsolatedCaller("startActivityIntentSender");
// Refuse possible leaked file descriptors
if (fillInIntent != null && fillInIntent.hasFileDescriptors()) {
throw new IllegalArgumentException("File descriptors passed in Intent");
@@ -421,15 +766,14 @@
synchronized (mGlobalLock) {
// If this is coming from the currently resumed activity, it is
// effectively saying that app switches are allowed at this point.
- final ActivityStack stack = mAm.getFocusedStack();
+ final ActivityStack stack = getTopDisplayFocusedStack();
if (stack.mResumedActivity != null &&
stack.mResumedActivity.info.applicationInfo.uid == Binder.getCallingUid()) {
- mAm.mAppSwitchesAllowedTime = 0;
+ mAppSwitchesAllowedTime = 0;
}
}
- int ret = pir.sendInner(0, fillInIntent, resolvedType, whitelistToken, null, null,
+ return pir.sendInner(0, fillInIntent, resolvedType, whitelistToken, null, null,
resultTo, resultWho, requestCode, flagsMask, flagsValues, bOptions);
- return ret;
}
@Override
@@ -447,7 +791,7 @@
SafeActivityOptions.abort(options);
return false;
}
- if (r.app == null || r.app.thread == null) {
+ if (!r.attachedToProcess()) {
// The caller is not running... d'oh!
SafeActivityOptions.abort(options);
return false;
@@ -527,7 +871,7 @@
// TODO(b/64750076): Check if calling pid should really be -1.
final int res = getActivityStartController()
.obtainStarter(intent, "startNextMatchingActivity")
- .setCaller(r.app.thread)
+ .setCaller(r.app.getThread())
.setResolvedType(r.resolvedType)
.setActivityInfo(aInfo)
.setResultTo(resultTo != null ? resultTo.appToken : null)
@@ -556,10 +900,9 @@
int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId) {
final WaitResult res = new WaitResult();
synchronized (mGlobalLock) {
- mAm.enforceNotIsolatedCaller("startActivityAndWait");
- userId = mAm.mUserController.handleIncomingUser(Binder.getCallingPid(),
- Binder.getCallingUid(), userId, false, ALLOW_FULL_ONLY,
- "startActivityAndWait", null);
+ enforceNotIsolatedCaller("startActivityAndWait");
+ userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
+ userId, "startActivityAndWait");
// TODO: Switch to user app stacks here.
getActivityStartController().obtainStarter(intent, "startActivityAndWait")
.setCaller(caller)
@@ -583,10 +926,9 @@
Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
int startFlags, Configuration config, Bundle bOptions, int userId) {
synchronized (mGlobalLock) {
- mAm.enforceNotIsolatedCaller("startActivityWithConfig");
- userId = mAm.mUserController.handleIncomingUser(Binder.getCallingPid(),
- Binder.getCallingUid(), userId, false, ALLOW_FULL_ONLY,
- "startActivityWithConfig", null);
+ enforceNotIsolatedCaller("startActivityWithConfig");
+ userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId,
+ "startActivityWithConfig");
// TODO: Switch to user app stacks here.
return getActivityStartController().obtainStarter(intent, "startActivityWithConfig")
.setCaller(caller)
@@ -632,12 +974,12 @@
if (sourceRecord.app == null) {
throw new SecurityException("Called without a process attached to activity");
}
- if (UserHandle.getAppId(sourceRecord.app.uid) != SYSTEM_UID) {
+ if (UserHandle.getAppId(sourceRecord.app.mUid) != SYSTEM_UID) {
// This is still okay, as long as this activity is running under the
// uid of the original calling activity.
- if (sourceRecord.app.uid != sourceRecord.launchedFromUid) {
+ if (sourceRecord.app.mUid != sourceRecord.launchedFromUid) {
throw new SecurityException(
- "Calling activity in uid " + sourceRecord.app.uid
+ "Calling activity in uid " + sourceRecord.app.mUid
+ " must be system uid or original calling uid "
+ sourceRecord.launchedFromUid);
}
@@ -658,7 +1000,7 @@
}
if (userId == UserHandle.USER_NULL) {
- userId = UserHandle.getUserId(sourceRecord.app.uid);
+ userId = UserHandle.getUserId(sourceRecord.app.mUid);
}
// TODO: Switch to user app stacks here.
@@ -692,17 +1034,21 @@
}
}
+ int handleIncomingUser(int callingPid, int callingUid, int userId, String name) {
+ return mAmInternal.handleIncomingUser(callingPid, callingUid, userId, false /* allowAll */,
+ ALLOW_FULL_ONLY, name, null /* callerPackage */);
+ }
+
@Override
public int startVoiceActivity(String callingPackage, int callingPid, int callingUid,
Intent intent, String resolvedType, IVoiceInteractionSession session,
IVoiceInteractor interactor, int startFlags, ProfilerInfo profilerInfo,
Bundle bOptions, int userId) {
- mAm.enforceCallingPermission(BIND_VOICE_INTERACTION, "startVoiceActivity()");
+ mAmInternal.enforceCallingPermission(BIND_VOICE_INTERACTION, "startVoiceActivity()");
if (session == null || interactor == null) {
throw new NullPointerException("null session or interactor");
}
- userId = mAm.mUserController.handleIncomingUser(callingPid, callingUid, userId, false,
- ALLOW_FULL_ONLY, "startVoiceActivity", null);
+ userId = handleIncomingUser(callingPid, callingUid, userId, "startVoiceActivity");
// TODO: Switch to user app stacks here.
return getActivityStartController().obtainStarter(intent, "startVoiceActivity")
.setCallingUid(callingUid)
@@ -720,9 +1066,8 @@
@Override
public int startAssistantActivity(String callingPackage, int callingPid, int callingUid,
Intent intent, String resolvedType, Bundle bOptions, int userId) {
- mAm.enforceCallingPermission(BIND_VOICE_INTERACTION, "startAssistantActivity()");
- userId = mAm.mUserController.handleIncomingUser(callingPid, callingUid, userId, false,
- ALLOW_FULL_ONLY, "startAssistantActivity", null);
+ mAmInternal.enforceCallingPermission(BIND_VOICE_INTERACTION, "startAssistantActivity()");
+ userId = handleIncomingUser(callingPid, callingUid, userId, "startAssistantActivity");
return getActivityStartController().obtainStarter(intent, "startAssistantActivity")
.setCallingUid(callingUid)
@@ -736,7 +1081,7 @@
@Override
public void startRecentsActivity(Intent intent, IAssistDataReceiver assistDataReceiver,
IRecentsAnimationRunner recentsAnimationRunner) {
- mAm.enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "startRecentsActivity()");
+ enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "startRecentsActivity()");
final int callingPid = Binder.getCallingPid();
final long origId = Binder.clearCallingIdentity();
try {
@@ -745,9 +1090,8 @@
final int recentsUid = mRecentTasks.getRecentsComponentUid();
// Start a new recents animation
- final RecentsAnimation anim = new RecentsAnimation(mAm, mStackSupervisor,
- getActivityStartController(), mAm.mWindowManager, mAm.mUserController,
- callingPid);
+ final RecentsAnimation anim = new RecentsAnimation(this, mStackSupervisor,
+ getActivityStartController(), mWindowManager, callingPid);
anim.startRecentsActivity(intent, recentsAnimationRunner, recentsComponent,
recentsUid, assistDataReceiver);
}
@@ -758,7 +1102,7 @@
@Override
public final int startActivityFromRecents(int taskId, Bundle bOptions) {
- mAm.enforceCallerIsRecentsOrHasPermission(START_TASKS_FROM_RECENTS,
+ enforceCallerIsRecentsOrHasPermission(START_TASKS_FROM_RECENTS,
"startActivityFromRecents()");
final int callingPid = Binder.getCallingPid();
@@ -810,16 +1154,18 @@
return false;
}
- if (mAm.mController != null) {
+ // TODO: There is a dup. of this block of code in ActivityStack.navigateUpToLocked
+ // We should consolidate.
+ if (mController != null) {
// Find the first activity that is not finishing.
ActivityRecord next = r.getStack().topRunningActivityLocked(token, 0);
if (next != null) {
// ask watcher if this is allowed
boolean resumeOK = true;
try {
- resumeOK = mAm.mController.activityResuming(next.packageName);
+ resumeOK = mController.activityResuming(next.packageName);
} catch (RemoteException e) {
- mAm.mController = null;
+ mController = null;
Watchdog.getInstance().setActivityController(null);
}
@@ -886,20 +1232,25 @@
@Override
public final void activityIdle(IBinder token, Configuration config, boolean stopProfiling) {
final long origId = Binder.clearCallingIdentity();
- synchronized (mGlobalLock) {
- ActivityStack stack = ActivityRecord.getStackLocked(token);
- if (stack != null) {
- ActivityRecord r =
- mStackSupervisor.activityIdleInternalLocked(token, false /* fromTimeout */,
- false /* processPausingActivities */, config);
- if (stopProfiling) {
- if ((mAm.mProfileProc == r.app) && mAm.mProfilerInfo != null) {
- mAm.clearProfilerLocked();
- }
+ try {
+ WindowProcessController proc = null;
+ synchronized (mGlobalLock) {
+ ActivityStack stack = ActivityRecord.getStackLocked(token);
+ if (stack == null) {
+ return;
+ }
+ final ActivityRecord r = mStackSupervisor.activityIdleInternalLocked(token,
+ false /* fromTimeout */, false /* processPausingActivities */, config);
+ if (r != null) {
+ proc = r.app;
+ }
+ if (stopProfiling && proc != null) {
+ proc.clearProfilerIfNeeded();
}
}
+ } finally {
+ Binder.restoreCallingIdentity(origId);
}
- Binder.restoreCallingIdentity(origId);
}
@Override
@@ -907,7 +1258,7 @@
final long origId = Binder.clearCallingIdentity();
synchronized (mGlobalLock) {
ActivityRecord.activityResumedLocked(token);
- mAm.mWindowManager.notifyAppResumedFinished(token);
+ mWindowManager.notifyAppResumedFinished(token);
}
Binder.restoreCallingIdentity(origId);
}
@@ -943,7 +1294,7 @@
}
}
- mAm.trimApplications();
+ mAmInternal.trimApplications();
Binder.restoreCallingIdentity(origId);
}
@@ -1020,13 +1371,32 @@
r.immersive = immersive;
// update associated state if we're frontmost
- if (r == mStackSupervisor.getResumedActivityLocked()) {
+ if (r.isResumedActivityOnDisplay()) {
if (DEBUG_IMMERSIVE) Slog.d(TAG_IMMERSIVE, "Frontmost changed immersion: "+ r);
- mAm.applyUpdateLockStateLocked(r);
+ applyUpdateLockStateLocked(r);
}
}
}
+ void applyUpdateLockStateLocked(ActivityRecord r) {
+ // Modifications to the UpdateLock state are done on our handler, outside
+ // the activity manager's locks. The new state is determined based on the
+ // state *now* of the relevant activity record. The object is passed to
+ // the handler solely for logging detail, not to be consulted/modified.
+ final boolean nextState = r != null && r.immersive;
+ mH.post(() -> {
+ if (mUpdateLock.isHeld() != nextState) {
+ if (DEBUG_IMMERSIVE) Slog.d(TAG_IMMERSIVE,
+ "Applying new update lock state '" + nextState + "' for " + r);
+ if (nextState) {
+ mUpdateLock.acquire();
+ } else {
+ mUpdateLock.release();
+ }
+ }
+ });
+ }
+
@Override
public boolean isImmersive(IBinder token) {
synchronized (mGlobalLock) {
@@ -1040,9 +1410,9 @@
@Override
public boolean isTopActivityImmersive() {
- mAm.enforceNotIsolatedCaller("isTopActivityImmersive");
+ enforceNotIsolatedCaller("isTopActivityImmersive");
synchronized (mGlobalLock) {
- final ActivityRecord r = mAm.getFocusedStack().topRunningActivityLocked();
+ final ActivityRecord r = getTopDisplayFocusedStack().topRunningActivityLocked();
return (r != null) ? r.immersive : false;
}
}
@@ -1060,7 +1430,7 @@
if (self.isState(
ActivityStack.ActivityState.RESUMED, ActivityStack.ActivityState.PAUSING)) {
- mAm.mWindowManager.overridePendingAppTransition(packageName,
+ mWindowManager.overridePendingAppTransition(packageName,
enterAnim, exitAnim, null);
}
@@ -1070,19 +1440,34 @@
@Override
public int getFrontActivityScreenCompatMode() {
- mAm.enforceNotIsolatedCaller("getFrontActivityScreenCompatMode");
+ enforceNotIsolatedCaller("getFrontActivityScreenCompatMode");
+ ApplicationInfo ai;
synchronized (mGlobalLock) {
- return mAm.mCompatModePackages.getFrontActivityScreenCompatModeLocked();
+ final ActivityRecord r = getTopDisplayFocusedStack().topRunningActivityLocked();
+ if (r == null) {
+ return ActivityManager.COMPAT_MODE_UNKNOWN;
+ }
+ ai = r.info.applicationInfo;
}
+
+ return mAmInternal.getPackageScreenCompatMode(ai);
}
@Override
public void setFrontActivityScreenCompatMode(int mode) {
- mAm.enforceCallingPermission(android.Manifest.permission.SET_SCREEN_COMPATIBILITY,
+ mAmInternal.enforceCallingPermission(android.Manifest.permission.SET_SCREEN_COMPATIBILITY,
"setFrontActivityScreenCompatMode");
+ ApplicationInfo ai;
synchronized (mGlobalLock) {
- mAm.mCompatModePackages.setFrontActivityScreenCompatModeLocked(mode);
+ final ActivityRecord r = getTopDisplayFocusedStack().topRunningActivityLocked();
+ if (r == null) {
+ Slog.w(TAG, "setFrontActivityScreenCompatMode failed: no top activity");
+ return;
+ }
+ ai = r.info.applicationInfo;
}
+
+ mAmInternal.setPackageScreenCompatMode(ai, mode);
}
@Override
@@ -1122,7 +1507,7 @@
if (translucentChanged) {
mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
}
- mAm.mWindowManager.setAppFullscreen(token, true);
+ mWindowManager.setAppFullscreen(token, true);
return translucentChanged;
}
} finally {
@@ -1151,7 +1536,7 @@
r.getStack().convertActivityToTranslucent(r);
}
mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
- mAm.mWindowManager.setAppFullscreen(token, false);
+ mWindowManager.setAppFullscreen(token, false);
return translucentChanged;
}
} finally {
@@ -1194,11 +1579,11 @@
@Override
public ActivityManager.StackInfo getFocusedStackInfo() throws RemoteException {
- mAm.enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "getStackInfo()");
+ enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "getStackInfo()");
long ident = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
- ActivityStack focusedStack = mAm.getFocusedStack();
+ ActivityStack focusedStack = getTopDisplayFocusedStack();
if (focusedStack != null) {
return mStackSupervisor.getStackInfo(focusedStack.mStackId);
}
@@ -1211,7 +1596,7 @@
@Override
public void setFocusedStack(int stackId) {
- mAm.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "setFocusedStack()");
+ mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "setFocusedStack()");
if (DEBUG_FOCUS) Slog.d(TAG_FOCUS, "setFocusedStack: stackId=" + stackId);
final long callingId = Binder.clearCallingIdentity();
try {
@@ -1234,7 +1619,7 @@
@Override
public void setFocusedTask(int taskId) {
- mAm.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "setFocusedTask()");
+ mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "setFocusedTask()");
if (DEBUG_FOCUS) Slog.d(TAG_FOCUS, "setFocusedTask: taskId=" + taskId);
final long callingId = Binder.clearCallingIdentity();
try {
@@ -1255,7 +1640,7 @@
@Override
public boolean removeTask(int taskId) {
- mAm.enforceCallerIsRecentsOrHasPermission(REMOVE_TASKS, "removeTask()");
+ enforceCallerIsRecentsOrHasPermission(REMOVE_TASKS, "removeTask()");
synchronized (mGlobalLock) {
final long ident = Binder.clearCallingIdentity();
try {
@@ -1312,7 +1697,7 @@
*/
@Override
public boolean moveActivityTaskToBack(IBinder token, boolean nonRoot) {
- mAm.enforceNotIsolatedCaller("moveActivityTaskToBack");
+ enforceNotIsolatedCaller("moveActivityTaskToBack");
synchronized (mGlobalLock) {
final long origId = Binder.clearCallingIdentity();
try {
@@ -1330,7 +1715,7 @@
@Override
public Rect getTaskBounds(int taskId) {
- mAm.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "getTaskBounds()");
+ mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "getTaskBounds()");
long ident = Binder.clearCallingIdentity();
Rect rect = new Rect();
try {
@@ -1364,7 +1749,7 @@
@Override
public ActivityManager.TaskDescription getTaskDescription(int id) {
synchronized (mGlobalLock) {
- mAm.enforceCallerIsRecentsOrHasPermission(
+ enforceCallerIsRecentsOrHasPermission(
MANAGE_ACTIVITY_STACKS, "getTaskDescription()");
final TaskRecord tr = mStackSupervisor.anyTaskForIdLocked(id,
MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
@@ -1382,7 +1767,7 @@
toTop, ANIMATE, null /* initialBounds */, true /* showRecents */);
return;
}
- mAm.enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "setTaskWindowingMode()");
+ enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "setTaskWindowingMode()");
synchronized (mGlobalLock) {
final long ident = Binder.clearCallingIdentity();
try {
@@ -1414,7 +1799,7 @@
@Override
public String getCallingPackage(IBinder token) {
- synchronized (this) {
+ synchronized (mGlobalLock) {
ActivityRecord r = getCallingRecordLocked(token);
return r != null ? r.info.packageName : null;
}
@@ -1422,7 +1807,7 @@
@Override
public ComponentName getCallingActivity(IBinder token) {
- synchronized (this) {
+ synchronized (mGlobalLock) {
ActivityRecord r = getCallingRecordLocked(token);
return r != null ? r.intent.getComponent() : null;
}
@@ -1438,12 +1823,12 @@
@Override
public void unhandledBack() {
- mAm.enforceCallingPermission(android.Manifest.permission.FORCE_BACK, "unhandledBack()");
+ mAmInternal.enforceCallingPermission(android.Manifest.permission.FORCE_BACK, "unhandledBack()");
synchronized (mGlobalLock) {
final long origId = Binder.clearCallingIdentity();
try {
- mAm.getFocusedStack().unhandledBackLocked();
+ getTopDisplayFocusedStack().unhandledBackLocked();
} finally {
Binder.restoreCallingIdentity(origId);
}
@@ -1455,7 +1840,7 @@
*/
@Override
public void moveTaskToFront(int taskId, int flags, Bundle bOptions) {
- mAm.enforceCallingPermission(android.Manifest.permission.REORDER_TASKS, "moveTaskToFront()");
+ mAmInternal.enforceCallingPermission(android.Manifest.permission.REORDER_TASKS, "moveTaskToFront()");
if (DEBUG_STACK) Slog.d(TAG_STACK, "moveTaskToFront: moving taskId=" + taskId);
synchronized (mGlobalLock) {
@@ -1467,7 +1852,7 @@
void moveTaskToFrontLocked(int taskId, int flags, SafeActivityOptions options,
boolean fromRecents) {
- if (!mAm.checkAppSwitchAllowedLocked(Binder.getCallingPid(),
+ if (!checkAppSwitchAllowedLocked(Binder.getCallingPid(),
Binder.getCallingUid(), -1, -1, "Task to front")) {
SafeActivityOptions.abort(options);
return;
@@ -1503,6 +1888,69 @@
SafeActivityOptions.abort(options);
}
+ boolean checkAppSwitchAllowedLocked(int sourcePid, int sourceUid,
+ int callingPid, int callingUid, String name) {
+ if (mAppSwitchesAllowedTime < SystemClock.uptimeMillis()) {
+ return true;
+ }
+
+ if (getRecentTasks().isCallerRecents(sourceUid)) {
+ return true;
+ }
+
+ int perm = checkComponentPermission(STOP_APP_SWITCHES, sourcePid, sourceUid, -1, true);
+ if (perm == PackageManager.PERMISSION_GRANTED) {
+ return true;
+ }
+ if (checkAllowAppSwitchUid(sourceUid)) {
+ return true;
+ }
+
+ // If the actual IPC caller is different from the logical source, then
+ // also see if they are allowed to control app switches.
+ if (callingUid != -1 && callingUid != sourceUid) {
+ perm = checkComponentPermission(STOP_APP_SWITCHES, callingPid, callingUid, -1, true);
+ if (perm == PackageManager.PERMISSION_GRANTED) {
+ return true;
+ }
+ if (checkAllowAppSwitchUid(callingUid)) {
+ return true;
+ }
+ }
+
+ Slog.w(TAG, name + " request from " + sourceUid + " stopped");
+ return false;
+ }
+
+ private boolean checkAllowAppSwitchUid(int uid) {
+ ArrayMap<String, Integer> types = mAllowAppSwitchUids.get(UserHandle.getUserId(uid));
+ if (types != null) {
+ for (int i = types.size() - 1; i >= 0; i--) {
+ if (types.valueAt(i).intValue() == uid) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public void setActivityController(IActivityController controller, boolean imAMonkey) {
+ mAmInternal.enforceCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER,
+ "setActivityController()");
+ synchronized (mGlobalLock) {
+ mController = controller;
+ mControllerIsAMonkey = imAMonkey;
+ Watchdog.getInstance().setActivityController(controller);
+ }
+ }
+
+ boolean isControllerAMonkey() {
+ synchronized (mGlobalLock) {
+ return mController != null && mControllerIsAMonkey;
+ }
+ }
+
@Override
public int getTaskForActivity(IBinder token, boolean onlyRoot) {
synchronized (mGlobalLock) {
@@ -1525,7 +1973,7 @@
synchronized (mGlobalLock) {
if (DEBUG_ALL) Slog.v(TAG, "getTasks: max=" + maxNum);
- final boolean allowed = mAm.isGetTasksAllowed("getTasks", Binder.getCallingPid(),
+ final boolean allowed = isGetTasksAllowed("getTasks", Binder.getCallingPid(),
callingUid);
mStackSupervisor.getRunningTasks(maxNum, list, ignoreActivityType,
ignoreWindowingMode, callingUid, allowed);
@@ -1548,7 +1996,7 @@
@Override
public boolean willActivityBeVisible(IBinder token) {
- synchronized(this) {
+ synchronized (mGlobalLock) {
ActivityStack stack = ActivityRecord.getStackLocked(token);
if (stack != null) {
return stack.willActivityBeVisibleLocked(token);
@@ -1559,7 +2007,7 @@
@Override
public void moveTaskToStack(int taskId, int stackId, boolean toTop) {
- mAm.enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "moveTaskToStack()");
+ enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "moveTaskToStack()");
synchronized (mGlobalLock) {
final long ident = Binder.clearCallingIdentity();
try {
@@ -1582,7 +2030,7 @@
+ taskId + " to stack " + stackId);
}
if (stack.inSplitScreenPrimaryWindowingMode()) {
- mAm.mWindowManager.setDockedStackCreateState(
+ mWindowManager.setDockedStackCreateState(
SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT, null /* initialBounds */);
}
task.reparent(stack, toTop, REPARENT_KEEP_STACK_AT_FRONT, ANIMATE, !DEFER_RESUME,
@@ -1596,7 +2044,7 @@
@Override
public void resizeStack(int stackId, Rect destBounds, boolean allowResizeInDockedMode,
boolean preserveWindows, boolean animate, int animationDuration) {
- mAm.enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "resizeStack()");
+ enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "resizeStack()");
final long ident = Binder.clearCallingIdentity();
try {
@@ -1648,7 +2096,7 @@
@Override
public boolean setTaskWindowingModeSplitScreenPrimary(int taskId, int createMode,
boolean toTop, boolean animate, Rect initialBounds, boolean showRecents) {
- mAm.enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
+ enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
"setTaskWindowingModeSplitScreenPrimary()");
synchronized (mGlobalLock) {
final long ident = Binder.clearCallingIdentity();
@@ -1666,7 +2114,7 @@
+ " non-standard task " + taskId + " to split-screen windowing mode");
}
- mAm.mWindowManager.setDockedStackCreateState(createMode, initialBounds);
+ mWindowManager.setDockedStackCreateState(createMode, initialBounds);
final int windowingMode = task.getWindowingMode();
final ActivityStack stack = task.getStack();
if (toTop) {
@@ -1687,7 +2135,7 @@
*/
@Override
public void removeStacksInWindowingModes(int[] windowingModes) {
- mAm.enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
+ enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
"removeStacksInWindowingModes()");
synchronized (mGlobalLock) {
@@ -1702,7 +2150,7 @@
@Override
public void removeStacksWithActivityTypes(int[] activityTypes) {
- mAm.enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
+ enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
"removeStacksWithActivityTypes()");
synchronized (mGlobalLock) {
@@ -1719,12 +2167,12 @@
public ParceledListSlice<ActivityManager.RecentTaskInfo> getRecentTasks(int maxNum, int flags,
int userId) {
final int callingUid = Binder.getCallingUid();
- userId = mAm.mUserController.handleIncomingUser(Binder.getCallingPid(), callingUid, userId,
- false, ALLOW_FULL_ONLY, "getRecentTasks", null);
- final boolean allowed = mAm.isGetTasksAllowed("getRecentTasks", Binder.getCallingPid(),
+ userId = handleIncomingUser(Binder.getCallingPid(), callingUid, userId, "getRecentTasks");
+ final boolean allowed = isGetTasksAllowed("getRecentTasks", Binder.getCallingPid(),
callingUid);
- final boolean detailed = mAm.checkCallingPermission(
- android.Manifest.permission.GET_DETAILED_TASKS)
+ final boolean detailed = checkGetTasksPermission(
+ android.Manifest.permission.GET_DETAILED_TASKS, Binder.getCallingPid(),
+ UserHandle.getAppId(callingUid))
== PackageManager.PERMISSION_GRANTED;
synchronized (mGlobalLock) {
@@ -1735,7 +2183,7 @@
@Override
public List<ActivityManager.StackInfo> getAllStackInfos() {
- mAm.enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "getAllStackInfos()");
+ enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "getAllStackInfos()");
long ident = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
@@ -1748,7 +2196,7 @@
@Override
public ActivityManager.StackInfo getStackInfo(int windowingMode, int activityType) {
- mAm.enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "getStackInfo()");
+ enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "getStackInfo()");
long ident = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
@@ -1761,13 +2209,13 @@
@Override
public void cancelRecentsAnimation(boolean restoreHomeStackPosition) {
- mAm.enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "cancelRecentsAnimation()");
+ enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "cancelRecentsAnimation()");
final long callingUid = Binder.getCallingUid();
final long origId = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
// Cancel the recents animation synchronously (do not hold the WM lock)
- mAm.mWindowManager.cancelRecentsAnimationSynchronously(restoreHomeStackPosition
+ mWindowManager.cancelRecentsAnimationSynchronously(restoreHomeStackPosition
? REORDER_MOVE_TO_ORIGINAL_POSITION
: REORDER_KEEP_IN_PLACE, "cancelRecentsAnimation/uid=" + callingUid);
}
@@ -1789,7 +2237,7 @@
@Override
public void startSystemLockTaskMode(int taskId) throws RemoteException {
- mAm.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "startSystemLockTaskMode");
+ mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "startSystemLockTaskMode");
// This makes inner call to look as if it was initiated by system.
long ident = Binder.clearCallingIdentity();
try {
@@ -1822,7 +2270,7 @@
*/
@Override
public void stopSystemLockTaskMode() throws RemoteException {
- mAm.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "stopSystemLockTaskMode");
+ mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "stopSystemLockTaskMode");
stopLockTaskModeInternal(null, true /* isSystemCaller */);
}
@@ -1832,7 +2280,7 @@
return;
}
- final ActivityStack stack = mStackSupervisor.getFocusedStack();
+ final ActivityStack stack = mStackSupervisor.getTopDisplayFocusedStack();
if (stack == null || task != stack.topTask()) {
throw new IllegalArgumentException("Invalid task, not in foreground");
}
@@ -1964,9 +2412,9 @@
mH.post(() -> {
synchronized (mGlobalLock) {
ActivityRecord r = ActivityRecord.forTokenLocked(token);
- if (r != null && r.app != null && r.app.thread != null) {
+ if (r != null && r.attachedToProcess()) {
try {
- r.app.thread.scheduleEnterAnimationComplete(r.appToken);
+ r.app.getThread().scheduleEnterAnimationComplete(r.appToken);
} catch (RemoteException e) {
}
}
@@ -1979,7 +2427,7 @@
@Override
public void reportAssistContextExtras(IBinder token, Bundle extras, AssistStructure structure,
AssistContent content, Uri referrer) {
- PendingAssistExtras pae = (PendingAssistExtras)token;
+ PendingAssistExtras pae = (PendingAssistExtras) token;
synchronized (pae) {
pae.result = extras;
pae.structure = structure;
@@ -2003,13 +2451,13 @@
synchronized (mGlobalLock) {
buildAssistBundleLocked(pae, extras);
boolean exists = mPendingAssistExtras.remove(pae);
- mAm.mUiHandler.removeCallbacks(pae);
+ mUiHandler.removeCallbacks(pae);
if (!exists) {
// Timed out.
return;
}
- if ((sendReceiver=pae.receiver) != null) {
+ if ((sendReceiver = pae.receiver) != null) {
// Caller wants result sent back to them.
sendBundle = new Bundle();
sendBundle.putBundle(ASSIST_KEY_DATA, pae.extras);
@@ -2037,7 +2485,7 @@
pae.intent.setFlags(FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_SINGLE_TOP
| Intent.FLAG_ACTIVITY_CLEAR_TOP);
- mAm.closeSystemDialogs("assist");
+ mAmInternal.closeSystemDialogs("assist");
try {
mContext.startActivityAsUser(pae.intent, new UserHandle(pae.userHandle));
@@ -2068,11 +2516,11 @@
throw new IllegalArgumentException("Intent " + intent
+ " must specify explicit component");
}
- if (thumbnail.getWidth() != mAm.mThumbnailWidth
- || thumbnail.getHeight() != mAm.mThumbnailHeight) {
+ if (thumbnail.getWidth() != mThumbnailWidth
+ || thumbnail.getHeight() != mThumbnailHeight) {
throw new IllegalArgumentException("Bad thumbnail size: got "
+ thumbnail.getWidth() + "x" + thumbnail.getHeight() + ", require "
- + mAm.mThumbnailWidth + "x" + mAm.mThumbnailHeight);
+ + mThumbnailWidth + "x" + mThumbnailHeight);
}
if (intent.getSelector() != null) {
intent.setSelector(null);
@@ -2118,7 +2566,7 @@
@Override
public Point getAppTaskThumbnailSize() {
synchronized (mGlobalLock) {
- return new Point(mAm.mThumbnailWidth, mAm.mThumbnailHeight);
+ return new Point(mThumbnailWidth, mThumbnailHeight);
}
}
@@ -2137,7 +2585,7 @@
@Override
public void resizeTask(int taskId, Rect bounds, int resizeMode) {
- mAm.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "resizeTask()");
+ mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "resizeTask()");
long ident = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
@@ -2204,7 +2652,8 @@
synchronized (mGlobalLock) {
final long origId = Binder.clearCallingIdentity();
try {
- ProcessRecord app = mAm.getRecordForAppLocked(appInt);
+ WindowProcessController app =
+ mAm.getRecordForAppLocked(appInt).getWindowProcessController();
mStackSupervisor.releaseSomeActivitiesLocked(app, "low-mem");
} finally {
Binder.restoreCallingIdentity(origId);
@@ -2215,7 +2664,7 @@
@Override
public void setLockScreenShown(boolean keyguardShowing, boolean aodShowing,
int secondaryDisplayShowing) {
- if (mAm.checkCallingPermission(android.Manifest.permission.DEVICE_POWER)
+ if (checkCallingPermission(android.Manifest.permission.DEVICE_POWER)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Requires permission "
+ android.Manifest.permission.DEVICE_POWER);
@@ -2235,14 +2684,25 @@
}
}
- mAm.mHandler.obtainMessage(DISPATCH_SCREEN_KEYGUARD_MSG, keyguardShowing ? 1 : 0, 0)
- .sendToTarget();
+ mH.post(() -> {
+ for (int i = mScreenObservers.size() - 1; i >= 0; i--) {
+ mScreenObservers.get(i).onKeyguardStateChanged(keyguardShowing);
+ }
+ });
+ }
+
+ void onScreenAwakeChanged(boolean isAwake) {
+ mH.post(() -> {
+ for (int i = mScreenObservers.size() - 1; i >= 0; i--) {
+ mScreenObservers.get(i).onAwakeStateChanged(isAwake);
+ }
+ });
}
@Override
public Bitmap getTaskDescriptionIcon(String filePath, int userId) {
- userId = mAm.mUserController.handleIncomingUser(Binder.getCallingPid(),
- Binder.getCallingUid(), userId, false, ALLOW_FULL_ONLY, "getTaskDescriptionIcon", null);
+ userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
+ userId, "getTaskDescriptionIcon");
final File passedIconFile = new File(filePath);
final File legitIconFile = new File(TaskPersister.getUserImagesDir(userId),
@@ -2256,8 +2716,7 @@
}
@Override
- public void startInPlaceAnimationOnFrontMostApplication(Bundle opts)
- throws RemoteException {
+ public void startInPlaceAnimationOnFrontMostApplication(Bundle opts) {
final SafeActivityOptions safeOptions = SafeActivityOptions.fromBundle(opts);
final ActivityOptions activityOptions = safeOptions != null
? safeOptions.getOptions(mStackSupervisor)
@@ -2268,15 +2727,15 @@
throw new IllegalArgumentException("Expected in-place ActivityOption " +
"with valid animation");
}
- mAm.mWindowManager.prepareAppTransition(TRANSIT_TASK_IN_PLACE, false);
- mAm.mWindowManager.overridePendingAppTransitionInPlace(activityOptions.getPackageName(),
+ mWindowManager.prepareAppTransition(TRANSIT_TASK_IN_PLACE, false);
+ mWindowManager.overridePendingAppTransitionInPlace(activityOptions.getPackageName(),
activityOptions.getCustomInPlaceResId());
- mAm.mWindowManager.executeAppTransition();
+ mWindowManager.executeAppTransition();
}
@Override
public void removeStack(int stackId) {
- mAm.enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "removeStack()");
+ enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "removeStack()");
synchronized (mGlobalLock) {
final long ident = Binder.clearCallingIdentity();
try {
@@ -2298,7 +2757,7 @@
@Override
public void moveStackToDisplay(int stackId, int displayId) {
- mAm.enforceCallingPermission(INTERNAL_SYSTEM_WINDOW, "moveStackToDisplay()");
+ mAmInternal.enforceCallingPermission(INTERNAL_SYSTEM_WINDOW, "moveStackToDisplay()");
synchronized (mGlobalLock) {
final long ident = Binder.clearCallingIdentity();
@@ -2314,7 +2773,7 @@
@Override
public int createStackOnDisplay(int displayId) {
- mAm.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "createStackOnDisplay()");
+ mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "createStackOnDisplay()");
synchronized (mGlobalLock) {
final ActivityDisplay display =
mStackSupervisor.getActivityDisplayOrCreateLocked(displayId);
@@ -2356,7 +2815,7 @@
/** Sets the task stack listener that gets callbacks when a task stack changes. */
@Override
public void registerTaskStackListener(ITaskStackListener listener) {
- mAm.enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
+ enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
"registerTaskStackListener()");
mTaskChangeNotificationController.registerTaskStackListener(listener);
}
@@ -2364,7 +2823,7 @@
/** Unregister a task stack listener so that it stops receiving callbacks. */
@Override
public void unregisterTaskStackListener(ITaskStackListener listener) {
- mAm.enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
+ enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
"unregisterTaskStackListener()");
mTaskChangeNotificationController.unregisterTaskStackListener(listener);
}
@@ -2418,25 +2877,83 @@
synchronized (mGlobalLock) {
buildAssistBundleLocked(pae, pae.result);
mPendingAssistExtras.remove(pae);
- mAm.mUiHandler.removeCallbacks(pae);
+ mUiHandler.removeCallbacks(pae);
}
return pae.extras;
}
+ /**
+ * Binder IPC calls go through the public entry point.
+ * This can be called with or without the global lock held.
+ */
+ private static int checkCallingPermission(String permission) {
+ return checkPermission(
+ permission, Binder.getCallingPid(), UserHandle.getAppId(Binder.getCallingUid()));
+ }
+
+ /** This can be called with or without the global lock held. */
+ void enforceCallerIsRecentsOrHasPermission(String permission, String func) {
+ if (!getRecentTasks().isCallerRecents(Binder.getCallingUid())) {
+ mAmInternal.enforceCallingPermission(permission, func);
+ }
+ }
+
+ @VisibleForTesting
+ int checkGetTasksPermission(String permission, int pid, int uid) {
+ return checkPermission(permission, pid, uid);
+ }
+
+ static int checkPermission(String permission, int pid, int uid) {
+ if (permission == null) {
+ return PackageManager.PERMISSION_DENIED;
+ }
+ return checkComponentPermission(permission, pid, uid, -1, true);
+ }
+
+ boolean isGetTasksAllowed(String caller, int callingPid, int callingUid) {
+ if (getRecentTasks().isCallerRecents(callingUid)) {
+ // Always allow the recents component to get tasks
+ return true;
+ }
+
+ boolean allowed = checkGetTasksPermission(android.Manifest.permission.REAL_GET_TASKS,
+ callingPid, callingUid) == PackageManager.PERMISSION_GRANTED;
+ if (!allowed) {
+ if (checkGetTasksPermission(android.Manifest.permission.GET_TASKS,
+ callingPid, callingUid) == PackageManager.PERMISSION_GRANTED) {
+ // Temporary compatibility: some existing apps on the system image may
+ // still be requesting the old permission and not switched to the new
+ // one; if so, we'll still allow them full access. This means we need
+ // to see if they are holding the old permission and are a system app.
+ try {
+ if (AppGlobals.getPackageManager().isUidPrivileged(callingUid)) {
+ allowed = true;
+ if (DEBUG_TASKS) Slog.w(TAG, caller + ": caller " + callingUid
+ + " is using old GET_TASKS but privileged; allowing");
+ }
+ } catch (RemoteException e) {
+ }
+ }
+ if (DEBUG_TASKS) Slog.w(TAG, caller + ": caller " + callingUid
+ + " does not hold REAL_GET_TASKS; limiting output");
+ }
+ return allowed;
+ }
+
private PendingAssistExtras enqueueAssistContext(int requestType, Intent intent, String hint,
IAssistDataReceiver receiver, Bundle receiverExtras, IBinder activityToken,
boolean focused, boolean newSessionId, int userHandle, Bundle args, long timeout,
int flags) {
- mAm.enforceCallingPermission(android.Manifest.permission.GET_TOP_ACTIVITY_INFO,
+ mAmInternal.enforceCallingPermission(android.Manifest.permission.GET_TOP_ACTIVITY_INFO,
"enqueueAssistContext()");
synchronized (mGlobalLock) {
- ActivityRecord activity = mAm.getFocusedStack().getTopActivity();
+ ActivityRecord activity = getTopDisplayFocusedStack().getTopActivity();
if (activity == null) {
Slog.w(TAG, "getAssistContextExtras failed: no top activity");
return null;
}
- if (activity.app == null || activity.app.thread == null) {
+ if (!activity.attachedToProcess()) {
Slog.w(TAG, "getAssistContextExtras failed: no process for " + activity);
return null;
}
@@ -2456,7 +2973,7 @@
+ " couldn't be found");
return null;
}
- if (activity.app == null || activity.app.thread == null) {
+ if (!activity.attachedToProcess()) {
Slog.w(TAG, "enqueueAssistContext failed: no process for " + activity);
return null;
}
@@ -2468,7 +2985,7 @@
extras.putAll(args);
}
extras.putString(Intent.EXTRA_ASSIST_PACKAGE, activity.packageName);
- extras.putInt(Intent.EXTRA_ASSIST_UID, activity.app.uid);
+ extras.putInt(Intent.EXTRA_ASSIST_UID, activity.app.mUid);
pae = new PendingAssistExtras(activity, extras, intent, hint, receiver, receiverExtras,
userHandle);
@@ -2479,10 +2996,10 @@
mViSessionId++;
}
try {
- activity.app.thread.requestAssistContextExtras(activity.appToken, pae, requestType,
- mViSessionId, flags);
+ activity.app.getThread().requestAssistContextExtras(activity.appToken, pae,
+ requestType, mViSessionId, flags);
mPendingAssistExtras.add(pae);
- mAm.mUiHandler.postDelayed(pae, timeout);
+ mUiHandler.postDelayed(pae, timeout);
} catch (RemoteException e) {
Slog.w(TAG, "getAssistContextExtras failed: crash calling " + activity);
return null;
@@ -2559,7 +3076,7 @@
public boolean isAssistDataAllowedOnCurrentActivity() {
int userId;
synchronized (mGlobalLock) {
- final ActivityStack focusedStack = mAm.getFocusedStack();
+ final ActivityStack focusedStack = getTopDisplayFocusedStack();
if (focusedStack == null || focusedStack.isActivityTypeAssistant()) {
return false;
}
@@ -2579,7 +3096,7 @@
try {
synchronized (mGlobalLock) {
ActivityRecord caller = ActivityRecord.forTokenLocked(token);
- ActivityRecord top = mAm.getFocusedStack().getTopActivity();
+ ActivityRecord top = getTopDisplayFocusedStack().getTopActivity();
if (top != caller) {
Slog.w(TAG, "showAssistFromActivity failed: caller " + caller
+ " is not current top " + top);
@@ -2609,6 +3126,64 @@
}
}
+ private void onLocalVoiceInteractionStartedLocked(IBinder activity,
+ IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor) {
+ ActivityRecord activityToCallback = ActivityRecord.forTokenLocked(activity);
+ if (activityToCallback == null) return;
+ activityToCallback.setVoiceSessionLocked(voiceSession);
+
+ // Inform the activity
+ try {
+ activityToCallback.app.getThread().scheduleLocalVoiceInteractionStarted(activity,
+ voiceInteractor);
+ long token = Binder.clearCallingIdentity();
+ try {
+ startRunningVoiceLocked(voiceSession, activityToCallback.appInfo.uid);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ // TODO: VI Should we cache the activity so that it's easier to find later
+ // rather than scan through all the stacks and activities?
+ } catch (RemoteException re) {
+ activityToCallback.clearVoiceSessionLocked();
+ // TODO: VI Should this terminate the voice session?
+ }
+ }
+
+ private void startRunningVoiceLocked(IVoiceInteractionSession session, int targetUid) {
+ Slog.d(TAG, "<<< startRunningVoiceLocked()");
+ mVoiceWakeLock.setWorkSource(new WorkSource(targetUid));
+ if (mRunningVoice == null || mRunningVoice.asBinder() != session.asBinder()) {
+ boolean wasRunningVoice = mRunningVoice != null;
+ mRunningVoice = session;
+ if (!wasRunningVoice) {
+ mVoiceWakeLock.acquire();
+ updateSleepIfNeededLocked();
+ }
+ }
+ }
+
+ void finishRunningVoiceLocked() {
+ if (mRunningVoice != null) {
+ mRunningVoice = null;
+ mVoiceWakeLock.release();
+ updateSleepIfNeededLocked();
+ }
+ }
+
+ @Override
+ public void setVoiceKeepAwake(IVoiceInteractionSession session, boolean keepAwake) {
+ synchronized (mGlobalLock) {
+ if (mRunningVoice != null && mRunningVoice.asBinder() == session.asBinder()) {
+ if (keepAwake) {
+ mVoiceWakeLock.acquire();
+ } else {
+ mVoiceWakeLock.release();
+ }
+ }
+ }
+ }
+
@Override
public ComponentName getActivityClassForToken(IBinder token) {
synchronized (mGlobalLock) {
@@ -2644,7 +3219,7 @@
@Override
public void keyguardGoingAway(int flags) {
- mAm.enforceNotIsolatedCaller("keyguardGoingAway");
+ enforceNotIsolatedCaller("keyguardGoingAway");
final long token = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
@@ -2662,7 +3237,7 @@
*/
@Override
public void positionTaskInStack(int taskId, int stackId, int position) {
- mAm.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "positionTaskInStack()");
+ mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "positionTaskInStack()");
synchronized (mGlobalLock) {
long ident = Binder.clearCallingIdentity();
try {
@@ -2723,7 +3298,7 @@
*/
@Override
public void dismissSplitScreenMode(boolean toTop) {
- mAm.enforceCallerIsRecentsOrHasPermission(
+ enforceCallerIsRecentsOrHasPermission(
MANAGE_ACTIVITY_STACKS, "dismissSplitScreenMode()");
final long ident = Binder.clearCallingIdentity();
try {
@@ -2739,7 +3314,7 @@
// Caller wants the current split-screen primary stack to be the top stack after
// it goes fullscreen, so move it to the front.
stack.moveToFront("dismissSplitScreenMode");
- } else if (mStackSupervisor.isFocusedStack(stack)) {
+ } else if (mStackSupervisor.isTopDisplayFocusedStack(stack)) {
// In this case the current split-screen primary stack shouldn't be the top
// stack after it goes fullscreen, but it current has focus, so we move the
// focus to the top-most split-screen secondary stack next to it.
@@ -2765,7 +3340,7 @@
*/
@Override
public void dismissPip(boolean animate, int animationDuration) {
- mAm.enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "dismissPip()");
+ enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "dismissPip()");
final long ident = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
@@ -2793,7 +3368,7 @@
@Override
public void suppressResizeConfigChanges(boolean suppress) throws RemoteException {
- mAm.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "suppressResizeConfigChanges()");
+ mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "suppressResizeConfigChanges()");
synchronized (mGlobalLock) {
mSuppressResizeConfigChanges = suppress;
}
@@ -2807,7 +3382,7 @@
@Override
// TODO: API should just be about changing windowing modes...
public void moveTasksToFullscreenStack(int fromStackId, boolean onTop) {
- mAm.enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
+ enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
"moveTasksToFullscreenStack()");
synchronized (mGlobalLock) {
final long origId = Binder.clearCallingIdentity();
@@ -2837,10 +3412,10 @@
*/
@Override
public boolean moveTopActivityToPinnedStack(int stackId, Rect bounds) {
- mAm.enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
+ enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
"moveTopActivityToPinnedStack()");
synchronized (mGlobalLock) {
- if (!mAm.mSupportsPictureInPicture) {
+ if (!mSupportsPictureInPicture) {
throw new IllegalStateException("moveTopActivityToPinnedStack:"
+ "Device doesn't support picture-in-picture mode");
}
@@ -2942,8 +3517,8 @@
// device is currently locked).
dismissKeyguard(token, new KeyguardDismissCallback() {
@Override
- public void onDismissSucceeded() throws RemoteException {
- mAm.mHandler.post(enterPipRunnable);
+ public void onDismissSucceeded() {
+ mH.post(enterPipRunnable);
}
}, null /* message */);
} else {
@@ -3012,7 +3587,7 @@
*/
private ActivityRecord ensureValidPictureInPictureActivityParamsLocked(String caller,
IBinder token, PictureInPictureParams params) {
- if (!mAm.mSupportsPictureInPicture) {
+ if (!mSupportsPictureInPicture) {
throw new IllegalStateException(caller
+ ": Device doesn't support picture-in-picture mode.");
}
@@ -3029,7 +3604,7 @@
}
if (params.hasSetAspectRatio()
- && !mAm.mWindowManager.isValidPictureInPictureAspectRatio(r.getStack().mDisplayId,
+ && !mWindowManager.isValidPictureInPictureAspectRatio(r.getStack().mDisplayId,
params.getAspectRatio())) {
final float minAspectRatio = mContext.getResources().getFloat(
com.android.internal.R.dimen.config_pictureInPictureMinAspectRatio);
@@ -3048,7 +3623,7 @@
@Override
public IBinder getUriPermissionOwnerForActivity(IBinder activityToken) {
- mAm.enforceNotIsolatedCaller("getUriPermissionOwnerForActivity");
+ enforceNotIsolatedCaller("getUriPermissionOwnerForActivity");
synchronized (mGlobalLock) {
ActivityRecord r = ActivityRecord.isInStackLocked(activityToken);
if (r == null) {
@@ -3063,7 +3638,7 @@
public void resizeDockedStack(Rect dockedBounds, Rect tempDockedTaskBounds,
Rect tempDockedTaskInsetBounds,
Rect tempOtherTaskBounds, Rect tempOtherTaskInsetBounds) {
- mAm.enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "resizeDockedStack()");
+ enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "resizeDockedStack()");
long ident = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
@@ -3078,7 +3653,7 @@
@Override
public void setSplitScreenResizing(boolean resizing) {
- mAm.enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "setSplitScreenResizing()");
+ enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "setSplitScreenResizing()");
final long ident = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
@@ -3089,9 +3664,20 @@
}
}
+ /**
+ * Check that we have the features required for VR-related API calls, and throw an exception if
+ * not.
+ */
+ void enforceSystemHasVrFeature() {
+ if (!mContext.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_VR_MODE_HIGH_PERFORMANCE)) {
+ throw new UnsupportedOperationException("VR mode not supported on this device!");
+ }
+ }
+
@Override
public int setVrMode(IBinder token, boolean enabled, ComponentName packageName) {
- mAm.enforceSystemHasVrFeature();
+ enforceSystemHasVrFeature();
final VrManagerInternal vrService = LocalServices.getService(VrManagerInternal.class);
@@ -3117,7 +3703,7 @@
r.requestedVrComponent = (enabled) ? packageName : null;
// Update associated state if this activity is currently focused
- if (r == mStackSupervisor.getResumedActivityLocked()) {
+ if (r.isResumedActivityOnDisplay()) {
applyUpdateVrModeLocked(r);
}
return 0;
@@ -3131,11 +3717,11 @@
public void startLocalVoiceInteraction(IBinder callingActivity, Bundle options) {
Slog.i(TAG, "Activity tried to startLocalVoiceInteraction");
synchronized (mGlobalLock) {
- ActivityRecord activity = mAm.getFocusedStack().getTopActivity();
+ ActivityRecord activity = getTopDisplayFocusedStack().getTopActivity();
if (ActivityRecord.forTokenLocked(callingActivity) != activity) {
throw new SecurityException("Only focused activity can call startVoiceInteraction");
}
- if (mAm.mRunningVoice != null || activity.getTask().voiceSession != null
+ if (mRunningVoice != null || activity.getTask().voiceSession != null
|| activity.voiceSession != null) {
Slog.w(TAG, "Already in a voice interaction, cannot start new voice interaction");
return;
@@ -3176,7 +3762,7 @@
@Override
public void resizePinnedStack(Rect pinnedBounds, Rect tempPinnedTaskBounds) {
- mAm.enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "resizePinnedStack()");
+ enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "resizePinnedStack()");
final long ident = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
@@ -3189,7 +3775,7 @@
@Override
public boolean updateDisplayOverrideConfiguration(Configuration values, int displayId) {
- mAm.enforceCallingPermission(CHANGE_CONFIGURATION, "updateDisplayOverrideConfiguration()");
+ mAmInternal.enforceCallingPermission(CHANGE_CONFIGURATION, "updateDisplayOverrideConfiguration()");
synchronized (mGlobalLock) {
// Check if display is initialized in AM.
@@ -3202,14 +3788,14 @@
return false;
}
- if (values == null && mAm.mWindowManager != null) {
+ if (values == null && mWindowManager != null) {
// sentinel: fetch the current configuration from the window manager
- values = mAm.mWindowManager.computeNewConfiguration(displayId);
+ values = mWindowManager.computeNewConfiguration(displayId);
}
- if (mAm.mWindowManager != null) {
+ if (mWindowManager != null) {
// Update OOM levels based on display size.
- mAm.mProcessList.applyDisplaySize(mAm.mWindowManager);
+ mAm.mProcessList.applyDisplaySize(mWindowManager);
}
final long origId = Binder.clearCallingIdentity();
@@ -3217,7 +3803,7 @@
if (values != null) {
Settings.System.clearConfiguration(values);
}
- mAm.updateDisplayOverrideConfigurationLocked(values, null /* starting */,
+ updateDisplayOverrideConfigurationLocked(values, null /* starting */,
false /* deferResume */, displayId, mTmpUpdateConfigurationResult);
return mTmpUpdateConfigurationResult.changes != 0;
} finally {
@@ -3228,17 +3814,17 @@
@Override
public boolean updateConfiguration(Configuration values) {
- mAm.enforceCallingPermission(CHANGE_CONFIGURATION, "updateConfiguration()");
+ mAmInternal.enforceCallingPermission(CHANGE_CONFIGURATION, "updateConfiguration()");
synchronized (mGlobalLock) {
- if (values == null && mAm.mWindowManager != null) {
+ if (values == null && mWindowManager != null) {
// sentinel: fetch the current configuration from the window manager
- values = mAm.mWindowManager.computeNewConfiguration(DEFAULT_DISPLAY);
+ values = mWindowManager.computeNewConfiguration(DEFAULT_DISPLAY);
}
- if (mAm.mWindowManager != null) {
+ if (mWindowManager != null) {
// Update OOM levels based on display size.
- mAm.mProcessList.applyDisplaySize(mAm.mWindowManager);
+ mAm.mProcessList.applyDisplaySize(mWindowManager);
}
final long origId = Binder.clearCallingIdentity();
@@ -3246,7 +3832,7 @@
if (values != null) {
Settings.System.clearConfiguration(values);
}
- mAm.updateConfigurationLocked(values, null, false, false /* persistent */,
+ updateConfigurationLocked(values, null, false, false /* persistent */,
UserHandle.USER_NULL, false /* deferResume */,
mTmpUpdateConfigurationResult);
return mTmpUpdateConfigurationResult.changes != 0;
@@ -3260,7 +3846,7 @@
public void dismissKeyguard(IBinder token, IKeyguardDismissCallback callback,
CharSequence message) {
if (message != null) {
- mAm.enforceCallingPermission(
+ mAmInternal.enforceCallingPermission(
Manifest.permission.SHOW_KEYGUARD_MESSAGE, "dismissKeyguard()");
}
final long callingId = Binder.clearCallingIdentity();
@@ -3275,7 +3861,7 @@
@Override
public void cancelTaskWindowTransition(int taskId) {
- mAm.enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
+ enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
"cancelTaskWindowTransition()");
final long ident = Binder.clearCallingIdentity();
try {
@@ -3295,7 +3881,7 @@
@Override
public ActivityManager.TaskSnapshot getTaskSnapshot(int taskId, boolean reducedResolution) {
- mAm.enforceCallerIsRecentsOrHasPermission(READ_FRAME_BUFFER, "getTaskSnapshot()");
+ enforceCallerIsRecentsOrHasPermission(READ_FRAME_BUFFER, "getTaskSnapshot()");
final long ident = Binder.clearCallingIdentity();
try {
final TaskRecord task;
@@ -3336,13 +3922,13 @@
@Override
public @UserIdInt
int getLastResumedActivityUserId() {
- mAm.enforceCallingPermission(
+ mAmInternal.enforceCallingPermission(
Manifest.permission.INTERACT_ACROSS_USERS_FULL, "getLastResumedActivityUserId()");
synchronized (mGlobalLock) {
- if (mAm.mLastResumedActivity == null) {
- return mAm.mUserController.getCurrentUserId();
+ if (mLastResumedActivity == null) {
+ return getCurrentUserId();
}
- return mAm.mLastResumedActivity.userId;
+ return mLastResumedActivity.userId;
}
}
@@ -3350,7 +3936,7 @@
public void updateLockTaskFeatures(int userId, int flags) {
final int callingUid = Binder.getCallingUid();
if (callingUid != 0 && callingUid != SYSTEM_UID) {
- mAm.enforceCallingPermission(android.Manifest.permission.UPDATE_LOCK_TASK_PACKAGES,
+ mAmInternal.enforceCallingPermission(android.Manifest.permission.UPDATE_LOCK_TASK_PACKAGES,
"updateLockTaskFeatures()");
}
synchronized (mGlobalLock) {
@@ -3394,7 +3980,7 @@
@Override
public void registerRemoteAnimations(IBinder token, RemoteAnimationDefinition definition) {
- mAm.enforceCallingPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS,
+ mAmInternal.enforceCallingPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS,
"registerRemoteAnimations");
definition.setCallingPid(Binder.getCallingPid());
synchronized (mGlobalLock) {
@@ -3414,7 +4000,7 @@
@Override
public void registerRemoteAnimationForNextActivityStart(String packageName,
RemoteAnimationAdapter adapter) {
- mAm.enforceCallingPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS,
+ mAmInternal.enforceCallingPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS,
"registerRemoteAnimationForNextActivityStart");
adapter.setCallingPid(Binder.getCallingPid());
synchronized (mGlobalLock) {
@@ -3443,19 +4029,19 @@
@Override
public void setVrThread(int tid) {
- mAm.enforceSystemHasVrFeature();
+ enforceSystemHasVrFeature();
synchronized (mGlobalLock) {
synchronized (mAm.mPidsSelfLocked) {
final int pid = Binder.getCallingPid();
final ProcessRecord proc = mAm.mPidsSelfLocked.get(pid);
- mVrController.setVrThreadLocked(tid, pid, proc);
+ mVrController.setVrThreadLocked(tid, pid, proc.getWindowProcessController());
}
}
}
@Override
public void setPersistentVrThread(int tid) {
- if (mAm.checkCallingPermission(Manifest.permission.RESTRICTED_VR_ACCESS)
+ if (checkCallingPermission(Manifest.permission.RESTRICTED_VR_ACCESS)
!= PERMISSION_GRANTED) {
final String msg = "Permission Denial: setPersistentVrThread() from pid="
+ Binder.getCallingPid()
@@ -3464,7 +4050,7 @@
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
- mAm.enforceSystemHasVrFeature();
+ enforceSystemHasVrFeature();
synchronized (mGlobalLock) {
synchronized (mAm.mPidsSelfLocked) {
final int pid = Binder.getCallingPid();
@@ -3474,9 +4060,41 @@
}
}
- /**
- * @return whether the system should disable UI modes incompatible with VR mode.
- */
+ @Override
+ public void stopAppSwitches() {
+ enforceCallerIsRecentsOrHasPermission(STOP_APP_SWITCHES, "stopAppSwitches");
+ synchronized (mGlobalLock) {
+ mAppSwitchesAllowedTime = SystemClock.uptimeMillis() + APP_SWITCH_DELAY_TIME;
+ mDidAppSwitch = false;
+ getActivityStartController().schedulePendingActivityLaunches(APP_SWITCH_DELAY_TIME);
+ }
+ }
+
+ @Override
+ public void resumeAppSwitches() {
+ enforceCallerIsRecentsOrHasPermission(STOP_APP_SWITCHES, "resumeAppSwitches");
+ synchronized (mGlobalLock) {
+ // Note that we don't execute any pending app switches... we will
+ // let those wait until either the timeout, or the next start
+ // activity request.
+ mAppSwitchesAllowedTime = 0;
+ }
+ }
+
+ void onStartActivitySetDidAppSwitch() {
+ if (mDidAppSwitch) {
+ // This is the second allowed switch since we stopped switches, so now just generally
+ // allow switches. Use case:
+ // - user presses home (switches disabled, switch to home, mDidAppSwitch now true);
+ // - user taps a home icon (coming from home so allowed, we hit here and now allow
+ // anyone to switch again).
+ mAppSwitchesAllowedTime = 0;
+ } else {
+ mDidAppSwitch = true;
+ }
+ }
+
+ /** @return whether the system should disable UI modes incompatible with VR mode. */
boolean shouldDisableNonVrUiLocked() {
return mVrController.shouldDisableNonVrUiLocked();
}
@@ -3512,12 +4130,16 @@
});
}
+ ActivityStack getTopDisplayFocusedStack() {
+ return mStackSupervisor.getTopDisplayFocusedStack();
+ }
+
/** Pokes the task persister. */
void notifyTaskPersisterLocked(TaskRecord task, boolean flush) {
mRecentTasks.notifyTaskPersisterLocked(task, flush);
}
- void onTopProcChangedLocked(ProcessRecord proc) {
+ void onTopProcChangedLocked(WindowProcessController proc) {
mVrController.onTopProcChangedLocked(proc);
}
@@ -3532,25 +4154,745 @@
|| transit == TRANSIT_TASK_TO_FRONT;
}
- void dumpVrControllerLocked(PrintWriter pw) {
- pw.println(" mVrController=" + mVrController);
+ void dumpSleepStates(PrintWriter pw, boolean testPssMode) {
+ synchronized (mGlobalLock) {
+ pw.println(" mSleepTokens=" + mStackSupervisor.mSleepTokens);
+ if (mRunningVoice != null) {
+ pw.println(" mRunningVoice=" + mRunningVoice);
+ pw.println(" mVoiceWakeLock" + mVoiceWakeLock);
+ }
+ pw.println(" mSleeping=" + mSleeping);
+ pw.println(" mShuttingDown=" + mShuttingDown + " mTestPssMode=" + testPssMode);
+ pw.println(" mVrController=" + mVrController);
+ }
}
- void writeVrControllerToProto(ProtoOutputStream proto, long fieldId) {
- mVrController.writeToProto(proto, fieldId);
+ void writeSleepStateToProto(ProtoOutputStream proto) {
+ for (ActivityTaskManagerInternal.SleepToken st : mStackSupervisor.mSleepTokens) {
+ proto.write(ActivityManagerServiceDumpProcessesProto.SleepStatus.SLEEP_TOKENS,
+ st.toString());
+ }
+
+ if (mRunningVoice != null) {
+ final long vrToken = proto.start(
+ ActivityManagerServiceDumpProcessesProto.RUNNING_VOICE);
+ proto.write(ActivityManagerServiceDumpProcessesProto.Voice.SESSION,
+ mRunningVoice.toString());
+ mVoiceWakeLock.writeToProto(
+ proto, ActivityManagerServiceDumpProcessesProto.Voice.WAKELOCK);
+ proto.end(vrToken);
+ }
+
+ proto.write(ActivityManagerServiceDumpProcessesProto.SleepStatus.SLEEPING, mSleeping);
+ proto.write(ActivityManagerServiceDumpProcessesProto.SleepStatus.SHUTTING_DOWN,
+ mShuttingDown);
+ mVrController.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.VR_CONTROLLER);
+ }
+
+ int getCurrentUserId() {
+ return mAmInternal.getCurrentUserId();
+ }
+
+ private void enforceNotIsolatedCaller(String caller) {
+ if (UserHandle.isIsolated(Binder.getCallingUid())) {
+ throw new SecurityException("Isolated process not allowed to call " + caller);
+ }
+ }
+
+ public Configuration getConfiguration() {
+ Configuration ci;
+ synchronized(mGlobalLock) {
+ ci = new Configuration(getGlobalConfiguration());
+ ci.userSetLocale = false;
+ }
+ return ci;
+ }
+
+ /**
+ * Current global configuration information. Contains general settings for the entire system,
+ * also corresponds to the merged configuration of the default display.
+ */
+ Configuration getGlobalConfiguration() {
+ return mStackSupervisor.getConfiguration();
+ }
+
+ boolean updateConfigurationLocked(Configuration values, ActivityRecord starting,
+ boolean initLocale) {
+ return updateConfigurationLocked(values, starting, initLocale, false /* deferResume */);
+ }
+
+ boolean updateConfigurationLocked(Configuration values, ActivityRecord starting,
+ boolean initLocale, boolean deferResume) {
+ // pass UserHandle.USER_NULL as userId because we don't persist configuration for any user
+ return updateConfigurationLocked(values, starting, initLocale, false /* persistent */,
+ UserHandle.USER_NULL, deferResume);
+ }
+
+ void updatePersistentConfiguration(Configuration values, @UserIdInt int userId) {
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ synchronized (mGlobalLock) {
+ updateConfigurationLocked(values, null, false, true, userId,
+ false /* deferResume */);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+
+ void updateUserConfiguration() {
+ synchronized (mGlobalLock) {
+ final Configuration configuration = new Configuration(getGlobalConfiguration());
+ final int currentUserId = mAmInternal.getCurrentUserId();
+ Settings.System.adjustConfigurationForUser(mContext.getContentResolver(), configuration,
+ currentUserId, Settings.System.canWrite(mContext));
+ updateConfigurationLocked(configuration, null /* starting */, false /* initLocale */,
+ false /* persistent */, currentUserId, false /* deferResume */);
+ }
+ }
+
+ private boolean updateConfigurationLocked(Configuration values, ActivityRecord starting,
+ boolean initLocale, boolean persistent, int userId, boolean deferResume) {
+ return updateConfigurationLocked(values, starting, initLocale, persistent, userId,
+ deferResume, null /* result */);
+ }
+
+ /**
+ * Do either or both things: (1) change the current configuration, and (2)
+ * make sure the given activity is running with the (now) current
+ * configuration. Returns true if the activity has been left running, or
+ * false if <var>starting</var> is being destroyed to match the new
+ * configuration.
+ *
+ * @param userId is only used when persistent parameter is set to true to persist configuration
+ * for that particular user
+ */
+ boolean updateConfigurationLocked(Configuration values, ActivityRecord starting,
+ boolean initLocale, boolean persistent, int userId, boolean deferResume,
+ ActivityTaskManagerService.UpdateConfigurationResult result) {
+ int changes = 0;
+ boolean kept = true;
+
+ if (mWindowManager != null) {
+ mWindowManager.deferSurfaceLayout();
+ }
+ try {
+ if (values != null) {
+ changes = updateGlobalConfigurationLocked(values, initLocale, persistent, userId,
+ deferResume);
+ }
+
+ kept = ensureConfigAndVisibilityAfterUpdate(starting, changes);
+ } finally {
+ if (mWindowManager != null) {
+ mWindowManager.continueSurfaceLayout();
+ }
+ }
+
+ if (result != null) {
+ result.changes = changes;
+ result.activityRelaunched = !kept;
+ }
+ return kept;
+ }
+
+ /**
+ * Returns true if this configuration change is interesting enough to send an
+ * {@link Intent#ACTION_SPLIT_CONFIGURATION_CHANGED} broadcast.
+ */
+ private static boolean isSplitConfigurationChange(int configDiff) {
+ return (configDiff & (ActivityInfo.CONFIG_LOCALE | ActivityInfo.CONFIG_DENSITY)) != 0;
+ }
+
+ /** Update default (global) configuration and notify listeners about changes. */
+ private int updateGlobalConfigurationLocked(@NonNull Configuration values, boolean initLocale,
+ boolean persistent, int userId, boolean deferResume) {
+ mTempConfig.setTo(getGlobalConfiguration());
+ final int changes = mTempConfig.updateFrom(values);
+ if (changes == 0) {
+ // Since calling to Activity.setRequestedOrientation leads to freezing the window with
+ // setting WindowManagerService.mWaitingForConfig to true, it is important that we call
+ // performDisplayOverrideConfigUpdate in order to send the new display configuration
+ // (even if there are no actual changes) to unfreeze the window.
+ performDisplayOverrideConfigUpdate(values, deferResume, DEFAULT_DISPLAY);
+ return 0;
+ }
+
+ if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.i(TAG_CONFIGURATION,
+ "Updating global configuration to: " + values);
+
+ EventLog.writeEvent(EventLogTags.CONFIGURATION_CHANGED, changes);
+ StatsLog.write(StatsLog.RESOURCE_CONFIGURATION_CHANGED,
+ values.colorMode,
+ values.densityDpi,
+ values.fontScale,
+ values.hardKeyboardHidden,
+ values.keyboard,
+ values.keyboardHidden,
+ values.mcc,
+ values.mnc,
+ values.navigation,
+ values.navigationHidden,
+ values.orientation,
+ values.screenHeightDp,
+ values.screenLayout,
+ values.screenWidthDp,
+ values.smallestScreenWidthDp,
+ values.touchscreen,
+ values.uiMode);
+
+
+ if (!initLocale && !values.getLocales().isEmpty() && values.userSetLocale) {
+ final LocaleList locales = values.getLocales();
+ int bestLocaleIndex = 0;
+ if (locales.size() > 1) {
+ if (mSupportedSystemLocales == null) {
+ mSupportedSystemLocales = Resources.getSystem().getAssets().getLocales();
+ }
+ bestLocaleIndex = Math.max(0, locales.getFirstMatchIndex(mSupportedSystemLocales));
+ }
+ SystemProperties.set("persist.sys.locale",
+ locales.get(bestLocaleIndex).toLanguageTag());
+ LocaleList.setDefault(locales, bestLocaleIndex);
+ mAm.mHandler.sendMessage(mAm.mHandler.obtainMessage(SEND_LOCALE_TO_MOUNT_DAEMON_MSG,
+ locales.get(bestLocaleIndex)));
+ }
+
+ mConfigurationSeq = Math.max(++mConfigurationSeq, 1);
+ mTempConfig.seq = mConfigurationSeq;
+
+ // Update stored global config and notify everyone about the change.
+ mStackSupervisor.onConfigurationChanged(mTempConfig);
+
+ Slog.i(TAG, "Config changes=" + Integer.toHexString(changes) + " " + mTempConfig);
+ // TODO(multi-display): Update UsageEvents#Event to include displayId.
+ mAm.mUsageStatsService.reportConfigurationChange(
+ mTempConfig, mAmInternal.getCurrentUserId());
+
+ // TODO: If our config changes, should we auto dismiss any currently showing dialogs?
+ updateShouldShowDialogsLocked(mTempConfig);
+
+ AttributeCache ac = AttributeCache.instance();
+ if (ac != null) {
+ ac.updateConfiguration(mTempConfig);
+ }
+
+ // Make sure all resources in our process are updated right now, so that anyone who is going
+ // to retrieve resource values after we return will be sure to get the new ones. This is
+ // especially important during boot, where the first config change needs to guarantee all
+ // resources have that config before following boot code is executed.
+ mAm.mSystemThread.applyConfigurationToResources(mTempConfig);
+
+ // We need another copy of global config because we're scheduling some calls instead of
+ // running them in place. We need to be sure that object we send will be handled unchanged.
+ final Configuration configCopy = new Configuration(mTempConfig);
+ if (persistent && Settings.System.hasInterestingConfigurationChanges(changes)) {
+ Message msg = mAm.mHandler.obtainMessage(UPDATE_CONFIGURATION_MSG);
+ msg.obj = configCopy;
+ msg.arg1 = userId;
+ mAm.mHandler.sendMessage(msg);
+ }
+
+ for (int i = mAm.mLruProcesses.size() - 1; i >= 0; i--) {
+ ProcessRecord app = mAm.mLruProcesses.get(i);
+ try {
+ if (app.thread != null) {
+ if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, "Sending to proc "
+ + app.processName + " new config " + configCopy);
+ getLifecycleManager().scheduleTransaction(app.thread,
+ ConfigurationChangeItem.obtain(configCopy));
+ }
+ } catch (Exception e) {
+ Slog.e(TAG_CONFIGURATION, "Failed to schedule configuration change", e);
+ }
+ }
+
+ Intent intent = new Intent(Intent.ACTION_CONFIGURATION_CHANGED);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_REPLACE_PENDING
+ | Intent.FLAG_RECEIVER_FOREGROUND
+ | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
+ mAm.broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null,
+ OP_NONE, null, false, false, MY_PID, SYSTEM_UID,
+ UserHandle.USER_ALL);
+ if ((changes & ActivityInfo.CONFIG_LOCALE) != 0) {
+ intent = new Intent(Intent.ACTION_LOCALE_CHANGED);
+ intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND
+ | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND
+ | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
+ if (initLocale || !mAm.mProcessesReady) {
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ }
+ mAm.broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null,
+ OP_NONE, null, false, false, MY_PID, SYSTEM_UID,
+ UserHandle.USER_ALL);
+ }
+
+ // Send a broadcast to PackageInstallers if the configuration change is interesting
+ // for the purposes of installing additional splits.
+ if (!initLocale && isSplitConfigurationChange(changes)) {
+ intent = new Intent(Intent.ACTION_SPLIT_CONFIGURATION_CHANGED);
+ intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING
+ | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+
+ // Typically only app stores will have this permission.
+ String[] permissions = new String[] { android.Manifest.permission.INSTALL_PACKAGES };
+ mAm.broadcastIntentLocked(null, null, intent, null, null, 0, null, null, permissions,
+ OP_NONE, null, false, false, MY_PID, SYSTEM_UID, UserHandle.USER_ALL);
+ }
+
+ // Override configuration of the default display duplicates global config, so we need to
+ // update it also. This will also notify WindowManager about changes.
+ performDisplayOverrideConfigUpdate(mStackSupervisor.getConfiguration(), deferResume,
+ DEFAULT_DISPLAY);
+
+ return changes;
+ }
+
+ boolean updateDisplayOverrideConfigurationLocked(Configuration values, ActivityRecord starting,
+ boolean deferResume, int displayId) {
+ return updateDisplayOverrideConfigurationLocked(values, starting, deferResume /* deferResume */,
+ displayId, null /* result */);
+ }
+
+ /**
+ * Updates override configuration specific for the selected display. If no config is provided,
+ * new one will be computed in WM based on current display info.
+ */
+ boolean updateDisplayOverrideConfigurationLocked(Configuration values,
+ ActivityRecord starting, boolean deferResume, int displayId,
+ ActivityTaskManagerService.UpdateConfigurationResult result) {
+ int changes = 0;
+ boolean kept = true;
+
+ if (mWindowManager != null) {
+ mWindowManager.deferSurfaceLayout();
+ }
+ try {
+ if (values != null) {
+ if (displayId == DEFAULT_DISPLAY) {
+ // Override configuration of the default display duplicates global config, so
+ // we're calling global config update instead for default display. It will also
+ // apply the correct override config.
+ changes = updateGlobalConfigurationLocked(values, false /* initLocale */,
+ false /* persistent */, UserHandle.USER_NULL /* userId */, deferResume);
+ } else {
+ changes = performDisplayOverrideConfigUpdate(values, deferResume, displayId);
+ }
+ }
+
+ kept = ensureConfigAndVisibilityAfterUpdate(starting, changes);
+ } finally {
+ if (mWindowManager != null) {
+ mWindowManager.continueSurfaceLayout();
+ }
+ }
+
+ if (result != null) {
+ result.changes = changes;
+ result.activityRelaunched = !kept;
+ }
+ return kept;
+ }
+
+ private int performDisplayOverrideConfigUpdate(Configuration values, boolean deferResume,
+ int displayId) {
+ mTempConfig.setTo(mStackSupervisor.getDisplayOverrideConfiguration(displayId));
+ final int changes = mTempConfig.updateFrom(values);
+ if (changes != 0) {
+ Slog.i(TAG, "Override config changes=" + Integer.toHexString(changes) + " "
+ + mTempConfig + " for displayId=" + displayId);
+ mStackSupervisor.setDisplayOverrideConfiguration(mTempConfig, displayId);
+
+ final boolean isDensityChange = (changes & ActivityInfo.CONFIG_DENSITY) != 0;
+ if (isDensityChange && displayId == DEFAULT_DISPLAY) {
+ mAm.mAppWarnings.onDensityChanged();
+
+ mAm.killAllBackgroundProcessesExcept(N,
+ ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
+ }
+ }
+
+ // Update the configuration with WM first and check if any of the stacks need to be resized
+ // due to the configuration change. If so, resize the stacks now and do any relaunches if
+ // necessary. This way we don't need to relaunch again afterwards in
+ // ensureActivityConfiguration().
+ if (mWindowManager != null) {
+ final int[] resizedStacks =
+ mWindowManager.setNewDisplayOverrideConfiguration(mTempConfig, displayId);
+ if (resizedStacks != null) {
+ for (int stackId : resizedStacks) {
+ resizeStackWithBoundsFromWindowManager(stackId, deferResume);
+ }
+ }
+ }
+
+ return changes;
+ }
+
+ private void updateEventDispatchingLocked(boolean booted) {
+ mWindowManager.setEventDispatching(booted && !mShuttingDown);
+ }
+
+ void enableScreenAfterBoot(boolean booted) {
+ EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_ENABLE_SCREEN,
+ SystemClock.uptimeMillis());
+ mWindowManager.enableScreenAfterBoot();
+
+ synchronized (mGlobalLock) {
+ updateEventDispatchingLocked(booted);
+ }
+ }
+
+ boolean canShowErrorDialogs() {
+ return mShowDialogs && !mSleeping && !mShuttingDown
+ && !mKeyguardController.isKeyguardOrAodShowing(DEFAULT_DISPLAY)
+ && !hasUserRestriction(UserManager.DISALLOW_SYSTEM_ERROR_DIALOGS,
+ mAmInternal.getCurrentUserId())
+ && !(UserManager.isDeviceInDemoMode(mContext)
+ && mAmInternal.getCurrentUser().isDemo());
+ }
+
+ /**
+ * Decide based on the configuration whether we should show the ANR,
+ * crash, etc dialogs. The idea is that if there is no affordance to
+ * press the on-screen buttons, or the user experience would be more
+ * greatly impacted than the crash itself, we shouldn't show the dialog.
+ *
+ * A thought: SystemUI might also want to get told about this, the Power
+ * dialog / global actions also might want different behaviors.
+ */
+ private void updateShouldShowDialogsLocked(Configuration config) {
+ final boolean inputMethodExists = !(config.keyboard == Configuration.KEYBOARD_NOKEYS
+ && config.touchscreen == Configuration.TOUCHSCREEN_NOTOUCH
+ && config.navigation == Configuration.NAVIGATION_NONAV);
+ int modeType = config.uiMode & Configuration.UI_MODE_TYPE_MASK;
+ final boolean uiModeSupportsDialogs = (modeType != Configuration.UI_MODE_TYPE_CAR
+ && !(modeType == Configuration.UI_MODE_TYPE_WATCH && Build.IS_USER)
+ && modeType != Configuration.UI_MODE_TYPE_TELEVISION
+ && modeType != Configuration.UI_MODE_TYPE_VR_HEADSET);
+ final boolean hideDialogsSet = Settings.Global.getInt(mContext.getContentResolver(),
+ HIDE_ERROR_DIALOGS, 0) != 0;
+ mShowDialogs = inputMethodExists && uiModeSupportsDialogs && !hideDialogsSet;
+ }
+
+ private void updateFontScaleIfNeeded(@UserIdInt int userId) {
+ final float scaleFactor = Settings.System.getFloatForUser(mContext.getContentResolver(),
+ FONT_SCALE, 1.0f, userId);
+
+ synchronized (this) {
+ if (getGlobalConfiguration().fontScale == scaleFactor) {
+ return;
+ }
+
+ final Configuration configuration
+ = mWindowManager.computeNewConfiguration(DEFAULT_DISPLAY);
+ configuration.fontScale = scaleFactor;
+ updatePersistentConfiguration(configuration, userId);
+ }
+ }
+
+ // Actually is sleeping or shutting down or whatever else in the future
+ // is an inactive state.
+ boolean isSleepingOrShuttingDownLocked() {
+ return isSleepingLocked() || mShuttingDown;
+ }
+
+ boolean isSleepingLocked() {
+ return mSleeping;
+ }
+
+ /**
+ * Update AMS states when an activity is resumed. This should only be called by
+ * {@link ActivityStack#onActivityStateChanged(
+ * ActivityRecord, ActivityStack.ActivityState, String)} when an activity is resumed.
+ */
+ void setResumedActivityUncheckLocked(ActivityRecord r, String reason) {
+ final TaskRecord task = r.getTask();
+ if (task.isActivityTypeStandard()) {
+ if (mCurAppTimeTracker != r.appTimeTracker) {
+ // We are switching app tracking. Complete the current one.
+ if (mCurAppTimeTracker != null) {
+ mCurAppTimeTracker.stop();
+ mH.obtainMessage(
+ REPORT_TIME_TRACKER_MSG, mCurAppTimeTracker).sendToTarget();
+ mStackSupervisor.clearOtherAppTimeTrackers(r.appTimeTracker);
+ mCurAppTimeTracker = null;
+ }
+ if (r.appTimeTracker != null) {
+ mCurAppTimeTracker = r.appTimeTracker;
+ startTimeTrackingFocusedActivityLocked();
+ }
+ } else {
+ startTimeTrackingFocusedActivityLocked();
+ }
+ } else {
+ r.appTimeTracker = null;
+ }
+ // TODO: VI Maybe r.task.voiceInteractor || r.voiceInteractor != null
+ // TODO: Probably not, because we don't want to resume voice on switching
+ // back to this activity
+ if (task.voiceInteractor != null) {
+ startRunningVoiceLocked(task.voiceSession, r.info.applicationInfo.uid);
+ } else {
+ finishRunningVoiceLocked();
+
+ if (mLastResumedActivity != null) {
+ final IVoiceInteractionSession session;
+
+ final TaskRecord lastResumedActivityTask = mLastResumedActivity.getTask();
+ if (lastResumedActivityTask != null
+ && lastResumedActivityTask.voiceSession != null) {
+ session = lastResumedActivityTask.voiceSession;
+ } else {
+ session = mLastResumedActivity.voiceSession;
+ }
+
+ if (session != null) {
+ // We had been in a voice interaction session, but now focused has
+ // move to something different. Just finish the session, we can't
+ // return to it and retain the proper state and synchronization with
+ // the voice interaction service.
+ finishVoiceTask(session);
+ }
+ }
+ }
+
+ if (mLastResumedActivity != null && r.userId != mLastResumedActivity.userId) {
+ mAmInternal.sendForegroundProfileChanged(r.userId);
+ }
+ updateResumedAppTrace(r);
+ mLastResumedActivity = r;
+
+ mWindowManager.setFocusedApp(r.appToken, true);
+
+ applyUpdateLockStateLocked(r);
+ applyUpdateVrModeLocked(r);
+
+ EventLogTags.writeAmSetResumedActivity(
+ r == null ? -1 : r.userId,
+ r == null ? "NULL" : r.shortComponentName,
+ reason);
+ }
+
+ ActivityTaskManagerInternal.SleepToken acquireSleepToken(String tag, int displayId) {
+ synchronized (mGlobalLock) {
+ final ActivityTaskManagerInternal.SleepToken token = mStackSupervisor.createSleepTokenLocked(tag, displayId);
+ updateSleepIfNeededLocked();
+ return token;
+ }
+ }
+
+ void updateSleepIfNeededLocked() {
+ final boolean shouldSleep = !mStackSupervisor.hasAwakeDisplay();
+ final boolean wasSleeping = mSleeping;
+ boolean updateOomAdj = false;
+
+ if (!shouldSleep) {
+ // If wasSleeping is true, we need to wake up activity manager state from when
+ // we started sleeping. In either case, we need to apply the sleep tokens, which
+ // will wake up stacks or put them to sleep as appropriate.
+ if (wasSleeping) {
+ mSleeping = false;
+ startTimeTrackingFocusedActivityLocked();
+ mTopProcessState = ActivityManager.PROCESS_STATE_TOP;
+ mStackSupervisor.comeOutOfSleepIfNeededLocked();
+ }
+ mStackSupervisor.applySleepTokensLocked(true /* applyToStacks */);
+ if (wasSleeping) {
+ updateOomAdj = true;
+ }
+ } else if (!mSleeping && shouldSleep) {
+ mSleeping = true;
+ if (mCurAppTimeTracker != null) {
+ mCurAppTimeTracker.stop();
+ }
+ mTopProcessState = ActivityManager.PROCESS_STATE_TOP_SLEEPING;
+ mStackSupervisor.goingToSleepLocked();
+ updateResumedAppTrace(null /* resumed */);
+ updateOomAdj = true;
+ }
+ if (updateOomAdj) {
+ mH.post(mAmInternal::updateOomAdj);
+ }
+ }
+
+ void updateOomAdj() {
+ mH.post(mAmInternal::updateOomAdj);
+ }
+
+ // TODO(b/111541062): Update app time tracking to make it aware of multiple resumed activities
+ private void startTimeTrackingFocusedActivityLocked() {
+ final ActivityRecord resumedActivity = mStackSupervisor.getTopResumedActivity();
+ if (!mSleeping && mCurAppTimeTracker != null && resumedActivity != null) {
+ mCurAppTimeTracker.start(resumedActivity.packageName);
+ }
+ }
+
+ private void updateResumedAppTrace(@Nullable ActivityRecord resumed) {
+ if (mTracedResumedActivity != null) {
+ Trace.asyncTraceEnd(TRACE_TAG_ACTIVITY_MANAGER,
+ constructResumedTraceName(mTracedResumedActivity.packageName), 0);
+ }
+ if (resumed != null) {
+ Trace.asyncTraceBegin(TRACE_TAG_ACTIVITY_MANAGER,
+ constructResumedTraceName(resumed.packageName), 0);
+ }
+ mTracedResumedActivity = resumed;
+ }
+
+ private String constructResumedTraceName(String packageName) {
+ return "focused app: " + packageName;
+ }
+
+ /** Helper method that requests bounds from WM and applies them to stack. */
+ private void resizeStackWithBoundsFromWindowManager(int stackId, boolean deferResume) {
+ final Rect newStackBounds = new Rect();
+ final ActivityStack stack = mStackSupervisor.getStack(stackId);
+
+ // TODO(b/71548119): Revert CL introducing below once cause of mismatch is found.
+ if (stack == null) {
+ final StringWriter writer = new StringWriter();
+ final PrintWriter printWriter = new PrintWriter(writer);
+ mStackSupervisor.dumpDisplays(printWriter);
+ printWriter.flush();
+
+ Log.wtf(TAG, "stack not found:" + stackId + " displays:" + writer);
+ }
+
+ stack.getBoundsForNewConfiguration(newStackBounds);
+ mStackSupervisor.resizeStackLocked(
+ stack, !newStackBounds.isEmpty() ? newStackBounds : null /* bounds */,
+ null /* tempTaskBounds */, null /* tempTaskInsetBounds */,
+ false /* preserveWindows */, false /* allowResizeInDockedMode */, deferResume);
+ }
+
+ /** Applies latest configuration and/or visibility updates if needed. */
+ private boolean ensureConfigAndVisibilityAfterUpdate(ActivityRecord starting, int changes) {
+ boolean kept = true;
+ final ActivityStack mainStack = mStackSupervisor.getTopDisplayFocusedStack();
+ // mainStack is null during startup.
+ if (mainStack != null) {
+ if (changes != 0 && starting == null) {
+ // If the configuration changed, and the caller is not already
+ // in the process of starting an activity, then find the top
+ // activity to check if its configuration needs to change.
+ starting = mainStack.topRunningActivityLocked();
+ }
+
+ if (starting != null) {
+ kept = starting.ensureActivityConfiguration(changes,
+ false /* preserveWindow */);
+ // And we need to make sure at this point that all other activities
+ // are made visible with the correct configuration.
+ mStackSupervisor.ensureActivitiesVisibleLocked(starting, changes,
+ !PRESERVE_WINDOWS);
+ }
+ }
+
+ return kept;
+ }
+
+ void logAppTooSlow(WindowProcessController app, long startTime, String msg) {
+ if (true || Build.IS_USER) {
+ return;
+ }
+
+ StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
+ StrictMode.allowThreadDiskWrites();
+ try {
+ File tracesDir = new File("/data/anr");
+ File tracesFile = null;
+ try {
+ tracesFile = File.createTempFile("app_slow", null, tracesDir);
+
+ StringBuilder sb = new StringBuilder();
+ Time tobj = new Time();
+ tobj.set(System.currentTimeMillis());
+ sb.append(tobj.format("%Y-%m-%d %H:%M:%S"));
+ sb.append(": ");
+ TimeUtils.formatDuration(SystemClock.uptimeMillis()-startTime, sb);
+ sb.append(" since ");
+ sb.append(msg);
+ FileOutputStream fos = new FileOutputStream(tracesFile);
+ fos.write(sb.toString().getBytes());
+ if (app == null) {
+ fos.write("\n*** No application process!".getBytes());
+ }
+ fos.close();
+ FileUtils.setPermissions(tracesFile.getPath(), 0666, -1, -1); // -rw-rw-rw-
+ } catch (IOException e) {
+ Slog.w(TAG, "Unable to prepare slow app traces file: " + tracesFile, e);
+ return;
+ }
+
+ if (app != null && app.getPid() > 0) {
+ ArrayList<Integer> firstPids = new ArrayList<Integer>();
+ firstPids.add(app.getPid());
+ dumpStackTraces(tracesFile.getAbsolutePath(), firstPids, null, null);
+ }
+
+ File lastTracesFile = null;
+ File curTracesFile = null;
+ for (int i=9; i>=0; i--) {
+ String name = String.format(Locale.US, "slow%02d.txt", i);
+ curTracesFile = new File(tracesDir, name);
+ if (curTracesFile.exists()) {
+ if (lastTracesFile != null) {
+ curTracesFile.renameTo(lastTracesFile);
+ } else {
+ curTracesFile.delete();
+ }
+ }
+ lastTracesFile = curTracesFile;
+ }
+ tracesFile.renameTo(curTracesFile);
+ } finally {
+ StrictMode.setThreadPolicy(oldPolicy);
+ }
}
final class H extends Handler {
+ static final int REPORT_TIME_TRACKER_MSG = 1;
+
public H(Looper looper) {
super(looper, null, true);
}
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case REPORT_TIME_TRACKER_MSG: {
+ AppTimeTracker tracker = (AppTimeTracker) msg.obj;
+ tracker.deliverResult(mContext);
+ } break;
+ }
+ }
+ }
+
+ final class UiHandler extends Handler {
+ static final int DISMISS_DIALOG_UI_MSG = 1;
+
+ public UiHandler() {
+ super(com.android.server.UiThread.get().getLooper(), null, true);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case DISMISS_DIALOG_UI_MSG: {
+ final Dialog d = (Dialog) msg.obj;
+ d.dismiss();
+ break;
+ }
+ }
+ }
}
final class LocalService extends ActivityTaskManagerInternal {
@Override
public SleepToken acquireSleepToken(String tag, int displayId) {
Preconditions.checkNotNull(tag);
- return mAm.acquireSleepToken(tag, displayId);
+ return ActivityTaskManagerService.this.acquireSleepToken(tag, displayId);
}
@Override
@@ -3565,7 +4907,7 @@
public void onLocalVoiceInteractionStarted(IBinder activity,
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor) {
synchronized (mGlobalLock) {
- mAm.onLocalVoiceInteractionStartedLocked(activity, voiceSession, voiceInteractor);
+ onLocalVoiceInteractionStartedLocked(activity, voiceSession, voiceInteractor);
}
}
@@ -3657,9 +4999,9 @@
// We might change the visibilities here, so prepare an empty app transition which
// might be overridden later if we actually change visibilities.
final boolean wasTransitionSet =
- mAm.mWindowManager.getPendingAppTransition() != TRANSIT_NONE;
+ mWindowManager.getPendingAppTransition() != TRANSIT_NONE;
if (!wasTransitionSet) {
- mAm.mWindowManager.prepareAppTransition(TRANSIT_NONE,
+ mWindowManager.prepareAppTransition(TRANSIT_NONE,
false /* alwaysKeepCurrent */);
}
mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
@@ -3667,7 +5009,7 @@
// If there was a transition set already we don't want to interfere with it as we
// might be starting it too early.
if (!wasTransitionSet) {
- mAm.mWindowManager.executeAppTransition();
+ mWindowManager.executeAppTransition();
}
}
if (callback != null) {
@@ -3693,7 +5035,7 @@
public void setVr2dDisplayId(int vr2dDisplayId) {
if (DEBUG_STACK) Slog.d(TAG, "setVr2dDisplayId called for: " + vr2dDisplayId);
synchronized (mGlobalLock) {
- mAm.mVr2dDisplayId = vr2dDisplayId;
+ mVr2dDisplayId = vr2dDisplayId;
}
}
@@ -3713,28 +5055,8 @@
}
@Override
- public boolean hasRunningActivity(int uid, @Nullable String packageName) {
- if (packageName == null) return false;
-
- synchronized (mGlobalLock) {
- for (int i = 0; i < mAm.mLruProcesses.size(); i++) {
- final ProcessRecord processRecord = mAm.mLruProcesses.get(i);
- if (processRecord.uid == uid) {
- for (int j = 0; j < processRecord.activities.size(); j++) {
- final ActivityRecord activityRecord = processRecord.activities.get(j);
- if (packageName.equals(activityRecord.packageName)) {
- return true;
- }
- }
- }
- }
- }
- return false;
- }
-
- @Override
public void registerScreenObserver(ScreenObserver observer) {
- mAm.mScreenObservers.add(observer);
+ mScreenObservers.add(observer);
}
@Override
@@ -3754,7 +5076,7 @@
@Override
public void enforceCallerIsRecentsOrHasPermission(String permission, String func) {
- mAm.enforceCallerIsRecentsOrHasPermission(permission, func);
+ ActivityTaskManagerService.this.enforceCallerIsRecentsOrHasPermission(permission, func);
}
@Override
@@ -3763,5 +5085,160 @@
mActiveVoiceInteractionServiceComponent = component;
}
}
+
+ @Override
+ public void setAllowAppSwitches(@NonNull String type, int uid, int userId) {
+ if (!mAmInternal.isUserRunning(userId, ActivityManager.FLAG_OR_STOPPED)) {
+ return;
+ }
+ synchronized (mGlobalLock) {
+ ArrayMap<String, Integer> types = mAllowAppSwitchUids.get(userId);
+ if (types == null) {
+ if (uid < 0) {
+ return;
+ }
+ types = new ArrayMap<>();
+ mAllowAppSwitchUids.put(userId, types);
+ }
+ if (uid < 0) {
+ types.remove(type);
+ } else {
+ types.put(type, uid);
+ }
+ }
+ }
+
+ @Override
+ public void onUserStopped(int userId) {
+ synchronized (mGlobalLock) {
+ getRecentTasks().unloadUserDataFromMemoryLocked(userId);
+ mAllowAppSwitchUids.remove(userId);
+ }
+ }
+
+ @Override
+ public boolean isGetTasksAllowed(String caller, int callingPid, int callingUid) {
+ synchronized (mGlobalLock) {
+ return ActivityTaskManagerService.this.isGetTasksAllowed(
+ caller, callingPid, callingUid);
+ }
+ }
+
+ @Override
+ public void onProcessAdded(WindowProcessController proc) {
+ synchronized (mGlobalLock) {
+ mProcessNames.put(proc.mName, proc.mUid, proc);
+ }
+ }
+
+ @Override
+ public void onProcessRemoved(String name, int uid) {
+ synchronized (mGlobalLock) {
+ mProcessNames.remove(name, uid);
+ }
+ }
+
+ @Override
+ public void onCleanUpApplicationRecord(WindowProcessController proc) {
+ synchronized (mGlobalLock) {
+ if (proc == mHomeProcess) {
+ mHomeProcess = null;
+ }
+ if (proc == mPreviousProcess) {
+ mPreviousProcess = null;
+ }
+ }
+ }
+
+ @Override
+ public int getTopProcessState() {
+ synchronized (mGlobalLock) {
+ return mTopProcessState;
+ }
+ }
+
+ @Override
+ public boolean isSleeping() {
+ synchronized (mGlobalLock) {
+ return isSleepingLocked();
+ }
+ }
+
+ @Override
+ public boolean isShuttingDown() {
+ synchronized (mGlobalLock) {
+ return mShuttingDown;
+ }
+ }
+
+ @Override
+ public boolean shuttingDown(boolean booted, int timeout) {
+ synchronized (mGlobalLock) {
+ mShuttingDown = true;
+ mStackSupervisor.prepareForShutdownLocked();
+ updateEventDispatchingLocked(booted);
+ return mStackSupervisor.shutdownLocked(timeout);
+ }
+ }
+
+ @Override
+ public void enableScreenAfterBoot(boolean booted) {
+ synchronized (mGlobalLock) {
+ EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_ENABLE_SCREEN,
+ SystemClock.uptimeMillis());
+ mWindowManager.enableScreenAfterBoot();
+ updateEventDispatchingLocked(booted);
+ }
+ }
+
+ @Override
+ public boolean showStrictModeViolationDialog() {
+ synchronized (mGlobalLock) {
+ return mShowDialogs && !mSleeping && !mShuttingDown;
+ }
+ }
+
+ @Override
+ public void showSystemReadyErrorDialogsIfNeeded() {
+ synchronized (mGlobalLock) {
+ try {
+ if (AppGlobals.getPackageManager().hasSystemUidErrors()) {
+ Slog.e(TAG, "UIDs on the system are inconsistent, you need to wipe your"
+ + " data partition or your device will be unstable.");
+ mUiHandler.post(() -> {
+ if (mShowDialogs) {
+ AlertDialog d = new BaseErrorDialog(mUiContext);
+ d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ERROR);
+ d.setCancelable(false);
+ d.setTitle(mUiContext.getText(R.string.android_system_label));
+ d.setMessage(mUiContext.getText(R.string.system_error_wipe_data));
+ d.setButton(DialogInterface.BUTTON_POSITIVE,
+ mUiContext.getText(R.string.ok),
+ mUiHandler.obtainMessage(DISMISS_DIALOG_UI_MSG, d));
+ d.show();
+ }
+ });
+ }
+ } catch (RemoteException e) {
+ }
+
+ if (!Build.isBuildConsistent()) {
+ Slog.e(TAG, "Build fingerprint is not consistent, warning user");
+ mUiHandler.post(() -> {
+ if (mShowDialogs) {
+ AlertDialog d = new BaseErrorDialog(mUiContext);
+ d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ERROR);
+ d.setCancelable(false);
+ d.setTitle(mUiContext.getText(R.string.android_system_label));
+ d.setMessage(mUiContext.getText(R.string.system_error_manufacturer));
+ d.setButton(DialogInterface.BUTTON_POSITIVE,
+ mUiContext.getText(R.string.ok),
+ mUiHandler.obtainMessage(DISMISS_DIALOG_UI_MSG, d));
+ d.show();
+ }
+ });
+ }
+ }
+ }
}
}
diff --git a/services/core/java/com/android/server/am/AppErrorDialog.java b/services/core/java/com/android/server/am/AppErrorDialog.java
index 68c63a2..cde633d 100644
--- a/services/core/java/com/android/server/am/AppErrorDialog.java
+++ b/services/core/java/com/android/server/am/AppErrorDialog.java
@@ -92,7 +92,7 @@
attrs.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SYSTEM_ERROR
| WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
getWindow().setAttributes(attrs);
- if (mProc.persistent) {
+ if (mProc.isPersistent()) {
getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ERROR);
}
diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java
index eac3501..27567a7 100644
--- a/services/core/java/com/android/server/am/AppErrors.java
+++ b/services/core/java/com/android/server/am/AppErrors.java
@@ -25,7 +25,6 @@
import android.app.ActivityManager;
import android.app.ActivityOptions;
-import android.app.ActivityThread;
import android.app.AppOpsManager;
import android.app.ApplicationErrorReport;
import android.app.Dialog;
@@ -45,7 +44,6 @@
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.EventLog;
-import android.util.Log;
import android.util.Slog;
import android.util.StatsLog;
import android.util.SparseArray;
@@ -57,7 +55,6 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
-import java.util.HashMap;
import java.util.Set;
import static com.android.server.Watchdog.NATIVE_STACKS_OF_INTEREST;
@@ -314,9 +311,9 @@
}
void killAppAtUserRequestLocked(ProcessRecord app, Dialog fromDialog) {
- app.crashing = false;
+ app.setCrashing(false);
app.crashingReport = null;
- app.notResponding = false;
+ app.setNotResponding(false);
app.notRespondingReport = null;
if (app.anrDialog == fromDialog) {
app.anrDialog = null;
@@ -409,7 +406,7 @@
// If a persistent app is stuck in a crash loop, the device isn't very
// usable, so we want to consider sending out a rescue party.
- if (r != null && r.persistent) {
+ if (r != null && r.isPersistent()) {
RescueParty.notePersistentAppCrash(mContext, r.uid);
}
@@ -493,8 +490,8 @@
long orig = Binder.clearCallingIdentity();
try {
// Kill it with fire!
- mService.mStackSupervisor.handleAppCrashLocked(r);
- if (!r.persistent) {
+ mService.mStackSupervisor.handleAppCrashLocked(r.getWindowProcessController());
+ if (!r.isPersistent()) {
mService.removeProcessLocked(r, false, false, "crash");
mService.mStackSupervisor.resumeFocusedStackTopActivityLocked();
}
@@ -532,7 +529,7 @@
String shortMsg, String longMsg,
String stackTrace, long timeMillis,
int callingPid, int callingUid) {
- if (mService.mController == null) {
+ if (mService.mActivityTaskManager.mController == null) {
return false;
}
@@ -540,7 +537,7 @@
String name = r != null ? r.processName : null;
int pid = r != null ? r.pid : callingPid;
int uid = r != null ? r.info.uid : callingUid;
- if (!mService.mController.appCrashed(name, pid,
+ if (!mService.mActivityTaskManager.mController.appCrashed(name, pid,
shortMsg, longMsg, timeMillis, crashInfo.stackTrace)) {
if ("1".equals(SystemProperties.get(SYSTEM_DEBUGGABLE, "0"))
&& "Native crash".equals(crashInfo.exceptionClassName)) {
@@ -563,7 +560,7 @@
return true;
}
} catch (RemoteException e) {
- mService.mController = null;
+ mService.mActivityTaskManager.mController = null;
Watchdog.getInstance().setActivityController(null);
}
return false;
@@ -571,11 +568,11 @@
private boolean makeAppCrashingLocked(ProcessRecord app,
String shortMsg, String longMsg, String stackTrace, AppErrorDialog.Data data) {
- app.crashing = true;
+ app.setCrashing(true);
app.crashingReport = generateProcessError(app,
ActivityManager.ProcessErrorStateInfo.CRASHED, null, shortMsg, longMsg, stackTrace);
startAppProblemLocked(app);
- app.stopFreezingAllLocked();
+ app.getWindowProcessController().stopFreezingActivities();
return handleAppCrashLocked(app, "force-crash" /*reason*/, shortMsg, longMsg, stackTrace,
data);
}
@@ -643,7 +640,7 @@
return null;
}
- if (!r.crashing && !r.notResponding && !r.forceCrashReport) {
+ if (!r.isCrashing() && !r.isNotResponding() && !r.forceCrashReport) {
return null;
}
@@ -654,10 +651,10 @@
report.time = timeMillis;
report.systemApp = (r.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
- if (r.crashing || r.forceCrashReport) {
+ if (r.isCrashing() || r.forceCrashReport) {
report.type = ApplicationErrorReport.TYPE_CRASH;
report.crashInfo = crashInfo;
- } else if (r.notResponding) {
+ } else if (r.isNotResponding()) {
report.type = ApplicationErrorReport.TYPE_ANR;
report.anrInfo = new ApplicationErrorReport.AnrInfo();
@@ -715,8 +712,8 @@
+ " has crashed too many times: killing!");
EventLog.writeEvent(EventLogTags.AM_PROCESS_CRASHED_TOO_MUCH,
app.userId, app.info.processName, app.uid);
- mService.mStackSupervisor.handleAppCrashLocked(app);
- if (!app.persistent) {
+ mService.mStackSupervisor.handleAppCrashLocked(app.getWindowProcessController());
+ if (!app.isPersistent()) {
// We don't want to start this process again until the user
// explicitly does so... but for persistent process, we really
// need to keep it running. If a persistent process is actually
@@ -744,7 +741,7 @@
mService.mStackSupervisor.resumeFocusedStackTopActivityLocked();
} else {
final TaskRecord affectedTask =
- mService.mStackSupervisor.finishTopCrashedActivitiesLocked(app, reason);
+ mService.mStackSupervisor.finishTopCrashedActivitiesLocked(app.getWindowProcessController(), reason);
if (data != null) {
data.task = affectedTask;
}
@@ -762,21 +759,11 @@
// replaced by a third-party app, clear the package preferred activities from packages
// with a home activity running in the process to prevent a repeatedly crashing app
// from blocking the user to manually clear the list.
- final ArrayList<ActivityRecord> activities = app.activities;
- if (app == mService.mHomeProcess && activities.size() > 0
- && (mService.mHomeProcess.info.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
- for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
- final ActivityRecord r = activities.get(activityNdx);
- if (r.isActivityTypeHome()) {
- Log.i(TAG, "Clearing package preferred activities from " + r.packageName);
- try {
- ActivityThread.getPackageManager()
- .clearPackagePreferredActivities(r.packageName);
- } catch (RemoteException c) {
- // pm is in same process, this will never happen.
- }
- }
- }
+ final WindowProcessController proc = app.getWindowProcessController();
+ final WindowProcessController homeProc = mService.mActivityTaskManager.mHomeProcess;
+ if (proc == homeProc && proc.hasActivities()
+ && (homeProc.mInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
+ proc.clearPackagePreferredForHomeActivities();
}
if (!app.isolated) {
@@ -837,7 +824,8 @@
mService.mUserController.getCurrentUserId()) != 0;
final boolean crashSilenced = mAppsNotReportingCrashes != null &&
mAppsNotReportingCrashes.contains(proc.info.packageName);
- if ((mService.canShowErrorDialogs() || showBackground) && !crashSilenced
+ if ((mService.mActivityTaskManager.canShowErrorDialogs() || showBackground)
+ && !crashSilenced
&& (showFirstCrash || showFirstCrashDevOption || data.repeating)) {
proc.crashDialog = dialogToShow = new AppErrorDialog(mContext, mService, data);
} else {
@@ -887,16 +875,16 @@
ArrayList<Integer> firstPids = new ArrayList<Integer>(5);
SparseArray<Boolean> lastPids = new SparseArray<Boolean>(20);
- if (mService.mController != null) {
+ if (mService.mActivityTaskManager.mController != null) {
try {
// 0 == continue, -1 = kill process immediately
- int res = mService.mController.appEarlyNotResponding(
+ int res = mService.mActivityTaskManager.mController.appEarlyNotResponding(
app.processName, app.pid, annotation);
if (res < 0 && app.pid != MY_PID) {
app.kill("anr", true);
}
} catch (RemoteException e) {
- mService.mController = null;
+ mService.mActivityTaskManager.mController = null;
Watchdog.getInstance().setActivityController(null);
}
}
@@ -914,13 +902,13 @@
synchronized (mService) {
// PowerManager.reboot() can block for a long time, so ignore ANRs while shutting down.
- if (mService.mShuttingDown) {
+ if (mService.mActivityTaskManager.mShuttingDown) {
Slog.i(TAG, "During shutdown skipping ANR: " + app + " " + annotation);
return;
- } else if (app.notResponding) {
+ } else if (app.isNotResponding()) {
Slog.i(TAG, "Skipping duplicate ANR: " + app + " " + annotation);
return;
- } else if (app.crashing) {
+ } else if (app.isCrashing()) {
Slog.i(TAG, "Crashing app skipping ANR: " + app + " " + annotation);
return;
} else if (app.killedByAm) {
@@ -933,7 +921,7 @@
// In case we come through here for the same app before completing
// this one, mark as anring now so we will bail out.
- app.notResponding = true;
+ app.setNotResponding(true);
// Log the ANR to the event log.
EventLog.writeEvent(EventLogTags.AM_ANR, app.userId, app.pid,
@@ -946,8 +934,8 @@
isSilentANR = !showBackground && !isInterestingForBackgroundTraces(app);
if (!isSilentANR) {
int parentPid = app.pid;
- if (parent != null && parent.app != null && parent.app.pid > 0) {
- parentPid = parent.app.pid;
+ if (parent != null && parent.app != null && parent.app.getPid() > 0) {
+ parentPid = parent.app.getPid();
}
if (parentPid != app.pid) firstPids.add(parentPid);
@@ -958,7 +946,7 @@
if (r != null && r.thread != null) {
int pid = r.pid;
if (pid > 0 && pid != app.pid && pid != parentPid && pid != MY_PID) {
- if (r.persistent) {
+ if (r.isPersistent()) {
firstPids.add(pid);
if (DEBUG_ANR) Slog.i(TAG, "Adding persistent proc: " + r);
} else if (r.treatLikeActivity) {
@@ -1054,10 +1042,10 @@
mService.addErrorToDropBox("anr", app, app.processName, activity, parent, annotation,
cpuInfo, tracesFile, null);
- if (mService.mController != null) {
+ if (mService.mActivityTaskManager.mController != null) {
try {
// 0 == show dialog, 1 = keep waiting, -1 = kill process immediately
- int res = mService.mController.appNotResponding(
+ int res = mService.mActivityTaskManager.mController.appNotResponding(
app.processName, app.pid, info.toString());
if (res != 0) {
if (res < 0 && app.pid != MY_PID) {
@@ -1070,7 +1058,7 @@
return;
}
} catch (RemoteException e) {
- mService.mController = null;
+ mService.mActivityTaskManager.mController = null;
Watchdog.getInstance().setActivityController(null);
}
}
@@ -1100,12 +1088,12 @@
private void makeAppNotRespondingLocked(ProcessRecord app,
String activity, String shortMsg, String longMsg) {
- app.notResponding = true;
+ app.setNotResponding(true);
app.notRespondingReport = generateProcessError(app,
ActivityManager.ProcessErrorStateInfo.NOT_RESPONDING,
activity, shortMsg, longMsg, null);
startAppProblemLocked(app);
- app.stopFreezingAllLocked();
+ app.getWindowProcessController().stopFreezingActivities();
}
void handleShowAnrUi(Message msg) {
@@ -1135,7 +1123,7 @@
boolean showBackground = Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.ANR_SHOW_BACKGROUND, 0) != 0;
- if (mService.canShowErrorDialogs() || showBackground) {
+ if (mService.mActivityTaskManager.canShowErrorDialogs() || showBackground) {
dialogToShow = new AppNotRespondingDialog(mService, mContext, data);
proc.anrDialog = dialogToShow;
} else {
diff --git a/services/core/java/com/android/server/am/AppNotRespondingDialog.java b/services/core/java/com/android/server/am/AppNotRespondingDialog.java
index 8a88a69..7c983ff 100644
--- a/services/core/java/com/android/server/am/AppNotRespondingDialog.java
+++ b/services/core/java/com/android/server/am/AppNotRespondingDialog.java
@@ -157,7 +157,7 @@
System.currentTimeMillis(), null);
}
- app.notResponding = false;
+ app.setNotResponding(false);
app.notRespondingReport = null;
if (app.anrDialog == AppNotRespondingDialog.this) {
app.anrDialog = null;
diff --git a/services/core/java/com/android/server/am/AppTaskImpl.java b/services/core/java/com/android/server/am/AppTaskImpl.java
index 953d3b8..a1f1ff9 100644
--- a/services/core/java/com/android/server/am/AppTaskImpl.java
+++ b/services/core/java/com/android/server/am/AppTaskImpl.java
@@ -20,7 +20,6 @@
import static com.android.server.am.ActivityStackSupervisor.REMOVE_FROM_RECENTS;
import android.app.ActivityManager;
-import android.app.ActivityOptions;
import android.app.IAppTask;
import android.app.IApplicationThread;
import android.content.Intent;
@@ -98,7 +97,7 @@
final int callingUid = Binder.getCallingUid();
final long origId = Binder.clearCallingIdentity();
try {
- synchronized (this) {
+ synchronized (mService.mGlobalLock) {
mService.mStackSupervisor.startActivityFromRecents(callingPid, callingUid, mTaskId,
null);
}
diff --git a/services/core/java/com/android/server/am/AssistDataRequester.java b/services/core/java/com/android/server/am/AssistDataRequester.java
index b54441a..395b0da 100644
--- a/services/core/java/com/android/server/am/AssistDataRequester.java
+++ b/services/core/java/com/android/server/am/AssistDataRequester.java
@@ -17,13 +17,12 @@
package com.android.server.am;
import static android.app.ActivityManager.ASSIST_CONTEXT_FULL;
-import static android.app.ActivityTaskManagerInternal.ASSIST_KEY_RECEIVER_EXTRAS;
import static android.app.AppOpsManager.MODE_ALLOWED;
import static android.app.AppOpsManager.OP_NONE;
+import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_RECEIVER_EXTRAS;
import android.app.ActivityTaskManager;
import android.app.AppOpsManager;
-import android.app.IActivityManager;
import android.app.IAssistDataReceiver;
import android.content.Context;
import android.graphics.Bitmap;
@@ -49,7 +48,6 @@
public static final String KEY_RECEIVER_EXTRA_COUNT = "count";
public static final String KEY_RECEIVER_EXTRA_INDEX = "index";
- private IActivityManager mService;
private IWindowManager mWindowManager;
private Context mContext;
private AppOpsManager mAppOpsManager;
@@ -118,14 +116,13 @@
* This can be {@link AppOpsManager#OP_NONE} to indicate that
* screenshots should never be fetched.
*/
- public AssistDataRequester(Context context, IActivityManager service,
+ public AssistDataRequester(Context context,
IWindowManager windowManager, AppOpsManager appOpsManager,
AssistDataRequesterCallbacks callbacks, Object callbacksLock,
int requestStructureAppOps, int requestScreenshotAppOps) {
mCallbacks = callbacks;
mCallbacksLock = callbacksLock;
mWindowManager = windowManager;
- mService = service;
mContext = context;
mAppOpsManager = appOpsManager;
mRequestStructureAppOps = requestStructureAppOps;
diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
index 094cf06..d9a8818 100644
--- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
+++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
@@ -546,14 +546,25 @@
final long idleTimeMs = latest.mControllerIdleTimeMs - lastIdleMs;
final long scanTimeMs = latest.mControllerScanTimeMs - lastScanMs;
- if (txTimeMs < 0 || rxTimeMs < 0 || scanTimeMs < 0) {
+ if (txTimeMs < 0 || rxTimeMs < 0 || scanTimeMs < 0 || idleTimeMs < 0) {
// The stats were reset by the WiFi system (which is why our delta is negative).
- // Returns the unaltered stats.
- delta.mControllerEnergyUsed = latest.mControllerEnergyUsed;
- delta.mControllerRxTimeMs = latest.mControllerRxTimeMs;
- delta.mControllerTxTimeMs = latest.mControllerTxTimeMs;
- delta.mControllerIdleTimeMs = latest.mControllerIdleTimeMs;
- delta.mControllerScanTimeMs = latest.mControllerScanTimeMs;
+ // Returns the unaltered stats. The total on time should not exceed the time
+ // duartion between reports.
+ final long totalOnTimeMs = latest.mControllerTxTimeMs + latest.mControllerRxTimeMs
+ + latest.mControllerIdleTimeMs;
+ if (totalOnTimeMs <= timePeriodMs + MAX_WIFI_STATS_SAMPLE_ERROR_MILLIS) {
+ delta.mControllerEnergyUsed = latest.mControllerEnergyUsed;
+ delta.mControllerRxTimeMs = latest.mControllerRxTimeMs;
+ delta.mControllerTxTimeMs = latest.mControllerTxTimeMs;
+ delta.mControllerIdleTimeMs = latest.mControllerIdleTimeMs;
+ delta.mControllerScanTimeMs = latest.mControllerScanTimeMs;
+ } else {
+ delta.mControllerEnergyUsed = 0;
+ delta.mControllerRxTimeMs = 0;
+ delta.mControllerTxTimeMs = 0;
+ delta.mControllerIdleTimeMs = 0;
+ delta.mControllerScanTimeMs = 0;
+ }
Slog.v(TAG, "WiFi energy data was reset, new WiFi energy data is " + delta);
} else {
final long totalActiveTimeMs = txTimeMs + rxTimeMs;
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index ef23a83..ab9ba08 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -59,6 +59,7 @@
import com.android.internal.os.PowerProfile;
import com.android.internal.os.RpmStats;
import com.android.internal.util.DumpUtils;
+import com.android.internal.util.ParseUtils;
import com.android.server.LocalServices;
import java.io.File;
@@ -395,6 +396,7 @@
mStats.writeToParcel(out, 0);
}
byte[] data = out.marshall();
+ if (DBG) Slog.d(TAG, "getStatisticsStream parcel size is:" + data.length);
out.recycle();
try {
return ParcelFileDescriptor.fromData(data, "battery-stats");
@@ -1222,6 +1224,7 @@
pw.println(" --proto: write the current aggregate stats (without history) in proto format.");
pw.println(" --history: show only history data.");
pw.println(" --history-start <num>: show only history data starting at given time offset.");
+ pw.println(" --history-create-events <num>: create <num> of battery history events.");
pw.println(" --charged: only output data since last charged.");
pw.println(" --daily: only output full daily data.");
pw.println(" --reset: reset the stats, clearing all current data.");
@@ -1310,8 +1313,21 @@
dumpHelp(pw);
return;
}
- historyStart = Long.parseLong(args[i]);
+ historyStart = ParseUtils.parseLong(args[i], 0);
writeData = true;
+ } else if ("--history-create-events".equals(arg)) {
+ i++;
+ if (i >= args.length) {
+ pw.println("Missing events argument for --history-create-events");
+ dumpHelp(pw);
+ return;
+ }
+ final long events = ParseUtils.parseLong(args[i], 0);
+ synchronized (mStats) {
+ mStats.createFakeHistoryEvents(events);
+ pw.println("Battery history create events started.");
+ noOutput = true;
+ }
} else if ("-c".equals(arg)) {
useCheckinFormat = true;
flags |= BatteryStats.DUMP_INCLUDE_HISTORY;
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index c9a26cb..a9fd51d 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -304,7 +304,7 @@
app.thread.scheduleReceiver(new Intent(r.intent), r.curReceiver,
mService.compatibilityInfoForPackageLocked(r.curReceiver.applicationInfo),
r.resultCode, r.resultData, r.resultExtras, r.ordered, r.userId,
- app.repProcState);
+ app.getReportedProcState());
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
"Process cur broadcast " + r + " DELIVERED for app " + app);
started = true;
@@ -492,7 +492,7 @@
// correctly ordered with other one-way calls.
try {
app.thread.scheduleRegisteredReceiver(receiver, intent, resultCode,
- data, extras, ordered, sticky, sendingUser, app.repProcState);
+ data, extras, ordered, sticky, sendingUser, app.getReportedProcState());
// TODO: Uncomment this when (b/28322359) is fixed and we aren't getting
// DeadObjectException when the process isn't actually dead.
//} catch (DeadObjectException ex) {
@@ -618,7 +618,7 @@
}
if (!skip && (filter.receiverList.app == null || filter.receiverList.app.killed
- || filter.receiverList.app.crashing)) {
+ || filter.receiverList.app.isCrashing())) {
Slog.w(TAG, "Skipping deliver [" + mQueueName + "] " + r
+ " to " + filter.receiverList + ": process gone or crashing");
skip = true;
@@ -885,7 +885,7 @@
synchronized (mService.mPidsSelfLocked) {
ProcessRecord proc = mService.mPidsSelfLocked.get(
mPendingBroadcast.curApp.pid);
- isDead = proc == null || proc.crashing;
+ isDead = proc == null || proc.isCrashing();
}
} else {
final ProcessRecord proc = mService.mProcessNames.get(
@@ -1210,7 +1210,7 @@
+ " (uid " + r.callingUid + ")");
skip = true;
}
- if (r.curApp != null && r.curApp.crashing) {
+ if (r.curApp != null && r.curApp.isCrashing()) {
// If the target process is crashing, just skip it.
Slog.w(TAG, "Skipping deliver ordered [" + mQueueName + "] " + r
+ " to " + r.curApp + ": process crashing");
diff --git a/services/core/java/com/android/server/am/CompatModePackages.java b/services/core/java/com/android/server/am/CompatModePackages.java
index c6947ab..77efbfc 100644
--- a/services/core/java/com/android/server/am/CompatModePackages.java
+++ b/services/core/java/com/android/server/am/CompatModePackages.java
@@ -219,25 +219,10 @@
: ActivityManager.COMPAT_MODE_DISABLED;
}
- public boolean getFrontActivityAskCompatModeLocked() {
- ActivityRecord r = mService.getFocusedStack().topRunningActivityLocked();
- if (r == null) {
- return false;
- }
- return getPackageAskCompatModeLocked(r.packageName);
- }
-
public boolean getPackageAskCompatModeLocked(String packageName) {
return (getPackageFlags(packageName)&COMPAT_FLAG_DONT_ASK) == 0;
}
- public void setFrontActivityAskCompatModeLocked(boolean ask) {
- ActivityRecord r = mService.getFocusedStack().topRunningActivityLocked();
- if (r != null) {
- setPackageAskCompatModeLocked(r.packageName, ask);
- }
- }
-
public void setPackageAskCompatModeLocked(String packageName, boolean ask) {
setPackageFlagLocked(packageName, COMPAT_FLAG_DONT_ASK, ask);
}
@@ -255,23 +240,6 @@
}
}
- public int getFrontActivityScreenCompatModeLocked() {
- ActivityRecord r = mService.getFocusedStack().topRunningActivityLocked();
- if (r == null) {
- return ActivityManager.COMPAT_MODE_UNKNOWN;
- }
- return computeCompatModeLocked(r.info.applicationInfo);
- }
-
- public void setFrontActivityScreenCompatModeLocked(int mode) {
- ActivityRecord r = mService.getFocusedStack().topRunningActivityLocked();
- if (r == null) {
- Slog.w(TAG, "setFrontActivityScreenCompatMode failed: no top activity");
- return;
- }
- setPackageScreenCompatModeLocked(r.info.applicationInfo, mode);
- }
-
public int getPackageScreenCompatModeLocked(String packageName) {
ApplicationInfo ai = null;
try {
@@ -297,7 +265,7 @@
setPackageScreenCompatModeLocked(ai, mode);
}
- private void setPackageScreenCompatModeLocked(ApplicationInfo ai, int mode) {
+ void setPackageScreenCompatModeLocked(ApplicationInfo ai, int mode) {
final String packageName = ai.packageName;
int curFlags = getPackageFlags(packageName);
@@ -349,7 +317,7 @@
scheduleWrite();
- final ActivityStack stack = mService.getFocusedStack();
+ final ActivityStack stack = mService.mActivityTaskManager.getTopDisplayFocusedStack();
ActivityRecord starting = stack.restartPackage(packageName);
// Tell all processes that loaded this package about the change.
diff --git a/services/core/java/com/android/server/am/ConnectionRecord.java b/services/core/java/com/android/server/am/ConnectionRecord.java
index 679024ee..fa8e6c4 100644
--- a/services/core/java/com/android/server/am/ConnectionRecord.java
+++ b/services/core/java/com/android/server/am/ConnectionRecord.java
@@ -16,12 +16,18 @@
package com.android.server.am;
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
+
import android.app.IServiceConnection;
import android.app.PendingIntent;
import android.content.Context;
+import android.util.Slog;
import android.util.proto.ProtoOutputStream;
import android.util.proto.ProtoUtils;
+import com.android.internal.app.procstats.AssociationState;
+import com.android.internal.app.procstats.ProcessStats;
+
import java.io.PrintWriter;
/**
@@ -34,6 +40,9 @@
final int flags; // Binding options.
final int clientLabel; // String resource labeling this client.
final PendingIntent clientIntent; // How to launch the client.
+ final int clientUid; // The identity of this connection's client
+ final String clientProcessName; // The source process of this connection's client
+ public AssociationState.SourceState association; // Association tracking
String stringName; // Caching of toString.
boolean serviceDead; // Well is it?
@@ -83,14 +92,54 @@
}
ConnectionRecord(AppBindRecord _binding, ActivityRecord _activity,
- IServiceConnection _conn, int _flags,
- int _clientLabel, PendingIntent _clientIntent) {
+ IServiceConnection _conn, int _flags,
+ int _clientLabel, PendingIntent _clientIntent,
+ int _clientUid, String _clientProcessName) {
binding = _binding;
activity = _activity;
conn = _conn;
flags = _flags;
clientLabel = _clientLabel;
clientIntent = _clientIntent;
+ clientUid = _clientUid;
+ clientProcessName = _clientProcessName;
+ }
+
+ public void startAssociationIfNeeded() {
+ // If we don't already have an active association, create one... but only if this
+ // is an association between two different processes.
+ if (ActivityManagerService.TRACK_PROCSTATS_ASSOCIATIONS
+ && association == null && binding.service.app != null
+ && (binding.service.appInfo.uid != clientUid
+ || !binding.service.processName.equals(clientProcessName))) {
+ ProcessStats.ProcessStateHolder holder = binding.service.app.pkgList.get(
+ binding.service.name.getPackageName());
+ if (holder == null) {
+ Slog.wtf(TAG_AM, "No package in referenced service "
+ + binding.service.name.toShortString() + ": proc=" + binding.service.app);
+ } else if (holder.pkg == null) {
+ Slog.wtf(TAG_AM, "Inactive holder in referenced service "
+ + binding.service.name.toShortString() + ": proc=" + binding.service.app);
+ } else {
+ association = holder.pkg.getAssociationStateLocked(holder.state,
+ binding.service.name.getClassName()).startSource(clientUid,
+ clientProcessName);
+
+ }
+ }
+ }
+
+ public void trackProcState(int procState, int seq, long now) {
+ if (association != null) {
+ association.trackProcState(procState, seq, now);
+ }
+ }
+
+ public void stopAssociation() {
+ if (association != null) {
+ association.stop();
+ association = null;
+ }
}
public String toString() {
diff --git a/services/core/java/com/android/server/am/ContentProviderConnection.java b/services/core/java/com/android/server/am/ContentProviderConnection.java
index f2c9e2f..f2d4f73 100644
--- a/services/core/java/com/android/server/am/ContentProviderConnection.java
+++ b/services/core/java/com/android/server/am/ContentProviderConnection.java
@@ -18,14 +18,21 @@
import android.os.Binder;
import android.os.SystemClock;
+import android.util.Slog;
import android.util.TimeUtils;
+import com.android.internal.app.procstats.AssociationState;
+import com.android.internal.app.procstats.ProcessStats;
+
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
+
/**
* Represents a link between a content provider and client.
*/
public final class ContentProviderConnection extends Binder {
public final ContentProviderRecord provider;
public final ProcessRecord client;
+ public AssociationState.SourceState association;
public final long createTime;
public int stableCount;
public int unstableCount;
@@ -45,6 +52,42 @@
createTime = SystemClock.elapsedRealtime();
}
+ public void startAssociationIfNeeded() {
+ // If we don't already have an active association, create one... but only if this
+ // is an association between two different processes.
+ if (ActivityManagerService.TRACK_PROCSTATS_ASSOCIATIONS
+ && association == null && provider.proc != null
+ && (provider.appInfo.uid != client.uid
+ || !provider.info.processName.equals(client.processName))) {
+ ProcessStats.ProcessStateHolder holder = provider.proc.pkgList.get(
+ provider.name.getPackageName());
+ if (holder == null) {
+ Slog.wtf(TAG_AM, "No package in referenced provider "
+ + provider.name.toShortString() + ": proc=" + provider.proc);
+ } else if (holder.pkg == null) {
+ Slog.wtf(TAG_AM, "Inactive holder in referenced provider "
+ + provider.name.toShortString() + ": proc=" + provider.proc);
+ } else {
+ association = holder.pkg.getAssociationStateLocked(holder.state,
+ provider.name.getClassName()).startSource(client.uid, client.processName);
+
+ }
+ }
+ }
+
+ public void trackProcState(int procState, int seq, long now) {
+ if (association != null) {
+ association.trackProcState(procState, seq, now);
+ }
+ }
+
+ public void stopAssociation() {
+ if (association != null) {
+ association.stop();
+ association = null;
+ }
+ }
+
public String toString() {
StringBuilder sb = new StringBuilder(128);
sb.append("ContentProviderConnection{");
diff --git a/services/core/java/com/android/server/am/ContentProviderRecord.java b/services/core/java/com/android/server/am/ContentProviderRecord.java
index cd39bcd..2fc4adc 100644
--- a/services/core/java/com/android/server/am/ContentProviderRecord.java
+++ b/services/core/java/com/android/server/am/ContentProviderRecord.java
@@ -16,6 +16,8 @@
package com.android.server.am;
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
+
import android.app.ContentProviderHolder;
import android.content.ComponentName;
import android.content.IContentProvider;
@@ -26,11 +28,14 @@
import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.util.ArrayMap;
import android.util.Slog;
+import com.android.internal.app.procstats.AssociationState;
+import com.android.internal.app.procstats.ProcessStats;
+
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.HashMap;
final class ContentProviderRecord implements ComponentName.WithComponentName {
final ActivityManagerService service;
@@ -46,7 +51,7 @@
= new ArrayList<ContentProviderConnection>();
//final HashSet<ProcessRecord> clients = new HashSet<ProcessRecord>();
// Handles for non-framework processes supported by this provider
- HashMap<IBinder, ExternalProcessHandle> externalProcessTokenToHandle;
+ ArrayMap<IBinder, ExternalProcessHandle> externalProcessTokenToHandle;
// Count for external process for which we have no handles.
int externalProcessNoHandleCount;
ProcessRecord proc; // if non-null, hosting process.
@@ -62,7 +67,8 @@
appInfo = ai;
name = _name;
singleton = _singleton;
- noReleaseNeeded = uid == 0 || uid == Process.SYSTEM_UID;
+ noReleaseNeeded = (uid == 0 || uid == Process.SYSTEM_UID)
+ && (_name == null || !"com.android.settings".equals(_name.getPackageName()));
}
public ContentProviderRecord(ContentProviderRecord cpr) {
@@ -83,22 +89,47 @@
return holder;
}
+ public void setProcess(ProcessRecord proc) {
+ this.proc = proc;
+ if (ActivityManagerService.TRACK_PROCSTATS_ASSOCIATIONS) {
+ for (int iconn = connections.size() - 1; iconn >= 0; iconn--) {
+ final ContentProviderConnection conn = connections.get(iconn);
+ if (proc != null) {
+ conn.startAssociationIfNeeded();
+ } else {
+ conn.stopAssociation();
+ }
+ }
+ if (externalProcessTokenToHandle != null) {
+ for (int iext = externalProcessTokenToHandle.size() - 1; iext >= 0; iext--) {
+ final ExternalProcessHandle handle = externalProcessTokenToHandle.valueAt(iext);
+ if (proc != null) {
+ handle.startAssociationIfNeeded(this);
+ } else {
+ handle.stopAssociation();
+ }
+ }
+ }
+ }
+ }
+
public boolean canRunHere(ProcessRecord app) {
return (info.multiprocess || info.processName.equals(app.processName))
&& uid == app.info.uid;
}
- public void addExternalProcessHandleLocked(IBinder token) {
+ public void addExternalProcessHandleLocked(IBinder token, int callingUid, String callingTag) {
if (token == null) {
externalProcessNoHandleCount++;
} else {
if (externalProcessTokenToHandle == null) {
- externalProcessTokenToHandle = new HashMap<IBinder, ExternalProcessHandle>();
+ externalProcessTokenToHandle = new ArrayMap<>();
}
ExternalProcessHandle handle = externalProcessTokenToHandle.get(token);
if (handle == null) {
- handle = new ExternalProcessHandle(token);
+ handle = new ExternalProcessHandle(token, callingUid, callingTag);
externalProcessTokenToHandle.put(token, handle);
+ handle.startAssociationIfNeeded(this);
}
handle.mAcquisitionCount++;
}
@@ -129,6 +160,7 @@
private void removeExternalProcessHandleInternalLocked(IBinder token) {
ExternalProcessHandle handle = externalProcessTokenToHandle.get(token);
handle.unlinkFromOwnDeathLocked();
+ handle.stopAssociation();
externalProcessTokenToHandle.remove(token);
if (externalProcessTokenToHandle.size() == 0) {
externalProcessTokenToHandle = null;
@@ -234,11 +266,16 @@
private class ExternalProcessHandle implements DeathRecipient {
private static final String LOG_TAG = "ExternalProcessHanldle";
- private final IBinder mToken;
- private int mAcquisitionCount;
+ final IBinder mToken;
+ final int mOwningUid;
+ final String mOwningProcessName;
+ int mAcquisitionCount;
+ AssociationState.SourceState mAssociation;
- public ExternalProcessHandle(IBinder token) {
+ public ExternalProcessHandle(IBinder token, int owningUid, String owningProcessName) {
mToken = token;
+ mOwningUid = owningUid;
+ mOwningProcessName = owningProcessName;
try {
token.linkToDeath(this, 0);
} catch (RemoteException re) {
@@ -250,6 +287,37 @@
mToken.unlinkToDeath(this, 0);
}
+ public void startAssociationIfNeeded(ContentProviderRecord provider) {
+ // If we don't already have an active association, create one... but only if this
+ // is an association between two different processes.
+ if (ActivityManagerService.TRACK_PROCSTATS_ASSOCIATIONS
+ && mAssociation == null && provider.proc != null
+ && (provider.appInfo.uid != mOwningUid
+ || !provider.info.processName.equals(mOwningProcessName))) {
+ ProcessStats.ProcessStateHolder holder = provider.proc.pkgList.get(
+ provider.name.getPackageName());
+ if (holder == null) {
+ Slog.wtf(TAG_AM, "No package in referenced provider "
+ + provider.name.toShortString() + ": proc=" + provider.proc);
+ } else if (holder.pkg == null) {
+ Slog.wtf(TAG_AM, "Inactive holder in referenced provider "
+ + provider.name.toShortString() + ": proc=" + provider.proc);
+ } else {
+ mAssociation = holder.pkg.getAssociationStateLocked(holder.state,
+ provider.name.getClassName()).startSource(mOwningUid,
+ mOwningProcessName);
+
+ }
+ }
+ }
+
+ public void stopAssociation() {
+ if (mAssociation != null) {
+ mAssociation.stop();
+ mAssociation = null;
+ }
+ }
+
@Override
public void binderDied() {
synchronized (service) {
diff --git a/services/core/java/com/android/server/am/KeyguardController.java b/services/core/java/com/android/server/am/KeyguardController.java
index fb55a47..e345b4d 100644
--- a/services/core/java/com/android/server/am/KeyguardController.java
+++ b/services/core/java/com/android/server/am/KeyguardController.java
@@ -19,6 +19,13 @@
import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
+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 static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_NO_WINDOW_ANIMATIONS;
import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_TO_SHADE;
import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_WITH_WALLPAPER;
@@ -27,15 +34,7 @@
import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
import static com.android.server.am.KeyguardControllerProto.KEYGUARD_OCCLUDED;
import static com.android.server.am.KeyguardControllerProto.KEYGUARD_SHOWING;
-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.ActivityTaskManagerInternal.SleepToken;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.Trace;
@@ -44,6 +43,7 @@
import com.android.internal.policy.IKeyguardDismissCallback;
import com.android.server.policy.WindowManagerPolicy;
+import com.android.server.wm.ActivityTaskManagerInternal.SleepToken;
import com.android.server.wm.WindowManagerService;
import java.io.PrintWriter;
@@ -58,7 +58,7 @@
private static final String TAG = TAG_WITH_CLASS_NAME ? "KeyguardController" : TAG_AM;
- private final ActivityManagerService mService;
+ private final ActivityTaskManagerService mService;
private final ActivityStackSupervisor mStackSupervisor;
private WindowManagerService mWindowManager;
private boolean mKeyguardShowing;
@@ -72,7 +72,7 @@
private SleepToken mSleepToken;
private int mSecondaryDisplayShowing = INVALID_DISPLAY;
- KeyguardController(ActivityManagerService service,
+ KeyguardController(ActivityTaskManagerService service,
ActivityStackSupervisor stackSupervisor) {
mService = service;
mStackSupervisor = stackSupervisor;
@@ -272,7 +272,7 @@
// Only the top activity of the focused stack on the default display may control
// occluded state.
if (display.mDisplayId == DEFAULT_DISPLAY
- && mStackSupervisor.isFocusedStack(stack)) {
+ && mStackSupervisor.isTopDisplayFocusedStack(stack)) {
// A dismissing activity occludes Keyguard in the insecure case for legacy
// reasons.
@@ -381,7 +381,7 @@
return;
}
mStackSupervisor.moveTasksToFullscreenStackLocked(stack,
- mStackSupervisor.mFocusedStack == stack);
+ stack.isFocusedStackOnDisplay());
}
}
diff --git a/services/core/java/com/android/server/am/LaunchParamsController.java b/services/core/java/com/android/server/am/LaunchParamsController.java
index 4330d2c..6415c3e 100644
--- a/services/core/java/com/android/server/am/LaunchParamsController.java
+++ b/services/core/java/com/android/server/am/LaunchParamsController.java
@@ -38,7 +38,7 @@
* registered {@link LaunchParamsModifier}s.
*/
class LaunchParamsController {
- private final ActivityManagerService mService;
+ private final ActivityTaskManagerService mService;
private final List<LaunchParamsModifier> mModifiers = new ArrayList<>();
// Temporary {@link LaunchParams} for internal calculations. This is kept separate from
@@ -48,7 +48,7 @@
private final LaunchParams mTmpCurrent = new LaunchParams();
private final LaunchParams mTmpResult = new LaunchParams();
- LaunchParamsController(ActivityManagerService service) {
+ LaunchParamsController(ActivityTaskManagerService service) {
mService = service;
}
@@ -125,7 +125,7 @@
try {
if (mTmpParams.hasPreferredDisplay()
&& mTmpParams.mPreferredDisplayId != task.getStack().getDisplay().mDisplayId) {
- mService.mActivityTaskManager.moveStackToDisplay(task.getStackId(), mTmpParams.mPreferredDisplayId);
+ mService.moveStackToDisplay(task.getStackId(), mTmpParams.mPreferredDisplayId);
}
if (mTmpParams.hasWindowingMode()
diff --git a/services/core/java/com/android/server/am/NativeCrashListener.java b/services/core/java/com/android/server/am/NativeCrashListener.java
index 9348023..6e42aee 100644
--- a/services/core/java/com/android/server/am/NativeCrashListener.java
+++ b/services/core/java/com/android/server/am/NativeCrashListener.java
@@ -29,7 +29,6 @@
import java.io.File;
import java.io.FileDescriptor;
import java.io.InterruptedIOException;
-import java.net.InetSocketAddress;
/**
* Set up a Unix domain socket that debuggerd will connect() to in
@@ -226,7 +225,7 @@
}
if (pr != null) {
// Don't attempt crash reporting for persistent apps
- if (pr.persistent) {
+ if (pr.isPersistent()) {
if (DEBUG) {
Slog.v(TAG, "Skipping report for persistent app " + pr);
}
@@ -260,7 +259,7 @@
// even though the process will vanish as soon as we let
// debuggerd proceed.
synchronized (mAm) {
- pr.crashing = true;
+ pr.setCrashing(true);
pr.forceCrashReport = true;
}
diff --git a/services/core/java/com/android/server/am/OomAdjProfiler.java b/services/core/java/com/android/server/am/OomAdjProfiler.java
new file mode 100644
index 0000000..6230e0d
--- /dev/null
+++ b/services/core/java/com/android/server/am/OomAdjProfiler.java
@@ -0,0 +1,185 @@
+/*
+ * 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.am;
+
+import android.os.PowerManagerInternal;
+import android.os.Process;
+import android.os.SystemClock;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.os.BackgroundThread;
+import com.android.internal.os.ProcessCpuTracker;
+import com.android.internal.util.RingBuffer;
+import com.android.internal.util.function.pooled.PooledLambda;
+
+import java.io.PrintWriter;
+
+public class OomAdjProfiler {
+ @GuardedBy("this")
+ private boolean mOnBattery;
+ @GuardedBy("this")
+ private boolean mScreenOff;
+
+ @GuardedBy("this")
+ private long mOomAdjStartTimeMs;
+ @GuardedBy("this")
+ private boolean mOomAdjStarted;
+
+ @GuardedBy("this")
+ private CpuTimes mOomAdjRunTime = new CpuTimes();
+ @GuardedBy("this")
+ private CpuTimes mSystemServerCpuTime = new CpuTimes();
+
+ @GuardedBy("this")
+ private long mLastSystemServerCpuTimeMs;
+ @GuardedBy("this")
+ private boolean mSystemServerCpuTimeUpdateScheduled;
+ private final ProcessCpuTracker mProcessCpuTracker = new ProcessCpuTracker(false);
+
+ @GuardedBy("this")
+ final RingBuffer<CpuTimes> mOomAdjRunTimesHist = new RingBuffer<>(CpuTimes.class, 10);
+ @GuardedBy("this")
+ final RingBuffer<CpuTimes> mSystemServerCpuTimesHist = new RingBuffer<>(CpuTimes.class, 10);
+
+ void batteryPowerChanged(boolean onBattery) {
+ synchronized (this) {
+ scheduleSystemServerCpuTimeUpdate();
+ mOnBattery = onBattery;
+ }
+ }
+
+ void onWakefulnessChanged(int wakefulness) {
+ synchronized (this) {
+ scheduleSystemServerCpuTimeUpdate();
+ mScreenOff = wakefulness != PowerManagerInternal.WAKEFULNESS_AWAKE;
+ }
+ }
+
+ void oomAdjStarted() {
+ synchronized (this) {
+ mOomAdjStartTimeMs = SystemClock.currentThreadTimeMillis();
+ mOomAdjStarted = true;
+ }
+ }
+
+ void oomAdjEnded() {
+ synchronized (this) {
+ if (!mOomAdjStarted) {
+ return;
+ }
+ mOomAdjRunTime.addCpuTimeMs(SystemClock.currentThreadTimeMillis() - mOomAdjStartTimeMs);
+ }
+ }
+
+ private void scheduleSystemServerCpuTimeUpdate() {
+ synchronized (this) {
+ if (mSystemServerCpuTimeUpdateScheduled) {
+ return;
+ }
+ mSystemServerCpuTimeUpdateScheduled = true;
+ BackgroundThread.getHandler().post(PooledLambda.obtainRunnable(
+ OomAdjProfiler::updateSystemServerCpuTime,
+ this, mOnBattery, mScreenOff).recycleOnUse());
+ }
+ }
+
+ private void updateSystemServerCpuTime(boolean onBattery, boolean screenOff) {
+ final long cpuTimeMs = mProcessCpuTracker.getCpuTimeForPid(Process.myPid());
+ synchronized (this) {
+ mSystemServerCpuTime.addCpuTimeMs(
+ cpuTimeMs - mLastSystemServerCpuTimeMs, onBattery, screenOff);
+ mLastSystemServerCpuTimeMs = cpuTimeMs;
+ mSystemServerCpuTimeUpdateScheduled = false;
+ notifyAll();
+ }
+ }
+
+ void reset() {
+ synchronized (this) {
+ if (mSystemServerCpuTime.isEmpty()) {
+ return;
+ }
+ mOomAdjRunTimesHist.append(mOomAdjRunTime);
+ mSystemServerCpuTimesHist.append(mSystemServerCpuTime);
+ mOomAdjRunTime = new CpuTimes();
+ mSystemServerCpuTime = new CpuTimes();
+ }
+ }
+
+ void dump(PrintWriter pw) {
+ synchronized (this) {
+ if (mSystemServerCpuTimeUpdateScheduled) {
+ while (mSystemServerCpuTimeUpdateScheduled) {
+ try {
+ wait();
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+ }
+ } else {
+ updateSystemServerCpuTime(mOnBattery, mScreenOff);
+ }
+
+ pw.println("System server and oomAdj runtimes (ms) in recent battery sessions "
+ + "(most recent first):");
+ if (!mSystemServerCpuTime.isEmpty()) {
+ pw.print(" ");
+ pw.print("system_server=");
+ pw.print(mSystemServerCpuTime);
+ pw.print(" ");
+ pw.print("oom_adj=");
+ pw.println(mOomAdjRunTime);
+ }
+ final CpuTimes[] systemServerCpuTimes = mSystemServerCpuTimesHist.toArray();
+ final CpuTimes[] oomAdjRunTimes = mOomAdjRunTimesHist.toArray();
+ for (int i = oomAdjRunTimes.length - 1; i >= 0; --i) {
+ pw.print(" ");
+ pw.print("system_server=");
+ pw.print(systemServerCpuTimes[i]);
+ pw.print(" ");
+ pw.print("oom_adj=");
+ pw.println(oomAdjRunTimes[i]);
+ }
+ }
+ }
+
+ private class CpuTimes {
+ private long mOnBatteryTimeMs;
+ private long mOnBatteryScreenOffTimeMs;
+
+ public void addCpuTimeMs(long cpuTimeMs) {
+ addCpuTimeMs(cpuTimeMs, mOnBattery, mScreenOff);
+ }
+
+ public void addCpuTimeMs(long cpuTimeMs, boolean onBattery, boolean screenOff) {
+ if (onBattery) {
+ mOnBatteryTimeMs += cpuTimeMs;
+ if (screenOff) {
+ mOnBatteryScreenOffTimeMs += cpuTimeMs;
+ }
+ }
+ }
+
+ public boolean isEmpty() {
+ return mOnBatteryTimeMs == 0 && mOnBatteryScreenOffTimeMs == 0;
+ }
+
+ public String toString() {
+ return "[" + mOnBatteryTimeMs + "," + mOnBatteryScreenOffTimeMs + "]";
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/am/ProcessMemInfo.java b/services/core/java/com/android/server/am/ProcessMemInfo.java
index 83d29e2..6c10a2a 100644
--- a/services/core/java/com/android/server/am/ProcessMemInfo.java
+++ b/services/core/java/com/android/server/am/ProcessMemInfo.java
@@ -24,6 +24,7 @@
final String adjType;
final String adjReason;
long pss;
+ long swapPss;
long memtrack;
public ProcessMemInfo(String _name, int _pid, int _oomAdj, int _procState,
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index caf52e3..ea6d134 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -54,7 +54,7 @@
* Full information about a particular process that
* is currently running.
*/
-final class ProcessRecord {
+final class ProcessRecord implements WindowProcessListener {
private static final String TAG = TAG_WITH_CLASS_NAME ? "ProcessRecord" : TAG_AM;
private final ActivityManagerService mService; // where we came from
@@ -65,7 +65,41 @@
final int userId; // user of process.
final String processName; // name of the process
// List of packages running in the process
- final ArrayMap<String, ProcessStats.ProcessStateHolder> pkgList = new ArrayMap<>();
+ final PackageList pkgList = new PackageList();
+ final class PackageList {
+ final ArrayMap<String, ProcessStats.ProcessStateHolder> mPkgList = new ArrayMap<>();
+
+ ProcessStats.ProcessStateHolder put(String key, ProcessStats.ProcessStateHolder value) {
+ mWindowProcessController.addPackage(key);
+ return mPkgList.put(key, value);
+ }
+
+ void clear() {
+ mPkgList.clear();
+ mWindowProcessController.clearPackageList();
+ }
+
+ int size() {
+ return mPkgList.size();
+ }
+
+ String keyAt(int index) {
+ return mPkgList.keyAt(index);
+ }
+
+ public ProcessStats.ProcessStateHolder valueAt(int index) {
+ return mPkgList.valueAt(index);
+ }
+
+ ProcessStats.ProcessStateHolder get(String pkgName) {
+ return mPkgList.get(pkgName);
+ }
+
+ boolean containsKey(Object key) {
+ return mPkgList.containsKey(key);
+ }
+ }
+
final ProcessList.ProcStateMemTracker procStateMemTracker
= new ProcessList.ProcStateMemTracker();
UidRecord uidRecord; // overall state of process's uid.
@@ -78,7 +112,7 @@
int pid; // The process of this application; 0 if none
String procStatFile; // path to /proc/<pid>/stat
int[] gids; // The gids this process was launched with
- String requiredAbi; // The ABI this process was launched with
+ private String mRequiredAbi;// The ABI this process was launched with
String instructionSet; // The instruction set this process was launched with
boolean starting; // True if the process is being started
long lastActivityTime; // For managing the LRU list
@@ -96,12 +130,11 @@
int curAdj; // Current OOM adjustment for this process
int setAdj; // Last set OOM adjustment for this process
int verifiedAdj; // The last adjustment that was verified as actually being set
- int curSchedGroup; // Currently desired scheduling class
+ private int mCurSchedGroup; // Currently desired scheduling class
int setSchedGroup; // Last set to background scheduling class
- int vrThreadTid; // Thread currently set for VR scheduling
int trimMemoryLevel; // Last selected memory trimming level
int curProcState = PROCESS_STATE_NONEXISTENT; // Currently computed process state
- int repProcState = PROCESS_STATE_NONEXISTENT; // Last reported process state
+ private int mRepProcState = PROCESS_STATE_NONEXISTENT; // Last reported process state
int setProcState = PROCESS_STATE_NONEXISTENT; // Last set process state in process tracker
int pssProcState = PROCESS_STATE_NONEXISTENT; // Currently requesting pss for
int pssStatType; // The type of stat collection that we are currently requesting
@@ -112,7 +145,7 @@
boolean notCachedSinceIdle; // Has this process not been in a cached state since last idle?
boolean hasClientActivities; // Are there any client services with activities?
boolean hasStartedServices; // Are there any started services running in this process?
- boolean foregroundServices; // Running any services that are foreground?
+ private boolean mHasForegroundServices; // Running any services that are foreground?
boolean foregroundActivities; // Running any activities that are foreground?
boolean repForegroundActivities; // Last reported foreground activities.
boolean systemNoUi; // This is a system process, but not currently showing UI.
@@ -173,10 +206,8 @@
Object adjTarget; // Debugging: target component impacting oom_adj.
Runnable crashHandler; // Optional local handler to be invoked in the process crash.
- // all activities running in the process
- final ArrayList<ActivityRecord> activities = new ArrayList<>();
- // any tasks this process had run root activities in
- final ArrayList<TaskRecord> recentTasks = new ArrayList<>();
+ // Controller for driving the process state on the window manager side.
+ final private WindowProcessController mWindowProcessController;
// all ServiceRecord running in this process
final ArraySet<ServiceRecord> services = new ArraySet<>();
// services that are currently executing code (need to remain foreground).
@@ -194,11 +225,11 @@
String[] isolatedEntryPointArgs; // Arguments to pass to isolatedEntryPoint's main().
boolean execServicesFg; // do we need to be executing services in the foreground?
- boolean persistent; // always keep this application running?
- boolean crashing; // are we in the process of crashing?
+ private boolean mPersistent;// always keep this application running?
+ private boolean mCrashing; // are we in the process of crashing?
Dialog crashDialog; // dialog being displayed due to crash.
boolean forceCrashReport; // suppress normal auto-dismiss of crash dialog & report UI?
- boolean notResponding; // does the app have a not responding dialog?
+ private boolean mNotResponding; // does the app have a not responding dialog?
Dialog anrDialog; // dialog being displayed due to app not resp.
boolean removed; // has app package been removed from device?
boolean debugging; // was app launched for debugging?
@@ -259,7 +290,7 @@
}
}
pw.println("}");
- pw.print(prefix); pw.print("requiredAbi="); pw.print(requiredAbi);
+ pw.print(prefix); pw.print("mRequiredAbi="); pw.print(mRequiredAbi);
pw.print(" instructionSet="); pw.println(instructionSet);
if (info.className != null) {
pw.print(prefix); pw.print("class="); pw.println(info.className);
@@ -324,15 +355,12 @@
pw.print(" setRaw="); pw.print(setRawAdj);
pw.print(" cur="); pw.print(curAdj);
pw.print(" set="); pw.println(setAdj);
- pw.print(prefix); pw.print("curSchedGroup="); pw.print(curSchedGroup);
+ pw.print(prefix); pw.print("mCurSchedGroup="); pw.print(mCurSchedGroup);
pw.print(" setSchedGroup="); pw.print(setSchedGroup);
pw.print(" systemNoUi="); pw.print(systemNoUi);
pw.print(" trimMemoryLevel="); pw.println(trimMemoryLevel);
- if (vrThreadTid != 0) {
- pw.print(prefix); pw.print("vrThreadTid="); pw.println(vrThreadTid);
- }
pw.print(prefix); pw.print("curProcState="); pw.print(curProcState);
- pw.print(" repProcState="); pw.print(repProcState);
+ pw.print(" mRepProcState="); pw.print(mRepProcState);
pw.print(" pssProcState="); pw.print(pssProcState);
pw.print(" setProcState="); pw.print(setProcState);
pw.print(" lastStateTime=");
@@ -349,8 +377,8 @@
pw.print(" hasOverlayUi="); pw.print(hasOverlayUi);
pw.print(" runningRemoteAnimation="); pw.println(runningRemoteAnimation);
}
- if (foregroundServices || forcingToImportant != null) {
- pw.print(prefix); pw.print("foregroundServices="); pw.print(foregroundServices);
+ if (mHasForegroundServices || forcingToImportant != null) {
+ pw.print(prefix); pw.print("mHasForegroundServices="); pw.print(mHasForegroundServices);
pw.print(" forcingToImportant="); pw.println(forcingToImportant);
}
if (reportedInteraction || fgInteractionTime != 0) {
@@ -366,8 +394,8 @@
}
pw.println();
}
- if (persistent || removed) {
- pw.print(prefix); pw.print("persistent="); pw.print(persistent);
+ if (mPersistent || removed) {
+ pw.print(prefix); pw.print("persistent="); pw.print(mPersistent);
pw.print(" removed="); pw.println(removed);
}
if (hasClientActivities || foregroundActivities || repForegroundActivities) {
@@ -407,16 +435,16 @@
pw.print(" killedByAm="); pw.print(killedByAm);
pw.print(" waitingToKill="); pw.println(waitingToKill);
}
- if (debugging || crashing || crashDialog != null || notResponding
+ if (debugging || mCrashing || crashDialog != null || mNotResponding
|| anrDialog != null || bad) {
pw.print(prefix); pw.print("debugging="); pw.print(debugging);
- pw.print(" crashing="); pw.print(crashing);
+ pw.print(" mCrashing="); pw.print(mCrashing);
pw.print(" "); pw.print(crashDialog);
- pw.print(" notResponding="); pw.print(notResponding);
+ pw.print(" mNotResponding="); pw.print(mNotResponding);
pw.print(" " ); pw.print(anrDialog);
pw.print(" bad="); pw.print(bad);
- // crashing or notResponding is always set before errorReportReceiver
+ // mCrashing or mNotResponding is always set before errorReportReceiver
if (errorReportReceiver != null) {
pw.print(" errorReportReceiver=");
pw.print(errorReportReceiver.flattenToShortString());
@@ -431,18 +459,7 @@
pw.print(prefix); pw.print("isolatedEntryPointArgs=");
pw.println(Arrays.toString(isolatedEntryPointArgs));
}
- if (activities.size() > 0) {
- pw.print(prefix); pw.println("Activities:");
- for (int i=0; i<activities.size(); i++) {
- pw.print(prefix); pw.print(" - "); pw.println(activities.get(i));
- }
- }
- if (recentTasks.size() > 0) {
- pw.print(prefix); pw.println("Recent Tasks:");
- for (int i=0; i<recentTasks.size(); i++) {
- pw.print(prefix); pw.print(" - "); pw.println(recentTasks.get(i));
- }
- }
+ mWindowProcessController.dump(pw, prefix);
if (services.size() > 0) {
pw.print(prefix); pw.println("Services:");
for (int i=0; i<services.size(); i++) {
@@ -498,17 +515,20 @@
uid = _uid;
userId = UserHandle.getUserId(_uid);
processName = _processName;
- pkgList.put(_info.packageName, new ProcessStats.ProcessStateHolder(_info.longVersionCode));
maxAdj = ProcessList.UNKNOWN_ADJ;
curRawAdj = setRawAdj = ProcessList.INVALID_ADJ;
curAdj = setAdj = verifiedAdj = ProcessList.INVALID_ADJ;
- persistent = false;
+ mPersistent = false;
removed = false;
lastStateTime = lastPssTime = nextPssTime = SystemClock.uptimeMillis();
+ mWindowProcessController = new WindowProcessController(
+ mService.mActivityTaskManager, info, processName, uid, userId, this, this);
+ pkgList.put(_info.packageName, new ProcessStats.ProcessStateHolder(_info.longVersionCode));
}
public void setPid(int _pid) {
pid = _pid;
+ mWindowProcessController.setPid(pid);
procStatFile = null;
shortStringName = null;
stringName = null;
@@ -519,7 +539,7 @@
final ProcessState origBase = baseProcessTracker;
if (origBase != null) {
origBase.setState(ProcessStats.STATE_NOTHING,
- tracker.getMemFactorLocked(), SystemClock.uptimeMillis(), pkgList);
+ tracker.getMemFactorLocked(), SystemClock.uptimeMillis(), pkgList.mPkgList);
origBase.makeInactive();
}
baseProcessTracker = tracker.getProcessStateLocked(info.packageName, uid,
@@ -530,7 +550,7 @@
if (holder.state != null && holder.state != origBase) {
holder.state.makeInactive();
}
- holder.state = tracker.getProcessStateLocked(pkgList.keyAt(i), uid,
+ tracker.updateProcessStateHolderLocked(holder, pkgList.keyAt(i), uid,
info.longVersionCode, processName);
if (holder.state != baseProcessTracker) {
holder.state.makeActive();
@@ -538,15 +558,17 @@
}
}
thread = _thread;
+ mWindowProcessController.setThread(thread);
}
public void makeInactive(ProcessStatsService tracker) {
thread = null;
+ mWindowProcessController.setThread(null);
final ProcessState origBase = baseProcessTracker;
if (origBase != null) {
if (origBase != null) {
origBase.setState(ProcessStats.STATE_NOTHING,
- tracker.getMemFactorLocked(), SystemClock.uptimeMillis(), pkgList);
+ tracker.getMemFactorLocked(), SystemClock.uptimeMillis(), pkgList.mPkgList);
origBase.makeInactive();
}
baseProcessTracker = null;
@@ -555,16 +577,30 @@
if (holder.state != null && holder.state != origBase) {
holder.state.makeInactive();
}
+ holder.pkg = null;
holder.state = null;
}
}
}
- public void clearRecentTasks() {
- for (int i = recentTasks.size() - 1; i >= 0; i--) {
- recentTasks.get(i).clearRootProcess();
- }
- recentTasks.clear();
+ boolean hasActivities() {
+ return mWindowProcessController.hasActivities();
+ }
+
+ void clearActivities() {
+ mWindowProcessController.clearActivities();
+ }
+
+ boolean hasActivitiesOrRecentTasks() {
+ return mWindowProcessController.hasActivitiesOrRecentTasks();
+ }
+
+ boolean hasRecentTasks() {
+ return mWindowProcessController.hasRecentTasks();
+ }
+
+ void clearRecentTasks() {
+ mWindowProcessController.clearRecentTasks();
}
/**
@@ -572,12 +608,8 @@
* to the user. See HistoryRecord.isInterestingToUserLocked()
*/
public boolean isInterestingToUserLocked() {
- final int size = activities.size();
- for (int i = 0 ; i < size ; i++) {
- ActivityRecord r = activities.get(i);
- if (r.isInterestingToUserLocked()) {
- return true;
- }
+ if (mWindowProcessController.isInterestingToUser()) {
+ return true;
}
final int servicesSize = services.size();
@@ -590,14 +622,6 @@
return false;
}
- public void stopFreezingAllLocked() {
- int i = activities.size();
- while (i > 0) {
- i--;
- activities.get(i).stopFreezingScreenLocked(true);
- }
- }
-
public void unlinkDeathRecipient() {
if (deathRecipient != null && thread != null) {
thread.asBinder().unlinkToDeath(deathRecipient, 0);
@@ -676,7 +700,7 @@
} else {
pendingStart = false;
}
- if (!persistent) {
+ if (!mPersistent) {
killed = true;
killedByAm = true;
}
@@ -697,7 +721,7 @@
proto.write(ProcessRecordProto.ISOLATED_APP_ID, UserHandle.getAppId(uid));
}
}
- proto.write(ProcessRecordProto.PERSISTENT, persistent);
+ proto.write(ProcessRecordProto.PERSISTENT, mPersistent);
proto.end(token);
}
@@ -782,8 +806,7 @@
ProcessStats.ProcessStateHolder holder = new ProcessStats.ProcessStateHolder(
versionCode);
if (baseProcessTracker != null) {
- holder.state = tracker.getProcessStateLocked(
- pkg, uid, versionCode, processName);
+ tracker.updateProcessStateHolderLocked(holder, pkg, uid, versionCode, processName);
pkgList.put(pkg, holder);
if (holder.state != baseProcessTracker) {
holder.state.makeActive();
@@ -806,8 +829,8 @@
}
public void forceProcessStateUpTo(int newState) {
- if (repProcState > newState) {
- curProcState = repProcState = newState;
+ if (mRepProcState > newState) {
+ curProcState = mRepProcState = newState;
}
}
@@ -819,7 +842,7 @@
if (baseProcessTracker != null) {
long now = SystemClock.uptimeMillis();
baseProcessTracker.setState(ProcessStats.STATE_NOTHING,
- tracker.getMemFactorLocked(), now, pkgList);
+ tracker.getMemFactorLocked(), now, pkgList.mPkgList);
if (N != 1) {
for (int i=0; i<N; i++) {
ProcessStats.ProcessStateHolder holder = pkgList.valueAt(i);
@@ -829,14 +852,13 @@
}
pkgList.clear();
- ProcessState ps = tracker.getProcessStateLocked(
- info.packageName, uid, info.longVersionCode, processName);
ProcessStats.ProcessStateHolder holder = new ProcessStats.ProcessStateHolder(
info.longVersionCode);
- holder.state = ps;
+ tracker.updateProcessStateHolderLocked(holder, info.packageName, uid,
+ info.longVersionCode, processName);
pkgList.put(info.packageName, holder);
- if (ps != baseProcessTracker) {
- ps.makeActive();
+ if (holder.state != baseProcessTracker) {
+ holder.state.makeActive();
}
}
} else if (N != 1) {
@@ -856,4 +878,135 @@
}
return list;
}
+
+ WindowProcessController getWindowProcessController() {
+ return mWindowProcessController;
+ }
+
+ void setCurrentSchedulingGroup(int curSchedGroup) {
+ mCurSchedGroup = curSchedGroup;
+ mWindowProcessController.setCurrentSchedulingGroup(curSchedGroup);
+ }
+
+ int getCurrentSchedulingGroup() {
+ return mCurSchedGroup;
+ }
+
+ void setReportedProcState(int repProcState) {
+ mRepProcState = repProcState;
+ mWindowProcessController.setReportedProcState(repProcState);
+ }
+
+ int getReportedProcState() {
+ return mRepProcState;
+ }
+
+ void setCrashing(boolean crashing) {
+ mCrashing = crashing;
+ mWindowProcessController.setCrashing(crashing);
+ }
+
+ boolean isCrashing() {
+ return mCrashing;
+ }
+
+ void setNotResponding(boolean notResponding) {
+ mNotResponding = notResponding;
+ mWindowProcessController.setNotResponding(notResponding);
+ }
+
+ boolean isNotResponding() {
+ return mNotResponding;
+ }
+
+ void setPersistent(boolean persistent) {
+ mPersistent = persistent;
+ mWindowProcessController.setPersistent(persistent);
+ }
+
+ boolean isPersistent() {
+ return mPersistent;
+ }
+
+ public void setRequiredAbi(String requiredAbi) {
+ mRequiredAbi = requiredAbi;
+ mWindowProcessController.setRequiredAbi(requiredAbi);
+ }
+
+ String getRequiredAbi() {
+ return mRequiredAbi;
+ }
+
+ void setHasForegroundServices(boolean hasForegroundServices) {
+ mHasForegroundServices = hasForegroundServices;
+ mWindowProcessController.setHasForegroundServices(hasForegroundServices);
+ }
+
+ boolean hasForegroundServices() {
+ return mHasForegroundServices;
+ }
+
+ @Override
+ public void clearProfilerIfNeeded() {
+ synchronized (mService) {
+ if (mService.mProfileProc == null || mService.mProfilerInfo == null
+ || mService.mProfileProc != this) {
+ return;
+ }
+ mService.clearProfilerLocked();
+ }
+ }
+
+ @Override
+ public void updateServiceConnectionActivities() {
+ synchronized (mService) {
+ mService.mServices.updateServiceConnectionActivitiesLocked(this);
+ }
+ }
+
+ @Override
+ public void setPendingUiClean(boolean pendingUiClean) {
+ synchronized (mService) {
+ this.pendingUiClean = true;
+ }
+ }
+
+ @Override
+ public void setPendingUiCleanAndForceProcessStateUpTo(int newState) {
+ synchronized (mService) {
+ pendingUiClean = true;
+ forceProcessStateUpTo(newState);
+ }
+ }
+
+ @Override
+ public void updateProcessInfo(boolean updateServiceConnectionActivities, boolean updateLru,
+ boolean activityChange, boolean updateOomAdj) {
+ synchronized (mService) {
+ if (updateServiceConnectionActivities) {
+ mService.mServices.updateServiceConnectionActivitiesLocked(this);
+ }
+ if (updateLru) {
+ mService.updateLruProcessLocked(this, activityChange, null);
+ }
+ if (updateOomAdj) {
+ mService.updateOomAdjLocked();
+ }
+ }
+ }
+
+ @Override
+ public void setRemoved(boolean removed) {
+ synchronized (mService) {
+ this.removed = removed;
+ }
+ }
+
+ /**
+ * Returns the total time (in milliseconds) spent executing in both user and system code.
+ * Safe to call without lock held.
+ */
+ public long getCpuTime() {
+ return mService.mProcessCpuTracker.getCpuTimeForPid(pid);
+ }
}
diff --git a/services/core/java/com/android/server/am/ProcessStatsService.java b/services/core/java/com/android/server/am/ProcessStatsService.java
index c10d81b..f0bd8fa 100644
--- a/services/core/java/com/android/server/am/ProcessStatsService.java
+++ b/services/core/java/com/android/server/am/ProcessStatsService.java
@@ -16,14 +16,12 @@
package com.android.server.am;
-import android.content.pm.PackageManager;
import android.os.Binder;
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.SystemProperties;
-import android.service.procstats.ProcessStatsProto;
import android.service.procstats.ProcessStatsServiceDumpProto;
import android.util.ArrayMap;
import android.util.AtomicFile;
@@ -120,11 +118,20 @@
}
}
+ @GuardedBy("mAm")
+ public void updateProcessStateHolderLocked(ProcessStats.ProcessStateHolder holder,
+ String packageName, int uid, long versionCode, String processName) {
+ holder.pkg = mProcessStats.getPackageStateLocked(packageName, uid, versionCode);
+ holder.state = mProcessStats.getProcessStateLocked(holder.pkg, processName);
+ }
+
+ @GuardedBy("mAm")
public ProcessState getProcessStateLocked(String packageName,
int uid, long versionCode, String processName) {
return mProcessStats.getProcessStateLocked(packageName, uid, versionCode, processName);
}
+ @GuardedBy("mAm")
public ServiceState getServiceStateLocked(String packageName, int uid,
long versionCode, String processName, String className) {
return mProcessStats.getServiceStateLocked(packageName, uid, versionCode, processName,
@@ -174,15 +181,23 @@
return false;
}
+ @GuardedBy("mAm")
public int getMemFactorLocked() {
return mProcessStats.mMemFactor != ProcessStats.STATE_NOTHING ? mProcessStats.mMemFactor : 0;
}
+ @GuardedBy("mAm")
public void addSysMemUsageLocked(long cachedMem, long freeMem, long zramMem, long kernelMem,
long nativeMem) {
mProcessStats.addSysMemUsage(cachedMem, freeMem, zramMem, kernelMem, nativeMem);
}
+ @GuardedBy("mAm")
+ public void updateTrackingAssociationsLocked(int curSeq, long now) {
+ mProcessStats.updateTrackingAssociationsLocked(curSeq, now);
+ }
+
+ @GuardedBy("mAm")
public boolean shouldWriteNowLocked(long now) {
if (now > (mLastWriteTime+WRITE_PERIOD)) {
if (SystemClock.elapsedRealtime()
@@ -196,6 +211,7 @@
return false;
}
+ @GuardedBy("mAm")
public void shutdownLocked() {
Slog.w(TAG, "Writing process stats before shutdown...");
mProcessStats.mFlags |= ProcessStats.FLAG_SHUTDOWN;
@@ -203,14 +219,17 @@
mShuttingDown = true;
}
+ @GuardedBy("mAm")
public void writeStateAsyncLocked() {
writeStateLocked(false);
}
+ @GuardedBy("mAm")
public void writeStateSyncLocked() {
writeStateLocked(true);
}
+ @GuardedBy("mAm")
private void writeStateLocked(boolean sync) {
if (mShuttingDown) {
return;
@@ -220,6 +239,7 @@
writeStateLocked(sync, commitPending);
}
+ @GuardedBy("mAm")
public void writeStateLocked(boolean sync, final boolean commit) {
final long totalTime;
synchronized (mPendingWriteLock) {
@@ -298,6 +318,7 @@
}
}
+ @GuardedBy("mAm")
boolean readLocked(ProcessStats stats, AtomicFile file) {
try {
FileInputStream stream = file.openRead();
@@ -342,6 +363,13 @@
+ ": " + pkgState.mServices.valueAt(isvc));
}
+ final int NASCS = pkgState.mAssociations.size();
+ for (int iasc=0; iasc<NASCS; iasc++) {
+ Slog.w(TAG, " Association "
+ + pkgState.mServices.keyAt(iasc)
+ + ": " + pkgState.mAssociations.valueAt(iasc));
+
+ }
}
}
}
@@ -383,6 +411,7 @@
return filesArray;
}
+ @GuardedBy("mAm")
public void trimHistoricStatesWriteLocked() {
ArrayList<String> filesArray = getCommittedFiles(MAX_HISTORIC_STATES, false, true);
if (filesArray == null) {
@@ -395,6 +424,7 @@
}
}
+ @GuardedBy("mAm")
boolean dumpFilteredProcessesCsvLocked(PrintWriter pw, String header,
boolean sepScreenStates, int[] screenStates, boolean sepMemStates, int[] memStates,
boolean sepProcStates, int[] procStates, long now, String reqPackage) {
diff --git a/services/core/java/com/android/server/am/ProviderMap.java b/services/core/java/com/android/server/am/ProviderMap.java
index 2f52002..29c1657 100644
--- a/services/core/java/com/android/server/am/ProviderMap.java
+++ b/services/core/java/com/android/server/am/ProviderMap.java
@@ -32,7 +32,6 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
@@ -203,7 +202,7 @@
&& (filterByClasses == null
|| filterByClasses.contains(provider.name.getClassName())));
if (sameComponent
- && (provider.proc == null || evenPersistent || !provider.proc.persistent)) {
+ && (provider.proc == null || evenPersistent || !provider.proc.isPersistent())) {
if (!doit) {
return true;
}
diff --git a/services/core/java/com/android/server/am/RecentTasks.java b/services/core/java/com/android/server/am/RecentTasks.java
index 749589b..1967c76 100644
--- a/services/core/java/com/android/server/am/RecentTasks.java
+++ b/services/core/java/com/android/server/am/RecentTasks.java
@@ -78,6 +78,7 @@
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
+import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
@@ -140,7 +141,6 @@
private final TaskPersister mTaskPersister;
private final ActivityTaskManagerService mService;
private final ActivityStackSupervisor mSupervisor;
- private final UserController mUserController;
/**
* Keeps track of the static recents package/component which is granted additional permissions
@@ -181,11 +181,9 @@
private final TaskActivitiesReport mTmpReport = new TaskActivitiesReport();
@VisibleForTesting
- RecentTasks(ActivityTaskManagerService service, TaskPersister taskPersister,
- UserController userController) {
+ RecentTasks(ActivityTaskManagerService service, TaskPersister taskPersister) {
mService = service;
mSupervisor = mService.mStackSupervisor;
- mUserController = userController;
mTaskPersister = taskPersister;
mGlobalMaxNumTasks = ActivityTaskManager.getMaxRecentTasksStatic();
mHasVisibleRecentTasks = true;
@@ -196,7 +194,6 @@
final Resources res = service.mContext.getResources();
mService = service;
mSupervisor = mService.mStackSupervisor;
- mUserController = service.mAm.mUserController;
mTaskPersister = new TaskPersister(systemDir, stackSupervisor, service, this);
mGlobalMaxNumTasks = ActivityTaskManager.getMaxRecentTasksStatic();
mHasVisibleRecentTasks = res.getBoolean(com.android.internal.R.bool.config_hasRecents);
@@ -712,6 +709,27 @@
return list;
}
+ @VisibleForTesting
+ Set<Integer> getProfileIds(int userId) {
+ Set<Integer> userIds = new ArraySet<>();
+ final List<UserInfo> profiles = mService.getUserManager().getProfiles(userId,
+ false /* enabledOnly */);
+ for (int i = profiles.size() - 1; i >= 0; --i) {
+ userIds.add(profiles.get(i).id);
+ }
+ return userIds;
+ }
+
+ @VisibleForTesting
+ UserInfo getUserInfo(int userId) {
+ return mService.getUserManager().getUserInfo(userId);
+ }
+
+ @VisibleForTesting
+ int[] getCurrentProfileIds() {
+ return mService.mAmInternal.getCurrentProfileIds();
+ }
+
/**
* @return the list of recent tasks for presentation.
*/
@@ -725,7 +743,7 @@
}
loadUserRecentsLocked(userId);
- final Set<Integer> includedUsers = mUserController.getProfileIds(userId);
+ final Set<Integer> includedUsers = getProfileIds(userId);
includedUsers.add(Integer.valueOf(userId));
final ArrayList<ActivityManager.RecentTaskInfo> res = new ArrayList<>();
@@ -1040,10 +1058,10 @@
}
// Remove any tasks that belong to currently quiet profiles
- final int[] profileUserIds = mUserController.getCurrentProfileIds();
+ final int[] profileUserIds = getCurrentProfileIds();
mTmpQuietProfileUserIds.clear();
for (int userId : profileUserIds) {
- final UserInfo userInfo = mUserController.getUserInfo(userId);
+ final UserInfo userInfo = getUserInfo(userId);
if (userInfo != null && userInfo.isManagedProfile() && userInfo.isQuietModeEnabled()) {
mTmpQuietProfileUserIds.put(userId, true);
}
diff --git a/services/core/java/com/android/server/am/RecentsAnimation.java b/services/core/java/com/android/server/am/RecentsAnimation.java
index bd49bd1..1c7ad3f 100644
--- a/services/core/java/com/android/server/am/RecentsAnimation.java
+++ b/services/core/java/com/android/server/am/RecentsAnimation.java
@@ -54,11 +54,10 @@
private static final String TAG = RecentsAnimation.class.getSimpleName();
private static final boolean DEBUG = false;
- private final ActivityManagerService mService;
+ private final ActivityTaskManagerService mService;
private final ActivityStackSupervisor mStackSupervisor;
private final ActivityStartController mActivityStartController;
private final WindowManagerService mWindowManager;
- private final UserController mUserController;
private final ActivityDisplay mDefaultDisplay;
private final int mCallingPid;
@@ -68,15 +67,14 @@
// The stack to restore the target stack behind when the animation is finished
private ActivityStack mRestoreTargetBehindStack;
- RecentsAnimation(ActivityManagerService am, ActivityStackSupervisor stackSupervisor,
+ RecentsAnimation(ActivityTaskManagerService atm, ActivityStackSupervisor stackSupervisor,
ActivityStartController activityStartController, WindowManagerService wm,
- UserController userController, int callingPid) {
- mService = am;
+ int callingPid) {
+ mService = atm;
mStackSupervisor = stackSupervisor;
mDefaultDisplay = stackSupervisor.getDefaultDisplay();
mActivityStartController = activityStartController;
mWindowManager = wm;
- mUserController = userController;
mCallingPid = callingPid;
}
@@ -122,7 +120,7 @@
mStackSupervisor.getActivityMetricsLogger().notifyActivityLaunching();
- mService.setRunningRemoteAnimation(mCallingPid, true);
+ mService.mAmInternal.setRunningRemoteAnimation(mCallingPid, true);
mWindowManager.deferSurfaceLayout();
try {
@@ -132,7 +130,7 @@
mService.mContext.getSystemService(Context.APP_OPS_SERVICE);
final AssistDataReceiverProxy proxy = new AssistDataReceiverProxy(
assistDataReceiver, recentsComponent.getPackageName());
- mAssistDataRequester = new AssistDataRequester(mService.mContext, mService,
+ mAssistDataRequester = new AssistDataRequester(mService.mContext,
mWindowManager, appOpsManager, proxy, this, OP_ASSIST_STRUCTURE, OP_NONE);
mAssistDataRequester.requestAssistData(mStackSupervisor.getTopVisibleActivities(),
true /* fetchData */, false /* fetchScreenshots */,
@@ -165,7 +163,7 @@
.setCallingUid(recentsUid)
.setCallingPackage(recentsComponent.getPackageName())
.setActivityOptions(SafeActivityOptions.fromBundle(options.toBundle()))
- .setMayWait(mUserController.getCurrentUserId())
+ .setMayWait(mService.getCurrentUserId())
.execute();
mWindowManager.prepareAppTransition(TRANSIT_NONE, false);
mWindowManager.executeAppTransition();
@@ -210,7 +208,7 @@
}
private void finishAnimation(@RecentsAnimationController.ReorderMode int reorderMode) {
- synchronized (mService) {
+ synchronized (mService.mGlobalLock) {
if (DEBUG) Slog.d(TAG, "onAnimationFinished(): controller="
+ mWindowManager.getRecentsAnimationController()
+ " reorderMode=" + reorderMode);
@@ -232,7 +230,7 @@
mStackSupervisor.sendPowerHintForLaunchEndIfNeeded();
}
- mService.setRunningRemoteAnimation(mCallingPid, false);
+ mService.mAmInternal.setRunningRemoteAnimation(mCallingPid, false);
mWindowManager.inSurfaceTransaction(() -> {
Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER,
@@ -317,7 +315,7 @@
if (runSychronously) {
finishAnimation(reorderMode);
} else {
- mService.mHandler.post(() -> finishAnimation(reorderMode));
+ mService.mH.post(() -> finishAnimation(reorderMode));
}
}
diff --git a/services/core/java/com/android/server/am/SafeActivityOptions.java b/services/core/java/com/android/server/am/SafeActivityOptions.java
index 778990b..f7de7f4 100644
--- a/services/core/java/com/android/server/am/SafeActivityOptions.java
+++ b/services/core/java/com/android/server/am/SafeActivityOptions.java
@@ -117,7 +117,7 @@
* @param callerApp The record of the caller.
*/
ActivityOptions getOptions(@Nullable Intent intent, @Nullable ActivityInfo aInfo,
- @Nullable ProcessRecord callerApp,
+ @Nullable WindowProcessController callerApp,
ActivityStackSupervisor supervisor) throws SecurityException {
if (mOriginalOptions != null) {
checkPermissions(intent, aInfo, callerApp, supervisor, mOriginalOptions,
@@ -186,13 +186,13 @@
}
private void checkPermissions(@Nullable Intent intent, @Nullable ActivityInfo aInfo,
- @Nullable ProcessRecord callerApp, ActivityStackSupervisor supervisor,
+ @Nullable WindowProcessController callerApp, ActivityStackSupervisor supervisor,
ActivityOptions options, int callingPid, int callingUid) {
// If a launch task id is specified, then ensure that the caller is the recents
// component or has the START_TASKS_FROM_RECENTS permission
if (options.getLaunchTaskId() != INVALID_TASK_ID
&& !supervisor.mRecentTasks.isCallerRecents(callingUid)) {
- final int startInTaskPerm = supervisor.mService.mAm.checkPermission(
+ final int startInTaskPerm = ActivityTaskManagerService.checkPermission(
START_TASKS_FROM_RECENTS, callingPid, callingUid);
if (startInTaskPerm == PERMISSION_DENIED) {
final String msg = "Permission Denial: starting " + getIntentString(intent)
@@ -230,7 +230,7 @@
// Check permission for remote animations
final RemoteAnimationAdapter adapter = options.getRemoteAnimationAdapter();
- if (adapter != null && supervisor.mService.mAm.checkPermission(
+ if (adapter != null && supervisor.mService.checkPermission(
CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS, callingPid, callingUid)
!= PERMISSION_GRANTED) {
final String msg = "Permission Denial: starting " + getIntentString(intent)
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index 4d89d01..8f43620 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -500,6 +500,23 @@
restartTracker.setRestarting(true, memFactor, now);
}
+ public void setProcess(ProcessRecord _proc) {
+ app = _proc;
+ if (ActivityManagerService.TRACK_PROCSTATS_ASSOCIATIONS) {
+ for (int conni = connections.size() - 1; conni >= 0; conni--) {
+ ArrayList<ConnectionRecord> cr = connections.valueAt(conni);
+ for (int i = 0; i < cr.size(); i++) {
+ final ConnectionRecord conn = cr.get(i);
+ if (_proc != null) {
+ conn.startAssociationIfNeeded();
+ } else {
+ conn.stopAssociation();
+ }
+ }
+ }
+ }
+ }
+
public AppBindRecord retrieveAppBindingLocked(Intent intent,
ProcessRecord app) {
Intent.FilterComparison filter = new Intent.FilterComparison(intent);
diff --git a/services/core/java/com/android/server/am/TEST_MAPPING b/services/core/java/com/android/server/am/TEST_MAPPING
new file mode 100644
index 0000000..9e11eb0
--- /dev/null
+++ b/services/core/java/com/android/server/am/TEST_MAPPING
@@ -0,0 +1,70 @@
+{
+ "presubmit": [
+ {
+ "name": "CtsActivityManagerDeviceTestCases",
+ "options": [
+ {
+ "include-annotation": "android.platform.test.annotations.Presubmit"
+ },
+ {
+ "exclude-annotation": "android.support.test.filters.FlakyTest"
+ }
+ ]
+ },
+ {
+ "name": "CtsActivityManagerDeviceSdk25TestCases",
+ "options": [
+ {
+ "include-annotation": "android.platform.test.annotations.Presubmit"
+ },
+ {
+ "exclude-annotation": "android.support.test.filters.FlakyTest"
+ }
+ ]
+ },
+ {
+ "name": "CtsAppTestCases",
+ "options": [
+ {
+ "include-filter": "android.app.cts.TaskDescriptionTest"
+ },
+ {
+ "include-annotation": "android.platform.test.annotations.Presubmit"
+ },
+ {
+ "exclude-annotation": "android.support.test.filters.FlakyTest"
+ }
+ ]
+ },
+ {
+ "name": "FrameworksServicesTests",
+ "options": [
+ {
+ "include-filter": "com.android.server.am."
+ },
+ {
+ "include-annotation": "android.platform.test.annotations.Presubmit"
+ },
+ {
+ "exclude-annotation": "android.support.test.filters.FlakyTest"
+ }
+ ]
+ }
+ ],
+ "postsubmit": [
+ {
+ "name": "CtsActivityManagerDeviceTestCases"
+ },
+ {
+ "name": "CtsActivityManagerDeviceSdk25TestCases"
+ },
+ {
+ "name": "FrameworksServicesTests",
+ "options": [
+ {
+ "include-filter": "com.android.server.am."
+ }
+ ]
+ }
+ ]
+}
diff --git a/services/core/java/com/android/server/am/TaskPersister.java b/services/core/java/com/android/server/am/TaskPersister.java
index 465fa67..481bb2b 100644
--- a/services/core/java/com/android/server/am/TaskPersister.java
+++ b/services/core/java/com/android/server/am/TaskPersister.java
@@ -655,7 +655,7 @@
synchronized (mService.mGlobalLock) {
if (DEBUG) Slog.d(TAG, "mRecents=" + mRecentTasks);
mRecentTasks.getPersistableTaskIds(persistentTaskIds);
- mService.mAm.mWindowManager.removeObsoleteTaskFiles(persistentTaskIds,
+ mService.mWindowManager.removeObsoleteTaskFiles(persistentTaskIds,
mRecentTasks.usersWithRecentsLoadedLocked());
}
removeObsoleteFiles(persistentTaskIds);
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 6eac4bc..11684af 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -264,7 +264,7 @@
/** The process that had previously hosted the root activity of this task.
* Used to know that we should try harder to keep this process around, in case the
* user wants to return to it. */
- private ProcessRecord mRootProcess;
+ private WindowProcessController mRootProcess;
/** Takes on same value as first root activity */
boolean isPersistable = false;
@@ -542,8 +542,14 @@
if (r != null && !deferResume) {
kept = r.ensureActivityConfiguration(0 /* globalChanges */,
preserveWindow);
+ // Preserve other windows for resizing because if resizing happens when there
+ // is a dialog activity in the front, the activity that still shows some
+ // content to the user will become black and cause flickers. Note in most cases
+ // this won't cause tons of irrelevant windows being preserved because only
+ // activities in this task may experience a bounds change. Configs for other
+ // activities stay the same.
mService.mStackSupervisor.ensureActivitiesVisibleLocked(r, 0,
- !PRESERVE_WINDOWS);
+ preserveWindow);
if (!kept) {
mService.mStackSupervisor.resumeFocusedStackTopActivityLocked();
}
@@ -651,7 +657,7 @@
boolean kept = true;
try {
final ActivityRecord r = topRunningActivityLocked();
- final boolean wasFocused = r != null && supervisor.isFocusedStack(sourceStack)
+ final boolean wasFocused = r != null && supervisor.isTopDisplayFocusedStack(sourceStack)
&& (topRunningActivityLocked() == r);
final boolean wasResumed = r != null && sourceStack.getResumedActivity() == r;
final boolean wasPaused = r != null && sourceStack.mPausingActivity == r;
@@ -1131,7 +1137,7 @@
// activity
reportOut.numRunning = 0;
}
- if (r.app != null && r.app.thread != null) {
+ if (r.attachedToProcess()) {
// Increment the number of actually running activities
reportOut.numRunning++;
}
@@ -1494,7 +1500,7 @@
}
private boolean isResizeable(boolean checkSupportsPip) {
- return (mService.mAm.mForceResizableActivities || ActivityInfo.isResizeableMode(mResizeMode)
+ return (mService.mForceResizableActivities || ActivityInfo.isResizeableMode(mResizeMode)
|| (checkSupportsPip && mSupportsPictureInPicture));
}
@@ -1507,8 +1513,8 @@
// A task can not be docked even if it is considered resizeable because it only supports
// picture-in-picture mode but has a non-resizeable resizeMode
return super.supportsSplitScreenWindowingMode()
- && mService.mAm.mSupportsSplitScreenMultiWindow
- && (mService.mAm.mForceResizableActivities
+ && mService.mSupportsSplitScreenMultiWindow
+ && (mService.mForceResizableActivities
|| (isResizeable(false /* checkSupportsPip */)
&& !ActivityInfo.isPreserveOrientationMode(mResizeMode)));
}
@@ -1841,7 +1847,9 @@
final int compatScreenHeightDp = (int) (mTmpNonDecorBounds.height() / density);
// We're only overriding LONG, SIZE and COMPAT parts of screenLayout, so we start override
// calculation with partial default.
- final int sl = Configuration.SCREENLAYOUT_LONG_YES | Configuration.SCREENLAYOUT_SIZE_XLARGE;
+ // Reducing the screen layout starting from its parent config.
+ final int sl = parentConfig.screenLayout &
+ (Configuration.SCREENLAYOUT_LONG_MASK | Configuration.SCREENLAYOUT_SIZE_MASK);
final int longSize = Math.max(compatScreenHeightDp, compatScreenWidthDp);
final int shortSize = Math.min(compatScreenHeightDp, compatScreenWidthDp);
config.screenLayout = Configuration.reduceScreenLayout(sl, longSize, shortSize);
@@ -1909,18 +1917,18 @@
}
}
- void setRootProcess(ProcessRecord proc) {
+ void setRootProcess(WindowProcessController proc) {
clearRootProcess();
if (intent != null &&
(intent.getFlags() & Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) == 0) {
mRootProcess = proc;
- proc.recentTasks.add(this);
+ mRootProcess.addRecentTask(this);
}
}
void clearRootProcess() {
if (mRootProcess != null) {
- mRootProcess.recentTasks.remove(this);
+ mRootProcess.removeRecentTask(this);
mRootProcess = null;
}
}
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index b500bbf..fa670a2 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -28,9 +28,9 @@
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_MU;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.am.ActivityManagerService.ALLOW_FULL_ONLY;
-import static com.android.server.am.ActivityManagerService.ALLOW_NON_FULL;
-import static com.android.server.am.ActivityManagerService.ALLOW_NON_FULL_IN_PROFILE;
+import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY;
+import static android.app.ActivityManagerInternal.ALLOW_NON_FULL;
+import static android.app.ActivityManagerInternal.ALLOW_NON_FULL_IN_PROFILE;
import static com.android.server.am.ActivityManagerService.MY_PID;
import static com.android.server.am.UserState.STATE_BOOTING;
import static com.android.server.am.UserState.STATE_RUNNING_LOCKED;
@@ -41,6 +41,7 @@
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
+import com.android.server.wm.ActivityTaskManagerInternal;
import android.app.AppGlobals;
import android.app.AppOpsManager;
import android.app.Dialog;
@@ -80,7 +81,6 @@
import android.text.format.DateUtils;
import android.util.ArraySet;
import android.util.IntArray;
-import android.util.Log;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
@@ -88,7 +88,6 @@
import android.util.TimingsTraceLog;
import android.util.proto.ProtoOutputStream;
-import android.view.Window;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -260,11 +259,14 @@
}
void finishUserSwitch(UserState uss) {
- finishUserBoot(uss);
- startProfiles();
- synchronized (mLock) {
- stopRunningUsersLU(mMaxRunningUsers);
- }
+ // This call holds the AM lock so we post to the handler.
+ mHandler.post(() -> {
+ finishUserBoot(uss);
+ startProfiles();
+ synchronized (mLock) {
+ stopRunningUsersLU(mMaxRunningUsers);
+ }
+ });
}
List<Integer> getRunningUsersLU() {
@@ -1574,7 +1576,9 @@
}
boolean hasStartedUserState(int userId) {
- return mStartedUsers.get(userId) != null;
+ synchronized (mLock) {
+ return mStartedUsers.get(userId) != null;
+ }
}
private void updateStartedUserArrayLU() {
@@ -1750,7 +1754,7 @@
return ums != null ? ums.getUserIds() : new int[] { 0 };
}
- UserInfo getUserInfo(int userId) {
+ private UserInfo getUserInfo(int userId) {
return mInjector.getUserManager().getUserInfo(userId);
}
@@ -1776,7 +1780,7 @@
return mInjector.getUserManager().exists(userId);
}
- void enforceShellRestriction(String restriction, int userHandle) {
+ private void enforceShellRestriction(String restriction, int userHandle) {
if (Binder.getCallingUid() == SHELL_UID) {
if (userHandle < 0 || hasUserRestriction(restriction, userHandle)) {
throw new SecurityException("Shell does not have permission to access user "
@@ -1789,16 +1793,6 @@
return mInjector.getUserManager().hasUserRestriction(restriction, userId);
}
- Set<Integer> getProfileIds(int userId) {
- Set<Integer> userIds = new HashSet<>();
- final List<UserInfo> profiles = mInjector.getUserManager().getProfiles(userId,
- false /* enabledOnly */);
- for (UserInfo user : profiles) {
- userIds.add(user.id);
- }
- return userIds;
- }
-
boolean isSameProfileGroup(int callingUserId, int targetUserId) {
if (callingUserId == targetUserId) {
return true;
@@ -2097,9 +2091,7 @@
return mService.mWindowManager;
}
void activityManagerOnUserStopped(int userId) {
- synchronized (mService) {
- mService.onUserStoppedLocked(userId);
- }
+ LocalServices.getService(ActivityTaskManagerInternal.class).onUserStopped(userId);
}
void systemServiceManagerCleanupUser(int userId) {
@@ -2174,9 +2166,7 @@
}
void updateUserConfiguration() {
- synchronized (mService) {
- mService.updateUserConfigurationLocked();
- }
+ mService.mActivityTaskManager.updateUserConfiguration();
}
void clearBroadcastQueueForUser(int userId) {
diff --git a/services/core/java/com/android/server/am/VrController.java b/services/core/java/com/android/server/am/VrController.java
index 45410d7..366f95a 100644
--- a/services/core/java/com/android/server/am/VrController.java
+++ b/services/core/java/com/android/server/am/VrController.java
@@ -147,14 +147,15 @@
*
* <p>Note: This must be called with the global ActivityManagerService lock held.
*
- * @param proc is the ProcessRecord of the process that entered or left the TOP_APP scheduling
- * group.
+ * @param proc is the WindowProcessController of the process that entered or left the TOP_APP
+ * scheduling group.
*/
- public void onTopProcChangedLocked(ProcessRecord proc) {
- if (proc.curSchedGroup == ProcessList.SCHED_GROUP_TOP_APP) {
- setVrRenderThreadLocked(proc.vrThreadTid, proc.curSchedGroup, true);
+ public void onTopProcChangedLocked(WindowProcessController proc) {
+ final int curSchedGroup = proc.getCurrentSchedulingGroup();
+ if (curSchedGroup == ProcessList.SCHED_GROUP_TOP_APP) {
+ setVrRenderThreadLocked(proc.mVrThreadTid, curSchedGroup, true);
} else {
- if (proc.vrThreadTid == mVrRenderThreadTid) {
+ if (proc.mVrThreadTid == mVrRenderThreadTid) {
clearVrRenderThreadLocked(true);
}
}
@@ -190,7 +191,7 @@
changed = changeVrModeLocked(vrMode, record.app);
if (record.app != null) {
- processId = record.app.pid;
+ processId = record.app.getPid();
}
}
@@ -213,9 +214,9 @@
*
* @param tid the tid of the thread to set, or 0 to unset the current thread.
* @param pid the pid of the process owning the thread to set.
- * @param proc the ProcessRecord of the process owning the thread to set.
+ * @param proc the WindowProcessController of the process owning the thread to set.
*/
- public void setVrThreadLocked(int tid, int pid, ProcessRecord proc) {
+ public void setVrThreadLocked(int tid, int pid, WindowProcessController proc) {
if (hasPersistentVrFlagSet()) {
Slog.w(TAG, "VR thread cannot be set in persistent VR mode!");
return;
@@ -230,9 +231,9 @@
if (!inVrMode()) {
Slog.w(TAG, "VR thread cannot be set when not in VR mode!");
} else {
- setVrRenderThreadLocked(tid, proc.curSchedGroup, false);
+ setVrRenderThreadLocked(tid, proc.getCurrentSchedulingGroup(), false);
}
- proc.vrThreadTid = (tid > 0) ? tid : 0;
+ proc.mVrThreadTid = (tid > 0) ? tid : 0;
}
/**
@@ -280,11 +281,11 @@
* <p>Note: This must be called with the global ActivityManagerService lock held.
*
* @param vrMode {@code true} if the system VR mode is being enabled.
- * @param proc the ProcessRecord of the process enabling the system VR mode.
+ * @param proc the WindowProcessController of the process enabling the system VR mode.
*
* @return {@code true} if our state changed.
*/
- private boolean changeVrModeLocked(boolean vrMode, ProcessRecord proc) {
+ private boolean changeVrModeLocked(boolean vrMode, WindowProcessController proc) {
final int oldVrState = mVrState;
// This is the only place where mVrState should have its FLAG_VR_MODE setting
@@ -299,8 +300,9 @@
if (changed) {
if (proc != null) {
- if (proc.vrThreadTid > 0) {
- setVrRenderThreadLocked(proc.vrThreadTid, proc.curSchedGroup, false);
+ if (proc.mVrThreadTid > 0) {
+ setVrRenderThreadLocked(
+ proc.mVrThreadTid, proc.getCurrentSchedulingGroup(), false);
}
} else {
clearVrRenderThreadLocked(false);
diff --git a/services/core/java/com/android/server/am/WindowProcessController.java b/services/core/java/com/android/server/am/WindowProcessController.java
new file mode 100644
index 0000000..817905a
--- /dev/null
+++ b/services/core/java/com/android/server/am/WindowProcessController.java
@@ -0,0 +1,531 @@
+/*
+ * 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.am;
+
+import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_RELEASE;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_RELEASE;
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.am.ActivityStack.ActivityState.DESTROYED;
+import static com.android.server.am.ActivityStack.ActivityState.DESTROYING;
+import static com.android.server.am.ActivityStack.ActivityState.PAUSED;
+import static com.android.server.am.ActivityStack.ActivityState.PAUSING;
+import static com.android.server.am.ActivityStack.ActivityState.RESUMED;
+import static com.android.server.am.ActivityStack.ActivityState.STOPPING;
+
+import android.app.Activity;
+import android.app.ActivityTaskManager;
+import android.app.ActivityThread;
+import android.app.IApplicationThread;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.os.RemoteException;
+import android.util.ArraySet;
+import android.util.Log;
+import android.util.Slog;
+
+import com.android.internal.app.HeavyWeightSwitcherActivity;
+import com.android.internal.util.function.pooled.PooledLambda;
+import com.android.internal.util.function.pooled.PooledRunnable;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
+/**
+ * The Activity Manager (AM) package manages the lifecycle of processes in the system through
+ * {@link ProcessRecord}. However, it is important for the Window Manager (WM) package to be aware
+ * of the processes and their state since it affects how WM manages windows and activities. This
+ * class that allows the {@link ProcessRecord} object in the AM package to communicate important
+ * changes to its state to the WM package in a structured way. WM package also uses
+ * {@link WindowProcessListener} to request changes to the process state on the AM side.
+ * Note that public calls into this class are assumed to be originating from outside the
+ * window manager so the window manager lock is held and appropriate permissions are checked before
+ * calls are allowed to proceed.
+ */
+public class WindowProcessController {
+ private static final String TAG = TAG_WITH_CLASS_NAME ? "WindowProcessController" : TAG_AM;
+ private static final String TAG_RELEASE = TAG + POSTFIX_RELEASE;
+
+ // all about the first app in the process
+ final ApplicationInfo mInfo;
+ final String mName;
+ final int mUid;
+ // The process of this application; 0 if none
+ private volatile int mPid;
+ // user of process.
+ final int mUserId;
+ // The owner of this window process controller object. Mainly for identification when we
+ // communicate back to the activity manager side.
+ public final Object mOwner;
+ // List of packages running in the process
+ final ArraySet<String> mPkgList = new ArraySet<>();
+ private final WindowProcessListener mListener;
+ private final ActivityTaskManagerService mAtm;
+ // The actual proc... may be null only if 'persistent' is true (in which case we are in the
+ // process of launching the app)
+ private volatile IApplicationThread mThread;
+ // Currently desired scheduling class
+ private volatile int mCurSchedGroup;
+ // Last reported process state;
+ private volatile int mRepProcState = PROCESS_STATE_NONEXISTENT;
+ // are we in the process of crashing?
+ private volatile boolean mCrashing;
+ // does the app have a not responding dialog?
+ private volatile boolean mNotResponding;
+ // always keep this application running?
+ private volatile boolean mPersistent;
+ // The ABI this process was launched with
+ private volatile String mRequiredAbi;
+ // Running any services that are foreground?
+ private volatile boolean mHasForegroundServices;
+
+ // Thread currently set for VR scheduling
+ int mVrThreadTid;
+
+ // all activities running in the process
+ private final ArrayList<ActivityRecord> mActivities = new ArrayList<>();
+ // any tasks this process had run root activities in
+ private final ArrayList<TaskRecord> mRecentTasks = new ArrayList<>();
+
+ WindowProcessController(ActivityTaskManagerService atm, ApplicationInfo info, String name,
+ int uid, int userId, Object owner, WindowProcessListener listener) {
+ mInfo = info;
+ mName = name;
+ mUid = uid;
+ mUserId = userId;
+ mOwner = owner;
+ mListener = listener;
+ mAtm = atm;
+ }
+
+ public void setPid(int pid) {
+ mPid = pid;
+ }
+
+ int getPid() {
+ return mPid;
+ }
+
+ public void setThread(IApplicationThread thread) {
+ mThread = thread;
+ }
+
+ IApplicationThread getThread() {
+ return mThread;
+ }
+
+ boolean hasThread() {
+ return mThread != null;
+ }
+
+ public void setCurrentSchedulingGroup(int curSchedGroup) {
+ mCurSchedGroup = curSchedGroup;
+ }
+
+ int getCurrentSchedulingGroup() {
+ return mCurSchedGroup;
+ }
+
+ public void setReportedProcState(int repProcState) {
+ mRepProcState = repProcState;
+ }
+
+ int getReportedProcState() {
+ return mRepProcState;
+ }
+
+ public void setCrashing(boolean crashing) {
+ mCrashing = crashing;
+ }
+
+ boolean isCrashing() {
+ return mCrashing;
+ }
+
+ public void setNotResponding(boolean notResponding) {
+ mNotResponding = notResponding;
+ }
+
+ boolean isNotResponding() {
+ return mNotResponding;
+ }
+
+ public void setPersistent(boolean persistent) {
+ mPersistent = persistent;
+ }
+
+ boolean isPersistent() {
+ return mPersistent;
+ }
+
+ public void setHasForegroundServices(boolean hasForegroundServices) {
+ mHasForegroundServices = hasForegroundServices;
+ }
+
+ boolean hasForegroundServices() {
+ return mHasForegroundServices;
+ }
+
+ public void setRequiredAbi(String requiredAbi) {
+ mRequiredAbi = requiredAbi;
+ }
+
+ String getRequiredAbi() {
+ return mRequiredAbi;
+ }
+
+ public void addPackage(String packageName) {
+ synchronized (mAtm.mGlobalLock) {
+ mPkgList.add(packageName);
+ }
+ }
+
+ public void clearPackageList() {
+ synchronized (mAtm.mGlobalLock) {
+ mPkgList.clear();
+ }
+ }
+
+ void addActivityIfNeeded(ActivityRecord r) {
+ if (mActivities.contains(r)) {
+ return;
+ }
+ mActivities.add(r);
+ }
+
+ void removeActivity(ActivityRecord r) {
+ mActivities.remove(r);
+ }
+
+ public void clearActivities() {
+ synchronized (mAtm.mGlobalLock) {
+ mActivities.clear();
+ }
+ }
+
+ public boolean hasActivities() {
+ synchronized (mAtm.mGlobalLock) {
+ return !mActivities.isEmpty();
+ }
+ }
+
+ public boolean hasVisibleActivities() {
+ synchronized (mAtm.mGlobalLock) {
+ for (int i = mActivities.size() - 1; i >= 0; --i) {
+ final ActivityRecord r = mActivities.get(i);
+ if (r.visible) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ public boolean hasActivitiesOrRecentTasks() {
+ synchronized (mAtm.mGlobalLock) {
+ return !mActivities.isEmpty() || !mRecentTasks.isEmpty();
+ }
+ }
+
+ public void stopFreezingActivities() {
+ synchronized (mAtm.mGlobalLock) {
+ int i = mActivities.size();
+ while (i > 0) {
+ i--;
+ mActivities.get(i).stopFreezingScreenLocked(true);
+ }
+ }
+ }
+
+ public void finishActivities() {
+ synchronized (mAtm.mGlobalLock) {
+ ArrayList<ActivityRecord> activities = new ArrayList<>(mActivities);
+ for (int i = 0; i < activities.size(); i++) {
+ final ActivityRecord r = activities.get(i);
+ if (!r.finishing && r.isInStackLocked()) {
+ r.getStack().finishActivityLocked(r, Activity.RESULT_CANCELED,
+ null, "finish-heavy", true);
+ }
+ }
+ }
+ }
+
+ public boolean isInterestingToUser() {
+ synchronized (mAtm.mGlobalLock) {
+ final int size = mActivities.size();
+ for (int i = 0; i < size; i++) {
+ ActivityRecord r = mActivities.get(i);
+ if (r.isInterestingToUserLocked()) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ public boolean hasRunningActivity(String packageName) {
+ synchronized (mAtm.mGlobalLock) {
+ for (int i = mActivities.size() - 1; i >= 0; --i) {
+ final ActivityRecord r = mActivities.get(i);
+ if (packageName.equals(r.packageName)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ public void clearPackagePreferredForHomeActivities() {
+ synchronized (mAtm.mGlobalLock) {
+ for (int i = mActivities.size() - 1; i >= 0; --i) {
+ final ActivityRecord r = mActivities.get(i);
+ if (r.isActivityTypeHome()) {
+ Log.i(TAG, "Clearing package preferred activities from " + r.packageName);
+ try {
+ ActivityThread.getPackageManager()
+ .clearPackagePreferredActivities(r.packageName);
+ } catch (RemoteException c) {
+ // pm is in same process, this will never happen.
+ }
+ }
+ }
+ }
+ }
+
+ boolean hasStartedActivity(ActivityRecord launchedActivity) {
+ for (int i = mActivities.size() - 1; i >= 0; i--) {
+ final ActivityRecord activity = mActivities.get(i);
+ if (launchedActivity == activity) {
+ continue;
+ }
+ if (!activity.stopped) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+
+ public void updateIntentForHeavyWeightActivity(Intent intent) {
+ synchronized (mAtm.mGlobalLock) {
+ if (mActivities.isEmpty()) {
+ return;
+ }
+ ActivityRecord hist = mActivities.get(0);
+ intent.putExtra(HeavyWeightSwitcherActivity.KEY_CUR_APP, hist.packageName);
+ intent.putExtra(HeavyWeightSwitcherActivity.KEY_CUR_TASK, hist.getTask().taskId);
+ }
+ }
+
+ boolean shouldKillProcessForRemovedTask(TaskRecord tr) {
+ for (int k = 0; k < mActivities.size(); k++) {
+ final TaskRecord otherTask = mActivities.get(k).getTask();
+ if (tr.taskId != otherTask.taskId && otherTask.inRecents) {
+ // Don't kill process(es) that has an activity in a different task that is
+ // also in recents.
+ return false;
+ }
+ }
+ return true;
+ }
+
+ ArraySet<TaskRecord> getReleaseSomeActivitiesTasks() {
+ // Examine all activities currently running in the process.
+ TaskRecord firstTask = null;
+ // Tasks is non-null only if two or more tasks are found.
+ ArraySet<TaskRecord> tasks = null;
+ if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Trying to release some activities in " + this);
+ for (int i = 0; i < mActivities.size(); i++) {
+ final ActivityRecord r = mActivities.get(i);
+ // First, if we find an activity that is in the process of being destroyed,
+ // then we just aren't going to do anything for now; we want things to settle
+ // down before we try to prune more activities.
+ if (r.finishing || r.isState(DESTROYING, DESTROYED)) {
+ if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Abort release; already destroying: " + r);
+ return null;
+ }
+ // Don't consider any activies that are currently not in a state where they
+ // can be destroyed.
+ if (r.visible || !r.stopped || !r.haveState
+ || r.isState(RESUMED, PAUSING, PAUSED, STOPPING)) {
+ if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Not releasing in-use activity: " + r);
+ continue;
+ }
+
+ final TaskRecord task = r.getTask();
+ if (task != null) {
+ if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Collecting release task " + task
+ + " from " + r);
+ if (firstTask == null) {
+ firstTask = task;
+ } else if (firstTask != task) {
+ if (tasks == null) {
+ tasks = new ArraySet<>();
+ tasks.add(firstTask);
+ }
+ tasks.add(task);
+ }
+ }
+ }
+
+ return tasks;
+ }
+
+ public interface ComputeOomAdjCallback {
+ void onVisibleActivity();
+ void onPausedActivity();
+ void onStoppingActivity(boolean finishing);
+ void onOtherActivity();
+ }
+
+ public int computeOomAdjFromActivities(int minTaskLayer, ComputeOomAdjCallback callback) {
+ synchronized (mAtm.mGlobalLock) {
+ final int activitiesSize = mActivities.size();
+ for (int j = 0; j < activitiesSize; j++) {
+ final ActivityRecord r = mActivities.get(j);
+ if (r.app != this) {
+ Log.e(TAG, "Found activity " + r + " in proc activity list using " + r.app
+ + " instead of expected " + this);
+ if (r.app == null || (r.app.mUid == mUid)) {
+ // Only fix things up when they look sane
+ r.setProcess(this);
+ } else {
+ continue;
+ }
+ }
+ if (r.visible) {
+ callback.onVisibleActivity();
+ final TaskRecord task = r.getTask();
+ if (task != null && minTaskLayer > 0) {
+ final int layer = task.mLayerRank;
+ if (layer >= 0 && minTaskLayer > layer) {
+ minTaskLayer = layer;
+ }
+ }
+ break;
+ } else if (r.isState(PAUSING, PAUSED)) {
+ callback.onPausedActivity();
+ } else if (r.isState(STOPPING)) {
+ callback.onStoppingActivity(r.finishing);
+ } else {
+ callback.onOtherActivity();
+ }
+ }
+ }
+
+ return minTaskLayer;
+ }
+
+ void clearProfilerIfNeeded() {
+ if (mListener == null) return;
+ // Posting on handler so WM lock isn't held when we call into AM.
+ mAtm.mH.post(() -> mListener.clearProfilerIfNeeded());
+ }
+
+ void updateProcessInfo(boolean updateServiceConnectionActivities, boolean updateLru,
+ boolean activityChange, boolean updateOomAdj) {
+ if (mListener == null) return;
+ // Posting on handler so WM lock isn't held when we call into AM.
+ final Runnable r = PooledLambda.obtainRunnable(WindowProcessListener::updateProcessInfo,
+ mListener, updateServiceConnectionActivities, updateLru, activityChange,
+ updateOomAdj);
+ mAtm.mH.post(r);
+ }
+
+ void updateServiceConnectionActivities() {
+ if (mListener == null) return;
+ // Posting on handler so WM lock isn't held when we call into AM.
+ mAtm.mH.post(() -> mListener.updateServiceConnectionActivities());
+ }
+
+ void setPendingUiClean(boolean pendingUiClean) {
+ if (mListener == null) return;
+ // Posting on handler so WM lock isn't held when we call into AM.
+ final Runnable r = PooledLambda.obtainRunnable(
+ WindowProcessListener::setPendingUiClean, mListener, pendingUiClean);
+ mAtm.mH.post(r);
+ }
+
+ void setPendingUiCleanAndForceProcessStateUpTo(int newState) {
+ if (mListener == null) return;
+ // Posting on handler so WM lock isn't held when we call into AM.
+ final Runnable r = PooledLambda.obtainRunnable(
+ WindowProcessListener::setPendingUiCleanAndForceProcessStateUpTo,
+ mListener, newState);
+ mAtm.mH.post(r);
+ }
+
+ void setRemoved(boolean removed) {
+ if (mListener == null) return;
+ // Posting on handler so WM lock isn't held when we call into AM.
+ final Runnable r = PooledLambda.obtainRunnable(
+ WindowProcessListener::setRemoved, mListener, removed);
+ mAtm.mH.post(r);
+ }
+
+ /** Returns the total time (in milliseconds) spent executing in both user and system code. */
+ public long getCpuTime() {
+ return (mListener != null) ? mListener.getCpuTime() : 0;
+ }
+
+ void addRecentTask(TaskRecord task) {
+ mRecentTasks.add(task);
+ }
+
+ void removeRecentTask(TaskRecord task) {
+ mRecentTasks.remove(task);
+ }
+
+ public boolean hasRecentTasks() {
+ synchronized (mAtm.mGlobalLock) {
+ return !mRecentTasks.isEmpty();
+ }
+ }
+
+ public void clearRecentTasks() {
+ synchronized (mAtm.mGlobalLock) {
+ for (int i = mRecentTasks.size() - 1; i >= 0; i--) {
+ mRecentTasks.get(i).clearRootProcess();
+ }
+ mRecentTasks.clear();
+ }
+ }
+
+ public void dump(PrintWriter pw, String prefix) {
+ synchronized (mAtm.mGlobalLock) {
+ if (mActivities.size() > 0) {
+ pw.print(prefix); pw.println("Activities:");
+ for (int i = 0; i < mActivities.size(); i++) {
+ pw.print(prefix); pw.print(" - "); pw.println(mActivities.get(i));
+ }
+ }
+
+ if (mRecentTasks.size() > 0) {
+ pw.println(prefix + "Recent Tasks:");
+ for (int i = 0; i < mRecentTasks.size(); i++) {
+ pw.println(prefix + " - " + mRecentTasks.get(i));
+ }
+ }
+
+ if (mVrThreadTid != 0) {
+ pw.print(prefix); pw.print("mVrThreadTid="); pw.println(mVrThreadTid);
+ }
+ }
+ }
+
+}
diff --git a/services/core/java/com/android/server/am/WindowProcessListener.java b/services/core/java/com/android/server/am/WindowProcessListener.java
new file mode 100644
index 0000000..2de3e37
--- /dev/null
+++ b/services/core/java/com/android/server/am/WindowProcessListener.java
@@ -0,0 +1,50 @@
+/*
+ * 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.am;
+
+/**
+ * Interface used by the owner/creator of a process that owns windows to listen to changes from the
+ * WM side.
+ * @see WindowProcessController
+ */
+public interface WindowProcessListener {
+
+ /** Clear the profiler record if we are currently profiling this process. */
+ void clearProfilerIfNeeded();
+
+ /** Update the service connection for this process based on activities it might have. */
+ void updateServiceConnectionActivities();
+
+ /** Set or clear flag that we would like to clean-up UI resources for this process. */
+ void setPendingUiClean(boolean pendingUiClean);
+
+ /**
+ * Set flag that we would like to clean-up UI resources for this process and force new process
+ * state.
+ */
+ void setPendingUiCleanAndForceProcessStateUpTo(int newState);
+
+ /** Update the process information. */
+ void updateProcessInfo(boolean updateServiceConnectionActivities, boolean updateLru,
+ boolean activityChange, boolean updateOomAdj);
+
+ /** Set process package been removed from device. */
+ void setRemoved(boolean removed);
+
+ /** Returns the total time (in milliseconds) spent executing in both user and system code. */
+ long getCpuTime();
+}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 571ee42..6bebbe2 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -20,8 +20,6 @@
import static android.media.AudioManager.RINGER_MODE_NORMAL;
import static android.media.AudioManager.RINGER_MODE_SILENT;
import static android.media.AudioManager.RINGER_MODE_VIBRATE;
-import static android.media.AudioManager.STREAM_ALARM;
-import static android.media.AudioManager.STREAM_MUSIC;
import static android.media.AudioManager.STREAM_SYSTEM;
import static android.os.Process.FIRST_APPLICATION_UID;
import static android.provider.Settings.Secure.VOLUME_HUSH_MUTE;
@@ -33,7 +31,6 @@
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
-import android.app.ActivityTaskManagerInternal;
import android.app.AppGlobals;
import android.app.AppOpsManager;
import android.app.IUidObserver;
@@ -60,6 +57,7 @@
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
import android.database.ContentObserver;
+import android.hardware.hdmi.HdmiAudioSystemClient;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiPlaybackClient;
import android.hardware.hdmi.HdmiTvClient;
@@ -142,6 +140,7 @@
import com.android.server.audio.AudioServiceEvents.VolumeEvent;
import com.android.server.audio.AudioServiceEvents.WiredDevConnectEvent;
import com.android.server.pm.UserManagerService;
+import com.android.server.wm.ActivityTaskManagerInternal;
import org.xmlpull.v1.XmlPullParserException;
@@ -928,14 +927,15 @@
}
if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_HDMI_CEC)) {
- mHdmiManager = mContext.getSystemService(HdmiControlManager.class);
- synchronized (mHdmiManager) {
+ synchronized (mHdmiClientLock) {
+ mHdmiManager = mContext.getSystemService(HdmiControlManager.class);
mHdmiTvClient = mHdmiManager.getTvClient();
if (mHdmiTvClient != null) {
mFixedVolumeDevices &= ~AudioSystem.DEVICE_ALL_HDMI_SYSTEM_AUDIO_AND_SPEAKER;
}
mHdmiPlaybackClient = mHdmiManager.getPlaybackClient();
mHdmiCecSink = false;
+ mHdmiAudioSystemClient = mHdmiManager.getAudioSystemClient();
}
}
@@ -1056,11 +1056,9 @@
sendEncodedSurroundMode(mContentResolver, "onAudioServerDied");
sendEnabledSurroundFormats(mContentResolver, true);
}
- if (mHdmiManager != null) {
- synchronized (mHdmiManager) {
- if (mHdmiTvClient != null) {
- setHdmiSystemAudioSupported(mHdmiSystemAudioSupported);
- }
+ synchronized (mHdmiClientLock) {
+ if (mHdmiManager != null && mHdmiTvClient != null) {
+ setHdmiSystemAudioSupported(mHdmiSystemAudioSupported);
}
}
@@ -1765,24 +1763,31 @@
if (streamTypeAlias == AudioSystem.STREAM_MUSIC) {
setSystemAudioVolume(oldIndex, newIndex, getStreamMaxVolume(streamType), flags);
}
- if (mHdmiManager != null) {
- synchronized (mHdmiManager) {
+ synchronized (mHdmiClientLock) {
+ if (mHdmiManager != null) {
// mHdmiCecSink true => mHdmiPlaybackClient != null
if (mHdmiCecSink &&
streamTypeAlias == AudioSystem.STREAM_MUSIC &&
oldIndex != newIndex) {
- synchronized (mHdmiPlaybackClient) {
- int keyCode = (direction == -1) ? KeyEvent.KEYCODE_VOLUME_DOWN :
- KeyEvent.KEYCODE_VOLUME_UP;
- final long ident = Binder.clearCallingIdentity();
- try {
- mHdmiPlaybackClient.sendKeyEvent(keyCode, true);
- mHdmiPlaybackClient.sendKeyEvent(keyCode, false);
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
+ int keyCode = (direction == -1) ? KeyEvent.KEYCODE_VOLUME_DOWN :
+ KeyEvent.KEYCODE_VOLUME_UP;
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mHdmiPlaybackClient.sendKeyEvent(keyCode, true);
+ mHdmiPlaybackClient.sendKeyEvent(keyCode, false);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
}
}
+
+ if (mHdmiAudioSystemClient != null &&
+ streamTypeAlias == AudioSystem.STREAM_MUSIC &&
+ (oldIndex != newIndex || isMuteAdjust)) {
+ mHdmiAudioSystemClient.sendReportAudioStatusCecCommand(
+ isMuteAdjust, getStreamVolume(AudioSystem.STREAM_MUSIC),
+ getStreamMaxVolume(AudioSystem.STREAM_MUSIC),
+ isStreamMute(AudioSystem.STREAM_MUSIC));
+ }
}
}
}
@@ -1801,22 +1806,19 @@
}
private void setSystemAudioVolume(int oldVolume, int newVolume, int maxVolume, int flags) {
- if (mHdmiManager == null
- || mHdmiTvClient == null
- || oldVolume == newVolume
- || (flags & AudioManager.FLAG_HDMI_SYSTEM_AUDIO_VOLUME) != 0) return;
-
// Sets the audio volume of AVR when we are in system audio mode. The new volume info
// is tranformed to HDMI-CEC commands and passed through CEC bus.
- synchronized (mHdmiManager) {
- if (!mHdmiSystemAudioSupported) return;
- synchronized (mHdmiTvClient) {
- final long token = Binder.clearCallingIdentity();
- try {
- mHdmiTvClient.setSystemAudioVolume(oldVolume, newVolume, maxVolume);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
+ synchronized (mHdmiClientLock) {
+ if (mHdmiManager == null
+ || mHdmiTvClient == null
+ || oldVolume == newVolume
+ || (flags & AudioManager.FLAG_HDMI_SYSTEM_AUDIO_VOLUME) != 0
+ || !mHdmiSystemAudioSupported) return;
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mHdmiTvClient.setSystemAudioVolume(oldVolume, newVolume, maxVolume);
+ } finally {
+ Binder.restoreCallingIdentity(token);
}
}
}
@@ -2039,6 +2041,17 @@
index = mStreamStates[streamType].getIndex(device);
}
}
+ synchronized (mHdmiClientLock) {
+ if (mHdmiManager != null &&
+ mHdmiAudioSystemClient != null &&
+ streamTypeAlias == AudioSystem.STREAM_MUSIC &&
+ (oldIndex != index)) {
+ mHdmiAudioSystemClient.sendReportAudioStatusCecCommand(
+ false, getStreamVolume(AudioSystem.STREAM_MUSIC),
+ getStreamMaxVolume(AudioSystem.STREAM_MUSIC),
+ isStreamMute(AudioSystem.STREAM_MUSIC));
+ }
+ }
sendVolumeUpdate(streamType, oldIndex, index, flags);
}
@@ -2179,8 +2192,8 @@
// If Hdmi-CEC system audio mode is on, we show volume bar only when TV
// receives volume notification from Audio Receiver.
private int updateFlagsForSystemAudio(int flags) {
- if (mHdmiTvClient != null) {
- synchronized (mHdmiTvClient) {
+ synchronized (mHdmiClientLock) {
+ if (mHdmiTvClient != null) {
if (mHdmiSystemAudioSupported &&
((flags & AudioManager.FLAG_HDMI_SYSTEM_AUDIO_VOLUME) == 0)) {
flags &= ~AudioManager.FLAG_SHOW_UI;
@@ -2235,16 +2248,13 @@
}
private void setSystemAudioMute(boolean state) {
- if (mHdmiManager == null || mHdmiTvClient == null) return;
- synchronized (mHdmiManager) {
- if (!mHdmiSystemAudioSupported) return;
- synchronized (mHdmiTvClient) {
- final long token = Binder.clearCallingIdentity();
- try {
- mHdmiTvClient.setSystemAudioMute(state);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
+ synchronized (mHdmiClientLock) {
+ if (mHdmiManager == null || mHdmiTvClient == null || !mHdmiSystemAudioSupported) return;
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mHdmiTvClient.setSystemAudioMute(state);
+ } finally {
+ Binder.restoreCallingIdentity(token);
}
}
}
@@ -3816,6 +3826,10 @@
int delay = checkSendBecomingNoisyIntent(
AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, intState,
AudioSystem.DEVICE_NONE);
+ final String addr = btDevice == null ? "null" : btDevice.getAddress();
+ mDeviceLogger.log(new AudioEventLogger.StringEvent(
+ "A2DP service connected: device addr=" + addr
+ + " state=" + state));
queueMsgUnderWakeLock(mAudioHandler,
MSG_SET_A2DP_SINK_CONNECTION_STATE,
state,
@@ -4679,7 +4693,14 @@
public int setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(BluetoothDevice device,
int state, int profile, boolean suppressNoisyIntent, int a2dpVolume)
{
+ mDeviceLogger.log(new AudioEventLogger.StringEvent(
+ "setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent state=" + state
+ // only querying address as this is the only readily available field on the device
+ + " addr=" + device.getAddress()
+ + " prof=" + profile + " supprNoisy=" + suppressNoisyIntent
+ + " vol=" + a2dpVolume));
if (mAudioHandler.hasMessages(MSG_SET_A2DP_SINK_CONNECTION_STATE, device)) {
+ mDeviceLogger.log(new AudioEventLogger.StringEvent("A2DP connection state ignored"));
return 0;
}
return setBluetoothA2dpDeviceConnectionStateInt(
@@ -5632,7 +5653,7 @@
case MSG_SET_WIRED_DEVICE_CONNECTION_STATE:
{ WiredDeviceConnectionState connectState =
(WiredDeviceConnectionState)msg.obj;
- mWiredDevLogger.log(new WiredDevConnectEvent(connectState));
+ mDeviceLogger.log(new WiredDevConnectEvent(connectState));
onSetWiredDeviceConnectionState(connectState.mType, connectState.mState,
connectState.mAddress, connectState.mName, connectState.mCaller);
mAudioEventWakeLock.release();
@@ -6087,10 +6108,14 @@
if (!BluetoothAdapter.checkBluetoothAddress(address)) {
address = "";
}
+ mDeviceLogger.log(new AudioEventLogger.StringEvent(
+ "onBluetoothA2dpDeviceConfigChange addr=" + address));
int device = AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP;
synchronized (mConnectedDevices) {
if (mAudioHandler.hasMessages(MSG_SET_A2DP_SINK_CONNECTION_STATE, btDevice)) {
+ mDeviceLogger.log(new AudioEventLogger.StringEvent(
+ "A2dp config change ignored"));
return;
}
final String key = makeDeviceListKey(device, address);
@@ -6357,12 +6382,10 @@
if (isPlatformTelevision() && ((device & AudioSystem.DEVICE_OUT_HDMI) != 0)) {
mFixedVolumeDevices |= AudioSystem.DEVICE_OUT_HDMI;
checkAllFixedVolumeDevices();
- if (mHdmiManager != null) {
- synchronized (mHdmiManager) {
- if (mHdmiPlaybackClient != null) {
- mHdmiCecSink = false;
- mHdmiPlaybackClient.queryDisplayStatus(mHdmiDisplayStatusCallback);
- }
+ synchronized (mHdmiClientLock) {
+ if (mHdmiManager != null && mHdmiPlaybackClient != null) {
+ mHdmiCecSink = false;
+ mHdmiPlaybackClient.queryDisplayStatus(mHdmiDisplayStatusCallback);
}
}
}
@@ -6371,8 +6394,8 @@
}
} else {
if (isPlatformTelevision() && ((device & AudioSystem.DEVICE_OUT_HDMI) != 0)) {
- if (mHdmiManager != null) {
- synchronized (mHdmiManager) {
+ synchronized (mHdmiClientLock) {
+ if (mHdmiManager != null) {
mHdmiCecSink = false;
}
}
@@ -7041,8 +7064,8 @@
private class MyDisplayStatusCallback implements HdmiPlaybackClient.DisplayStatusCallback {
public void onComplete(int status) {
- if (mHdmiManager != null) {
- synchronized (mHdmiManager) {
+ synchronized (mHdmiClientLock) {
+ if (mHdmiManager != null) {
mHdmiCecSink = (status != HdmiControlManager.POWER_STATUS_UNKNOWN);
// Television devices without CEC service apply software volume on HDMI output
if (isPlatformTelevision() && !mHdmiCecSink) {
@@ -7052,43 +7075,49 @@
}
}
}
- };
+ }
+
+ private final Object mHdmiClientLock = new Object();
// If HDMI-CEC system audio is supported
private boolean mHdmiSystemAudioSupported = false;
// Set only when device is tv.
+ @GuardedBy("mHdmiClientLock")
private HdmiTvClient mHdmiTvClient;
// true if the device has system feature PackageManager.FEATURE_LEANBACK.
// cached HdmiControlManager interface
+ @GuardedBy("mHdmiClientLock")
private HdmiControlManager mHdmiManager;
// Set only when device is a set-top box.
+ @GuardedBy("mHdmiClientLock")
private HdmiPlaybackClient mHdmiPlaybackClient;
// true if we are a set-top box, an HDMI sink is connected and it supports CEC.
private boolean mHdmiCecSink;
+ // Set only when device is an audio system.
+ @GuardedBy("mHdmiClientLock")
+ private HdmiAudioSystemClient mHdmiAudioSystemClient;
private MyDisplayStatusCallback mHdmiDisplayStatusCallback = new MyDisplayStatusCallback();
@Override
public int setHdmiSystemAudioSupported(boolean on) {
int device = AudioSystem.DEVICE_NONE;
- if (mHdmiManager != null) {
- synchronized (mHdmiManager) {
+ synchronized (mHdmiClientLock) {
+ if (mHdmiManager != null) {
if (mHdmiTvClient == null) {
Log.w(TAG, "Only Hdmi-Cec enabled TV device supports system audio mode.");
return device;
}
- synchronized (mHdmiTvClient) {
- if (mHdmiSystemAudioSupported != on) {
- mHdmiSystemAudioSupported = on;
- final int config = on ? AudioSystem.FORCE_HDMI_SYSTEM_AUDIO_ENFORCED :
- AudioSystem.FORCE_NONE;
- mForceUseLogger.log(new ForceUseEvent(AudioSystem.FOR_HDMI_SYSTEM_AUDIO,
- config, "setHdmiSystemAudioSupported"));
- AudioSystem.setForceUse(AudioSystem.FOR_HDMI_SYSTEM_AUDIO, config);
- }
- device = getDevicesForStream(AudioSystem.STREAM_MUSIC);
+ if (mHdmiSystemAudioSupported != on) {
+ mHdmiSystemAudioSupported = on;
+ final int config = on ? AudioSystem.FORCE_HDMI_SYSTEM_AUDIO_ENFORCED :
+ AudioSystem.FORCE_NONE;
+ mForceUseLogger.log(new ForceUseEvent(AudioSystem.FOR_HDMI_SYSTEM_AUDIO,
+ config, "setHdmiSystemAudioSupported"));
+ AudioSystem.setForceUse(AudioSystem.FOR_HDMI_SYSTEM_AUDIO, config);
}
+ device = getDevicesForStream(AudioSystem.STREAM_MUSIC);
}
}
return device;
@@ -7187,19 +7216,20 @@
//==========================================================================================
// AudioService logging and dumpsys
//==========================================================================================
- final int LOG_NB_EVENTS_PHONE_STATE = 20;
- final int LOG_NB_EVENTS_WIRED_DEV_CONNECTION = 30;
- final int LOG_NB_EVENTS_FORCE_USE = 20;
- final int LOG_NB_EVENTS_VOLUME = 40;
- final int LOG_NB_EVENTS_DYN_POLICY = 10;
+ static final int LOG_NB_EVENTS_PHONE_STATE = 20;
+ static final int LOG_NB_EVENTS_DEVICE_CONNECTION = 30;
+ static final int LOG_NB_EVENTS_FORCE_USE = 20;
+ static final int LOG_NB_EVENTS_VOLUME = 40;
+ static final int LOG_NB_EVENTS_DYN_POLICY = 10;
final private AudioEventLogger mModeLogger = new AudioEventLogger(LOG_NB_EVENTS_PHONE_STATE,
"phone state (logged after successfull call to AudioSystem.setPhoneState(int))");
- final private AudioEventLogger mWiredDevLogger = new AudioEventLogger(
- LOG_NB_EVENTS_WIRED_DEV_CONNECTION,
- "wired device connection (logged before onSetWiredDeviceConnectionState() is executed)"
- );
+ // logs for wired + A2DP device connections:
+ // - wired: logged before onSetWiredDeviceConnectionState() is executed
+ // - A2DP: logged at reception of method call
+ final private AudioEventLogger mDeviceLogger = new AudioEventLogger(
+ LOG_NB_EVENTS_DEVICE_CONNECTION, "wired/A2DP device connection");
final private AudioEventLogger mForceUseLogger = new AudioEventLogger(
LOG_NB_EVENTS_FORCE_USE,
@@ -7288,7 +7318,7 @@
pw.println("\nEvent logs:");
mModeLogger.dump(pw);
pw.println("\n");
- mWiredDevLogger.dump(pw);
+ mDeviceLogger.dump(pw);
pw.println("\n");
mForceUseLogger.dump(pw);
pw.println("\n");
diff --git a/services/core/java/com/android/server/fingerprint/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/common/AuthenticationClient.java
similarity index 70%
rename from services/core/java/com/android/server/fingerprint/AuthenticationClient.java
rename to services/core/java/com/android/server/biometrics/common/AuthenticationClient.java
index afd1a94..6e5858a 100644
--- a/services/core/java/com/android/server/fingerprint/AuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/common/AuthenticationClient.java
@@ -1,5 +1,5 @@
-/**
- * Copyright (C) 2016 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.
@@ -11,25 +11,23 @@
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
- * limitations under the License.
+ * limitations under the License
*/
-package com.android.server.fingerprint;
+package com.android.server.biometrics.common;
import android.content.Context;
-import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
+import android.hardware.biometrics.BiometricAuthenticator;
+import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricPrompt;
import android.hardware.biometrics.IBiometricPromptReceiver;
import android.hardware.fingerprint.Fingerprint;
import android.hardware.fingerprint.FingerprintManager;
-import android.hardware.fingerprint.IFingerprintServiceReceiver;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.statusbar.IStatusBarService;
/**
@@ -51,6 +49,7 @@
private Bundle mBundle;
private IStatusBarService mStatusBarService;
private boolean mInLockout;
+ // TODO: BiometricManager, after other biometric modalities are introduced.
private final FingerprintManager mFingerprintManager;
protected boolean mDialogDismissed;
@@ -62,12 +61,12 @@
try {
mDialogReceiverFromClient.onDialogDismissed(reason);
if (reason == BiometricPrompt.DISMISSED_REASON_USER_CANCEL) {
- onError(FingerprintManager.FINGERPRINT_ERROR_USER_CANCELED,
+ onError(getHalDeviceId(), BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED,
0 /* vendorCode */);
}
mDialogDismissed = true;
} catch (RemoteException e) {
- Slog.e(TAG, "Unable to notify dialog dismissed", e);
+ Slog.e(getLogTag(), "Unable to notify dialog dismissed", e);
}
stop(true /* initiatedByClient */);
}
@@ -85,11 +84,13 @@
*/
public abstract void onStop();
- public AuthenticationClient(Context context, long halDeviceId, IBinder token,
- IFingerprintServiceReceiver receiver, int targetUserId, int groupId, long opId,
+ public AuthenticationClient(Context context, Metrics metrics,
+ BiometricService.DaemonWrapper daemon, long halDeviceId, IBinder token,
+ BiometricService.ServiceListener listener, int targetUserId, int groupId, long opId,
boolean restricted, String owner, Bundle bundle,
IBiometricPromptReceiver dialogReceiver, IStatusBarService statusBarService) {
- super(context, halDeviceId, token, receiver, targetUserId, groupId, restricted, owner);
+ super(context, metrics, daemon, halDeviceId, token, listener, targetUserId, groupId,
+ restricted, owner);
mOpId = opId;
mBundle = bundle;
mDialogReceiverFromClient = dialogReceiver;
@@ -112,17 +113,17 @@
// If the dialog is showing, the client doesn't need to receive onAcquired messages.
if (mBundle != null) {
try {
- if (acquiredInfo != FingerprintManager.FINGERPRINT_ACQUIRED_GOOD) {
+ if (acquiredInfo != BiometricConstants.BIOMETRIC_ACQUIRED_GOOD) {
mStatusBarService.onFingerprintHelp(
mFingerprintManager.getAcquiredString(acquiredInfo, vendorCode));
}
return false; // acquisition continues
} catch (RemoteException e) {
- Slog.e(TAG, "Remote exception when sending acquired message", e);
+ Slog.e(getLogTag(), "Remote exception when sending acquired message", e);
return true; // client failed
} finally {
// Good scans will keep the device awake
- if (acquiredInfo == FingerprintManager.FINGERPRINT_ACQUIRED_GOOD) {
+ if (acquiredInfo == BiometricConstants.BIOMETRIC_ACQUIRED_GOOD) {
notifyUserActivity();
}
}
@@ -132,7 +133,7 @@
}
@Override
- public boolean onError(int error, int vendorCode) {
+ public boolean onError(long deviceId, int error, int vendorCode) {
if (mDialogDismissed) {
// If user cancels authentication, the application has already received the
// FingerprintManager.FINGERPRINT_ERROR_USER_CANCELED message from onDialogDismissed()
@@ -145,10 +146,10 @@
mStatusBarService.onFingerprintError(
mFingerprintManager.getErrorString(error, vendorCode));
} catch (RemoteException e) {
- Slog.e(TAG, "Remote exception when sending error", e);
+ Slog.e(getLogTag(), "Remote exception when sending error", e);
}
}
- return super.onError(error, vendorCode);
+ return super.onError(deviceId, error, vendorCode);
}
@Override
@@ -166,36 +167,35 @@
com.android.internal.R.string.fingerprint_not_recognized));
}
} catch (RemoteException e) {
- Slog.e(TAG, "Failed to notify Authenticated:", e);
+ Slog.e(getLogTag(), "Failed to notify Authenticated:", e);
}
}
- IFingerprintServiceReceiver receiver = getReceiver();
- if (receiver != null) {
+ final BiometricService.ServiceListener listener = getListener();
+ if (listener != null) {
try {
- MetricsLogger.action(getContext(), MetricsEvent.ACTION_FINGERPRINT_AUTH,
- authenticated);
+ mMetricsLogger.action(mMetrics.actionBiometricAuth(), authenticated);
if (!authenticated) {
- receiver.onAuthenticationFailed(getHalDeviceId());
+ listener.onAuthenticationFailed(getHalDeviceId());
} else {
if (DEBUG) {
- Slog.v(TAG, "onAuthenticated(owner=" + getOwnerString()
+ Slog.v(getLogTag(), "onAuthenticated(owner=" + getOwnerString()
+ ", id=" + fingerId + ", gp=" + groupId + ")");
}
Fingerprint fp = !getIsRestricted()
? new Fingerprint("" /* TODO */, groupId, fingerId, getHalDeviceId())
: null;
- receiver.onAuthenticationSucceeded(getHalDeviceId(), fp, getTargetUserId());
+ listener.onAuthenticationSucceeded(getHalDeviceId(), fp, getTargetUserId());
}
} catch (RemoteException e) {
- Slog.w(TAG, "Failed to notify Authenticated:", e);
+ Slog.w(getLogTag(), "Failed to notify Authenticated:", e);
result = true; // client failed
}
} else {
result = true; // client not listening
}
if (!authenticated) {
- if (receiver != null) {
+ if (listener != null) {
vibrateError();
}
// allow system-defined limit of number of attempts before giving up
@@ -203,17 +203,17 @@
if (lockoutMode != LOCKOUT_NONE) {
try {
mInLockout = true;
- Slog.w(TAG, "Forcing lockout (fp driver code should do this!), mode(" +
+ Slog.w(getLogTag(), "Forcing lockout (fp driver code should do this!), mode(" +
lockoutMode + ")");
stop(false);
int errorCode = lockoutMode == LOCKOUT_TIMED ?
- FingerprintManager.FINGERPRINT_ERROR_LOCKOUT :
- FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT;
+ BiometricConstants.BIOMETRIC_ERROR_LOCKOUT :
+ BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT;
// TODO: if the dialog is showing, this error should be delayed. On a similar
// note, AuthenticationClient should override onError and delay all other errors
// as well, if the dialog is showing
- receiver.onError(getHalDeviceId(), errorCode, 0 /* vendorCode */);
+ listener.onError(getHalDeviceId(), errorCode, 0 /* vendorCode */);
// Send the lockout message to the system dialog
if (mBundle != null) {
@@ -221,12 +221,12 @@
mFingerprintManager.getErrorString(errorCode, 0 /* vendorCode */));
}
} catch (RemoteException e) {
- Slog.w(TAG, "Failed to notify lockout:", e);
+ Slog.w(getLogTag(), "Failed to notify lockout:", e);
}
}
result |= lockoutMode != LOCKOUT_NONE; // in a lockout mode
} else {
- if (receiver != null) {
+ if (listener != null) {
vibrateSuccess();
}
result |= true; // we have a valid fingerprint, done
@@ -241,32 +241,28 @@
*/
@Override
public int start() {
- IBiometricsFingerprint daemon = getFingerprintDaemon();
- if (daemon == null) {
- Slog.w(TAG, "start authentication: no fingerprint HAL!");
- return ERROR_ESRCH;
- }
onStart();
try {
- final int result = daemon.authenticate(mOpId, getGroupId());
+ final int result = getDaemonWrapper().authenticate(mOpId, getGroupId());
if (result != 0) {
- Slog.w(TAG, "startAuthentication failed, result=" + result);
- MetricsLogger.histogram(getContext(), "fingeprintd_auth_start_error", result);
- onError(FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */);
+ Slog.w(getLogTag(), "startAuthentication failed, result=" + result);
+ mMetricsLogger.histogram(mMetrics.tagAuthStartError(), result);
+ onError(getHalDeviceId(), BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE,
+ 0 /* vendorCode */);
return result;
}
- if (DEBUG) Slog.w(TAG, "client " + getOwnerString() + " is authenticating...");
+ if (DEBUG) Slog.w(getLogTag(), "client " + getOwnerString() + " is authenticating...");
// If authenticating with system dialog, show the dialog
if (mBundle != null) {
try {
mStatusBarService.showFingerprintDialog(mBundle, mDialogReceiver);
} catch (RemoteException e) {
- Slog.e(TAG, "Unable to show fingerprint dialog", e);
+ Slog.e(getLogTag(), "Unable to show fingerprint dialog", e);
}
}
} catch (RemoteException e) {
- Slog.e(TAG, "startAuthentication failed", e);
+ Slog.e(getLogTag(), "startAuthentication failed", e);
return ERROR_ESRCH;
}
return 0; // success
@@ -275,25 +271,21 @@
@Override
public int stop(boolean initiatedByClient) {
if (mAlreadyCancelled) {
- Slog.w(TAG, "stopAuthentication: already cancelled!");
+ Slog.w(getLogTag(), "stopAuthentication: already cancelled!");
return 0;
}
onStop();
- IBiometricsFingerprint daemon = getFingerprintDaemon();
- if (daemon == null) {
- Slog.w(TAG, "stopAuthentication: no fingerprint HAL!");
- return ERROR_ESRCH;
- }
+
try {
- final int result = daemon.cancel();
+ final int result = getDaemonWrapper().cancel();
if (result != 0) {
- Slog.w(TAG, "stopAuthentication failed, result=" + result);
+ Slog.w(getLogTag(), "stopAuthentication failed, result=" + result);
return result;
}
- if (DEBUG) Slog.w(TAG, "client " + getOwnerString() + " is no longer authenticating");
+ if (DEBUG) Slog.w(getLogTag(), "client " + getOwnerString() + " is no longer authenticating");
} catch (RemoteException e) {
- Slog.e(TAG, "stopAuthentication failed", e);
+ Slog.e(getLogTag(), "stopAuthentication failed", e);
return ERROR_ESRCH;
} finally {
// If the user already cancelled authentication (via some interaction with the
@@ -304,29 +296,32 @@
try {
mStatusBarService.hideFingerprintDialog();
} catch (RemoteException e) {
- Slog.e(TAG, "Unable to hide fingerprint dialog", e);
+ Slog.e(getLogTag(), "Unable to hide fingerprint dialog", e);
}
}
}
+
mAlreadyCancelled = true;
return 0; // success
}
@Override
- public boolean onEnrollResult(int fingerId, int groupId, int remaining) {
- if (DEBUG) Slog.w(TAG, "onEnrollResult() called for authenticate!");
+ public boolean onEnrollResult(BiometricAuthenticator.Identifier identifier,
+ int remaining) {
+ if (DEBUG) Slog.w(getLogTag(), "onEnrollResult() called for authenticate!");
return true; // Invalid for Authenticate
}
@Override
- public boolean onRemoved(int fingerId, int groupId, int remaining) {
- if (DEBUG) Slog.w(TAG, "onRemoved() called for authenticate!");
+ public boolean onRemoved(BiometricAuthenticator.Identifier identifier, int remaining) {
+ if (DEBUG) Slog.w(getLogTag(), "onRemoved() called for authenticate!");
return true; // Invalid for Authenticate
}
@Override
- public boolean onEnumerationResult(int fingerId, int groupId, int remaining) {
- if (DEBUG) Slog.w(TAG, "onEnumerationResult() called for authenticate!");
+ public boolean onEnumerationResult(BiometricAuthenticator.Identifier identifier,
+ int remaining) {
+ if (DEBUG) Slog.w(getLogTag(), "onEnumerationResult() called for authenticate!");
return true; // Invalid for Authenticate
}
}
diff --git a/services/core/java/com/android/server/biometrics/common/BiometricService.java b/services/core/java/com/android/server/biometrics/common/BiometricService.java
new file mode 100644
index 0000000..41b1575
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/common/BiometricService.java
@@ -0,0 +1,1099 @@
+/*
+ * 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.biometrics.common;
+
+import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE;
+
+import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
+import android.app.AlarmManager;
+import android.app.AppOpsManager;
+import android.app.IActivityTaskManager;
+import android.app.PendingIntent;
+import android.app.SynchronousUserSwitchObserver;
+import android.app.TaskStackListener;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
+import android.hardware.biometrics.BiometricAuthenticator;
+import android.hardware.biometrics.BiometricConstants;
+import android.hardware.biometrics.IBiometricPromptReceiver;
+import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
+import android.hardware.fingerprint.Fingerprint;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.DeadObjectException;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.IHwBinder;
+import android.os.IRemoteCallback;
+import android.os.PowerManager;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.security.KeyStore;
+import android.util.Slog;
+import android.util.SparseBooleanArray;
+import android.util.SparseIntArray;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.statusbar.IStatusBarService;
+import com.android.server.SystemService;
+import com.android.server.biometrics.face.FaceService;
+import com.android.server.biometrics.fingerprint.FingerprintService;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Abstract base class containing all of the business logic for biometric services, e.g.
+ * Fingerprint, Face, Iris.
+ *
+ * @hide
+ */
+public abstract class BiometricService extends SystemService implements IHwBinder.DeathRecipient {
+
+ protected static final boolean DEBUG = true;
+
+ private static final String KEY_LOCKOUT_RESET_USER = "lockout_reset_user";
+ private static final int MSG_USER_SWITCHING = 10;
+ private static final long FAIL_LOCKOUT_TIMEOUT_MS = 30 * 1000;
+ private static final long CANCEL_TIMEOUT_LIMIT = 3000; // max wait for onCancel() from HAL,in ms
+
+ private final Context mContext;
+ private final String mKeyguardPackage;
+ private final AppOpsManager mAppOps;
+ private final SparseBooleanArray mTimedLockoutCleared;
+ private final SparseIntArray mFailedAttempts;
+ private final IActivityTaskManager mActivityTaskManager;
+ private final AlarmManager mAlarmManager;
+ private final PowerManager mPowerManager;
+ private final UserManager mUserManager;
+ private final MetricsLogger mMetricsLogger;
+ private final BiometricTaskStackListener mTaskStackListener = new BiometricTaskStackListener();
+ private final ResetClientStateRunnable mResetClientState = new ResetClientStateRunnable();
+ private final LockoutReceiver mLockoutReceiver = new LockoutReceiver();
+ private final ArrayList<LockoutResetMonitor> mLockoutMonitors = new ArrayList<>();
+
+ protected final Map<Integer, Long> mAuthenticatorIds =
+ Collections.synchronizedMap(new HashMap<>());
+ protected final ResetFailedAttemptsForUserRunnable mResetFailedAttemptsForCurrentUserRunnable =
+ new ResetFailedAttemptsForUserRunnable();
+ protected final H mHandler = new H();
+
+ private ClientMonitor mCurrentClient;
+ private ClientMonitor mPendingClient;
+ private PerformanceStats mPerformanceStats;
+ protected int mCurrentUserId = UserHandle.USER_NULL;
+ // Normal authentications are tracked by mPerformanceMap.
+ protected HashMap<Integer, PerformanceStats> mPerformanceMap = new HashMap<>();
+ // Transactions that make use of CryptoObjects are tracked by mCryptoPerformaceMap.
+ protected HashMap<Integer, PerformanceStats> mCryptoPerformanceMap = new HashMap<>();
+
+ protected class PerformanceStats {
+ public int accept; // number of accepted biometrics
+ public int reject; // number of rejected biometrics
+ public int acquire; // total number of acquisitions. Should be >= accept+reject due to poor
+ // image acquisition in some cases (too fast, too slow, dirty sensor, etc.)
+ public int lockout; // total number of lockouts
+ public int permanentLockout; // total number of permanent lockouts
+ }
+
+ /**
+ * @return the log tag.
+ */
+ protected abstract String getTag();
+
+ /**
+ * @return the biometric utilities for a specific implementation.
+ */
+ protected abstract BiometricUtils getBiometricUtils();
+
+ /**
+ * @return the number of failed attempts after which the user will be temporarily locked out
+ * from using the biometric. A strong auth (pin/pattern/pass) clears this counter.
+ */
+ protected abstract int getFailedAttemptsLockoutTimed();
+
+ /**
+ * @return the number of failed attempts after which the user will be permanently locked out
+ * from using the biometric. A strong auth (pin/pattern/pass) clears this counter.
+ */
+ protected abstract int getFailedAttemptsLockoutPermanent();
+
+ /**
+ * @return the metrics constants for a biometric implementation.
+ */
+ protected abstract Metrics getMetrics();
+
+ /**
+ * @param userId
+ * @return true if the enrollment limit has been reached.
+ */
+ protected abstract boolean hasReachedEnrollmentLimit(int userId);
+
+ /**
+ * Notifies the HAL that the user has changed.
+ * @param userId
+ * @param clientPackage
+ */
+ protected abstract void updateActiveGroup(int userId, String clientPackage);
+
+ /**
+ * @return The protected intent to reset lockout for a specific biometric.
+ */
+ protected abstract String getLockoutResetIntent();
+
+ /**
+ * @return The permission the sender is required to have in order for the lockout reset intent
+ * to be received by the BiometricService implementation.
+ */
+ protected abstract String getLockoutBroadcastPermission();
+
+ /**
+ * @return The HAL ID.
+ */
+ protected abstract long getHalDeviceId();
+
+ /**
+ * This method is called when the user switches. Implementations should probably notify the
+ * HAL.
+ * @param userId
+ */
+ protected abstract void handleUserSwitching(int userId);
+
+ /**
+ * @param userId
+ * @return Returns true if the user has any enrolled biometrics.
+ */
+ protected abstract boolean hasEnrolledBiometrics(int userId);
+
+ /**
+ * @return Returns the MANAGE_* permission string, which is required for enrollment, removal
+ * etc.
+ */
+ protected abstract String getManageBiometricPermission();
+
+ /**
+ * Checks if the caller has permission to use the biometric service - throws a SecurityException
+ * if not.
+ */
+ protected abstract void checkUseBiometricPermission();
+
+ /**
+ * @return Returns one of the {@link AppOpsManager} constants which pertains to the specific
+ * biometric service.
+ */
+ protected abstract int getAppOp();
+
+
+ /**
+ * Notifies clients of any change in the biometric state (active / idle). This is mainly for
+ * Fingerprint navigation gestures.
+ * @param isActive
+ */
+ protected void notifyClientActiveCallbacks(boolean isActive) {}
+
+ protected class AuthenticationClientImpl extends AuthenticationClient {
+
+ public AuthenticationClientImpl(Context context, DaemonWrapper daemon, long halDeviceId,
+ IBinder token, ServiceListener listener, int targetUserId, int groupId, long opId,
+ boolean restricted, String owner, Bundle bundle,
+ IBiometricPromptReceiver dialogReceiver,
+ IStatusBarService statusBarService) {
+ super(context, getMetrics(), daemon, halDeviceId, token, listener,
+ targetUserId, groupId, opId, restricted, owner, bundle, dialogReceiver,
+ statusBarService);
+ }
+
+ @Override
+ public void onStart() {
+ try {
+ mActivityTaskManager.registerTaskStackListener(mTaskStackListener);
+ } catch (RemoteException e) {
+ Slog.e(getTag(), "Could not register task stack listener", e);
+ }
+ }
+
+ @Override
+ public void onStop() {
+ try {
+ mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
+ } catch (RemoteException e) {
+ Slog.e(getTag(), "Could not unregister task stack listener", e);
+ }
+ }
+
+ @Override
+ public void resetFailedAttempts() {
+ resetFailedAttemptsForUser(true /* clearAttemptCounter */,
+ ActivityManager.getCurrentUser());
+ }
+
+ @Override
+ public void notifyUserActivity() {
+ userActivity();
+ }
+
+ @Override
+ public int handleFailedAttempt() {
+ final int currentUser = ActivityManager.getCurrentUser();
+ mFailedAttempts.put(currentUser, mFailedAttempts.get(currentUser, 0) + 1);
+ mTimedLockoutCleared.put(ActivityManager.getCurrentUser(), false);
+ final int lockoutMode = getLockoutMode();
+ if (lockoutMode == AuthenticationClient.LOCKOUT_PERMANENT) {
+ mPerformanceStats.permanentLockout++;
+ } else if (lockoutMode == AuthenticationClient.LOCKOUT_TIMED) {
+ mPerformanceStats.lockout++;
+ }
+
+ // Failing multiple times will continue to push out the lockout time
+ if (lockoutMode != AuthenticationClient.LOCKOUT_NONE) {
+ scheduleLockoutResetForUser(currentUser);
+ return lockoutMode;
+ }
+ return AuthenticationClient.LOCKOUT_NONE;
+ }
+ }
+
+ protected class EnrollClientImpl extends EnrollClient {
+
+ public EnrollClientImpl(Context context, DaemonWrapper daemon, long halDeviceId,
+ IBinder token, ServiceListener listener, int userId, int groupId,
+ byte[] cryptoToken, boolean restricted, String owner) {
+ super(context, getMetrics(), daemon, halDeviceId, token, listener,
+ userId, groupId, cryptoToken, restricted, owner, getBiometricUtils());
+ }
+
+ @Override
+ public void notifyUserActivity() {
+ userActivity();
+ }
+ }
+
+ protected class RemovalClientImpl extends RemovalClient {
+ private boolean mShouldNotify;
+
+ public RemovalClientImpl(Context context, DaemonWrapper daemon, long halDeviceId,
+ IBinder token, ServiceListener listener, int fingerId, int groupId, int userId,
+ boolean restricted, String owner) {
+ super(context, getMetrics(), daemon, halDeviceId, token, listener, fingerId, groupId,
+ userId, restricted, owner, getBiometricUtils());
+ }
+
+ public void setShouldNotifyUserActivity(boolean shouldNotify) {
+ mShouldNotify = shouldNotify;
+ }
+
+ @Override
+ public void notifyUserActivity() {
+ if (mShouldNotify) {
+ userActivity();
+ }
+ }
+ }
+
+ protected class EnumerateClientImpl extends EnumerateClient {
+
+ public EnumerateClientImpl(Context context, DaemonWrapper daemon, long halDeviceId,
+ IBinder token, ServiceListener listener, int groupId, int userId,
+ boolean restricted, String owner) {
+ super(context, getMetrics(), daemon, halDeviceId, token, listener, groupId, userId,
+ restricted, owner);
+ }
+
+ @Override
+ public void notifyUserActivity() {
+ userActivity();
+ }
+ }
+
+ /**
+ * Wraps the callback interface from Service -> Manager
+ */
+ protected interface ServiceListener {
+ void onEnrollResult(BiometricAuthenticator.Identifier identifier,
+ int remaining) throws RemoteException;
+
+ void onAcquired(long deviceId, int acquiredInfo, int vendorCode)
+ throws RemoteException;
+
+ void onAuthenticationSucceeded(long deviceId,
+ BiometricAuthenticator.Identifier biometric, int userId)
+ throws RemoteException;
+
+ void onAuthenticationFailed(long deviceId)
+ throws RemoteException;
+
+ void onError(long deviceId, int error, int vendorCode)
+ throws RemoteException;
+
+ void onRemoved(BiometricAuthenticator.Identifier identifier,
+ int remaining) throws RemoteException;
+
+ void onEnumerated(BiometricAuthenticator.Identifier identifier,
+ int remaining) throws RemoteException;
+ }
+
+ /**
+ * Wraps a portion of the interface from Service -> Daemon that is used by the ClientMonitor
+ * subclasses.
+ */
+ protected interface DaemonWrapper {
+ int ERROR_ESRCH = 3; // Likely fingerprint HAL is dead. see errno.h.
+ int authenticate(long operationId, int groupId) throws RemoteException;
+ int cancel() throws RemoteException;
+ int remove(int groupId, int biometricId) throws RemoteException;
+ int enumerate() throws RemoteException;
+ int enroll(byte[] cryptoToken, int groupId, int timeout) throws RemoteException;
+ }
+
+ /**
+ * Handler which all subclasses should post events to.
+ */
+ protected final class H extends Handler {
+ @Override
+ public void handleMessage(android.os.Message msg) {
+ switch (msg.what) {
+ case MSG_USER_SWITCHING:
+ handleUserSwitching(msg.arg1);
+ break;
+
+ default:
+ Slog.w(getTag(), "Unknown message:" + msg.what);
+ }
+ }
+ }
+
+ private final class BiometricTaskStackListener extends TaskStackListener {
+ @Override
+ public void onTaskStackChanged() {
+ try {
+ if (!(mCurrentClient instanceof AuthenticationClient)) {
+ return;
+ }
+ final String currentClient = mCurrentClient.getOwnerString();
+ if (isKeyguard(currentClient)) {
+ return; // Keyguard is always allowed
+ }
+ List<ActivityManager.RunningTaskInfo> runningTasks =
+ mActivityTaskManager.getTasks(1);
+ if (!runningTasks.isEmpty()) {
+ final String topPackage = runningTasks.get(0).topActivity.getPackageName();
+ if (!topPackage.contentEquals(currentClient)) {
+ Slog.e(getTag(), "Stopping background authentication, top: " + topPackage
+ + " currentClient: " + currentClient);
+ mCurrentClient.stop(false /* initiatedByClient */);
+ }
+ }
+ } catch (RemoteException e) {
+ Slog.e(getTag(), "Unable to get running tasks", e);
+ }
+ }
+ }
+
+ private final class ResetClientStateRunnable implements Runnable {
+ @Override
+ public void run() {
+ /**
+ * Warning: if we get here, the driver never confirmed our call to cancel the current
+ * operation (authenticate, enroll, remove, enumerate, etc), which is
+ * really bad. The result will be a 3-second delay in starting each new client.
+ * If you see this on a device, make certain the driver notifies with
+ * {@link BiometricConstants#BIOMETRIC_ERROR_CANCELED} in response to cancel()
+ * once it has successfully switched to the IDLE state in the HAL.
+ * Additionally,{@link BiometricConstants#BIOMETRIC_ERROR_CANCELED} should only be sent
+ * in response to an actual cancel() call.
+ */
+ Slog.w(getTag(), "Client "
+ + (mCurrentClient != null ? mCurrentClient.getOwnerString() : "null")
+ + " failed to respond to cancel, starting client "
+ + (mPendingClient != null ? mPendingClient.getOwnerString() : "null"));
+
+ mCurrentClient = null;
+ startClient(mPendingClient, false);
+ }
+ }
+
+ private final class LockoutReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Slog.v(getTag(), "Resetting lockout: " + intent.getAction());
+ if (getLockoutResetIntent().equals(intent.getAction())) {
+ final int user = intent.getIntExtra(KEY_LOCKOUT_RESET_USER, 0);
+ resetFailedAttemptsForUser(false /* clearAttemptCounter */, user);
+ }
+ }
+ }
+
+ private final class ResetFailedAttemptsForUserRunnable implements Runnable {
+ @Override
+ public void run() {
+ resetFailedAttemptsForUser(true /* clearAttemptCounter */,
+ ActivityManager.getCurrentUser());
+ }
+ }
+
+ private final class LockoutResetMonitor implements IBinder.DeathRecipient {
+ private static final long WAKELOCK_TIMEOUT_MS = 2000;
+ private final IBiometricServiceLockoutResetCallback mCallback;
+ private final PowerManager.WakeLock mWakeLock;
+
+ public LockoutResetMonitor(IBiometricServiceLockoutResetCallback callback) {
+ mCallback = callback;
+ mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
+ "lockout reset callback");
+ try {
+ mCallback.asBinder().linkToDeath(LockoutResetMonitor.this, 0);
+ } catch (RemoteException e) {
+ Slog.w(getTag(), "caught remote exception in linkToDeath", e);
+ }
+ }
+
+ public void sendLockoutReset() {
+ if (mCallback != null) {
+ try {
+ mWakeLock.acquire(WAKELOCK_TIMEOUT_MS);
+ mCallback.onLockoutReset(getHalDeviceId(), new IRemoteCallback.Stub() {
+ @Override
+ public void sendResult(Bundle data) throws RemoteException {
+ releaseWakelock();
+ }
+ });
+ } catch (DeadObjectException e) {
+ Slog.w(getTag(), "Death object while invoking onLockoutReset: ", e);
+ mHandler.post(mRemoveCallbackRunnable);
+ } catch (RemoteException e) {
+ Slog.w(getTag(), "Failed to invoke onLockoutReset: ", e);
+ releaseWakelock();
+ }
+ }
+ }
+
+ private final Runnable mRemoveCallbackRunnable = new Runnable() {
+ @Override
+ public void run() {
+ releaseWakelock();
+ removeLockoutResetCallback(LockoutResetMonitor.this);
+ }
+ };
+
+ @Override
+ public void binderDied() {
+ Slog.e(getTag(), "Lockout reset callback binder died");
+ mHandler.post(mRemoveCallbackRunnable);
+ }
+
+ private void releaseWakelock() {
+ if (mWakeLock.isHeld()) {
+ mWakeLock.release();
+ }
+ }
+ }
+
+ /**
+ * Initializes the system service.
+ * <p>
+ * Subclasses must define a single argument constructor that accepts the context
+ * and passes it to super.
+ * </p>
+ *
+ * @param context The system server context.
+ */
+ public BiometricService(Context context) {
+ super(context);
+ mContext = context;
+ mKeyguardPackage = ComponentName.unflattenFromString(context.getResources().getString(
+ com.android.internal.R.string.config_keyguardComponent)).getPackageName();
+ mAppOps = context.getSystemService(AppOpsManager.class);
+ mTimedLockoutCleared = new SparseBooleanArray();
+ mFailedAttempts = new SparseIntArray();
+ mActivityTaskManager = ((ActivityTaskManager) context.getSystemService(
+ Context.ACTIVITY_TASK_SERVICE)).getService();
+ mPowerManager = mContext.getSystemService(PowerManager.class);
+ mAlarmManager = mContext.getSystemService(AlarmManager.class);
+ mUserManager = UserManager.get(mContext);
+ mMetricsLogger = new MetricsLogger();
+ mContext.registerReceiver(mLockoutReceiver, new IntentFilter(getLockoutResetIntent()),
+ getLockoutBroadcastPermission(), null /* handler */);
+ }
+
+ @Override
+ public void onStart() {
+ listenForUserSwitches();
+ }
+
+ @Override
+ public void serviceDied(long cookie) {
+ Slog.e(getTag(), "HAL died");
+ mMetricsLogger.count(getMetrics().tagHalDied(), 1);
+ handleError(getHalDeviceId(), BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE,
+ 0 /*vendorCode */);
+ }
+
+ protected ClientMonitor getCurrentClient() {
+ return mCurrentClient;
+ }
+
+ protected ClientMonitor getPendingClient() {
+ return mPendingClient;
+ }
+
+ /**
+ * Callback handlers from the daemon. The caller must put this on a handler.
+ */
+
+ protected void handleAcquired(long deviceId, int acquiredInfo, int vendorCode) {
+ ClientMonitor client = mCurrentClient;
+ if (client != null && client.onAcquired(acquiredInfo, vendorCode)) {
+ removeClient(client);
+ }
+ if (mPerformanceStats != null && getLockoutMode() == AuthenticationClient.LOCKOUT_NONE
+ && client instanceof AuthenticationClient) {
+ // ignore enrollment acquisitions or acquisitions when we're locked out
+ mPerformanceStats.acquire++;
+ }
+ }
+
+ protected void handleAuthenticated(long deviceId, int biometricId, int groupId,
+ ArrayList<Byte> token) {
+ ClientMonitor client = mCurrentClient;
+ if (biometricId != 0) {
+ final byte[] byteToken = new byte[token.size()];
+ for (int i = 0; i < token.size(); i++) {
+ byteToken[i] = token.get(i);
+ }
+ KeyStore.getInstance().addAuthToken(byteToken);
+ }
+ if (client != null && client.onAuthenticated(biometricId, groupId)) {
+ removeClient(client);
+ }
+ if (biometricId != 0) {
+ mPerformanceStats.accept++;
+ } else {
+ mPerformanceStats.reject++;
+ }
+ }
+
+ protected void handleEnrollResult(BiometricAuthenticator.Identifier identifier,
+ int remaining) {
+ ClientMonitor client = mCurrentClient;
+ if (client != null && client.onEnrollResult(identifier, remaining)) {
+ removeClient(client);
+ // When enrollment finishes, update this group's authenticator id, as the HAL has
+ // already generated a new authenticator id when the new biometric is enrolled.
+ if (identifier instanceof Fingerprint) {
+ updateActiveGroup(((Fingerprint)identifier).getGroupId(), null);
+ } else {
+ updateActiveGroup(mCurrentUserId, null);
+ }
+
+ }
+ }
+
+ protected void handleError(long deviceId, int error, int vendorCode) {
+ final ClientMonitor client = mCurrentClient;
+
+ if (DEBUG) Slog.v(getTag(), "handleError(client="
+ + (client != null ? client.getOwnerString() : "null") + ", error = " + error + ")");
+
+ if (client != null && client.onError(deviceId, error, vendorCode)) {
+ removeClient(client);
+ }
+
+ if (error == BiometricConstants.BIOMETRIC_ERROR_CANCELED) {
+ mHandler.removeCallbacks(mResetClientState);
+ if (mPendingClient != null) {
+ if (DEBUG) Slog.v(getTag(), "start pending client " + mPendingClient.getOwnerString());
+ startClient(mPendingClient, false);
+ mPendingClient = null;
+ }
+ }
+ }
+
+ protected void handleRemoved(BiometricAuthenticator.Identifier identifier,
+ final int remaining) {
+ if (DEBUG) Slog.w(getTag(), "Removed: fid=" + identifier.getBiometricId()
+ + ", dev=" + identifier.getDeviceId()
+ + ", rem=" + remaining);
+
+ ClientMonitor client = mCurrentClient;
+ if (client != null && client.onRemoved(identifier, remaining)) {
+ removeClient(client);
+ // When the last biometric of a group is removed, update the authenticator id
+ int userId = mCurrentUserId;
+ if (identifier instanceof Fingerprint) {
+ userId = ((Fingerprint) identifier).getGroupId();
+ }
+ if (!hasEnrolledBiometrics(userId)) {
+ updateActiveGroup(userId, null);
+ }
+ }
+ }
+
+ /**
+ * Calls from the Manager. These are still on the calling binder's thread.
+ */
+
+ protected void enrollInternal(EnrollClientImpl client, int userId) {
+ if (hasReachedEnrollmentLimit(userId)) {
+ return;
+ }
+
+ // Group ID is arbitrarily set to parent profile user ID. It just represents
+ // the default biometrics for the user.
+ if (!isCurrentUserOrProfile(userId)) {
+ return;
+ }
+
+ mHandler.post(() -> {
+ startClient(client, true /* initiatedByClient */);
+ });
+ }
+
+ protected void cancelEnrollmentInternal(IBinder token) {
+ mHandler.post(() -> {
+ ClientMonitor client = mCurrentClient;
+ if (client instanceof EnrollClient && client.getToken() == token) {
+ client.stop(client.getToken() == token);
+ }
+ });
+ }
+
+ protected void authenticateInternal(AuthenticationClientImpl client, long opId,
+ String opPackageName) {
+ final int callingUid = Binder.getCallingUid();
+ final int callingPid = Binder.getCallingPid();
+ final int callingUserId = UserHandle.getCallingUserId();
+
+ if (!canUseBiometric(opPackageName, true /* foregroundOnly */, callingUid, callingPid,
+ callingUserId)) {
+ if (DEBUG) Slog.v(getTag(), "authenticate(): reject " + opPackageName);
+ return;
+ }
+
+ mHandler.post(() -> {
+ mMetricsLogger.histogram(getMetrics().tagAuthToken(), opId != 0L ? 1 : 0);
+
+ // Get performance stats object for this user.
+ HashMap<Integer, PerformanceStats> pmap
+ = (opId == 0) ? mPerformanceMap : mCryptoPerformanceMap;
+ PerformanceStats stats = pmap.get(mCurrentUserId);
+ if (stats == null) {
+ stats = new PerformanceStats();
+ pmap.put(mCurrentUserId, stats);
+ }
+ mPerformanceStats = stats;
+
+ startAuthentication(client, opPackageName);
+ });
+ }
+
+ protected void cancelAuthenticationInternal(final IBinder token, final String opPackageName) {
+ final int callingUid = Binder.getCallingUid();
+ final int callingPid = Binder.getCallingPid();
+ final int callingUserId = UserHandle.getCallingUserId();
+
+ if (!canUseBiometric(opPackageName, true /* foregroundOnly */, callingUid, callingPid,
+ callingUserId)) {
+ if (DEBUG) Slog.v(getTag(), "cancelAuthentication(): reject " + opPackageName);
+ return;
+ }
+
+ mHandler.post(() -> {
+ ClientMonitor client = mCurrentClient;
+ if (client instanceof AuthenticationClient) {
+ if (client.getToken() == token) {
+ if (DEBUG) Slog.v(getTag(), "stop client " + client.getOwnerString());
+ client.stop(client.getToken() == token);
+ } else {
+ if (DEBUG) Slog.v(getTag(), "can't stop client "
+ + client.getOwnerString() + " since tokens don't match");
+ }
+ } else if (client != null) {
+ if (DEBUG) Slog.v(getTag(), "can't cancel non-authenticating client "
+ + client.getOwnerString());
+ }
+ });
+ }
+
+ protected void setActiveUserInternal(int userId) {
+ mHandler.post(() -> {
+ updateActiveGroup(userId, null /* clientPackage */);
+ });
+ }
+
+ protected void removeInternal(RemovalClientImpl client) {
+ mHandler.post(() -> {
+ startClient(client, true /* initiatedByClient */);
+ });
+ }
+
+ protected void enumerateInternal(EnumerateClientImpl client) {
+ mHandler.post(() -> {
+ startClient(client, true /* initiatedByClient */);
+ });
+ }
+
+ // Should be done on a handler thread - not on the Binder's thread.
+ private void startAuthentication(AuthenticationClientImpl client, String opPackageName) {
+ updateActiveGroup(client.getGroupId(), opPackageName);
+
+ if (DEBUG) Slog.v(getTag(), "startAuthentication(" + opPackageName + ")");
+
+ int lockoutMode = getLockoutMode();
+ if (lockoutMode != AuthenticationClient.LOCKOUT_NONE) {
+ Slog.v(getTag(), "In lockout mode(" + lockoutMode +
+ ") ; disallowing authentication");
+ int errorCode = lockoutMode == AuthenticationClient.LOCKOUT_TIMED ?
+ BiometricConstants.BIOMETRIC_ERROR_LOCKOUT :
+ BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT;
+ if (!client.onError(getHalDeviceId(), errorCode, 0 /* vendorCode */)) {
+ Slog.w(getTag(), "Cannot send permanent lockout message to client");
+ }
+ return;
+ }
+ startClient(client, true /* initiatedByClient */);
+ }
+
+ protected void addLockoutResetCallback(IBiometricServiceLockoutResetCallback callback) {
+ mHandler.post(() -> {
+ final LockoutResetMonitor monitor = new LockoutResetMonitor(callback);
+ if (!mLockoutMonitors.contains(monitor)) {
+ mLockoutMonitors.add(monitor);
+ }
+ });
+ }
+
+ /**
+ * Helper methods.
+ */
+
+ /**
+ * @param opPackageName name of package for caller
+ * @param requireForeground only allow this call while app is in the foreground
+ * @return true if caller can use the biometric API
+ */
+ protected boolean canUseBiometric(String opPackageName, boolean requireForeground, int uid,
+ int pid, int userId) {
+ checkUseBiometricPermission();
+
+ if (isKeyguard(opPackageName)) {
+ return true; // Keyguard is always allowed
+ }
+ if (!isCurrentUserOrProfile(userId)) {
+ Slog.w(getTag(), "Rejecting " + opPackageName + "; not a current user or profile");
+ return false;
+ }
+ if (mAppOps.noteOp(getAppOp(), uid, opPackageName) != AppOpsManager.MODE_ALLOWED) {
+ Slog.w(getTag(), "Rejecting " + opPackageName + "; permission denied");
+ return false;
+ }
+ if (requireForeground && !(isForegroundActivity(uid, pid) || isCurrentClient(
+ opPackageName))) {
+ Slog.w(getTag(), "Rejecting " + opPackageName + "; not in foreground");
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * @param opPackageName package of the caller
+ * @return true if this is the same client currently using the biometric
+ */
+ private boolean isCurrentClient(String opPackageName) {
+ return mCurrentClient != null && mCurrentClient.getOwnerString().equals(opPackageName);
+ }
+
+ /**
+ * @return true if this is keyguard package
+ */
+ private boolean isKeyguard(String clientPackage) {
+ return mKeyguardPackage.equals(clientPackage);
+ }
+
+ private int getLockoutMode() {
+ final int currentUser = ActivityManager.getCurrentUser();
+ final int failedAttempts = mFailedAttempts.get(currentUser, 0);
+ if (failedAttempts >= getFailedAttemptsLockoutPermanent()) {
+ return AuthenticationClient.LOCKOUT_PERMANENT;
+ } else if (failedAttempts > 0 &&
+ mTimedLockoutCleared.get(currentUser, false) == false
+ && (failedAttempts % getFailedAttemptsLockoutTimed() == 0)) {
+ return AuthenticationClient.LOCKOUT_TIMED;
+ }
+ return AuthenticationClient.LOCKOUT_NONE;
+ }
+
+ private boolean isForegroundActivity(int uid, int pid) {
+ try {
+ List<ActivityManager.RunningAppProcessInfo> procs =
+ ActivityManager.getService().getRunningAppProcesses();
+ int N = procs.size();
+ for (int i = 0; i < N; i++) {
+ ActivityManager.RunningAppProcessInfo proc = procs.get(i);
+ if (proc.pid == pid && proc.uid == uid
+ && proc.importance <= IMPORTANCE_FOREGROUND_SERVICE) {
+ return true;
+ }
+ }
+ } catch (RemoteException e) {
+ Slog.w(getTag(), "am.getRunningAppProcesses() failed");
+ }
+ return false;
+ }
+
+ /**
+ * Calls the HAL to switch states to the new task. If there's already a current task,
+ * it calls cancel() and sets mPendingClient to begin when the current task finishes
+ * ({@link BiometricConstants#BIOMETRIC_ERROR_CANCELED}).
+ *
+ * @param newClient the new client that wants to connect
+ * @param initiatedByClient true for authenticate, remove and enroll
+ */
+ private void startClient(ClientMonitor newClient, boolean initiatedByClient) {
+ ClientMonitor currentClient = mCurrentClient;
+ if (currentClient != null) {
+ if (DEBUG) Slog.v(getTag(), "request stop current client " +
+ currentClient.getOwnerString());
+
+ // This check only matters for FingerprintService, since enumerate may call back
+ // multiple times.
+ if (currentClient instanceof FingerprintService.EnumerateClientImpl ||
+ currentClient instanceof FingerprintService.RemovalClientImpl) {
+ // This condition means we're currently running internal diagnostics to
+ // remove extra fingerprints in the hardware and/or the software
+ // TODO: design an escape hatch in case client never finishes
+ if (newClient != null) {
+ Slog.w(getTag(), "Internal cleanup in progress but trying to start client "
+ + newClient.getClass().getSuperclass().getSimpleName()
+ + "(" + newClient.getOwnerString() + ")"
+ + ", initiatedByClient = " + initiatedByClient);
+ }
+ } else {
+ currentClient.stop(initiatedByClient);
+ }
+ mPendingClient = newClient;
+ mHandler.removeCallbacks(mResetClientState);
+ mHandler.postDelayed(mResetClientState, CANCEL_TIMEOUT_LIMIT);
+ } else if (newClient != null) {
+ mCurrentClient = newClient;
+ if (DEBUG) Slog.v(getTag(), "starting client "
+ + newClient.getClass().getSuperclass().getSimpleName()
+ + "(" + newClient.getOwnerString() + ")"
+ + ", initiatedByClient = " + initiatedByClient);
+ notifyClientActiveCallbacks(true);
+
+ newClient.start();
+ }
+ }
+
+ protected void removeClient(ClientMonitor client) {
+ if (client != null) {
+ client.destroy();
+ if (client != mCurrentClient && mCurrentClient != null) {
+ Slog.w(getTag(), "Unexpected client: " + client.getOwnerString() + "expected: "
+ + mCurrentClient.getOwnerString());
+ }
+ }
+ if (mCurrentClient != null) {
+ if (DEBUG) Slog.v(getTag(), "Done with client: " + client.getOwnerString());
+ mCurrentClient = null;
+ }
+ if (mPendingClient == null) {
+ notifyClientActiveCallbacks(false);
+ }
+ }
+
+ /**
+ * Populates existing authenticator ids. To be used only during the start of the service.
+ */
+ protected void loadAuthenticatorIds() {
+ // This operation can be expensive, so keep track of the elapsed time. Might need to move to
+ // background if it takes too long.
+ long t = System.currentTimeMillis();
+ mAuthenticatorIds.clear();
+ for (UserInfo user : UserManager.get(getContext()).getUsers(true /* excludeDying */)) {
+ int userId = getUserOrWorkProfileId(null, user.id);
+ if (!mAuthenticatorIds.containsKey(userId)) {
+ updateActiveGroup(userId, null);
+ }
+ }
+
+ t = System.currentTimeMillis() - t;
+ if (t > 1000) {
+ Slog.w(getTag(), "loadAuthenticatorIds() taking too long: " + t + "ms");
+ }
+ }
+
+ /**
+ * @param clientPackage the package of the caller
+ * @return the profile id
+ */
+ protected int getUserOrWorkProfileId(String clientPackage, int userId) {
+ if (!isKeyguard(clientPackage) && isWorkProfile(userId)) {
+ return userId;
+ }
+ return getEffectiveUserId(userId);
+ }
+
+ protected boolean isRestricted() {
+ // Only give privileged apps (like Settings) access to biometric info
+ final boolean restricted = !hasPermission(getManageBiometricPermission());
+ return restricted;
+ }
+
+ protected boolean hasPermission(String permission) {
+ return getContext().checkCallingOrSelfPermission(permission)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+
+ protected void checkPermission(String permission) {
+ getContext().enforceCallingOrSelfPermission(permission,
+ "Must have " + permission + " permission.");
+ }
+
+ protected boolean isCurrentUserOrProfile(int userId) {
+ UserManager um = UserManager.get(mContext);
+ if (um == null) {
+ Slog.e(getTag(), "Unable to acquire UserManager");
+ return false;
+ }
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ // Allow current user or profiles of the current user...
+ for (int profileId : um.getEnabledProfileIds(ActivityManager.getCurrentUser())) {
+ if (profileId == userId) {
+ return true;
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+
+ return false;
+ }
+
+ /***
+ * @param opPackageName the name of the calling package
+ * @return authenticator id for the calling user
+ */
+ protected long getAuthenticatorId(String opPackageName) {
+ final int userId = getUserOrWorkProfileId(opPackageName, UserHandle.getCallingUserId());
+ return mAuthenticatorIds.getOrDefault(userId, 0L);
+ }
+
+ private void scheduleLockoutResetForUser(int userId) {
+ mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+ SystemClock.elapsedRealtime() + FAIL_LOCKOUT_TIMEOUT_MS,
+ getLockoutResetIntentForUser(userId));
+ }
+
+ private PendingIntent getLockoutResetIntentForUser(int userId) {
+ return PendingIntent.getBroadcast(mContext, userId,
+ new Intent(getLockoutResetIntent()).putExtra(KEY_LOCKOUT_RESET_USER, userId),
+ PendingIntent.FLAG_UPDATE_CURRENT);
+ }
+
+ private void userActivity() {
+ long now = SystemClock.uptimeMillis();
+ mPowerManager.userActivity(now, PowerManager.USER_ACTIVITY_EVENT_TOUCH, 0);
+ }
+
+ /**
+ * @param userId
+ * @return true if this is a work profile
+ */
+ private boolean isWorkProfile(int userId) {
+ UserInfo userInfo = null;
+ final long token = Binder.clearCallingIdentity();
+ try {
+ userInfo = mUserManager.getUserInfo(userId);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ return userInfo != null && userInfo.isManagedProfile();
+ }
+
+
+ private int getEffectiveUserId(int userId) {
+ UserManager um = UserManager.get(mContext);
+ if (um != null) {
+ final long callingIdentity = Binder.clearCallingIdentity();
+ userId = um.getCredentialOwnerProfile(userId);
+ Binder.restoreCallingIdentity(callingIdentity);
+ } else {
+ Slog.e(getTag(), "Unable to acquire UserManager");
+ }
+ return userId;
+ }
+
+ // Attempt counter should only be cleared when Keyguard goes away or when
+ // a biometric is successfully authenticated.
+ private void resetFailedAttemptsForUser(boolean clearAttemptCounter, int userId) {
+ if (DEBUG && getLockoutMode() != AuthenticationClient.LOCKOUT_NONE) {
+ Slog.v(getTag(), "Reset biometric lockout, clearAttemptCounter=" + clearAttemptCounter);
+ }
+ if (clearAttemptCounter) {
+ mFailedAttempts.put(userId, 0);
+ }
+ mTimedLockoutCleared.put(userId, true);
+ // If we're asked to reset failed attempts externally (i.e. from Keyguard),
+ // the alarm might still be pending; remove it.
+ cancelLockoutResetForUser(userId);
+ notifyLockoutResetMonitors();
+ }
+
+ private void cancelLockoutResetForUser(int userId) {
+ mAlarmManager.cancel(getLockoutResetIntentForUser(userId));
+ }
+
+ private void listenForUserSwitches() {
+ try {
+ ActivityManager.getService().registerUserSwitchObserver(
+ new SynchronousUserSwitchObserver() {
+ @Override
+ public void onUserSwitching(int newUserId) throws RemoteException {
+ mHandler.obtainMessage(MSG_USER_SWITCHING, newUserId, 0 /* unused */)
+ .sendToTarget();
+ }
+ }, getTag());
+ } catch (RemoteException e) {
+ Slog.w(getTag(), "Failed to listen for user switching event" ,e);
+ }
+ }
+
+ private void notifyLockoutResetMonitors() {
+ for (int i = 0; i < mLockoutMonitors.size(); i++) {
+ mLockoutMonitors.get(i).sendLockoutReset();
+ }
+ }
+
+ private void removeLockoutResetCallback(
+ LockoutResetMonitor monitor) {
+ mLockoutMonitors.remove(monitor);
+ }
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/biometrics/common/BiometricUserState.java b/services/core/java/com/android/server/biometrics/common/BiometricUserState.java
new file mode 100644
index 0000000..53eaac0
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/common/BiometricUserState.java
@@ -0,0 +1,213 @@
+/*
+ * 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.biometrics.common;
+
+import android.content.Context;
+import android.hardware.biometrics.BiometricAuthenticator;
+import android.os.AsyncTask;
+import android.os.Environment;
+import android.util.Slog;
+import android.util.Xml;
+
+import com.android.internal.annotations.GuardedBy;
+
+import libcore.io.IoUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Abstract base class for managing biometrics per user across device reboots.
+ * @hide
+ */
+public abstract class BiometricUserState<T extends BiometricAuthenticator.Identifier> {
+ private static final String TAG = "UserState";
+
+ @GuardedBy("this")
+ protected final ArrayList<T> mBiometrics = new ArrayList<>();
+ protected final Context mContext;
+ protected final File mFile;
+
+ private final Runnable mWriteStateRunnable = new Runnable() {
+ @Override
+ public void run() {
+ doWriteState();
+ }
+ };
+
+ /**
+ * @return The tag for the biometrics. There may be multiple instances of a biometric within.
+ */
+ protected abstract String getBiometricsTag();
+
+ /**
+ * @return The file where the biometric metadata should be stored.
+ */
+ protected abstract String getBiometricFile();
+
+ /**
+ * @return The resource for the name template, this is used to generate the default name.
+ */
+ protected abstract int getNameTemplateResource();
+
+ /**
+ * @return A copy of the list.
+ */
+ protected abstract ArrayList<T> getCopy(ArrayList<T> array);
+
+ /**
+ * @return Writes the cached data to persistent storage.
+ */
+ protected abstract void doWriteState();
+
+ /**
+ * @return
+ */
+ protected abstract void parseBiometricsLocked(XmlPullParser parser)
+ throws IOException, XmlPullParserException;
+
+
+ public BiometricUserState(Context context, int userId) {
+ mFile = getFileForUser(userId);
+ mContext = context;
+ synchronized (this) {
+ readStateSyncLocked();
+ }
+ }
+
+ public void addBiometric(T identifier) {
+ synchronized (this) {
+ mBiometrics.add(identifier);
+ scheduleWriteStateLocked();
+ }
+ }
+
+ public void removeBiometric(int biometricId) {
+ synchronized (this) {
+ for (int i = 0; i < mBiometrics.size(); i++) {
+ if (mBiometrics.get(i).getBiometricId() == biometricId) {
+ mBiometrics.remove(i);
+ scheduleWriteStateLocked();
+ break;
+ }
+ }
+ }
+ }
+
+ public void renameBiometric(int biometricId, CharSequence name) {
+ synchronized (this) {
+ for (int i = 0; i < mBiometrics.size(); i++) {
+ if (mBiometrics.get(i).getBiometricId() == biometricId) {
+ BiometricAuthenticator.Identifier identifier = mBiometrics.get(i);
+ identifier.setName(name);
+ scheduleWriteStateLocked();
+ break;
+ }
+ }
+ }
+ }
+
+ public List<T> getBiometrics() {
+ synchronized (this) {
+ return getCopy(mBiometrics);
+ }
+ }
+
+ /**
+ * Finds a unique name for the given fingerprint
+ * @return unique name
+ */
+ public String getUniqueName() {
+ int guess = 1;
+ while (true) {
+ // Not the most efficient algorithm in the world, but there shouldn't be more than 10
+ String name = mContext.getString(getNameTemplateResource(), guess);
+ if (isUnique(name)) {
+ return name;
+ }
+ guess++;
+ }
+ }
+
+ private boolean isUnique(String name) {
+ for (T identifier : mBiometrics) {
+ if (identifier.getName().equals(name)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private File getFileForUser(int userId) {
+ return new File(Environment.getUserSystemDirectory(userId), getBiometricFile());
+ }
+
+ private void scheduleWriteStateLocked() {
+ AsyncTask.execute(mWriteStateRunnable);
+ }
+
+ @GuardedBy("this")
+ private void readStateSyncLocked() {
+ FileInputStream in;
+ if (!mFile.exists()) {
+ return;
+ }
+ try {
+ in = new FileInputStream(mFile);
+ } catch (FileNotFoundException fnfe) {
+ Slog.i(TAG, "No fingerprint state");
+ return;
+ }
+ try {
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(in, null);
+ parseStateLocked(parser);
+
+ } catch (XmlPullParserException | IOException e) {
+ throw new IllegalStateException("Failed parsing settings file: "
+ + mFile , e);
+ } finally {
+ IoUtils.closeQuietly(in);
+ }
+ }
+
+ @GuardedBy("this")
+ private void parseStateLocked(XmlPullParser parser)
+ throws IOException, XmlPullParserException {
+ final int outerDepth = parser.getDepth();
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+ if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+ continue;
+ }
+
+ String tagName = parser.getName();
+ if (tagName.equals(getBiometricsTag())) {
+ parseBiometricsLocked(parser);
+ }
+ }
+ }
+
+}
diff --git a/services/core/java/com/android/server/biometrics/common/BiometricUtils.java b/services/core/java/com/android/server/biometrics/common/BiometricUtils.java
new file mode 100644
index 0000000..8f076f1
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/common/BiometricUtils.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.server.biometrics.common;
+
+import android.content.Context;
+import android.hardware.biometrics.BiometricAuthenticator;
+
+import java.util.List;
+
+/**
+ * Interface for utilities managing biometrics and their relevant settings.
+ * @hide
+ */
+public interface BiometricUtils<T extends BiometricAuthenticator.Identifier> {
+ List<T> getBiometricsForUser(Context context, int userId);
+ void addBiometricForUser(Context context, int userId, T identifier);
+ void removeBiometricForUser(Context context, int userId, int biometricId);
+ void renameBiometricForUser(Context context, int userId, int biometricId, CharSequence name);
+ CharSequence getUniqueName(Context context, int userId);
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/fingerprint/ClientMonitor.java b/services/core/java/com/android/server/biometrics/common/ClientMonitor.java
similarity index 60%
rename from services/core/java/com/android/server/fingerprint/ClientMonitor.java
rename to services/core/java/com/android/server/biometrics/common/ClientMonitor.java
index b935ba2..699fc32 100644
--- a/services/core/java/com/android/server/fingerprint/ClientMonitor.java
+++ b/services/core/java/com/android/server/biometrics/common/ClientMonitor.java
@@ -1,5 +1,5 @@
-/**
- * Copyright (C) 2016 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.
@@ -11,16 +11,14 @@
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
- * limitations under the License.
+ * limitations under the License
*/
-package com.android.server.fingerprint;
+package com.android.server.biometrics.common;
-import android.Manifest;
import android.content.Context;
-import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
-import android.hardware.fingerprint.FingerprintManager;
-import android.hardware.fingerprint.IFingerprintServiceReceiver;
+import android.hardware.biometrics.BiometricAuthenticator;
+import android.hardware.biometrics.BiometricConstants;
import android.media.AudioAttributes;
import android.os.IBinder;
import android.os.RemoteException;
@@ -28,23 +26,24 @@
import android.os.Vibrator;
import android.util.Slog;
+import com.android.internal.logging.MetricsLogger;
+
import java.util.NoSuchElementException;
/**
- * Abstract base class for keeping track and dispatching events from fingerprint HAL to the
+ * Abstract base class for keeping track and dispatching events from the biometric's HAL to the
* the current client. Subclasses are responsible for coordinating the interaction with
- * fingerprint HAL for the specific action (e.g. authenticate, enroll, enumerate, etc.).
+ * the biometric's HAL for the specific action (e.g. authenticate, enroll, enumerate, etc.).
*/
public abstract class ClientMonitor implements IBinder.DeathRecipient {
- protected static final String TAG = FingerprintService.TAG; // TODO: get specific name
- protected static final int ERROR_ESRCH = 3; // Likely fingerprint HAL is dead. See errno.h.
- protected static final boolean DEBUG = FingerprintService.DEBUG;
- private static final long[] DEFAULT_SUCCESS_VIBRATION_PATTERN = new long[] {0, 30};
+ protected static final int ERROR_ESRCH = 3; // Likely HAL is dead. See errno.h.
+ protected static final boolean DEBUG = BiometricService.DEBUG;
private static final AudioAttributes FINGERPRINT_SONFICATION_ATTRIBUTES =
new AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
.setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
.build();
+
private final Context mContext;
private final long mHalDeviceId;
private final int mTargetUserId;
@@ -54,51 +53,65 @@
private final String mOwner;
private final VibrationEffect mSuccessVibrationEffect;
private final VibrationEffect mErrorVibrationEffect;
+ private final BiometricService.DaemonWrapper mDaemon;
+
private IBinder mToken;
- private IFingerprintServiceReceiver mReceiver;
+ private BiometricService.ServiceListener mListener;
+
+ protected final MetricsLogger mMetricsLogger;
+ protected final Metrics mMetrics;
+
protected boolean mAlreadyCancelled;
/**
- * @param context context of FingerprintService
- * @param halDeviceId the HAL device ID of the associated fingerprint hardware
+ * @param context context of BiometricService
+ * @param daemon interface to call back to a specific biometric's daemon
+ * @param halDeviceId the HAL device ID of the associated biometric hardware
* @param token a unique token for the client
- * @param receiver recipient of related events (e.g. authentication)
+ * @param listener recipient of related events (e.g. authentication)
* @param userId target user id for operation
* @param groupId groupId for the fingerprint set
- * @param restricted whether or not client has the {@link Manifest#MANAGE_FINGERPRINT}
+ * @param restricted whether or not client has the MANAGE_* permission
* permission
* @param owner name of the client that owns this
*/
- public ClientMonitor(Context context, long halDeviceId, IBinder token,
- IFingerprintServiceReceiver receiver, int userId, int groupId,boolean restricted,
- String owner) {
+ public ClientMonitor(Context context, Metrics metrics, BiometricService.DaemonWrapper daemon,
+ long halDeviceId, IBinder token, BiometricService.ServiceListener listener, int userId,
+ int groupId, boolean restricted, String owner) {
mContext = context;
+ mMetrics = metrics;
+ mDaemon = daemon;
mHalDeviceId = halDeviceId;
mToken = token;
- mReceiver = receiver;
+ mListener = listener;
mTargetUserId = userId;
mGroupId = groupId;
mIsRestricted = restricted;
mOwner = owner;
mSuccessVibrationEffect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
mErrorVibrationEffect = VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK);
+ mMetricsLogger = new MetricsLogger();
try {
if (token != null) {
token.linkToDeath(this, 0);
}
} catch (RemoteException e) {
- Slog.w(TAG, "caught remote exception in linkToDeath: ", e);
+ Slog.w(getLogTag(), "caught remote exception in linkToDeath: ", e);
}
}
+ protected String getLogTag() {
+ return mMetrics.logTag();
+ }
+
/**
- * Contacts fingerprint HAL to start the client.
+ * Contacts the biometric's HAL to start the client.
* @return 0 on success, errno from driver on failure
*/
public abstract int start();
/**
- * Contacts fingerprint HAL to stop the client.
+ * Contacts the biometric's HAL to stop the client.
* @param initiatedByClient whether the operation is at the request of a client
*/
public abstract int stop(boolean initiatedByClient);
@@ -108,56 +121,54 @@
*/
public abstract void notifyUserActivity();
- /**
- * Gets the fingerprint daemon from the cached state in the container class.
- */
- public abstract IBiometricsFingerprint getFingerprintDaemon();
-
// Event callbacks from driver. Inappropriate calls is flagged/logged by the
// respective client (e.g. enrolling shouldn't get authenticate events).
// All of these return 'true' if the operation is completed and it's ok to move
- // to the next client (e.g. authentication accepts or rejects a fingerprint).
- public abstract boolean onEnrollResult(int fingerId, int groupId, int rem);
- public abstract boolean onAuthenticated(int fingerId, int groupId);
- public abstract boolean onRemoved(int fingerId, int groupId, int remaining);
- public abstract boolean onEnumerationResult(int fingerId, int groupId, int remaining);
+ // to the next client (e.g. authentication accepts or rejects a biometric).
+ public abstract boolean onEnrollResult(BiometricAuthenticator.Identifier identifier,
+ int remaining);
+ public abstract boolean onAuthenticated(int biometricId, int groupId);
+ public abstract boolean onRemoved(BiometricAuthenticator.Identifier identifier,
+ int remaining);
+ public abstract boolean onEnumerationResult(
+ BiometricAuthenticator.Identifier identifier, int remaining);
/**
- * Called when we get notification from fingerprint HAL that an image has been acquired.
+ * Called when we get notification from the biometric's HAL that an image has been acquired.
* Common to authenticate and enroll.
* @param acquiredInfo info about the current image acquisition
* @return true if client should be removed
*/
public boolean onAcquired(int acquiredInfo, int vendorCode) {
- if (mReceiver == null)
- return true; // client not connected
try {
- mReceiver.onAcquired(getHalDeviceId(), acquiredInfo, vendorCode);
+ if (mListener != null) {
+ mListener.onAcquired(getHalDeviceId(), acquiredInfo, vendorCode);
+ }
return false; // acquisition continues...
} catch (RemoteException e) {
- Slog.w(TAG, "Failed to invoke sendAcquired:", e);
- return true; // client failed
+ Slog.w(getLogTag(), "Failed to invoke sendAcquired", e);
+ return true;
} finally {
// Good scans will keep the device awake
- if (acquiredInfo == FingerprintManager.FINGERPRINT_ACQUIRED_GOOD) {
+ if (acquiredInfo == BiometricConstants.BIOMETRIC_ACQUIRED_GOOD) {
notifyUserActivity();
}
}
}
/**
- * Called when we get notification from fingerprint HAL that an error has occurred with the
+ * Called when we get notification from the biometric's HAL that an error has occurred with the
* current operation. Common to authenticate, enroll, enumerate and remove.
* @param error
* @return true if client should be removed
*/
- public boolean onError(int error, int vendorCode) {
- if (mReceiver != null) {
- try {
- mReceiver.onError(getHalDeviceId(), error, vendorCode);
- } catch (RemoteException e) {
- Slog.w(TAG, "Failed to invoke sendError:", e);
+ public boolean onError(long deviceId, int error, int vendorCode) {
+ try {
+ if (mListener != null) {
+ mListener.onError(deviceId, error, vendorCode);
}
+ } catch (RemoteException e) {
+ Slog.w(getLogTag(), "Failed to invoke sendError", e);
}
return true; // errors always remove current client
}
@@ -168,26 +179,28 @@
mToken.unlinkToDeath(this, 0);
} catch (NoSuchElementException e) {
// TODO: remove when duplicate call bug is found
- Slog.e(TAG, "destroy(): " + this + ":", new Exception("here"));
+ Slog.e(getLogTag(), "destroy(): " + this + ":", new Exception("here"));
}
mToken = null;
}
- mReceiver = null;
+ mListener = null;
}
@Override
public void binderDied() {
mToken = null;
- mReceiver = null;
- onError(FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */);
+ mListener = null;
+ onError(getHalDeviceId(), BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE,
+ 0 /* vendorCode */);
}
@Override
protected void finalize() throws Throwable {
try {
if (mToken != null) {
- if (DEBUG) Slog.w(TAG, "removing leaked reference: " + mToken);
- onError(FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */);
+ if (DEBUG) Slog.w(getLogTag(), "removing leaked reference: " + mToken);
+ onError(getHalDeviceId(), BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE,
+ 0 /* vendorCode */);
}
} finally {
super.finalize();
@@ -206,8 +219,12 @@
return mOwner;
}
- public final IFingerprintServiceReceiver getReceiver() {
- return mReceiver;
+ public final BiometricService.ServiceListener getListener() {
+ return mListener;
+ }
+
+ public final BiometricService.DaemonWrapper getDaemonWrapper() {
+ return mDaemon;
}
public final boolean getIsRestricted() {
diff --git a/services/core/java/com/android/server/biometrics/common/EnrollClient.java b/services/core/java/com/android/server/biometrics/common/EnrollClient.java
new file mode 100644
index 0000000..5744fdb
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/common/EnrollClient.java
@@ -0,0 +1,133 @@
+/*
+ * 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.biometrics.common;
+
+import android.content.Context;
+import android.hardware.biometrics.BiometricAuthenticator;
+import android.hardware.biometrics.BiometricConstants;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import java.util.Arrays;
+
+/**
+ * A class to keep track of the enrollment state for a given client.
+ */
+public abstract class EnrollClient extends ClientMonitor {
+ private static final long MS_PER_SEC = 1000;
+ private static final int ENROLLMENT_TIMEOUT_MS = 60 * 1000; // 1 minute
+ private final byte[] mCryptoToken;
+ private final BiometricUtils mBiometricUtils;
+
+ public EnrollClient(Context context, Metrics metrics, BiometricService.DaemonWrapper daemon,
+ long halDeviceId, IBinder token, BiometricService.ServiceListener listener, int userId,
+ int groupId, byte[] cryptoToken, boolean restricted, String owner,
+ BiometricUtils utils) {
+ super(context, metrics, daemon, halDeviceId, token, listener, userId, groupId, restricted,
+ owner);
+ mBiometricUtils = utils;
+ mCryptoToken = Arrays.copyOf(cryptoToken, cryptoToken.length);
+ }
+
+ @Override
+ public boolean onEnrollResult(BiometricAuthenticator.Identifier identifier,
+ int remaining) {
+ if (remaining == 0) {
+ mBiometricUtils.addBiometricForUser(getContext(), getTargetUserId(), identifier);
+ }
+ return sendEnrollResult(identifier, remaining);
+ }
+
+ /*
+ * @return true if we're done.
+ */
+ private boolean sendEnrollResult(BiometricAuthenticator.Identifier identifier,
+ int remaining) {
+ vibrateSuccess();
+ mMetricsLogger.action(mMetrics.actionBiometricEnroll());
+ try {
+ getListener().onEnrollResult(identifier, remaining);
+ return remaining == 0;
+ } catch (RemoteException e) {
+ Slog.w(getLogTag(), "Failed to notify EnrollResult:", e);
+ return true;
+ }
+ }
+
+ @Override
+ public int start() {
+ final int timeout = (int) (ENROLLMENT_TIMEOUT_MS / MS_PER_SEC);
+ try {
+ final int result = getDaemonWrapper().enroll(mCryptoToken, getGroupId(), timeout);
+ if (result != 0) {
+ Slog.w(getLogTag(), "startEnroll failed, result=" + result);
+ mMetricsLogger.histogram(mMetrics.tagEnrollStartError(), result);
+ onError(getHalDeviceId(), BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE,
+ 0 /* vendorCode */);
+ return result;
+ }
+ } catch (RemoteException e) {
+ Slog.e(getLogTag(), "startEnroll failed", e);
+ }
+ return 0; // success
+ }
+
+ @Override
+ public int stop(boolean initiatedByClient) {
+ if (mAlreadyCancelled) {
+ Slog.w(getLogTag(), "stopEnroll: already cancelled!");
+ return 0;
+ }
+
+ try {
+ final int result = getDaemonWrapper().cancel();
+ if (result != 0) {
+ Slog.w(getLogTag(), "startEnrollCancel failed, result = " + result);
+ return result;
+ }
+ } catch (RemoteException e) {
+ Slog.e(getLogTag(), "stopEnrollment failed", e);
+ }
+ if (initiatedByClient) {
+ onError(getHalDeviceId(), BiometricConstants.BIOMETRIC_ERROR_CANCELED,
+ 0 /* vendorCode */);
+ }
+ mAlreadyCancelled = true;
+ return 0;
+ }
+
+ @Override
+ public boolean onRemoved(BiometricAuthenticator.Identifier identifier, int remaining) {
+ if (DEBUG) Slog.w(getLogTag(), "onRemoved() called for enroll!");
+ return true; // Invalid for EnrollClient
+ }
+
+ @Override
+ public boolean onEnumerationResult(BiometricAuthenticator.Identifier identifier,
+ int remaining) {
+ if (DEBUG) Slog.w(getLogTag(), "onEnumerationResult() called for enroll!");
+ return true; // Invalid for EnrollClient
+ }
+
+ @Override
+ public boolean onAuthenticated(int biometricId, int groupId) {
+ if (DEBUG) Slog.w(getLogTag(), "onAuthenticated() called for enroll!");
+ return true; // Invalid for EnrollClient
+ }
+
+}
diff --git a/services/core/java/com/android/server/biometrics/common/EnumerateClient.java b/services/core/java/com/android/server/biometrics/common/EnumerateClient.java
new file mode 100644
index 0000000..e51c1c6
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/common/EnumerateClient.java
@@ -0,0 +1,112 @@
+/*
+ * 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.biometrics.common;
+
+import android.content.Context;
+import android.hardware.biometrics.BiometricAuthenticator;
+import android.hardware.biometrics.BiometricConstants;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Slog;
+
+/**
+ * A class to keep track of the enumeration state for a given client.
+ */
+public abstract class EnumerateClient extends ClientMonitor {
+ public EnumerateClient(Context context, Metrics metrics, BiometricService.DaemonWrapper daemon,
+ long halDeviceId, IBinder token, BiometricService.ServiceListener listener, int groupId,
+ int userId, boolean restricted, String owner) {
+ super(context, metrics, daemon, halDeviceId, token, listener, userId, groupId, restricted,
+ owner);
+ }
+
+ @Override
+ public int start() {
+ // The biometric template ids will be removed when we get confirmation from the HAL
+ try {
+ final int result = getDaemonWrapper().enumerate();
+ if (result != 0) {
+ Slog.w(getLogTag(), "start enumerate for user " + getTargetUserId()
+ + " failed, result=" + result);
+ mMetricsLogger.histogram(mMetrics.tagEnumerateStartError(), result);
+ onError(getHalDeviceId(), BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE,
+ 0 /* vendorCode */);
+ return result;
+ }
+ } catch (RemoteException e) {
+ Slog.e(getLogTag(), "startEnumeration failed", e);
+ }
+ return 0;
+ }
+
+ @Override
+ public int stop(boolean initiatedByClient) {
+ if (mAlreadyCancelled) {
+ Slog.w(getLogTag(), "stopEnumerate: already cancelled!");
+ return 0;
+ }
+
+ try {
+ final int result = getDaemonWrapper().cancel();
+ if (result != 0) {
+ Slog.w(getLogTag(), "stop enumeration failed, result=" + result);
+ return result;
+ }
+ } catch (RemoteException e) {
+ Slog.e(getLogTag(), "stopEnumeration failed", e);
+ return ERROR_ESRCH;
+ }
+
+ // We don't actually stop enumerate, but inform the client that the cancel operation
+ // succeeded so we can start the next operation.
+ if (initiatedByClient) {
+ onError(getHalDeviceId(), BiometricConstants.BIOMETRIC_ERROR_CANCELED,
+ 0 /* vendorCode */);
+ }
+ mAlreadyCancelled = true;
+ return 0; // success
+ }
+
+ @Override
+ public boolean onEnumerationResult(BiometricAuthenticator.Identifier identifier,
+ int remaining) {
+ try {
+ getListener().onEnumerated(identifier, remaining);
+ } catch (RemoteException e) {
+ Slog.w(getLogTag(), "Failed to notify enumerated:", e);
+ }
+ return remaining == 0;
+ }
+
+ @Override
+ public boolean onAuthenticated(int biometricId, int groupId) {
+ if (DEBUG) Slog.w(getLogTag(), "onAuthenticated() called for enumerate!");
+ return true; // Invalid for Enumerate.
+ }
+
+ @Override
+ public boolean onEnrollResult(BiometricAuthenticator.Identifier identifier, int rem) {
+ if (DEBUG) Slog.w(getLogTag(), "onEnrollResult() called for enumerate!");
+ return true; // Invalid for Enumerate.
+ }
+
+ @Override
+ public boolean onRemoved(BiometricAuthenticator.Identifier identifier, int remaining) {
+ if (DEBUG) Slog.w(getLogTag(), "onRemoved() called for enumerate!");
+ return true; // Invalid for Enumerate.
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/common/Metrics.java b/services/core/java/com/android/server/biometrics/common/Metrics.java
new file mode 100644
index 0000000..eb1a1f8
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/common/Metrics.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.server.biometrics.common;
+
+public interface Metrics {
+ /** The log tag */
+ String logTag();
+
+ /** Strings for MetricsLogger.count() and MetricsLogger.histogram() */
+ String tagHalDied();
+ String tagAuthToken();
+ String tagAuthStartError();
+ String tagEnrollStartError();
+ String tagEnumerateStartError();
+ String tagRemoveStartError();
+
+ /** Integers for MetricsLogger.action() */
+ int actionBiometricAuth();
+ int actionBiometricEnroll();
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/biometrics/common/RemovalClient.java b/services/core/java/com/android/server/biometrics/common/RemovalClient.java
new file mode 100644
index 0000000..23d5539
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/common/RemovalClient.java
@@ -0,0 +1,124 @@
+/*
+ * 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.biometrics.common;
+
+import android.content.Context;
+import android.hardware.biometrics.BiometricAuthenticator;
+import android.hardware.biometrics.BiometricConstants;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Slog;
+
+/**
+ * A class to keep track of the remove state for a given client.
+ */
+public abstract class RemovalClient extends ClientMonitor {
+ private final int mBiometricId;
+ private final BiometricUtils mBiometricUtils;
+
+ public RemovalClient(Context context, Metrics metrics, BiometricService.DaemonWrapper daemon,
+ long halDeviceId, IBinder token, BiometricService.ServiceListener listener,
+ int biometricId, int groupId, int userId, boolean restricted, String owner,
+ BiometricUtils utils) {
+ super(context, metrics, daemon, halDeviceId, token, listener, userId, groupId, restricted,
+ owner);
+ mBiometricId = biometricId;
+ mBiometricUtils = utils;
+ }
+
+ @Override
+ public int start() {
+ // The biometric template ids will be removed when we get confirmation from the HAL
+ try {
+ final int result = getDaemonWrapper().remove(getGroupId(), mBiometricId);
+ if (result != 0) {
+ Slog.w(getLogTag(), "startRemove with id = " + mBiometricId + " failed, result=" +
+ result);
+ mMetricsLogger.histogram(mMetrics.tagRemoveStartError(), result);
+ onError(getHalDeviceId(), BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE,
+ 0 /* vendorCode */);
+ return result;
+ }
+ } catch (RemoteException e) {
+ Slog.e(getLogTag(), "startRemove failed", e);
+ }
+ return 0;
+ }
+
+ @Override
+ public int stop(boolean initiatedByClient) {
+ if (mAlreadyCancelled) {
+ Slog.w(getLogTag(), "stopRemove: already cancelled!");
+ return 0;
+ }
+
+ try {
+ final int result = getDaemonWrapper().cancel();
+ if (result != 0) {
+ Slog.w(getLogTag(), "stopRemoval failed, result=" + result);
+ return result;
+ }
+ if (DEBUG) Slog.w(getLogTag(), "client " + getOwnerString() + " is no longer removing");
+ } catch (RemoteException e) {
+ Slog.e(getLogTag(), "stopRemoval failed", e);
+ return ERROR_ESRCH;
+ }
+ mAlreadyCancelled = true;
+ return 0; // success
+ }
+
+ /*
+ * @return true if we're done.
+ */
+ private boolean sendRemoved(BiometricAuthenticator.Identifier identifier,
+ int remaining) {
+ try {
+ getListener().onRemoved(identifier, remaining);
+ } catch (RemoteException e) {
+ Slog.w(getLogTag(), "Failed to notify Removed:", e);
+ }
+ return remaining == 0;
+ }
+
+ @Override
+ public boolean onRemoved(BiometricAuthenticator.Identifier identifier, int remaining) {
+ if (identifier.getBiometricId() != 0) {
+ mBiometricUtils.removeBiometricForUser(getContext(), getTargetUserId(),
+ identifier.getBiometricId());
+ }
+ return sendRemoved(identifier, remaining);
+ }
+
+ @Override
+ public boolean onEnrollResult(BiometricAuthenticator.Identifier identifier, int rem) {
+ if (DEBUG) Slog.w(getLogTag(), "onEnrollResult() called for remove!");
+ return true; // Invalid for Remove
+ }
+
+ @Override
+ public boolean onAuthenticated(int biometricId, int groupId) {
+ if (DEBUG) Slog.w(getLogTag(), "onAuthenticated() called for remove!");
+ return true; // Invalid for Remove.
+ }
+
+ @Override
+ public boolean onEnumerationResult(BiometricAuthenticator.Identifier identifier,
+ int remaining) {
+ if (DEBUG) Slog.w(getLogTag(), "onEnumerationResult() called for remove!");
+ return true; // Invalid for Remove.
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/face/FaceMetrics.java b/services/core/java/com/android/server/biometrics/face/FaceMetrics.java
new file mode 100644
index 0000000..4c907b1
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/face/FaceMetrics.java
@@ -0,0 +1,67 @@
+/*
+ * 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.biometrics.face;
+
+import com.android.internal.logging.nano.MetricsProto;
+import com.android.server.biometrics.common.Metrics;
+
+public class FaceMetrics implements Metrics {
+ @Override
+ public String logTag() {
+ return FaceService.TAG;
+ }
+
+ @Override
+ public String tagHalDied() {
+ return "faced_died";
+ }
+
+ @Override
+ public String tagAuthToken() {
+ return "face_token";
+ }
+
+ @Override
+ public String tagAuthStartError() {
+ return "faced_auth_start_error";
+ }
+
+ @Override
+ public String tagEnrollStartError() {
+ return "faced_enroll_start_error";
+ }
+
+ @Override
+ public String tagEnumerateStartError() {
+ return "faced_enum_start_error";
+ }
+
+ @Override
+ public String tagRemoveStartError() {
+ return "faced_remove_start_error";
+ }
+
+ @Override
+ public int actionBiometricAuth() {
+ return MetricsProto.MetricsEvent.ACTION_FACE_AUTH;
+ }
+
+ @Override
+ public int actionBiometricEnroll() {
+ return MetricsProto.MetricsEvent.ACTION_FACE_ENROLL;
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/face/FaceService.java b/services/core/java/com/android/server/biometrics/face/FaceService.java
new file mode 100644
index 0000000..35679a88
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/face/FaceService.java
@@ -0,0 +1,785 @@
+/*
+ * 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.biometrics.face;
+
+import static android.Manifest.permission.INTERACT_ACROSS_USERS;
+import static android.Manifest.permission.MANAGE_FACE;
+import static android.Manifest.permission.RESET_FACE_LOCKOUT;
+import static android.Manifest.permission.USE_BIOMETRIC;
+
+import android.app.ActivityManager;
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.content.pm.UserInfo;
+import android.hardware.biometrics.BiometricAuthenticator;
+import android.hardware.biometrics.BiometricConstants;
+import android.hardware.biometrics.IBiometricPromptReceiver;
+import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
+import android.hardware.biometrics.face.V1_0.IBiometricsFace;
+import android.hardware.biometrics.face.V1_0.IBiometricsFaceClientCallback;
+import android.hardware.face.Face;
+import android.hardware.face.IFaceService;
+import android.hardware.face.IFaceServiceReceiver;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Environment;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.SELinux;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.statusbar.IStatusBarService;
+import com.android.internal.util.DumpUtils;
+import com.android.server.SystemServerInitThreadPool;
+import com.android.server.biometrics.common.BiometricService;
+import com.android.server.biometrics.common.BiometricUtils;
+import com.android.server.biometrics.common.Metrics;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A service to manage multiple clients that want to access the face HAL API.
+ * The service is responsible for maintaining a list of clients and dispatching all
+ * face -related events.
+ *
+ * @hide
+ */
+public class FaceService extends BiometricService {
+
+ protected static final String TAG = "FaceService";
+ private static final boolean DEBUG = true;
+ private static final String FACE_DATA_DIR = "facedata";
+ private static final String ACTION_LOCKOUT_RESET =
+ "com.android.server.biometrics.face.ACTION_LOCKOUT_RESET";
+ private static final int MAX_FAILED_ATTEMPTS_LOCKOUT_TIMED = 3;
+ private static final int MAX_FAILED_ATTEMPTS_LOCKOUT_PERMANENT = 12;
+
+ /**
+ * Receives the incoming binder calls from FaceManager.
+ */
+ private final class FaceServiceWrapper extends IFaceService.Stub {
+
+ /**
+ * The following methods contain common code which is shared in biometrics/common.
+ */
+ @Override // Binder call
+ public long preEnroll(IBinder token) {
+ checkPermission(MANAGE_FACE);
+ return startPreEnroll(token);
+ }
+
+ @Override // Binder call
+ public int postEnroll(IBinder token) {
+ checkPermission(MANAGE_FACE);
+ return startPostEnroll(token);
+ }
+
+ @Override // Binder call
+ public void enroll(final IBinder token, final byte[] cryptoToken, final int userId,
+ final IFaceServiceReceiver receiver, final int flags,
+ final String opPackageName) {
+ checkPermission(MANAGE_FACE);
+
+ final boolean restricted = isRestricted();
+ final EnrollClientImpl client = new EnrollClientImpl(getContext(), mDaemonWrapper,
+ mHalDeviceId, token, new ServiceListenerImpl(receiver), mCurrentUserId,
+ 0 /* groupId */, cryptoToken, restricted, opPackageName);
+
+ enrollInternal(client, userId);
+ }
+
+ @Override // Binder call
+ public void cancelEnrollment(final IBinder token) {
+ checkPermission(MANAGE_FACE);
+ cancelEnrollmentInternal(token);
+ }
+
+ @Override // Binder call
+ public void authenticate(final IBinder token, final long opId,
+ final IFaceServiceReceiver receiver, final int flags,
+ final String opPackageName, final Bundle bundle,
+ final IBiometricPromptReceiver dialogReceiver) {
+ final boolean restricted = isRestricted();
+ final AuthenticationClientImpl client = new AuthenticationClientImpl(getContext(),
+ mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver),
+ mCurrentUserId, 0 /* groupId */, opId, restricted, opPackageName, bundle,
+ dialogReceiver, mStatusBarService);
+
+ authenticateInternal(client, opId, opPackageName);
+ }
+
+ @Override // Binder call
+ public void cancelAuthentication(final IBinder token, final String opPackageName) {
+ cancelAuthenticationInternal(token, opPackageName);
+ }
+
+ @Override // Binder call
+ public void setActiveUser(final int userId) {
+ checkPermission(MANAGE_FACE);
+ setActiveUserInternal(userId);
+ }
+
+ @Override // Binder call
+ public void remove(final IBinder token, final int faceId, final int userId,
+ final IFaceServiceReceiver receiver) {
+ checkPermission(MANAGE_FACE);
+
+ if (token == null) {
+ Slog.w(TAG, "remove(): token is null");
+ return;
+ }
+
+ final boolean restricted = isRestricted();
+ final RemovalClientImpl client = new RemovalClientImpl(getContext(), mDaemonWrapper,
+ mHalDeviceId, token, new ServiceListenerImpl(receiver), faceId, 0 /* groupId */,
+ userId, restricted, token.toString());
+ client.setShouldNotifyUserActivity(true);
+ removeInternal(client);
+ }
+
+ @Override
+ public void enumerate(final IBinder token, final int userId,
+ final IFaceServiceReceiver receiver) {
+ checkPermission(MANAGE_FACE);
+
+ final boolean restricted = isRestricted();
+ final EnumerateClientImpl client = new EnumerateClientImpl(getContext(), mDaemonWrapper,
+ mHalDeviceId, token, new ServiceListenerImpl(receiver), userId, userId,
+ restricted, getContext().getOpPackageName());
+ enumerateInternal(client);
+ }
+
+ @Override
+ public void addLockoutResetCallback(final IBiometricServiceLockoutResetCallback callback)
+ throws RemoteException {
+ FaceService.super.addLockoutResetCallback(callback);
+ }
+
+ @Override // Binder call
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) {
+ return;
+ }
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ if (args.length > 0 && "--proto".equals(args[0])) {
+ dumpProto(fd);
+ } else {
+ dumpInternal(pw);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ /**
+ * The following methods don't use any common code from BiometricService
+ */
+
+ // TODO: refactor out common code here
+ @Override // Binder call
+ public boolean isHardwareDetected(long deviceId, String opPackageName) {
+ if (!canUseBiometric(opPackageName, false /* foregroundOnly */,
+ Binder.getCallingUid(), Binder.getCallingPid(),
+ UserHandle.getCallingUserId())) {
+ return false;
+ }
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ IBiometricsFace daemon = getFaceDaemon();
+ return daemon != null && mHalDeviceId != 0;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override // Binder call
+ public void rename(final int faceId, final String name) {
+ checkPermission(MANAGE_FACE);
+ if (!isCurrentUserOrProfile(UserHandle.getCallingUserId())) {
+ return;
+ }
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ getBiometricUtils().renameBiometricForUser(getContext(), mCurrentUserId,
+ faceId, name);
+ }
+ });
+ }
+
+ @Override // Binder call
+ public List<Face> getEnrolledFaces(int userId, String opPackageName) {
+ if (!canUseBiometric(opPackageName, false /* foregroundOnly */,
+ Binder.getCallingUid(), Binder.getCallingPid(),
+ UserHandle.getCallingUserId())) {
+ return null;
+ }
+
+ return FaceService.this.getEnrolledFaces(userId);
+ }
+
+ @Override // Binder call
+ public boolean hasEnrolledFaces(int userId, String opPackageName) {
+ if (!canUseBiometric(opPackageName, false /* foregroundOnly */,
+ Binder.getCallingUid(), Binder.getCallingPid(),
+ UserHandle.getCallingUserId())) {
+ return false;
+ }
+
+ return FaceService.this.hasEnrolledBiometrics(userId);
+ }
+
+ @Override // Binder call
+ public long getAuthenticatorId(String opPackageName) {
+ // In this method, we're not checking whether the caller is permitted to use face
+ // API because current authenticator ID is leaked (in a more contrived way) via Android
+ // Keystore (android.security.keystore package): the user of that API can create a key
+ // which requires face authentication for its use, and then query the key's
+ // characteristics (hidden API) which returns, among other things, face
+ // authenticator ID which was active at key creation time.
+ //
+ // Reason: The part of Android Keystore which runs inside an app's process invokes this
+ // method in certain cases. Those cases are not always where the developer demonstrates
+ // explicit intent to use face functionality. Thus, to avoiding throwing an
+ // unexpected SecurityException this method does not check whether its caller is
+ // permitted to use face API.
+ //
+ // The permission check should be restored once Android Keystore no longer invokes this
+ // method from inside app processes.
+
+ return FaceService.this.getAuthenticatorId(opPackageName);
+ }
+
+ @Override // Binder call
+ public void resetTimeout(byte[] token) {
+ checkPermission(RESET_FACE_LOCKOUT);
+ // TODO: confirm security token when we move timeout management into the HAL layer.
+ mHandler.post(mResetFailedAttemptsForCurrentUserRunnable);
+ }
+ }
+
+ /**
+ * Receives callbacks from the ClientMonitor implementations. The results are forwarded to
+ * the FaceManager.
+ */
+ private class ServiceListenerImpl implements ServiceListener {
+
+ private IFaceServiceReceiver mFaceServiceReceiver;
+
+ public ServiceListenerImpl(IFaceServiceReceiver receiver) {
+ mFaceServiceReceiver = receiver;
+ }
+
+ @Override
+ public void onEnrollResult(BiometricAuthenticator.Identifier identifier, int remaining)
+ throws RemoteException {
+ if (mFaceServiceReceiver != null) {
+ mFaceServiceReceiver.onEnrollResult(identifier.getDeviceId(),
+ identifier.getBiometricId(),
+ remaining);
+ }
+ }
+
+ @Override
+ public void onAcquired(long deviceId, int acquiredInfo, int vendorCode)
+ throws RemoteException {
+ if (mFaceServiceReceiver != null) {
+ mFaceServiceReceiver.onAcquired(deviceId, acquiredInfo, vendorCode);
+ }
+ }
+
+ @Override
+ public void onAuthenticationSucceeded(long deviceId,
+ BiometricAuthenticator.Identifier biometric, int userId)
+ throws RemoteException {
+ if (mFaceServiceReceiver != null) {
+ if (biometric instanceof Face) {
+ mFaceServiceReceiver.onAuthenticationSucceeded(deviceId, (Face)biometric);
+ } else {
+ Slog.e(TAG, "onAuthenticationSucceeded received non-face biometric");
+ }
+ }
+ }
+
+ @Override
+ public void onAuthenticationFailed(long deviceId) throws RemoteException {
+ if (mFaceServiceReceiver != null) {
+ mFaceServiceReceiver.onAuthenticationFailed(deviceId);
+ }
+ }
+
+ @Override
+ public void onError(long deviceId, int error, int vendorCode) throws RemoteException {
+ if (mFaceServiceReceiver != null) {
+ mFaceServiceReceiver.onError(deviceId, error, vendorCode);
+ }
+ }
+
+ @Override
+ public void onRemoved(BiometricAuthenticator.Identifier identifier,
+ int remaining) throws RemoteException {
+ if (mFaceServiceReceiver != null) {
+ mFaceServiceReceiver.onRemoved(identifier.getDeviceId(),
+ identifier.getBiometricId(), remaining);
+ }
+ }
+
+ @Override
+ public void onEnumerated(BiometricAuthenticator.Identifier identifier, int remaining)
+ throws RemoteException {
+ if (mFaceServiceReceiver != null) {
+
+ }
+ }
+ }
+
+ private final FaceMetrics mFaceMetrics = new FaceMetrics();
+
+ @GuardedBy("this")
+ private IBiometricsFace mDaemon;
+
+ private long mHalDeviceId;
+ private IStatusBarService mStatusBarService;
+
+ /**
+ * Receives callbacks from the HAL.
+ */
+ private IBiometricsFaceClientCallback mDaemonCallback =
+ new IBiometricsFaceClientCallback.Stub() {
+ @Override
+ public void onEnrollResult(final long deviceId, int faceId, int userId,
+ int remaining) {
+ mHandler.post(() -> {
+ final Face face = new Face(getBiometricUtils()
+ .getUniqueName(getContext(), userId), faceId, deviceId);
+ FaceService.super.handleEnrollResult(face, remaining);
+ });
+ }
+
+ @Override
+ public void onAcquired(final long deviceId, final int userId,
+ final int acquiredInfo,
+ final int vendorCode) {
+ mHandler.post(() -> {
+ FaceService.super.handleAcquired(deviceId, acquiredInfo, vendorCode);
+ });
+ }
+
+ @Override
+ public void onAuthenticated(final long deviceId, final int faceId, final int userId,
+ ArrayList<Byte> token) {
+ mHandler.post(() -> {
+ FaceService.super.handleAuthenticated(deviceId, faceId, userId, token);
+ });
+ }
+
+ @Override
+ public void onError(final long deviceId, final int userId, final int error,
+ final int vendorCode) {
+ mHandler.post(() -> {
+ FaceService.super.handleError(deviceId, error, vendorCode);
+
+ // TODO: this chunk of code should be common to all biometric services
+ if (error == BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE) {
+ // If we get HW_UNAVAILABLE, try to connect again later...
+ Slog.w(TAG, "Got ERROR_HW_UNAVAILABLE; try reconnecting next client.");
+ synchronized (this) {
+ mDaemon = null;
+ mHalDeviceId = 0;
+ mCurrentUserId = UserHandle.USER_NULL;
+ }
+ }
+ });
+ }
+
+ @Override
+ public void onRemoved(final long deviceId, final int faceId, final int userId,
+ final int remaining) {
+ mHandler.post(() -> {
+ final Face face = new Face("", faceId, deviceId);
+ FaceService.super.handleRemoved(face, remaining);
+ });
+ }
+
+ @Override
+ public void onEnumerate(long deviceId, ArrayList<Integer> faceIds, int userId)
+ throws RemoteException {
+ // TODO
+ }
+ };
+
+ /**
+ * Wraps the HAL-specific code and is passed to the ClientMonitor implementations so that they
+ * can be shared between the multiple biometric services.
+ */
+ private final DaemonWrapper mDaemonWrapper = new DaemonWrapper() {
+ @Override
+ public int authenticate(long operationId, int groupId) throws RemoteException {
+ IBiometricsFace daemon = getFaceDaemon();
+ if (daemon == null) {
+ Slog.w(TAG, "authenticate(): no face HAL!");
+ return ERROR_ESRCH;
+ }
+ return daemon.authenticate(operationId);
+ }
+
+ @Override
+ public int cancel() throws RemoteException {
+ IBiometricsFace daemon = getFaceDaemon();
+ if (daemon == null) {
+ Slog.w(TAG, "cancel(): no face HAL!");
+ return ERROR_ESRCH;
+ }
+ return daemon.cancel();
+ }
+
+ @Override
+ public int remove(int groupId, int biometricId) throws RemoteException {
+ IBiometricsFace daemon = getFaceDaemon();
+ if (daemon == null) {
+ Slog.w(TAG, "remove(): no face HAL!");
+ return ERROR_ESRCH;
+ }
+ return daemon.remove(biometricId);
+ }
+
+ @Override
+ public int enumerate() throws RemoteException {
+ IBiometricsFace daemon = getFaceDaemon();
+ if (daemon == null) {
+ Slog.w(TAG, "enumerate(): no face HAL!");
+ return ERROR_ESRCH;
+ }
+ return daemon.enumerate();
+ }
+
+ @Override
+ public int enroll(byte[] cryptoToken, int groupId, int timeout) throws RemoteException {
+ IBiometricsFace daemon = getFaceDaemon();
+ if (daemon == null) {
+ Slog.w(TAG, "enroll(): no face HAL!");
+ return ERROR_ESRCH;
+ }
+ final ArrayList<Byte> token = new ArrayList<>();
+ for (int i = 0; i < cryptoToken.length; i++) {
+ token.add(cryptoToken[i]);
+ }
+ return daemon.enroll(token, timeout);
+ }
+ };
+
+
+ public FaceService(Context context) {
+ super(context);
+ // TODO: can this be retrieved from AuthenticationClient, or BiometricService?
+ mStatusBarService = IStatusBarService.Stub.asInterface(
+ ServiceManager.getService(Context.STATUS_BAR_SERVICE));
+ }
+
+ @Override
+ public void onStart() {
+ publishBinderService(Context.FACE_SERVICE, new FaceServiceWrapper());
+ SystemServerInitThreadPool.get().submit(this::getFaceDaemon, TAG + ".onStart");
+ }
+
+ @Override
+ public String getTag() {
+ return TAG;
+ }
+
+ @Override
+ protected BiometricUtils getBiometricUtils() {
+ return FaceUtils.getInstance();
+ }
+
+ @Override
+ protected int getFailedAttemptsLockoutTimed() {
+ return MAX_FAILED_ATTEMPTS_LOCKOUT_TIMED;
+ }
+
+ @Override
+ protected int getFailedAttemptsLockoutPermanent() {
+ return MAX_FAILED_ATTEMPTS_LOCKOUT_PERMANENT;
+ }
+
+ @Override
+ protected Metrics getMetrics() {
+ return mFaceMetrics;
+ }
+
+ @Override
+ protected boolean hasReachedEnrollmentLimit(int userId) {
+ final int limit = getContext().getResources().getInteger(
+ com.android.internal.R.integer.config_faceMaxTemplatesPerUser);
+ final int enrolled = FaceService.this.getEnrolledFaces(userId).size();
+ if (enrolled >= limit) {
+ Slog.w(TAG, "Too many faces registered");
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ protected void updateActiveGroup(int userId, String clientPackage) {
+ IBiometricsFace daemon = getFaceDaemon();
+
+ if (daemon != null) {
+ try {
+ userId = getUserOrWorkProfileId(clientPackage, userId);
+ if (userId != mCurrentUserId) {
+ final File baseDir = Environment.getDataVendorDeDirectory(userId);
+ final File faceDir = new File(baseDir, FACE_DATA_DIR);
+ if (!faceDir.exists()) {
+ if (!faceDir.mkdir()) {
+ Slog.v(TAG, "Cannot make directory: " + faceDir.getAbsolutePath());
+ return;
+ }
+ // Calling mkdir() from this process will create a directory with our
+ // permissions (inherited from the containing dir). This command fixes
+ // the label.
+ if (!SELinux.restorecon(faceDir)) {
+ Slog.w(TAG, "Restorecons failed. Directory will have wrong label.");
+ return;
+ }
+ }
+
+ daemon.setActiveUser(userId, faceDir.getAbsolutePath());
+ mCurrentUserId = userId;
+ }
+ mAuthenticatorIds.put(userId,
+ hasEnrolledBiometrics(userId) ? daemon.getAuthenticatorId().value : 0L);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to setActiveUser():", e);
+ }
+ }
+ }
+
+ @Override
+ protected String getLockoutResetIntent() {
+ return ACTION_LOCKOUT_RESET;
+ }
+
+ @Override
+ protected String getLockoutBroadcastPermission() {
+ return RESET_FACE_LOCKOUT;
+ }
+
+ @Override
+ protected long getHalDeviceId() {
+ return mHalDeviceId;
+ }
+
+ @Override
+ protected void handleUserSwitching(int userId) {
+ updateActiveGroup(userId, null);
+ }
+
+ @Override
+ protected boolean hasEnrolledBiometrics(int userId) {
+ if (userId != UserHandle.getCallingUserId()) {
+ checkPermission(INTERACT_ACROSS_USERS);
+ }
+ return getBiometricUtils().getBiometricsForUser(getContext(), userId).size() > 0;
+ }
+
+ @Override
+ protected String getManageBiometricPermission() {
+ return MANAGE_FACE;
+ }
+
+ @Override
+ protected void checkUseBiometricPermission() {
+ checkPermission(USE_BIOMETRIC);
+ }
+
+ @Override
+ protected int getAppOp() {
+ return AppOpsManager.OP_USE_FACE;
+ }
+
+ @Override
+ protected void notifyClientActiveCallbacks(boolean isActive) {
+ // noop for Face.
+ }
+
+ /** Gets the face daemon */
+ private synchronized IBiometricsFace getFaceDaemon() {
+ if (mDaemon == null) {
+ Slog.v(TAG, "mDaemon was null, reconnect to face");
+ try {
+ mDaemon = IBiometricsFace.getService();
+ } catch (java.util.NoSuchElementException e) {
+ // Service doesn't exist or cannot be opened. Logged below.
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to get biometric interface", e);
+ }
+ if (mDaemon == null) {
+ Slog.w(TAG, "face HIDL not available");
+ return null;
+ }
+
+ mDaemon.asBinder().linkToDeath(this, 0);
+
+ try {
+ mHalDeviceId = mDaemon.setCallback(mDaemonCallback).value;
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to open face HAL", e);
+ mDaemon = null; // try again later!
+ }
+
+ if (DEBUG) Slog.v(TAG, "Face HAL id: " + mHalDeviceId);
+ if (mHalDeviceId != 0) {
+ loadAuthenticatorIds();
+ updateActiveGroup(ActivityManager.getCurrentUser(), null);
+ } else {
+ Slog.w(TAG, "Failed to open Face HAL!");
+ MetricsLogger.count(getContext(), "faced_openhal_error", 1);
+ mDaemon = null;
+ }
+ }
+ return mDaemon;
+ }
+
+ private long startPreEnroll(IBinder token) {
+ IBiometricsFace daemon = getFaceDaemon();
+ if (daemon == null) {
+ Slog.w(TAG, "startPreEnroll: no face HAL!");
+ return 0;
+ }
+ try {
+ return daemon.preEnroll().value;
+ } catch (RemoteException e) {
+ Slog.e(TAG, "startPreEnroll failed", e);
+ }
+ return 0;
+ }
+
+ private int startPostEnroll(IBinder token) {
+ IBiometricsFace daemon = getFaceDaemon();
+ if (daemon == null) {
+ Slog.w(TAG, "startPostEnroll: no face HAL!");
+ return 0;
+ }
+ try {
+ return daemon.postEnroll();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "startPostEnroll failed", e);
+ }
+ return 0;
+ }
+
+ private List<Face> getEnrolledFaces(int userId) {
+ return getBiometricUtils().getBiometricsForUser(getContext(), userId);
+ }
+
+ private void dumpInternal(PrintWriter pw) {
+ JSONObject dump = new JSONObject();
+ try {
+ dump.put("service", "Face Manager");
+
+ JSONArray sets = new JSONArray();
+ for (UserInfo user : UserManager.get(getContext()).getUsers()) {
+ final int userId = user.getUserHandle().getIdentifier();
+ final int N = getBiometricUtils().getBiometricsForUser(getContext(), userId).size();
+ PerformanceStats stats = mPerformanceMap.get(userId);
+ PerformanceStats cryptoStats = mCryptoPerformanceMap.get(userId);
+ JSONObject set = new JSONObject();
+ set.put("id", userId);
+ set.put("count", N);
+ set.put("accept", (stats != null) ? stats.accept : 0);
+ set.put("reject", (stats != null) ? stats.reject : 0);
+ set.put("acquire", (stats != null) ? stats.acquire : 0);
+ set.put("lockout", (stats != null) ? stats.lockout : 0);
+ set.put("permanentLockout", (stats != null) ? stats.permanentLockout : 0);
+ // cryptoStats measures statistics about secure face transactions
+ // (e.g. to unlock password storage, make secure purchases, etc.)
+ set.put("acceptCrypto", (cryptoStats != null) ? cryptoStats.accept : 0);
+ set.put("rejectCrypto", (cryptoStats != null) ? cryptoStats.reject : 0);
+ set.put("acquireCrypto", (cryptoStats != null) ? cryptoStats.acquire : 0);
+ set.put("lockoutCrypto", (cryptoStats != null) ? cryptoStats.lockout : 0);
+ set.put("permanentLockoutCrypto",
+ (cryptoStats != null) ? cryptoStats.permanentLockout : 0);
+ sets.put(set);
+ }
+
+ dump.put("prints", sets);
+ } catch (JSONException e) {
+ Slog.e(TAG, "dump formatting failure", e);
+ }
+ pw.println(dump);
+ }
+
+ private void dumpProto(FileDescriptor fd) {
+ final ProtoOutputStream proto = new ProtoOutputStream(fd);
+ for (UserInfo user : UserManager.get(getContext()).getUsers()) {
+ final int userId = user.getUserHandle().getIdentifier();
+
+ final long userToken = proto.start(FaceServiceDumpProto.USERS);
+
+ proto.write(FaceUserStatsProto.USER_ID, userId);
+ proto.write(FaceUserStatsProto.NUM_FACES,
+ getBiometricUtils().getBiometricsForUser(getContext(), userId).size());
+
+ // Normal face authentications (e.g. lockscreen)
+ final PerformanceStats normal = mPerformanceMap.get(userId);
+ if (normal != null) {
+ final long countsToken = proto.start(FaceUserStatsProto.NORMAL);
+ proto.write(FaceActionStatsProto.ACCEPT, normal.accept);
+ proto.write(FaceActionStatsProto.REJECT, normal.reject);
+ proto.write(FaceActionStatsProto.ACQUIRE, normal.acquire);
+ proto.write(FaceActionStatsProto.LOCKOUT, normal.lockout);
+ proto.write(FaceActionStatsProto.LOCKOUT_PERMANENT, normal.lockout);
+ proto.end(countsToken);
+ }
+
+ // Statistics about secure face transactions (e.g. to unlock password
+ // storage, make secure purchases, etc.)
+ final PerformanceStats crypto = mCryptoPerformanceMap.get(userId);
+ if (crypto != null) {
+ final long countsToken = proto.start(FaceUserStatsProto.CRYPTO);
+ proto.write(FaceActionStatsProto.ACCEPT, crypto.accept);
+ proto.write(FaceActionStatsProto.REJECT, crypto.reject);
+ proto.write(FaceActionStatsProto.ACQUIRE, crypto.acquire);
+ proto.write(FaceActionStatsProto.LOCKOUT, crypto.lockout);
+ proto.write(FaceActionStatsProto.LOCKOUT_PERMANENT, crypto.lockout);
+ proto.end(countsToken);
+ }
+
+ proto.end(userToken);
+ }
+ proto.flush();
+ mPerformanceMap.clear();
+ mCryptoPerformanceMap.clear();
+ }
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/biometrics/face/FaceUserState.java b/services/core/java/com/android/server/biometrics/face/FaceUserState.java
new file mode 100644
index 0000000..7d67c62
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/face/FaceUserState.java
@@ -0,0 +1,156 @@
+/*
+ * 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.biometrics.face;
+
+import android.content.Context;
+import android.hardware.biometrics.BiometricAuthenticator;
+import android.hardware.face.Face;
+import android.util.AtomicFile;
+import android.util.Slog;
+import android.util.Xml;
+
+import com.android.server.biometrics.common.BiometricUserState;
+
+import libcore.io.IoUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+
+
+/**
+ * Class managing the set of faces per user across device reboots.
+ * @hide
+ */
+public class FaceUserState extends BiometricUserState {
+
+ private static final String TAG = "FaceState";
+ private static final String FACE_FILE = "settings_face.xml";
+
+ private static final String TAG_FACES = "faces";
+ private static final String TAG_FACE = "face";
+ private static final String ATTR_NAME = "name";
+ private static final String ATTR_FACE_ID = "faceId";
+ private static final String ATTR_DEVICE_ID = "deviceId";
+
+ public FaceUserState(Context ctx, int userId) {
+ super(ctx, userId);
+ }
+
+ @Override
+ protected String getBiometricsTag() {
+ return TAG_FACES;
+ }
+
+ @Override
+ protected String getBiometricFile() {
+ return FACE_FILE;
+ }
+
+ @Override
+ protected int getNameTemplateResource() {
+ return com.android.internal.R.string.face_name_template;
+ }
+
+ @Override
+ public void addBiometric(BiometricAuthenticator.Identifier identifier) {
+ if (identifier instanceof Face) {
+ super.addBiometric(identifier);
+ } else {
+ Slog.w(TAG, "Attempted to add non-face identifier");
+ }
+ }
+
+ @Override
+ protected ArrayList getCopy(ArrayList array) {
+ ArrayList<Face> result = new ArrayList<>(array.size());
+ for (int i = 0; i < array.size(); i++) {
+ Face f = (Face) array.get(i);
+ result.add(new Face(f.getName(), f.getFaceId(), f.getDeviceId()));
+ }
+ return result;
+ }
+
+ @Override
+ protected void doWriteState() {
+ AtomicFile destination = new AtomicFile(mFile);
+
+ ArrayList<Face> faces;
+
+ synchronized (this) {
+ faces = getCopy(mBiometrics);
+ }
+
+ FileOutputStream out = null;
+ try {
+ out = destination.startWrite();
+
+ XmlSerializer serializer = Xml.newSerializer();
+ serializer.setOutput(out, "utf-8");
+ serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
+ serializer.startDocument(null, true);
+ serializer.startTag(null, TAG_FACES);
+
+ final int count = faces.size();
+ for (int i = 0; i < count; i++) {
+ Face f = faces.get(i);
+ serializer.startTag(null, TAG_FACE);
+ serializer.attribute(null, ATTR_FACE_ID, Integer.toString(f.getFaceId()));
+ serializer.attribute(null, ATTR_NAME, f.getName().toString());
+ serializer.attribute(null, ATTR_DEVICE_ID, Long.toString(f.getDeviceId()));
+ serializer.endTag(null, TAG_FACE);
+ }
+
+ serializer.endTag(null, TAG_FACES);
+ serializer.endDocument();
+ destination.finishWrite(out);
+
+ // Any error while writing is fatal.
+ } catch (Throwable t) {
+ Slog.wtf(TAG, "Failed to write settings, restoring backup", t);
+ destination.failWrite(out);
+ throw new IllegalStateException("Failed to write faces", t);
+ } finally {
+ IoUtils.closeQuietly(out);
+ }
+ }
+
+ @Override
+ protected void parseBiometricsLocked(XmlPullParser parser)
+ throws IOException, XmlPullParserException {
+ final int outerDepth = parser.getDepth();
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+ if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+ continue;
+ }
+
+ String tagName = parser.getName();
+ if (tagName.equals(TAG_FACE)) {
+ String name = parser.getAttributeValue(null, ATTR_NAME);
+ String faceId = parser.getAttributeValue(null, ATTR_FACE_ID);
+ String deviceId = parser.getAttributeValue(null, ATTR_DEVICE_ID);
+ mBiometrics.add(new Face(name, Integer.parseInt(faceId), Integer.parseInt(deviceId)));
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/face/FaceUtils.java b/services/core/java/com/android/server/biometrics/face/FaceUtils.java
new file mode 100644
index 0000000..a7e85e0
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/face/FaceUtils.java
@@ -0,0 +1,94 @@
+/**
+ * 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.biometrics.face;
+
+import android.content.Context;
+import android.hardware.biometrics.BiometricAuthenticator;
+import android.hardware.face.Face;
+import android.text.TextUtils;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.biometrics.common.BiometricUtils;
+
+import java.util.List;
+
+/**
+ * Utility class for dealing with faces and face settings.
+ */
+public class FaceUtils implements BiometricUtils {
+
+ private static final Object sInstanceLock = new Object();
+ private static FaceUtils sInstance;
+
+ @GuardedBy("this")
+ private final SparseArray<FaceUserState> mUsers = new SparseArray<>();
+
+ public static FaceUtils getInstance() {
+ synchronized (sInstanceLock) {
+ if (sInstance == null) {
+ sInstance = new FaceUtils();
+ }
+ }
+ return sInstance;
+ }
+
+ private FaceUtils() {
+ }
+
+ @Override
+ public List<Face> getBiometricsForUser(Context ctx, int userId) {
+ return getStateForUser(ctx, userId).getBiometrics();
+ }
+
+ @Override
+ public void addBiometricForUser(Context ctx, int userId,
+ BiometricAuthenticator.Identifier identifier) {
+ getStateForUser(ctx, userId).addBiometric(identifier);
+ }
+
+ @Override
+ public void removeBiometricForUser(Context ctx, int userId, int faceId) {
+ getStateForUser(ctx, userId).removeBiometric(faceId);
+ }
+
+ @Override
+ public void renameBiometricForUser(Context ctx, int userId, int faceId, CharSequence name) {
+ if (TextUtils.isEmpty(name)) {
+ // Don't do the rename if it's empty
+ return;
+ }
+ getStateForUser(ctx, userId).renameBiometric(faceId, name);
+ }
+
+ @Override
+ public CharSequence getUniqueName(Context context, int userId) {
+ return getStateForUser(context, userId).getUniqueName();
+ }
+
+ private FaceUserState getStateForUser(Context ctx, int userId) {
+ synchronized (this) {
+ FaceUserState state = mUsers.get(userId);
+ if (state == null) {
+ state = new FaceUserState(ctx, userId);
+ mUsers.put(userId, state);
+ }
+ return state;
+ }
+ }
+}
+
diff --git a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintMetrics.java b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintMetrics.java
new file mode 100644
index 0000000..ba8b3b3
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintMetrics.java
@@ -0,0 +1,68 @@
+/*
+ * 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.biometrics.fingerprint;
+
+import com.android.internal.logging.nano.MetricsProto;
+import com.android.server.biometrics.common.Metrics;
+
+public class FingerprintMetrics implements Metrics {
+
+ @Override
+ public String logTag() {
+ return FingerprintService.TAG;
+ }
+
+ @Override
+ public String tagHalDied() {
+ return "fingerprintd_died";
+ }
+
+ @Override
+ public String tagAuthToken() {
+ return "fingerprint_token";
+ }
+
+ @Override
+ public String tagAuthStartError() {
+ return "fingerprintd_auth_start_error";
+ }
+
+ @Override
+ public String tagEnrollStartError() {
+ return "fingerprintd_enroll_start_error";
+ }
+
+ @Override
+ public String tagEnumerateStartError() {
+ return "fingerprintd_enum_start_error";
+ }
+
+ @Override
+ public String tagRemoveStartError() {
+ return "fingerprintd_remove_start_error";
+ }
+
+ @Override
+ public int actionBiometricAuth() {
+ return MetricsProto.MetricsEvent.ACTION_FINGERPRINT_AUTH;
+ }
+
+ @Override
+ public int actionBiometricEnroll() {
+ return MetricsProto.MetricsEvent.ACTION_FINGERPRINT_ENROLL;
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
new file mode 100644
index 0000000..7004e1b
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
@@ -0,0 +1,1026 @@
+/*
+ * 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.
+ */
+
+package com.android.server.biometrics.fingerprint;
+
+import static android.Manifest.permission.INTERACT_ACROSS_USERS;
+import static android.Manifest.permission.MANAGE_FINGERPRINT;
+import static android.Manifest.permission.RESET_FINGERPRINT_LOCKOUT;
+import static android.Manifest.permission.USE_BIOMETRIC;
+import static android.Manifest.permission.USE_FINGERPRINT;
+
+import android.app.ActivityManager;
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
+import android.hardware.biometrics.BiometricAuthenticator;
+import android.hardware.biometrics.BiometricConstants;
+import android.hardware.biometrics.IBiometricPromptReceiver;
+import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
+import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
+import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprintClientCallback;
+import android.hardware.fingerprint.Fingerprint;
+import android.hardware.fingerprint.IFingerprintClientActiveCallback;
+import android.hardware.fingerprint.IFingerprintService;
+import android.hardware.fingerprint.IFingerprintServiceReceiver;
+import android.os.Binder;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Environment;
+import android.os.IBinder;
+import android.os.PowerManager;
+import android.os.RemoteException;
+import android.os.SELinux;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.statusbar.IStatusBarService;
+import com.android.internal.util.DumpUtils;
+import com.android.server.SystemServerInitThreadPool;
+import com.android.server.biometrics.common.BiometricService;
+import com.android.server.biometrics.common.BiometricUtils;
+import com.android.server.biometrics.common.ClientMonitor;
+import com.android.server.biometrics.common.EnumerateClient;
+import com.android.server.biometrics.common.Metrics;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/**
+ * A service to manage multiple clients that want to access the fingerprint HAL API.
+ * The service is responsible for maintaining a list of clients and dispatching all
+ * fingerprint-related events.
+ *
+ * @hide
+ */
+public class FingerprintService extends BiometricService {
+
+ protected static final String TAG = "FingerprintService";
+ private static final boolean DEBUG = true;
+ private static final boolean CLEANUP_UNUSED_FP = true;
+ private static final String FP_DATA_DIR = "fpdata";
+ private static final String ACTION_LOCKOUT_RESET =
+ "com.android.server.biometrics.fingerprint.ACTION_LOCKOUT_RESET";
+ private static final int MAX_FAILED_ATTEMPTS_LOCKOUT_TIMED = 5;
+ private static final int MAX_FAILED_ATTEMPTS_LOCKOUT_PERMANENT = 20;
+
+ // TODO: This should be refactored into BiometricService
+ private final class UserFingerprint {
+ Fingerprint f;
+ int userId;
+ public UserFingerprint(Fingerprint f, int userId) {
+ this.f = f;
+ this.userId = userId;
+ }
+ }
+
+ /**
+ * Receives the incoming binder calls from FingerprintManager.
+ */
+ private final class FingerprintServiceWrapper extends IFingerprintService.Stub {
+
+ /**
+ * The following methods contain common code which is shared in biometrics/common.
+ */
+
+ @Override // Binder call
+ public long preEnroll(IBinder token) {
+ checkPermission(MANAGE_FINGERPRINT);
+ return startPreEnroll(token);
+ }
+
+ @Override // Binder call
+ public int postEnroll(IBinder token) {
+ checkPermission(MANAGE_FINGERPRINT);
+ return startPostEnroll(token);
+ }
+
+ @Override // Binder call
+ public void enroll(final IBinder token, final byte[] cryptoToken, final int userId,
+ final IFingerprintServiceReceiver receiver, final int flags,
+ final String opPackageName) {
+ checkPermission(MANAGE_FINGERPRINT);
+
+ final boolean restricted = isRestricted();
+ final int groupId = userId; // default group for fingerprint enrollment
+ final EnrollClientImpl client = new EnrollClientImpl(getContext(), mDaemonWrapper,
+ mHalDeviceId, token, new ServiceListenerImpl(receiver), mCurrentUserId, groupId,
+ cryptoToken, restricted, opPackageName);
+
+ enrollInternal(client, userId);
+ }
+
+ @Override // Binder call
+ public void cancelEnrollment(final IBinder token) {
+ checkPermission(MANAGE_FINGERPRINT);
+ cancelEnrollmentInternal(token);
+ }
+
+ @Override // Binder call
+ public void authenticate(final IBinder token, final long opId, final int groupId,
+ final IFingerprintServiceReceiver receiver, final int flags,
+ final String opPackageName, final Bundle bundle,
+ final IBiometricPromptReceiver dialogReceiver) {
+ final boolean restricted = isRestricted();
+ final AuthenticationClientImpl client = new AuthenticationClientImpl(getContext(),
+ mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver),
+ mCurrentUserId, groupId, opId, restricted, opPackageName, bundle,
+ dialogReceiver, mStatusBarService);
+
+ authenticateInternal(client, opId, opPackageName);
+ }
+
+ @Override // Binder call
+ public void cancelAuthentication(final IBinder token, final String opPackageName) {
+ cancelAuthenticationInternal(token, opPackageName);
+ }
+
+ @Override // Binder call
+ public void setActiveUser(final int userId) {
+ checkPermission(MANAGE_FINGERPRINT);
+ setActiveUserInternal(userId);
+ }
+
+ @Override // Binder call
+ public void remove(final IBinder token, final int fingerId, final int groupId,
+ final int userId, final IFingerprintServiceReceiver receiver) {
+ checkPermission(MANAGE_FINGERPRINT);
+
+ if (token == null) {
+ Slog.w(TAG, "remove(): token is null");
+ return;
+ }
+
+ final boolean restricted = isRestricted();
+ final RemovalClientImpl client = new RemovalClientImpl(getContext(), mDaemonWrapper,
+ mHalDeviceId, token, new ServiceListenerImpl(receiver), fingerId, groupId,
+ userId, restricted, token.toString());
+ client.setShouldNotifyUserActivity(true);
+ removeInternal(client);
+ }
+
+ @Override // Binder call
+ public void enumerate(final IBinder token, final int userId,
+ final IFingerprintServiceReceiver receiver) {
+ checkPermission(MANAGE_FINGERPRINT);
+
+ final boolean restricted = isRestricted();
+ final EnumerateClientImpl client = new EnumerateClientImpl(getContext(), mDaemonWrapper,
+ mHalDeviceId, token, new ServiceListenerImpl(receiver), userId, userId,
+ restricted, getContext().getOpPackageName());
+ enumerateInternal(client);
+ }
+
+ @Override
+ public void addLockoutResetCallback(final IBiometricServiceLockoutResetCallback callback)
+ throws RemoteException {
+ FingerprintService.super.addLockoutResetCallback(callback);
+ }
+
+ @Override // Binder call
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) {
+ return;
+ }
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ if (args.length > 0 && "--proto".equals(args[0])) {
+ dumpProto(fd);
+ } else {
+ dumpInternal(pw);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ /**
+ * The following methods don't use any common code from BiometricService
+ */
+
+ // TODO: refactor out common code here
+ @Override // Binder call
+ public boolean isHardwareDetected(long deviceId, String opPackageName) {
+ if (!canUseBiometric(opPackageName, false /* foregroundOnly */,
+ Binder.getCallingUid(), Binder.getCallingPid(),
+ UserHandle.getCallingUserId())) {
+ return false;
+ }
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ IBiometricsFingerprint daemon = getFingerprintDaemon();
+ return daemon != null && mHalDeviceId != 0;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override // Binder call
+ public void rename(final int fingerId, final int groupId, final String name) {
+ checkPermission(MANAGE_FINGERPRINT);
+ if (!isCurrentUserOrProfile(groupId)) {
+ return;
+ }
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ getBiometricUtils().renameBiometricForUser(getContext(), groupId,
+ fingerId, name);
+ }
+ });
+ }
+
+ @Override // Binder call
+ public List<Fingerprint> getEnrolledFingerprints(int userId, String opPackageName) {
+ if (!canUseBiometric(opPackageName, false /* foregroundOnly */,
+ Binder.getCallingUid(), Binder.getCallingPid(),
+ UserHandle.getCallingUserId())) {
+ return Collections.emptyList();
+ }
+
+ return FingerprintService.this.getEnrolledFingerprints(userId);
+ }
+
+ @Override // Binder call
+ public boolean hasEnrolledFingerprints(int userId, String opPackageName) {
+ if (!canUseBiometric(opPackageName, false /* foregroundOnly */,
+ Binder.getCallingUid(), Binder.getCallingPid(),
+ UserHandle.getCallingUserId())) {
+ return false;
+ }
+
+ return FingerprintService.this.hasEnrolledBiometrics(userId);
+ }
+
+ @Override // Binder call
+ public long getAuthenticatorId(String opPackageName) {
+ // In this method, we're not checking whether the caller is permitted to use fingerprint
+ // API because current authenticator ID is leaked (in a more contrived way) via Android
+ // Keystore (android.security.keystore package): the user of that API can create a key
+ // which requires fingerprint authentication for its use, and then query the key's
+ // characteristics (hidden API) which returns, among other things, fingerprint
+ // authenticator ID which was active at key creation time.
+ //
+ // Reason: The part of Android Keystore which runs inside an app's process invokes this
+ // method in certain cases. Those cases are not always where the developer demonstrates
+ // explicit intent to use fingerprint functionality. Thus, to avoiding throwing an
+ // unexpected SecurityException this method does not check whether its caller is
+ // permitted to use fingerprint API.
+ //
+ // The permission check should be restored once Android Keystore no longer invokes this
+ // method from inside app processes.
+
+ return FingerprintService.super.getAuthenticatorId(opPackageName);
+ }
+
+ @Override // Binder call
+ public void resetTimeout(byte [] token) {
+ checkPermission(RESET_FINGERPRINT_LOCKOUT);
+ // TODO: confirm security token when we move timeout management into the HAL layer.
+ mHandler.post(mResetFailedAttemptsForCurrentUserRunnable);
+ }
+
+ @Override
+ public boolean isClientActive() {
+ checkPermission(MANAGE_FINGERPRINT);
+ synchronized(FingerprintService.this) {
+ return (getCurrentClient() != null) || (getPendingClient() != null);
+ }
+ }
+
+ @Override
+ public void addClientActiveCallback(IFingerprintClientActiveCallback callback) {
+ checkPermission(MANAGE_FINGERPRINT);
+ mClientActiveCallbacks.add(callback);
+ }
+
+ @Override
+ public void removeClientActiveCallback(IFingerprintClientActiveCallback callback) {
+ checkPermission(MANAGE_FINGERPRINT);
+ mClientActiveCallbacks.remove(callback);
+ }
+ }
+
+ /**
+ * Receives callbacks from the ClientMonitor implementations. The results are forwarded to
+ * the FingerprintManager.
+ */
+ private class ServiceListenerImpl implements ServiceListener {
+
+ private IFingerprintServiceReceiver mFingerprintServiceReceiver;
+
+ public ServiceListenerImpl(IFingerprintServiceReceiver receiver) {
+ mFingerprintServiceReceiver = receiver;
+ }
+
+ @Override
+ public void onEnrollResult(BiometricAuthenticator.Identifier identifier, int remaining)
+ throws RemoteException {
+ if (mFingerprintServiceReceiver != null) {
+ // TODO: Pass up the fp directly instead
+ final Fingerprint fp = (Fingerprint) identifier;
+ mFingerprintServiceReceiver.onEnrollResult(fp.getDeviceId(), fp.getBiometricId(),
+ fp.getGroupId(), remaining);
+ }
+ }
+
+ @Override
+ public void onAcquired(long deviceId, int acquiredInfo, int vendorCode)
+ throws RemoteException {
+ if (mFingerprintServiceReceiver != null) {
+ mFingerprintServiceReceiver.onAcquired(deviceId, acquiredInfo, vendorCode);
+ }
+ }
+
+ @Override
+ public void onAuthenticationSucceeded(long deviceId,
+ BiometricAuthenticator.Identifier biometric, int userId)
+ throws RemoteException {
+ if (mFingerprintServiceReceiver != null) {
+ if (biometric == null || biometric instanceof Fingerprint) {
+ mFingerprintServiceReceiver
+ .onAuthenticationSucceeded(deviceId, (Fingerprint) biometric, userId);
+ } else {
+ Slog.e(TAG, "onAuthenticationSucceeded received non-fingerprint biometric");
+ }
+ }
+ }
+
+ @Override
+ public void onAuthenticationFailed(long deviceId) throws RemoteException {
+ if (mFingerprintServiceReceiver != null) {
+ mFingerprintServiceReceiver.onAuthenticationFailed(deviceId);
+ }
+ }
+
+ @Override
+ public void onError(long deviceId, int error, int vendorCode) throws RemoteException {
+ if (mFingerprintServiceReceiver != null) {
+ mFingerprintServiceReceiver.onError(deviceId, error, vendorCode);
+ }
+ }
+
+ @Override
+ public void onRemoved(BiometricAuthenticator.Identifier identifier, int remaining)
+ throws RemoteException {
+ if (mFingerprintServiceReceiver != null) {
+ // TODO: Pass up the fp directly instead
+ final Fingerprint fp = (Fingerprint) identifier;
+ mFingerprintServiceReceiver.onRemoved(fp.getDeviceId(), fp.getBiometricId(),
+ fp.getGroupId(), remaining);
+ }
+ }
+
+ @Override
+ public void onEnumerated(BiometricAuthenticator.Identifier identifier, int remaining)
+ throws RemoteException {
+ if (mFingerprintServiceReceiver != null) {
+ final Fingerprint fp = (Fingerprint) identifier;
+ mFingerprintServiceReceiver.onEnumerated(fp.getDeviceId(), fp.getBiometricId(),
+ fp.getGroupId(), remaining);
+ }
+ }
+ }
+
+ /**
+ * An internal class to help clean up unknown fingerprints in the hardware and software.
+ */
+ private final class InternalEnumerateClient extends BiometricService.EnumerateClientImpl {
+
+ private List<Fingerprint> mEnrolledList;
+ private List<Fingerprint> mUnknownFingerprints = new ArrayList<>(); // list of fp to delete
+
+ public InternalEnumerateClient(Context context, DaemonWrapper daemon, long halDeviceId,
+ IBinder token, ServiceListener listener, int groupId, int userId,
+ boolean restricted, String owner, List<Fingerprint> enrolledList) {
+ super(context, daemon, halDeviceId, token, listener, groupId, userId, restricted,
+ owner);
+ mEnrolledList = enrolledList;
+ }
+
+ private void handleEnumeratedFingerprint(
+ BiometricAuthenticator.Identifier identifier, int remaining) {
+ boolean matched = false;
+ for (int i = 0; i < mEnrolledList.size(); i++) {
+ if (mEnrolledList.get(i).getBiometricId() == identifier.getBiometricId()) {
+ mEnrolledList.remove(i);
+ matched = true;
+ break;
+ }
+ }
+
+ // fingerId 0 means no fingerprints are in hardware
+ if (!matched && identifier.getBiometricId() != 0) {
+ mUnknownFingerprints.add((Fingerprint) identifier);
+ }
+ }
+
+ private void doFingerprintCleanup() {
+ if (mEnrolledList == null) {
+ return;
+ }
+
+ for (Fingerprint f : mEnrolledList) {
+ Slog.e(TAG, "doFingerprintCleanup(): Removing dangling enrolled fingerprint: "
+ + f.getName() + " " + f.getBiometricId() + " " + f.getGroupId()
+ + " " + f.getDeviceId());
+ FingerprintUtils.getInstance().removeBiometricForUser(getContext(),
+ getTargetUserId(), f.getBiometricId());
+ }
+ mEnrolledList.clear();
+ }
+
+ public List<Fingerprint> getUnknownFingerprints() {
+ return mUnknownFingerprints;
+ }
+
+ @Override
+ public boolean onEnumerationResult(BiometricAuthenticator.Identifier identifier,
+ int remaining) {
+ handleEnumeratedFingerprint(identifier, remaining);
+ if (remaining == 0) {
+ doFingerprintCleanup();
+ }
+ return remaining == 0;
+ }
+ }
+
+ /**
+ * An internal class to help clean up unknown fingerprints in hardware and software.
+ */
+ private final class InternalRemovalClient extends BiometricService.RemovalClientImpl {
+ public InternalRemovalClient(Context context,
+ DaemonWrapper daemon, long halDeviceId, IBinder token,
+ ServiceListener listener, int fingerId, int groupId, int userId, boolean restricted,
+ String owner) {
+ super(context, daemon, halDeviceId, token, listener, fingerId, groupId, userId,
+ restricted,
+ owner);
+ }
+ }
+
+ private final FingerprintMetrics mFingerprintMetrics = new FingerprintMetrics();
+ private final CopyOnWriteArrayList<IFingerprintClientActiveCallback> mClientActiveCallbacks =
+ new CopyOnWriteArrayList<>();
+
+ @GuardedBy("this")
+ private IBiometricsFingerprint mDaemon;
+
+ private long mHalDeviceId;
+ private IStatusBarService mStatusBarService;
+ private IBinder mToken = new Binder(); // used for internal FingerprintService enumeration
+ private ArrayList<UserFingerprint> mUnknownFingerprints = new ArrayList<>(); // hw fingerprints
+
+ /**
+ * Receives callbacks from the HAL.
+ */
+ private IBiometricsFingerprintClientCallback mDaemonCallback =
+ new IBiometricsFingerprintClientCallback.Stub() {
+ @Override
+ public void onEnrollResult(final long deviceId, final int fingerId, final int groupId,
+ final int remaining) {
+ mHandler.post(() -> {
+ final Fingerprint fingerprint =
+ new Fingerprint(getBiometricUtils().getUniqueName(getContext(), groupId),
+ groupId, fingerId, deviceId);
+ FingerprintService.super.handleEnrollResult(fingerprint, remaining);
+ });
+ }
+
+ @Override
+ public void onAcquired(final long deviceId, final int acquiredInfo, final int vendorCode) {
+ mHandler.post(() -> {
+ FingerprintService.super.handleAcquired(deviceId, acquiredInfo, vendorCode);
+ });
+ }
+
+ @Override
+ public void onAuthenticated(final long deviceId, final int fingerId, final int groupId,
+ ArrayList<Byte> token) {
+ mHandler.post(() -> {
+ FingerprintService.super.handleAuthenticated(deviceId, fingerId, groupId, token);
+ });
+ }
+
+ @Override
+ public void onError(final long deviceId, final int error, final int vendorCode) {
+ mHandler.post(() -> {
+ ClientMonitor client = getCurrentClient();
+ if (client instanceof InternalRemovalClient
+ || client instanceof InternalEnumerateClient) {
+ clearEnumerateState();
+ }
+ FingerprintService.super.handleError(deviceId, error, vendorCode);
+
+ // TODO: this chunk of code should be common to all biometric services
+ if (error == BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE) {
+ // If we get HW_UNAVAILABLE, try to connect again later...
+ Slog.w(TAG, "Got ERROR_HW_UNAVAILABLE; try reconnecting next client.");
+ synchronized (this) {
+ mDaemon = null;
+ mHalDeviceId = 0;
+ mCurrentUserId = UserHandle.USER_NULL;
+ }
+ }
+ });
+ }
+
+ @Override
+ public void onRemoved(final long deviceId, final int fingerId, final int groupId,
+ final int remaining) {
+ mHandler.post(() -> {
+ ClientMonitor client = getCurrentClient();
+ final Fingerprint fp = new Fingerprint("", groupId, fingerId, deviceId);
+ FingerprintService.super.handleRemoved(fp, remaining);
+ if (client instanceof InternalRemovalClient && !mUnknownFingerprints.isEmpty()) {
+ cleanupUnknownFingerprints();
+ } else if (client instanceof InternalRemovalClient){
+ clearEnumerateState();
+ }
+ });
+ }
+
+ @Override
+ public void onEnumerate(final long deviceId, final int fingerId, final int groupId,
+ final int remaining) {
+ mHandler.post(() -> {
+ // TODO: factor out common enumerate logic if possible
+ final Fingerprint fp = new Fingerprint("", groupId, fingerId, deviceId);
+ FingerprintService.this.handleEnumerate(fp, remaining);
+ });
+
+ }
+ };
+
+ /**
+ * Wraps the HAL-specific code and is passed to the ClientMonitor implementations so that they
+ * can be shared between the multiple biometric services.
+ */
+ private final DaemonWrapper mDaemonWrapper = new DaemonWrapper() {
+ @Override
+ public int authenticate(long operationId, int groupId) throws RemoteException {
+ IBiometricsFingerprint daemon = getFingerprintDaemon();
+ if (daemon == null) {
+ Slog.w(TAG, "authenticate(): no fingerprint HAL!");
+ return ERROR_ESRCH;
+ }
+ return daemon.authenticate(operationId, groupId);
+ }
+
+ @Override
+ public int cancel() throws RemoteException {
+ IBiometricsFingerprint daemon = getFingerprintDaemon();
+ if (daemon == null) {
+ Slog.w(TAG, "cancel(): no fingerprint HAL!");
+ return ERROR_ESRCH;
+ }
+ return daemon.cancel();
+ }
+
+ @Override
+ public int remove(int groupId, int biometricId) throws RemoteException {
+ IBiometricsFingerprint daemon = getFingerprintDaemon();
+ if (daemon == null) {
+ Slog.w(TAG, "remove(): no fingerprint HAL!");
+ return ERROR_ESRCH;
+ }
+ return daemon.remove(groupId, biometricId);
+ }
+
+ @Override
+ public int enumerate() throws RemoteException {
+ IBiometricsFingerprint daemon = getFingerprintDaemon();
+ if (daemon == null) {
+ Slog.w(TAG, "enumerate(): no fingerprint HAL!");
+ return ERROR_ESRCH;
+ }
+ return daemon.enumerate();
+ }
+
+ @Override
+ public int enroll(byte[] cryptoToken, int groupId, int timeout) throws RemoteException {
+ IBiometricsFingerprint daemon = getFingerprintDaemon();
+ if (daemon == null) {
+ Slog.w(TAG, "enroll(): no fingerprint HAL!");
+ return ERROR_ESRCH;
+ }
+ return daemon.enroll(cryptoToken, groupId, timeout);
+ }
+ };
+
+ public FingerprintService(Context context) {
+ super(context);
+ // TODO: can this be retrieved from AuthenticationClient, or BiometricService?
+ mStatusBarService = IStatusBarService.Stub.asInterface(
+ ServiceManager.getService(Context.STATUS_BAR_SERVICE));
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ publishBinderService(Context.FINGERPRINT_SERVICE, new FingerprintServiceWrapper());
+ SystemServerInitThreadPool.get().submit(this::getFingerprintDaemon, TAG + ".onStart");
+ }
+
+ @Override
+ protected String getTag() {
+ return TAG;
+ }
+
+ @Override
+ protected BiometricUtils getBiometricUtils() {
+ return FingerprintUtils.getInstance();
+ }
+
+ @Override
+ protected int getFailedAttemptsLockoutTimed() {
+ return MAX_FAILED_ATTEMPTS_LOCKOUT_TIMED;
+ }
+
+ @Override
+ protected int getFailedAttemptsLockoutPermanent() {
+ return MAX_FAILED_ATTEMPTS_LOCKOUT_PERMANENT;
+ }
+
+ @Override
+ protected Metrics getMetrics() {
+ return mFingerprintMetrics;
+ }
+
+ @Override
+ protected boolean hasReachedEnrollmentLimit(int userId) {
+ final int limit = getContext().getResources().getInteger(
+ com.android.internal.R.integer.config_fingerprintMaxTemplatesPerUser);
+ final int enrolled = FingerprintService.this.getEnrolledFingerprints(userId).size();
+ if (enrolled >= limit) {
+ Slog.w(TAG, "Too many fingerprints registered");
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ protected void updateActiveGroup(int userId, String clientPackage) {
+ IBiometricsFingerprint daemon = getFingerprintDaemon();
+
+ if (daemon != null) {
+ try {
+ userId = getUserOrWorkProfileId(clientPackage, userId);
+ if (userId != mCurrentUserId) {
+ int firstSdkInt = Build.VERSION.FIRST_SDK_INT;
+ if (firstSdkInt < Build.VERSION_CODES.BASE) {
+ Slog.e(TAG, "First SDK version " + firstSdkInt + " is invalid; must be " +
+ "at least VERSION_CODES.BASE");
+ }
+ File baseDir;
+ if (firstSdkInt <= Build.VERSION_CODES.O_MR1) {
+ baseDir = Environment.getUserSystemDirectory(userId);
+ } else {
+ baseDir = Environment.getDataVendorDeDirectory(userId);
+ }
+
+ File fpDir = new File(baseDir, FP_DATA_DIR);
+ if (!fpDir.exists()) {
+ if (!fpDir.mkdir()) {
+ Slog.v(TAG, "Cannot make directory: " + fpDir.getAbsolutePath());
+ return;
+ }
+ // Calling mkdir() from this process will create a directory with our
+ // permissions (inherited from the containing dir). This command fixes
+ // the label.
+ if (!SELinux.restorecon(fpDir)) {
+ Slog.w(TAG, "Restorecons failed. Directory will have wrong label.");
+ return;
+ }
+ }
+
+ daemon.setActiveGroup(userId, fpDir.getAbsolutePath());
+ mCurrentUserId = userId;
+ }
+ mAuthenticatorIds.put(userId,
+ hasEnrolledBiometrics(userId) ? daemon.getAuthenticatorId() : 0L);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to setActiveGroup():", e);
+ }
+ }
+ }
+
+ @Override
+ protected String getLockoutResetIntent() {
+ return ACTION_LOCKOUT_RESET;
+ }
+
+ @Override
+ protected String getLockoutBroadcastPermission() {
+ return RESET_FINGERPRINT_LOCKOUT;
+ }
+
+ @Override
+ protected long getHalDeviceId() {
+ return mHalDeviceId;
+ }
+
+ @Override
+ protected void handleUserSwitching(int userId) {
+ if (getCurrentClient() instanceof InternalRemovalClient
+ || getCurrentClient() instanceof InternalEnumerateClient) {
+ Slog.w(TAG, "User switched while performing cleanup");
+ removeClient(getCurrentClient());
+ clearEnumerateState();
+ }
+ updateActiveGroup(userId, null);
+ doFingerprintCleanupForUser(userId);
+ }
+
+
+ @Override
+ protected boolean hasEnrolledBiometrics(int userId) {
+ if (userId != UserHandle.getCallingUserId()) {
+ checkPermission(INTERACT_ACROSS_USERS);
+ }
+ return getBiometricUtils().getBiometricsForUser(getContext(), userId).size() > 0;
+ }
+
+ @Override
+ protected String getManageBiometricPermission() {
+ return MANAGE_FINGERPRINT;
+ }
+
+ @Override
+ protected void checkUseBiometricPermission() {
+ if (getContext().checkCallingPermission(USE_FINGERPRINT)
+ != PackageManager.PERMISSION_GRANTED) {
+ checkPermission(USE_BIOMETRIC);
+ }
+ }
+
+ @Override
+ protected int getAppOp() {
+ return AppOpsManager.OP_USE_FINGERPRINT;
+ }
+
+ @Override
+ protected void notifyClientActiveCallbacks(boolean isActive) {
+ List<IFingerprintClientActiveCallback> callbacks = mClientActiveCallbacks;
+ for (int i = 0; i < callbacks.size(); i++) {
+ try {
+ callbacks.get(i).onClientActiveChanged(isActive);
+ } catch (RemoteException re) {
+ // If the remote is dead, stop notifying it
+ mClientActiveCallbacks.remove(callbacks.get(i));
+ }
+ }
+ }
+
+ /** Gets the fingerprint daemon */
+ private synchronized IBiometricsFingerprint getFingerprintDaemon() {
+ if (mDaemon == null) {
+ Slog.v(TAG, "mDaemon was null, reconnect to fingerprint");
+ try {
+ mDaemon = IBiometricsFingerprint.getService();
+ } catch (java.util.NoSuchElementException e) {
+ // Service doesn't exist or cannot be opened. Logged below.
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to get biometric interface", e);
+ }
+ if (mDaemon == null) {
+ Slog.w(TAG, "fingerprint HIDL not available");
+ return null;
+ }
+
+ mDaemon.asBinder().linkToDeath(this, 0);
+
+ try {
+ mHalDeviceId = mDaemon.setNotify(mDaemonCallback);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to open fingerprint HAL", e);
+ mDaemon = null; // try again later!
+ }
+
+ if (DEBUG) Slog.v(TAG, "Fingerprint HAL id: " + mHalDeviceId);
+ if (mHalDeviceId != 0) {
+ loadAuthenticatorIds();
+ updateActiveGroup(ActivityManager.getCurrentUser(), null);
+ doFingerprintCleanupForUser(ActivityManager.getCurrentUser());
+ } else {
+ Slog.w(TAG, "Failed to open Fingerprint HAL!");
+ MetricsLogger.count(getContext(), "fingerprintd_openhal_error", 1);
+ mDaemon = null;
+ }
+ }
+ return mDaemon;
+ }
+
+ /**
+ * This method should be called upon connection to the daemon, and when user switches.
+ * @param userId
+ */
+ private void doFingerprintCleanupForUser(int userId) {
+ if (CLEANUP_UNUSED_FP) {
+ enumerateUser(userId);
+ }
+ }
+
+ private void clearEnumerateState() {
+ if (DEBUG) Slog.v(TAG, "clearEnumerateState()");
+ mUnknownFingerprints.clear();
+ }
+
+ private void enumerateUser(int userId) {
+ if (DEBUG) Slog.v(TAG, "Enumerating user(" + userId + ")");
+
+ final boolean restricted = !hasPermission(MANAGE_FINGERPRINT);
+ final List<Fingerprint> enrolledList = getEnrolledFingerprints(userId);
+
+ InternalEnumerateClient client = new InternalEnumerateClient(getContext(), mDaemonWrapper,
+ mHalDeviceId, mToken, new ServiceListenerImpl(null), userId, userId, restricted,
+ getContext().getOpPackageName(), enrolledList);
+ enumerateInternal(client);
+ }
+
+ // Remove unknown fingerprints from hardware
+ private void cleanupUnknownFingerprints() {
+ if (!mUnknownFingerprints.isEmpty()) {
+ UserFingerprint uf = mUnknownFingerprints.get(0);
+ mUnknownFingerprints.remove(uf);
+ boolean restricted = !hasPermission(MANAGE_FINGERPRINT);
+ InternalRemovalClient client = new InternalRemovalClient(getContext(), mDaemonWrapper,
+ mHalDeviceId, mToken, new ServiceListenerImpl(null), uf.f.getBiometricId(),
+ uf.f.getGroupId(), uf.userId, restricted, getContext().getOpPackageName());
+ removeInternal(client);
+ } else {
+ clearEnumerateState();
+ }
+ }
+
+ private void handleEnumerate(Fingerprint fingerprint, int remaining) {
+ ClientMonitor client = getCurrentClient();
+
+ if ( !(client instanceof InternalRemovalClient) && !(client instanceof EnumerateClient) ) {
+ return;
+ }
+ client.onEnumerationResult(fingerprint, remaining);
+
+ // All fingerprints in hardware for this user were enumerated
+ if (remaining == 0) {
+ if (client instanceof InternalEnumerateClient) {
+ List<Fingerprint> unknownFingerprints =
+ ((InternalEnumerateClient) client).getUnknownFingerprints();
+
+ if (!unknownFingerprints.isEmpty()) {
+ Slog.w(TAG, "Adding " + unknownFingerprints.size() +
+ " fingerprints for deletion");
+ }
+ for (Fingerprint f : unknownFingerprints) {
+ mUnknownFingerprints.add(new UserFingerprint(f, client.getTargetUserId()));
+ }
+ removeClient(client);
+ cleanupUnknownFingerprints();
+ } else {
+ removeClient(client);
+ }
+ }
+ }
+
+ private long startPreEnroll(IBinder token) {
+ IBiometricsFingerprint daemon = getFingerprintDaemon();
+ if (daemon == null) {
+ Slog.w(TAG, "startPreEnroll: no fingerprint HAL!");
+ return 0;
+ }
+ try {
+ return daemon.preEnroll();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "startPreEnroll failed", e);
+ }
+ return 0;
+ }
+
+ private int startPostEnroll(IBinder token) {
+ IBiometricsFingerprint daemon = getFingerprintDaemon();
+ if (daemon == null) {
+ Slog.w(TAG, "startPostEnroll: no fingerprint HAL!");
+ return 0;
+ }
+ try {
+ return daemon.postEnroll();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "startPostEnroll failed", e);
+ }
+ return 0;
+ }
+
+ private List<Fingerprint> getEnrolledFingerprints(int userId) {
+ return getBiometricUtils().getBiometricsForUser(getContext(), userId);
+ }
+
+ private void dumpInternal(PrintWriter pw) {
+ JSONObject dump = new JSONObject();
+ try {
+ dump.put("service", "Fingerprint Manager");
+
+ JSONArray sets = new JSONArray();
+ for (UserInfo user : UserManager.get(getContext()).getUsers()) {
+ final int userId = user.getUserHandle().getIdentifier();
+ final int N = getBiometricUtils().getBiometricsForUser(getContext(), userId).size();
+ PerformanceStats stats = mPerformanceMap.get(userId);
+ PerformanceStats cryptoStats = mCryptoPerformanceMap.get(userId);
+ JSONObject set = new JSONObject();
+ set.put("id", userId);
+ set.put("count", N);
+ set.put("accept", (stats != null) ? stats.accept : 0);
+ set.put("reject", (stats != null) ? stats.reject : 0);
+ set.put("acquire", (stats != null) ? stats.acquire : 0);
+ set.put("lockout", (stats != null) ? stats.lockout : 0);
+ set.put("permanentLockout", (stats != null) ? stats.permanentLockout : 0);
+ // cryptoStats measures statistics about secure fingerprint transactions
+ // (e.g. to unlock password storage, make secure purchases, etc.)
+ set.put("acceptCrypto", (cryptoStats != null) ? cryptoStats.accept : 0);
+ set.put("rejectCrypto", (cryptoStats != null) ? cryptoStats.reject : 0);
+ set.put("acquireCrypto", (cryptoStats != null) ? cryptoStats.acquire : 0);
+ set.put("lockoutCrypto", (cryptoStats != null) ? cryptoStats.lockout : 0);
+ set.put("permanentLockoutCrypto",
+ (cryptoStats != null) ? cryptoStats.permanentLockout : 0);
+ sets.put(set);
+ }
+
+ dump.put("prints", sets);
+ } catch (JSONException e) {
+ Slog.e(TAG, "dump formatting failure", e);
+ }
+ pw.println(dump);
+ }
+
+ private void dumpProto(FileDescriptor fd) {
+ final ProtoOutputStream proto = new ProtoOutputStream(fd);
+ for (UserInfo user : UserManager.get(getContext()).getUsers()) {
+ final int userId = user.getUserHandle().getIdentifier();
+
+ final long userToken = proto.start(FingerprintServiceDumpProto.USERS);
+
+ proto.write(FingerprintUserStatsProto.USER_ID, userId);
+ proto.write(FingerprintUserStatsProto.NUM_FINGERPRINTS,
+ getBiometricUtils().getBiometricsForUser(getContext(), userId).size());
+
+ // Normal fingerprint authentications (e.g. lockscreen)
+ final PerformanceStats normal = mPerformanceMap.get(userId);
+ if (normal != null) {
+ final long countsToken = proto.start(FingerprintUserStatsProto.NORMAL);
+ proto.write(PerformanceStatsProto.ACCEPT, normal.accept);
+ proto.write(PerformanceStatsProto.REJECT, normal.reject);
+ proto.write(PerformanceStatsProto.ACQUIRE, normal.acquire);
+ proto.write(PerformanceStatsProto.LOCKOUT, normal.lockout);
+ proto.write(PerformanceStatsProto.PERMANENT_LOCKOUT, normal.permanentLockout);
+ proto.end(countsToken);
+ }
+
+ // Statistics about secure fingerprint transactions (e.g. to unlock password
+ // storage, make secure purchases, etc.)
+ final PerformanceStats crypto = mCryptoPerformanceMap.get(userId);
+ if (crypto != null) {
+ final long countsToken = proto.start(FingerprintUserStatsProto.CRYPTO);
+ proto.write(PerformanceStatsProto.ACCEPT, crypto.accept);
+ proto.write(PerformanceStatsProto.REJECT, crypto.reject);
+ proto.write(PerformanceStatsProto.ACQUIRE, crypto.acquire);
+ proto.write(PerformanceStatsProto.LOCKOUT, crypto.lockout);
+ proto.write(PerformanceStatsProto.PERMANENT_LOCKOUT, crypto.permanentLockout);
+ proto.end(countsToken);
+ }
+
+ proto.end(userToken);
+ }
+ proto.flush();
+ mPerformanceMap.clear();
+ mCryptoPerformanceMap.clear();
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintUserState.java b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintUserState.java
new file mode 100644
index 0000000..9e34ee8
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintUserState.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.biometrics.fingerprint;
+
+import android.content.Context;
+import android.hardware.biometrics.BiometricAuthenticator;
+import android.hardware.fingerprint.Fingerprint;
+import android.util.AtomicFile;
+import android.util.Slog;
+import android.util.Xml;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.biometrics.common.BiometricUserState;
+
+import libcore.io.IoUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+
+/**
+ * Class managing the set of fingerprint per user across device reboots.
+ * @hide
+ */
+public class FingerprintUserState extends BiometricUserState {
+
+ private static final String TAG = "FingerprintState";
+ private static final String FINGERPRINT_FILE = "settings_fingerprint.xml";
+
+ private static final String TAG_FINGERPRINTS = "fingerprints";
+ private static final String TAG_FINGERPRINT = "fingerprint";
+ private static final String ATTR_NAME = "name";
+ private static final String ATTR_GROUP_ID = "groupId";
+ private static final String ATTR_FINGER_ID = "fingerId";
+ private static final String ATTR_DEVICE_ID = "deviceId";
+
+ public FingerprintUserState(Context context, int userId) {
+ super(context, userId);
+ }
+
+ @Override
+ protected String getBiometricsTag() {
+ return TAG_FINGERPRINTS;
+ }
+
+ @Override
+ protected String getBiometricFile() {
+ return FINGERPRINT_FILE;
+ }
+
+ @Override
+ protected int getNameTemplateResource() {
+ return com.android.internal.R.string.fingerprint_name_template;
+ }
+
+ @Override
+ public void addBiometric(BiometricAuthenticator.Identifier identifier) {
+ if (identifier instanceof Fingerprint) {
+ super.addBiometric(identifier);
+ } else {
+ Slog.w(TAG, "Attempted to add non-fingerprint identifier");
+ }
+ }
+
+ @Override
+ protected ArrayList getCopy(ArrayList array) {
+ ArrayList<Fingerprint> result = new ArrayList<>();
+ for (int i = 0; i < array.size(); i++) {
+ Fingerprint fp = (Fingerprint) array.get(i);
+ result.add(new Fingerprint(fp.getName(), fp.getGroupId(), fp.getBiometricId(),
+ fp.getDeviceId()));
+ }
+ return result;
+ }
+
+ @Override
+ protected void doWriteState() {
+ AtomicFile destination = new AtomicFile(mFile);
+
+ ArrayList<Fingerprint> fingerprints;
+
+ synchronized (this) {
+ fingerprints = getCopy(mBiometrics);
+ }
+
+ FileOutputStream out = null;
+ try {
+ out = destination.startWrite();
+
+ XmlSerializer serializer = Xml.newSerializer();
+ serializer.setOutput(out, "utf-8");
+ serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
+ serializer.startDocument(null, true);
+ serializer.startTag(null, TAG_FINGERPRINTS);
+
+ final int count = fingerprints.size();
+ for (int i = 0; i < count; i++) {
+ Fingerprint fp = fingerprints.get(i);
+ serializer.startTag(null, TAG_FINGERPRINT);
+ serializer.attribute(null, ATTR_FINGER_ID, Integer.toString(fp.getBiometricId()));
+ serializer.attribute(null, ATTR_NAME, fp.getName().toString());
+ serializer.attribute(null, ATTR_GROUP_ID, Integer.toString(fp.getGroupId()));
+ serializer.attribute(null, ATTR_DEVICE_ID, Long.toString(fp.getDeviceId()));
+ serializer.endTag(null, TAG_FINGERPRINT);
+ }
+
+ serializer.endTag(null, TAG_FINGERPRINTS);
+ serializer.endDocument();
+ destination.finishWrite(out);
+
+ // Any error while writing is fatal.
+ } catch (Throwable t) {
+ Slog.wtf(TAG, "Failed to write settings, restoring backup", t);
+ destination.failWrite(out);
+ throw new IllegalStateException("Failed to write fingerprints", t);
+ } finally {
+ IoUtils.closeQuietly(out);
+ }
+ }
+
+ @GuardedBy("this")
+ @Override
+ protected void parseBiometricsLocked(XmlPullParser parser)
+ throws IOException, XmlPullParserException {
+
+ final int outerDepth = parser.getDepth();
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+ if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+ continue;
+ }
+
+ String tagName = parser.getName();
+ if (tagName.equals(TAG_FINGERPRINT)) {
+ String name = parser.getAttributeValue(null, ATTR_NAME);
+ String groupId = parser.getAttributeValue(null, ATTR_GROUP_ID);
+ String fingerId = parser.getAttributeValue(null, ATTR_FINGER_ID);
+ String deviceId = parser.getAttributeValue(null, ATTR_DEVICE_ID);
+ mBiometrics.add(new Fingerprint(name, Integer.parseInt(groupId),
+ Integer.parseInt(fingerId), Long.parseLong(deviceId)));
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintUtils.java b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintUtils.java
new file mode 100644
index 0000000..41216e9
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintUtils.java
@@ -0,0 +1,95 @@
+/**
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.fingerprint;
+
+import android.content.Context;
+import android.hardware.biometrics.BiometricAuthenticator;
+import android.hardware.fingerprint.Fingerprint;
+import android.text.TextUtils;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.biometrics.common.BiometricUtils;
+
+import java.util.List;
+
+/**
+ * Utility class for dealing with fingerprints and fingerprint settings.
+ */
+public class FingerprintUtils implements BiometricUtils {
+
+ private static final Object sInstanceLock = new Object();
+ private static FingerprintUtils sInstance;
+
+ @GuardedBy("this")
+ private final SparseArray<FingerprintUserState> mUsers = new SparseArray<>();
+
+ public static FingerprintUtils getInstance() {
+ synchronized (sInstanceLock) {
+ if (sInstance == null) {
+ sInstance = new FingerprintUtils();
+ }
+ }
+ return sInstance;
+ }
+
+ private FingerprintUtils() {
+ }
+
+ @Override
+ public List<Fingerprint> getBiometricsForUser(Context ctx, int userId) {
+ return getStateForUser(ctx, userId).getBiometrics();
+ }
+
+ @Override
+ public void addBiometricForUser(Context context, int userId,
+ BiometricAuthenticator.Identifier identifier) {
+ getStateForUser(context, userId).addBiometric(identifier);
+ }
+
+ @Override
+ public void removeBiometricForUser(Context context, int userId, int fingerId) {
+ getStateForUser(context, userId).removeBiometric(fingerId);
+ }
+
+ @Override
+ public void renameBiometricForUser(Context context, int userId, int fingerId,
+ CharSequence name) {
+ if (TextUtils.isEmpty(name)) {
+ // Don't do the rename if it's empty
+ return;
+ }
+ getStateForUser(context, userId).renameBiometric(fingerId, name);
+ }
+
+ @Override
+ public CharSequence getUniqueName(Context context, int userId) {
+ return getStateForUser(context, userId).getUniqueName();
+ }
+
+ private FingerprintUserState getStateForUser(Context ctx, int userId) {
+ synchronized (this) {
+ FingerprintUserState state = mUsers.get(userId);
+ if (state == null) {
+ state = new FingerprintUserState(ctx, userId);
+ mUsers.put(userId, state);
+ }
+ return state;
+ }
+ }
+}
+
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index df6a6f8..84bdbba 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -115,7 +115,6 @@
import com.android.server.connectivity.tethering.IControlsTethering;
import com.android.server.connectivity.tethering.IPv6TetheringCoordinator;
import com.android.server.connectivity.tethering.OffloadController;
-import com.android.server.connectivity.tethering.SimChangeListener;
import com.android.server.connectivity.tethering.TetherInterfaceStateMachine;
import com.android.server.connectivity.tethering.TetheringConfiguration;
import com.android.server.connectivity.tethering.TetheringDependencies;
@@ -201,8 +200,6 @@
// into a single coherent structure.
private final HashSet<TetherInterfaceStateMachine> mForwardedDownstreams;
private final VersionedBroadcastListener mCarrierConfigChange;
- // TODO: Delete SimChangeListener; it's obsolete.
- private final SimChangeListener mSimChange;
private final TetheringDependencies mDeps;
private volatile TetheringConfiguration mConfig;
@@ -252,14 +249,6 @@
updateConfiguration();
reevaluateSimCardProvisioning();
});
- // TODO: Remove SimChangeListener altogether. For now, we retain it
- // for logging purposes in case we need to debug something that might
- // be related to changing signals from ACTION_SIM_STATE_CHANGED to
- // ACTION_CARRIER_CONFIG_CHANGED.
- mSimChange = new SimChangeListener(
- mContext, smHandler, () -> {
- mLog.log("OBSERVED SIM card change");
- });
mStateReceiver = new StateReceiver();
@@ -1350,8 +1339,11 @@
// do not currently know how to watch for changes in DUN settings.
maybeUpdateConfiguration();
- final NetworkState ns = mUpstreamNetworkMonitor.selectPreferredUpstreamType(
- mConfig.preferredUpstreamIfaceTypes);
+ final TetheringConfiguration config = mConfig;
+ final NetworkState ns = (config.chooseUpstreamAutomatically)
+ ? mUpstreamNetworkMonitor.getCurrentPreferredUpstream()
+ : mUpstreamNetworkMonitor.selectPreferredUpstreamType(
+ config.preferredUpstreamIfaceTypes);
if (ns == null) {
if (tryCell) {
mUpstreamNetworkMonitor.registerMobileNetworkRequest();
@@ -1380,9 +1372,7 @@
}
notifyDownstreamsOfNewUpstreamIface(ifaces);
if (ns != null && pertainsToCurrentUpstream(ns)) {
- // If we already have NetworkState for this network examine
- // it immediately, because there likely will be no second
- // EVENT_ON_AVAILABLE (it was already received).
+ // If we already have NetworkState for this network update it immediately.
handleNewUpstreamNetworkState(ns);
} else if (mCurrentUpstreamIfaceSet == null) {
// There are no available upstream networks.
@@ -1498,15 +1488,6 @@
}
switch (arg1) {
- case UpstreamNetworkMonitor.EVENT_ON_AVAILABLE:
- // The default network changed, or DUN connected
- // before this callback was processed. Updates
- // for the current NetworkCapabilities and
- // LinkProperties have been requested (default
- // request) or are being sent shortly (DUN). Do
- // nothing until they arrive; if no updates
- // arrive there's nothing to do.
- break;
case UpstreamNetworkMonitor.EVENT_ON_CAPABILITIES:
handleNewUpstreamNetworkState(ns);
break;
@@ -1538,8 +1519,7 @@
return;
}
- mSimChange.startListening();
- mUpstreamNetworkMonitor.start();
+ mUpstreamNetworkMonitor.start(mDeps.getDefaultNetworkRequest());
// TODO: De-duplicate with updateUpstreamWanted() below.
if (upstreamWanted()) {
@@ -1554,7 +1534,6 @@
public void exit() {
mOffload.stop();
mUpstreamNetworkMonitor.stop();
- mSimChange.stopListening();
notifyDownstreamsOfNewUpstreamIface(null);
handleNewUpstreamNetworkState(null);
}
diff --git a/services/core/java/com/android/server/connectivity/tethering/SimChangeListener.java b/services/core/java/com/android/server/connectivity/tethering/SimChangeListener.java
deleted file mode 100644
index 33c9355..0000000
--- a/services/core/java/com/android/server/connectivity/tethering/SimChangeListener.java
+++ /dev/null
@@ -1,79 +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.connectivity.tethering;
-
-import static com.android.internal.telephony.IccCardConstants.INTENT_VALUE_ICC_LOADED;
-import static com.android.internal.telephony.IccCardConstants.INTENT_KEY_ICC_STATE;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.net.util.VersionedBroadcastListener;
-import android.net.util.VersionedBroadcastListener.IntentCallback;
-import android.os.Handler;
-import android.util.Log;
-
-import com.android.internal.telephony.TelephonyIntents;
-
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.function.Consumer;
-
-
-/**
- * A utility class that runs the provided callback on the provided handler when
- * observing a new SIM card having been loaded.
- *
- * @hide
- */
-public class SimChangeListener extends VersionedBroadcastListener {
- private static final String TAG = SimChangeListener.class.getSimpleName();
- private static final boolean DBG = false;
-
- public SimChangeListener(Context ctx, Handler handler, Runnable onSimCardLoadedCallback) {
- super(TAG, ctx, handler, makeIntentFilter(), makeCallback(onSimCardLoadedCallback));
- }
-
- private static IntentFilter makeIntentFilter() {
- final IntentFilter filter = new IntentFilter();
- filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
- return filter;
- }
-
- private static Consumer<Intent> makeCallback(Runnable onSimCardLoadedCallback) {
- return new Consumer<Intent>() {
- private boolean mSimNotLoadedSeen = false;
-
- @Override
- public void accept(Intent intent) {
- final String state = intent.getStringExtra(INTENT_KEY_ICC_STATE);
- Log.d(TAG, "got Sim changed to state " + state + ", mSimNotLoadedSeen=" +
- mSimNotLoadedSeen);
-
- if (!INTENT_VALUE_ICC_LOADED.equals(state)) {
- mSimNotLoadedSeen = true;
- return;
- }
-
- if (mSimNotLoadedSeen) {
- mSimNotLoadedSeen = false;
- onSimCardLoadedCallback.run();
- }
- }
- };
- }
-}
diff --git a/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java b/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java
index fbee86a..9e6b659 100644
--- a/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java
+++ b/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java
@@ -379,7 +379,7 @@
// that adding routes that already exist does not cause an
// error (EEXIST is silently ignored).
mNMService.addInterfaceToLocalNetwork(mIfaceName, toBeAdded);
- } catch (RemoteException e) {
+ } catch (Exception e) {
mLog.e("Failed to add IPv6 routes to local table: " + e);
}
diff --git a/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java b/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java
index 454c579..dd9fe05 100644
--- a/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java
+++ b/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java
@@ -27,6 +27,7 @@
import static com.android.internal.R.array.config_tether_usb_regexs;
import static com.android.internal.R.array.config_tether_upstream_types;
import static com.android.internal.R.array.config_tether_wifi_regexs;
+import static com.android.internal.R.bool.config_tether_upstream_automatic;
import static com.android.internal.R.string.config_mobile_hotspot_provision_app_no_ui;
import android.content.Context;
@@ -86,6 +87,7 @@
public final String[] tetherableBluetoothRegexs;
public final int dunCheck;
public final boolean isDunRequired;
+ public final boolean chooseUpstreamAutomatically;
public final Collection<Integer> preferredUpstreamIfaceTypes;
public final String[] dhcpRanges;
public final String[] defaultIPv4DNS;
@@ -106,6 +108,7 @@
dunCheck = checkDunRequired(ctx);
configLog.log("DUN check returned: " + dunCheckString(dunCheck));
+ chooseUpstreamAutomatically = getResourceBoolean(ctx, config_tether_upstream_automatic);
preferredUpstreamIfaceTypes = getUpstreamIfaceTypes(ctx, dunCheck);
isDunRequired = preferredUpstreamIfaceTypes.contains(TYPE_MOBILE_DUN);
@@ -142,6 +145,8 @@
pw.print("isDunRequired: ");
pw.println(isDunRequired);
+ pw.print("chooseUpstreamAutomatically: ");
+ pw.println(chooseUpstreamAutomatically);
dumpStringArray(pw, "preferredUpstreamIfaceTypes",
preferredUpstreamNames(preferredUpstreamIfaceTypes));
@@ -160,6 +165,7 @@
sj.add(String.format("tetherableBluetoothRegexs:%s",
makeString(tetherableBluetoothRegexs)));
sj.add(String.format("isDunRequired:%s", isDunRequired));
+ sj.add(String.format("chooseUpstreamAutomatically:%s", chooseUpstreamAutomatically));
sj.add(String.format("preferredUpstreamIfaceTypes:%s",
makeString(preferredUpstreamNames(preferredUpstreamIfaceTypes))));
sj.add(String.format("provisioningApp:%s", makeString(provisioningApp)));
@@ -286,6 +292,14 @@
}
}
+ private static boolean getResourceBoolean(Context ctx, int resId) {
+ try {
+ return ctx.getResources().getBoolean(resId);
+ } catch (Resources.NotFoundException e404) {
+ return false;
+ }
+ }
+
private static String[] getResourceStringArray(Context ctx, int resId) {
try {
final String[] strArray = ctx.getResources().getStringArray(resId);
diff --git a/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java b/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java
index 0ac7a36..605ee9c 100644
--- a/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java
+++ b/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java
@@ -18,6 +18,7 @@
import android.content.Context;
import android.net.INetd;
+import android.net.NetworkRequest;
import android.net.ip.RouterAdvertisementDaemon;
import android.net.util.InterfaceParams;
import android.net.util.NetdService;
@@ -64,4 +65,8 @@
public boolean isTetheringSupported() {
return true;
}
+
+ public NetworkRequest getDefaultNetworkRequest() {
+ return null;
+ }
}
diff --git a/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java b/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
index 3413291..f488be7 100644
--- a/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
+++ b/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
@@ -20,6 +20,9 @@
import static android.net.ConnectivityManager.TYPE_NONE;
import static android.net.ConnectivityManager.TYPE_MOBILE_DUN;
import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import android.content.Context;
import android.os.Handler;
@@ -74,14 +77,13 @@
private static final boolean DBG = false;
private static final boolean VDBG = false;
- public static final int EVENT_ON_AVAILABLE = 1;
- public static final int EVENT_ON_CAPABILITIES = 2;
- public static final int EVENT_ON_LINKPROPERTIES = 3;
- public static final int EVENT_ON_LOST = 4;
+ public static final int EVENT_ON_CAPABILITIES = 1;
+ public static final int EVENT_ON_LINKPROPERTIES = 2;
+ public static final int EVENT_ON_LOST = 3;
public static final int NOTIFY_LOCAL_PREFIXES = 10;
private static final int CALLBACK_LISTEN_ALL = 1;
- private static final int CALLBACK_TRACK_DEFAULT = 2;
+ private static final int CALLBACK_DEFAULT_INTERNET = 2;
private static final int CALLBACK_MOBILE_REQUEST = 3;
private final Context mContext;
@@ -117,7 +119,7 @@
mCM = cm;
}
- public void start() {
+ public void start(NetworkRequest defaultNetworkRequest) {
stop();
final NetworkRequest listenAllRequest = new NetworkRequest.Builder()
@@ -125,8 +127,16 @@
mListenAllCallback = new UpstreamNetworkCallback(CALLBACK_LISTEN_ALL);
cm().registerNetworkCallback(listenAllRequest, mListenAllCallback, mHandler);
- mDefaultNetworkCallback = new UpstreamNetworkCallback(CALLBACK_TRACK_DEFAULT);
- cm().registerDefaultNetworkCallback(mDefaultNetworkCallback, mHandler);
+ if (defaultNetworkRequest != null) {
+ // This is not really a "request", just a way of tracking the system default network.
+ // It's guaranteed not to actually bring up any networks because it's the same request
+ // as the ConnectivityService default request, and thus shares fate with it. We can't
+ // use registerDefaultNetworkCallback because it will not track the system default
+ // network if there is a VPN that applies to our UID.
+ final NetworkRequest trackDefaultRequest = new NetworkRequest(defaultNetworkRequest);
+ mDefaultNetworkCallback = new UpstreamNetworkCallback(CALLBACK_DEFAULT_INTERNET);
+ cm().requestNetwork(trackDefaultRequest, mDefaultNetworkCallback, mHandler);
+ }
}
public void stop() {
@@ -225,6 +235,20 @@
return typeStatePair.ns;
}
+ // Returns null if no current upstream available.
+ public NetworkState getCurrentPreferredUpstream() {
+ final NetworkState dfltState = (mDefaultInternetNetwork != null)
+ ? mNetworkMap.get(mDefaultInternetNetwork)
+ : null;
+ if (!mDunRequired) return dfltState;
+
+ if (isNetworkUsableAndNotCellular(dfltState)) return dfltState;
+
+ // Find a DUN network. Note that code in Tethering causes a DUN request
+ // to be filed, but this might be moved into this class in future.
+ return findFirstDunNetwork(mNetworkMap.values());
+ }
+
public void setCurrentUpstream(Network upstream) {
mTetheringUpstreamNetwork = upstream;
}
@@ -233,72 +257,16 @@
return (Set<IpPrefix>) mLocalPrefixes.clone();
}
- private void handleAvailable(int callbackType, Network network) {
- if (VDBG) Log.d(TAG, "EVENT_ON_AVAILABLE for " + network);
+ private void handleAvailable(Network network) {
+ if (mNetworkMap.containsKey(network)) return;
- if (!mNetworkMap.containsKey(network)) {
- mNetworkMap.put(network,
- new NetworkState(null, null, null, network, null, null));
- }
-
- // Always request whatever extra information we can, in case this
- // was already up when start() was called, in which case we would
- // not have been notified of any information that had not changed.
- switch (callbackType) {
- case CALLBACK_LISTEN_ALL:
- break;
-
- case CALLBACK_TRACK_DEFAULT:
- if (mDefaultNetworkCallback == null) {
- // The callback was unregistered in the interval between
- // ConnectivityService enqueueing onAvailable() and our
- // handling of it here on the mHandler thread.
- //
- // Clean-up of this network entry is deferred to the
- // handling of onLost() by other callbacks.
- //
- // These request*() calls can be deleted post oag/339444.
- return;
- }
- mDefaultInternetNetwork = network;
- break;
-
- case CALLBACK_MOBILE_REQUEST:
- if (mMobileNetworkCallback == null) {
- // The callback was unregistered in the interval between
- // ConnectivityService enqueueing onAvailable() and our
- // handling of it here on the mHandler thread.
- //
- // Clean-up of this network entry is deferred to the
- // handling of onLost() by other callbacks.
- return;
- }
- break;
- }
-
- // Requesting updates for mListenAllCallback is not currently possible
- // because it's a "listen". Two possible solutions to getting updates
- // about networks without waiting for a change (which might never come)
- // are:
- //
- // [1] extend request{NetworkCapabilities,LinkProperties}() to
- // take a Network argument and have ConnectivityService do
- // what's required (if the network satisfies the request)
- //
- // [2] explicitly file a NetworkRequest for each connectivity type
- // listed as a preferred upstream and wait for these callbacks
- // to be notified (requires tracking many more callbacks).
- //
- // Until this is addressed, networks that exist prior to the "listen"
- // registration and which do not subsequently change will not cause
- // us to learn their NetworkCapabilities nor their LinkProperties.
-
- // TODO: If sufficient information is available to select a more
- // preferable upstream, do so now and notify the target.
- notifyTarget(EVENT_ON_AVAILABLE, network);
+ if (VDBG) Log.d(TAG, "onAvailable for " + network);
+ mNetworkMap.put(network, new NetworkState(null, null, null, network, null, null));
}
- private void handleNetCap(Network network, NetworkCapabilities newNc) {
+ private void handleNetCap(int callbackType, Network network, NetworkCapabilities newNc) {
+ if (callbackType == CALLBACK_DEFAULT_INTERNET) mDefaultInternetNetwork = network;
+
final NetworkState prev = mNetworkMap.get(network);
if (prev == null || newNc.equals(prev.networkCapabilities)) {
// Ignore notifications about networks for which we have not yet
@@ -360,13 +328,17 @@
}
private void handleLost(int callbackType, Network network) {
- if (callbackType == CALLBACK_TRACK_DEFAULT) {
+ if (network.equals(mDefaultInternetNetwork)) {
mDefaultInternetNetwork = null;
- // Receiving onLost() for a default network does not necessarily
- // mean the network is gone. We wait for a separate notification
- // on either the LISTEN_ALL or MOBILE_REQUEST callbacks before
- // clearing all state.
- return;
+ // There are few TODOs within ConnectivityService's rematching code
+ // pertaining to spurious onLost() notifications.
+ //
+ // TODO: simplify this, probably if favor of code that:
+ // - selects a new upstream if mTetheringUpstreamNetwork has
+ // been lost (by any callback)
+ // - deletes the entry from the map only when the LISTEN_ALL
+ // callback gets notified.
+ if (callbackType == CALLBACK_DEFAULT_INTERNET) return;
}
if (!mNetworkMap.containsKey(network)) {
@@ -416,17 +388,19 @@
@Override
public void onAvailable(Network network) {
- handleAvailable(mCallbackType, network);
+ handleAvailable(network);
}
@Override
public void onCapabilitiesChanged(Network network, NetworkCapabilities newNc) {
- handleNetCap(network, newNc);
+ handleNetCap(mCallbackType, network, newNc);
}
@Override
public void onLinkPropertiesChanged(Network network, LinkProperties newLp) {
handleLinkProp(network, newLp);
+ // TODO(b/110335330): reduce the number of times this is called by
+ // only recomputing on the LISTEN_ALL callback.
recomputeLocalPrefixes();
}
@@ -443,6 +417,8 @@
@Override
public void onLost(Network network) {
handleLost(mCallbackType, network);
+ // TODO(b/110335330): reduce the number of times this is called by
+ // only recomputing on the LISTEN_ALL callback.
recomputeLocalPrefixes();
}
}
@@ -509,4 +485,31 @@
if (nc == null || !nc.hasSignalStrength()) return "unknown";
return Integer.toString(nc.getSignalStrength());
}
+
+ private static boolean isCellular(NetworkState ns) {
+ return (ns != null) && isCellular(ns.networkCapabilities);
+ }
+
+ private static boolean isCellular(NetworkCapabilities nc) {
+ return (nc != null) && nc.hasTransport(TRANSPORT_CELLULAR) &&
+ nc.hasCapability(NET_CAPABILITY_NOT_VPN);
+ }
+
+ private static boolean hasCapability(NetworkState ns, int netCap) {
+ return (ns != null) && (ns.networkCapabilities != null) &&
+ ns.networkCapabilities.hasCapability(netCap);
+ }
+
+ private static boolean isNetworkUsableAndNotCellular(NetworkState ns) {
+ return (ns != null) && (ns.networkCapabilities != null) && (ns.linkProperties != null) &&
+ !isCellular(ns.networkCapabilities);
+ }
+
+ private static NetworkState findFirstDunNetwork(Iterable<NetworkState> netStates) {
+ for (NetworkState ns : netStates) {
+ if (isCellular(ns) && hasCapability(ns, NET_CAPABILITY_DUN)) return ns;
+ }
+
+ return null;
+ }
}
diff --git a/services/core/java/com/android/server/content/SyncJobService.java b/services/core/java/com/android/server/content/SyncJobService.java
index 089632d..bfcc629 100644
--- a/services/core/java/com/android/server/content/SyncJobService.java
+++ b/services/core/java/com/android/server/content/SyncJobService.java
@@ -16,12 +16,10 @@
package com.android.server.content;
+import android.annotation.Nullable;
import android.app.job.JobParameters;
import android.app.job.JobService;
-import android.content.Intent;
import android.os.Message;
-import android.os.Messenger;
-import android.os.RemoteException;
import android.os.SystemClock;
import android.util.Log;
import android.util.Slog;
@@ -34,78 +32,86 @@
public class SyncJobService extends JobService {
private static final String TAG = "SyncManager";
- public static final String EXTRA_MESSENGER = "messenger";
+ private static final Object sLock = new Object();
- private Messenger mMessenger;
+ @GuardedBy("sLock")
+ private static SyncJobService sInstance;
- private final Object mLock = new Object();
+ @GuardedBy("sLock")
+ private static final SparseArray<JobParameters> sJobParamsMap = new SparseArray<>();
- @GuardedBy("mLock")
- private final SparseArray<JobParameters> mJobParamsMap = new SparseArray<>();
+ @GuardedBy("sLock")
+ private static final SparseBooleanArray sStartedSyncs = new SparseBooleanArray();
- @GuardedBy("mLock")
- private final SparseBooleanArray mStartedSyncs = new SparseBooleanArray();
+ @GuardedBy("sLock")
+ private static final SparseLongArray sJobStartUptimes = new SparseLongArray();
- @GuardedBy("mLock")
- private final SparseLongArray mJobStartUptimes = new SparseLongArray();
+ private static final SyncLogger sLogger = SyncLogger.getInstance();
- private final SyncLogger mLogger = SyncLogger.getInstance();
-
- /**
- * This service is started by the SyncManager which passes a messenger object to
- * communicate back with it. It never stops while the device is running.
- */
- @Override
- public int onStartCommand(Intent intent, int flags, int startId) {
- mMessenger = intent.getParcelableExtra(EXTRA_MESSENGER);
- Message m = Message.obtain();
- m.what = SyncManager.SyncHandler.MESSAGE_JOBSERVICE_OBJECT;
- m.obj = this;
- sendMessage(m);
-
- return START_NOT_STICKY;
+ private void updateInstance() {
+ synchronized (SyncJobService.class) {
+ sInstance = this;
+ }
}
- private void sendMessage(Message message) {
- if (mMessenger == null) {
- Slog.e(TAG, "Messenger not initialized.");
- return;
+ @Nullable
+ private static SyncJobService getInstance() {
+ synchronized (sLock) {
+ if (sInstance == null) {
+ Slog.wtf(TAG, "sInstance == null");
+ }
+ return sInstance;
}
- try {
- mMessenger.send(message);
- } catch (RemoteException e) {
- Slog.e(TAG, e.toString());
+ }
+
+ public static boolean isReady() {
+ synchronized (sLock) {
+ return sInstance != null;
}
}
@Override
public boolean onStartJob(JobParameters params) {
+ updateInstance();
- mLogger.purgeOldLogs();
+ sLogger.purgeOldLogs();
+
+ SyncOperation op = SyncOperation.maybeCreateFromJobExtras(params.getExtras());
+
+ if (op == null) {
+ Slog.wtf(TAG, "Got invalid job " + params.getJobId());
+ return false;
+ }
+
+ final boolean readyToSync = SyncManager.readyToSync(op.target.userId);
+
+ sLogger.log("onStartJob() jobid=", params.getJobId(), " op=", op,
+ " readyToSync", readyToSync);
+
+ if (!readyToSync) {
+ // If the user isn't unlocked or the device has been provisioned yet, just stop the job
+ // at this point. If it's a non-periodic sync, ask the job scheduler to reschedule it.
+ // If it's a periodic sync, then just wait until the next cycle.
+ final boolean wantsReschedule = !op.isPeriodic;
+ jobFinished(params, wantsReschedule);
+ return true;
+ }
boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
- synchronized (mLock) {
+ synchronized (sLock) {
final int jobId = params.getJobId();
- mJobParamsMap.put(jobId, params);
+ sJobParamsMap.put(jobId, params);
- mStartedSyncs.delete(jobId);
- mJobStartUptimes.put(jobId, SystemClock.uptimeMillis());
+ sStartedSyncs.delete(jobId);
+ sJobStartUptimes.put(jobId, SystemClock.uptimeMillis());
}
Message m = Message.obtain();
m.what = SyncManager.SyncHandler.MESSAGE_START_SYNC;
- SyncOperation op = SyncOperation.maybeCreateFromJobExtras(params.getExtras());
-
- mLogger.log("onStartJob() jobid=", params.getJobId(), " op=", op);
-
- if (op == null) {
- Slog.e(TAG, "Got invalid job " + params.getJobId());
- return false;
- }
if (isLoggable) {
Slog.v(TAG, "Got start job message " + op.target);
}
m.obj = op;
- sendMessage(m);
+ SyncManager.sendMessage(m);
return true;
}
@@ -115,15 +121,22 @@
Slog.v(TAG, "onStopJob called " + params.getJobId() + ", reason: "
+ params.getStopReason());
}
- final boolean readyToSync = SyncManager.readyToSync();
+ final SyncOperation op = SyncOperation.maybeCreateFromJobExtras(params.getExtras());
+ if (op == null) {
+ Slog.wtf(TAG, "Got invalid job " + params.getJobId());
+ return false;
+ }
- mLogger.log("onStopJob() ", mLogger.jobParametersToString(params),
+ final boolean readyToSync = SyncManager.readyToSync(op.target.userId);
+
+ sLogger.log("onStopJob() ", sLogger.jobParametersToString(params),
" readyToSync=", readyToSync);
- synchronized (mLock) {
- final int jobId = params.getJobId();
- mJobParamsMap.remove(jobId);
- final long startUptime = mJobStartUptimes.get(jobId);
+ synchronized (sLock) {
+ final int jobId = params.getJobId();
+ sJobParamsMap.remove(jobId);
+
+ final long startUptime = sJobStartUptimes.get(jobId);
final long nowUptime = SystemClock.uptimeMillis();
final long runtime = nowUptime - startUptime;
@@ -135,61 +148,57 @@
// WTF if startSyncH() hasn't happened, *unless* onStopJob() was called too soon.
// (1 minute threshold.)
// Also don't wtf when it's not ready to sync.
- if (readyToSync && !mStartedSyncs.get(jobId)) {
+ if (readyToSync && !sStartedSyncs.get(jobId)) {
wtf("Job " + jobId + " didn't start: "
+ " startUptime=" + startUptime
+ " nowUptime=" + nowUptime
+ " params=" + jobParametersToString(params));
}
- } else if (runtime < 10 * 1000) {
- // This happens too in a normal case too, and it's rather too often.
- // Disable it for now.
-// // Job stopped too soon. WTF.
-// wtf("Job " + jobId + " stopped in " + runtime + " ms: "
-// + " startUptime=" + startUptime
-// + " nowUptime=" + nowUptime
-// + " params=" + jobParametersToString(params));
}
- mStartedSyncs.delete(jobId);
- mJobStartUptimes.delete(jobId);
+ sStartedSyncs.delete(jobId);
+ sJobStartUptimes.delete(jobId);
}
Message m = Message.obtain();
m.what = SyncManager.SyncHandler.MESSAGE_STOP_SYNC;
- m.obj = SyncOperation.maybeCreateFromJobExtras(params.getExtras());
- if (m.obj == null) {
- return false;
- }
+ m.obj = op;
// Reschedule if this job was NOT explicitly canceled.
m.arg1 = params.getStopReason() != JobParameters.REASON_CANCELED ? 1 : 0;
// Apply backoff only if stop is called due to timeout.
m.arg2 = params.getStopReason() == JobParameters.REASON_TIMEOUT ? 1 : 0;
- sendMessage(m);
+ SyncManager.sendMessage(m);
return false;
}
- public void callJobFinished(int jobId, boolean needsReschedule, String why) {
- synchronized (mLock) {
- JobParameters params = mJobParamsMap.get(jobId);
- mLogger.log("callJobFinished()",
+ public static void callJobFinished(int jobId, boolean needsReschedule, String why) {
+ final SyncJobService instance = getInstance();
+ if (instance != null) {
+ instance.callJobFinishedInner(jobId, needsReschedule, why);
+ }
+ }
+
+ public void callJobFinishedInner(int jobId, boolean needsReschedule, String why) {
+ synchronized (sLock) {
+ JobParameters params = sJobParamsMap.get(jobId);
+ sLogger.log("callJobFinished()",
" jobid=", jobId,
" needsReschedule=", needsReschedule,
- " ", mLogger.jobParametersToString(params),
+ " ", sLogger.jobParametersToString(params),
" why=", why);
if (params != null) {
jobFinished(params, needsReschedule);
- mJobParamsMap.remove(jobId);
+ sJobParamsMap.remove(jobId);
} else {
Slog.e(TAG, "Job params not found for " + String.valueOf(jobId));
}
}
}
- public void markSyncStarted(int jobId) {
- synchronized (mLock) {
- mStartedSyncs.put(jobId, true);
+ public static void markSyncStarted(int jobId) {
+ synchronized (sLock) {
+ sStartedSyncs.put(jobId, true);
}
}
@@ -203,8 +212,8 @@
}
}
- private void wtf(String message) {
- mLogger.log(message);
+ private static void wtf(String message) {
+ sLogger.log(message);
Slog.wtf(TAG, message);
}
}
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index 0a640b8..d06e785 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -21,6 +21,7 @@
import android.accounts.AccountManager;
import android.accounts.AccountManagerInternal;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.AppGlobals;
@@ -72,7 +73,6 @@
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
-import android.os.Messenger;
import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteCallback;
@@ -89,6 +89,7 @@
import android.util.Log;
import android.util.Pair;
import android.util.Slog;
+import android.util.SparseBooleanArray;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
@@ -227,7 +228,6 @@
// TODO: add better locking around mRunningAccounts
private volatile AccountAndUser[] mRunningAccounts = INITIAL_ACCOUNTS_ARRAY;
- volatile private PowerManager.WakeLock mHandleAlarmWakeLock;
volatile private PowerManager.WakeLock mSyncManagerWakeLock;
volatile private boolean mDataConnectionIsConnected = false;
volatile private boolean mStorageIsLow = false;
@@ -238,7 +238,6 @@
private final IBatteryStats mBatteryStats;
private JobScheduler mJobScheduler;
private JobSchedulerInternal mJobSchedulerInternal;
- private SyncJobService mSyncJobService;
private SyncStorageEngine mSyncStorageEngine;
@@ -318,16 +317,6 @@
}
};
- private final BroadcastReceiver mBootCompletedReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- mBootCompleted = true;
- // Called because it gets all pending jobs and stores them in mScheduledSyncs cache.
- verifyJobScheduler();
- mSyncHandler.onBootCompleted();
- }
- };
-
private final BroadcastReceiver mAccountsUpdatedReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -371,14 +360,14 @@
m.sendToTarget();
}
- private void doDatabaseCleanup() {
+ private void removeStaleAccounts() {
for (UserInfo user : mUserManager.getUsers(true)) {
// Skip any partially created/removed users
if (user.partial) continue;
Account[] accountsForUser = AccountManagerService.getSingleton().getAccounts(
user.id, mContext.getOpPackageName());
- mSyncStorageEngine.doDatabaseCleanup(accountsForUser, user.id);
+ mSyncStorageEngine.removeStaleAccounts(accountsForUser, user.id);
}
}
@@ -464,8 +453,8 @@
private final SyncHandler mSyncHandler;
private final SyncManagerConstants mConstants;
- private volatile boolean mBootCompleted = false;
- private volatile boolean mJobServiceReady = false;
+ @GuardedBy("mUnlockedUsers")
+ private final SparseBooleanArray mUnlockedUsers = new SparseBooleanArray();
private ConnectivityManager getConnectivityManager() {
synchronized (this) {
@@ -641,12 +630,6 @@
IntentFilter intentFilter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
context.registerReceiver(mConnectivityIntentReceiver, intentFilter);
- if (!factoryTest) {
- intentFilter = new IntentFilter(Intent.ACTION_BOOT_COMPLETED);
- intentFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
- context.registerReceiver(mBootCompletedReceiver, intentFilter);
- }
-
intentFilter = new IntentFilter(Intent.ACTION_DEVICE_STORAGE_LOW);
intentFilter.addAction(Intent.ACTION_DEVICE_STORAGE_OK);
context.registerReceiver(mStorageIntentReceiver, intentFilter);
@@ -690,14 +673,6 @@
mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService(
BatteryStats.SERVICE_NAME));
- // This WakeLock is used to ensure that we stay awake between the time that we receive
- // a sync alarm notification and when we finish processing it. We need to do this
- // because we don't do the work in the alarm handler, rather we do it in a message
- // handler.
- mHandleAlarmWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
- HANDLE_SYNC_ALARM_WAKE_LOCK);
- mHandleAlarmWakeLock.setReferenceCounted(false);
-
// This WakeLock is used to ensure that we stay awake while running the sync loop
// message handler. Normally we will hold a sync adapter wake lock while it is being
// synced but during the execution of the sync loop it might finish a sync for
@@ -715,7 +690,6 @@
public void onChange(boolean selfChange) {
mProvisioned |= isDeviceProvisioned();
if (mProvisioned) {
- mSyncHandler.onDeviceProvisioned();
resolver.unregisterContentObserver(this);
}
}
@@ -744,19 +718,6 @@
null, null);
}
- // Set up the communication channel between the scheduled job and the sync manager.
- // This is posted to the *main* looper intentionally, to defer calling startService()
- // until after the lengthy primary boot sequence completes on that thread, to avoid
- // spurious ANR triggering.
- final Intent startServiceIntent = new Intent(mContext, SyncJobService.class);
- startServiceIntent.putExtra(SyncJobService.EXTRA_MESSENGER, new Messenger(mSyncHandler));
- new Handler(mContext.getMainLooper()).post(new Runnable() {
- @Override
- public void run() {
- mContext.startService(startServiceIntent);
- }
- });
-
// Sync adapters were able to access the synced account without the accounts
// permission which circumvents our permission model. Therefore, we require
// sync adapters that don't have access to the account to get user consent.
@@ -768,16 +729,31 @@
mLogger.log("Sync manager initialized: " + Build.FINGERPRINT);
}
- public void onStartUser(int userHandle) {
- mSyncHandler.post(() -> mLogger.log("onStartUser: user=", userHandle));
+ public void onStartUser(int userId) {
+ // Log on the handler to avoid slowing down device boot.
+ mSyncHandler.post(() -> mLogger.log("onStartUser: user=", userId));
}
- public void onUnlockUser(int userHandle) {
- mSyncHandler.post(() -> mLogger.log("onUnlockUser: user=", userHandle));
+ public void onUnlockUser(int userId) {
+ synchronized (mUnlockedUsers) {
+ mUnlockedUsers.put(userId, true);
+ }
+ // Log on the handler to avoid slowing down device boot.
+ mSyncHandler.post(() -> mLogger.log("onUnlockUser: user=", userId));
}
- public void onStopUser(int userHandle) {
- mSyncHandler.post(() -> mLogger.log("onStopUser: user=", userHandle));
+ public void onStopUser(int userId) {
+ synchronized (mUnlockedUsers) {
+ mUnlockedUsers.put(userId, false);
+ }
+ // Log on the handler to avoid slowing down user switch.
+ mSyncHandler.post(() -> mLogger.log("onStopUser: user=", userId));
+ }
+
+ private boolean isUserUnlocked(int userId) {
+ synchronized (mUnlockedUsers) {
+ return mUnlockedUsers.get(userId);
+ }
}
public void onBootPhase(int phase) {
@@ -1820,7 +1796,7 @@
updateRunningAccounts(null /* Don't sync any target */);
// Clean up the storage engine database
- mSyncStorageEngine.doDatabaseCleanup(new Account[0], userId);
+ mSyncStorageEngine.removeStaleAccounts(null, userId);
List<SyncOperation> ops = getAllPendingSyncs();
for (SyncOperation op: ops) {
if (op.target.userId == userId) {
@@ -2235,8 +2211,13 @@
mSyncStorageEngine.resetTodayStats(/* force=*/ false);
for (AccountAndUser account : accounts) {
- pw.printf("Account %s u%d %s\n",
- account.account.name, account.userId, account.account.type);
+ final boolean unlocked;
+ synchronized (mUnlockedUsers) {
+ unlocked = mUnlockedUsers.get(account.userId);
+ }
+ pw.printf("Account %s u%d %s%s\n",
+ account.account.name, account.userId, account.account.type,
+ (unlocked ? "" : " (locked)"));
pw.println("=======================================================================");
final PrintTable table = new PrintTable(16);
@@ -2872,13 +2853,29 @@
}
}
- /**
- * @return whether the device is ready to run sync jobs.
- */
- public static boolean readyToSync() {
+ @Nullable
+ private static SyncManager getInstance() {
synchronized (SyncManager.class) {
- return sInstance != null && sInstance.mProvisioned && sInstance.mBootCompleted
- && sInstance.mJobServiceReady;
+ if (sInstance == null) {
+ Slog.wtf(TAG, "sInstance == null"); // Maybe called too early?
+ }
+ return sInstance;
+ }
+ }
+
+ /**
+ * @return whether the device is ready to run sync jobs for a given user.
+ */
+ public static boolean readyToSync(int userId) {
+ final SyncManager instance = getInstance();
+ return (instance != null) && SyncJobService.isReady()
+ && instance.mProvisioned && instance.isUserUnlocked(userId);
+ }
+
+ public static void sendMessage(Message message) {
+ final SyncManager instance = getInstance();
+ if (instance != null) {
+ instance.mSyncHandler.sendMessage(message);
}
}
@@ -2889,11 +2886,9 @@
class SyncHandler extends Handler {
// Messages that can be sent on mHandler.
private static final int MESSAGE_SYNC_FINISHED = 1;
- private static final int MESSAGE_RELEASE_MESSAGES_FROM_QUEUE = 2;
private static final int MESSAGE_SERVICE_CONNECTED = 4;
private static final int MESSAGE_SERVICE_DISCONNECTED = 5;
private static final int MESSAGE_CANCEL = 6;
- static final int MESSAGE_JOBSERVICE_OBJECT = 7;
static final int MESSAGE_START_SYNC = 10;
static final int MESSAGE_STOP_SYNC = 11;
static final int MESSAGE_SCHEDULE_SYNC = 12;
@@ -2910,86 +2905,17 @@
public final SyncTimeTracker mSyncTimeTracker = new SyncTimeTracker();
private final HashMap<String, PowerManager.WakeLock> mWakeLocks = Maps.newHashMap();
- private List<Message> mUnreadyQueue = new ArrayList<Message>();
-
- void onBootCompleted() {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Slog.v(TAG, "Boot completed.");
- }
- checkIfDeviceReady();
- }
-
- void onDeviceProvisioned() {
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "mProvisioned=" + mProvisioned);
- }
- checkIfDeviceReady();
- }
-
- void checkIfDeviceReady() {
- if (mProvisioned && mBootCompleted && mJobServiceReady) {
- synchronized(this) {
- mSyncStorageEngine.restoreAllPeriodicSyncs();
- // Dispatch any stashed messages.
- obtainMessage(MESSAGE_RELEASE_MESSAGES_FROM_QUEUE).sendToTarget();
- }
- }
- }
-
- /**
- * Stash any messages that come to the handler before boot is complete or before the device
- * is properly provisioned (i.e. out of set-up wizard).
- * {@link #onBootCompleted()} and {@link SyncHandler#onDeviceProvisioned} both
- * need to come in before we start syncing.
- * @param msg Message to dispatch at a later point.
- * @return true if a message was enqueued, false otherwise. This is to avoid losing the
- * message if we manage to acquire the lock but by the time we do boot has completed.
- */
- private boolean tryEnqueueMessageUntilReadyToRun(Message msg) {
- synchronized (this) {
- if (!mBootCompleted || !mProvisioned || !mJobServiceReady) {
- // Need to copy the message bc looper will recycle it.
- Message m = Message.obtain(msg);
- mUnreadyQueue.add(m);
- return true;
- } else {
- return false;
- }
- }
- }
-
public SyncHandler(Looper looper) {
super(looper);
}
public void handleMessage(Message msg) {
+ // TODO Do we really need this wake lock?? If we actually needed it, this is probably
+ // not the best place to acquire the lock -- it's probably too late, because the device
+ // could have gone to sleep before we reach here.
+ mSyncManagerWakeLock.acquire();
try {
- mSyncManagerWakeLock.acquire();
- // We only want to enqueue sync related messages until device is ready.
- // Other messages are handled without enqueuing.
- if (msg.what == MESSAGE_JOBSERVICE_OBJECT) {
- Slog.i(TAG, "Got SyncJobService instance.");
- mSyncJobService = (SyncJobService) msg.obj;
- mJobServiceReady = true;
- checkIfDeviceReady();
- } else if (msg.what == SyncHandler.MESSAGE_ACCOUNTS_UPDATED) {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Slog.v(TAG, "handleSyncHandlerMessage: MESSAGE_ACCOUNTS_UPDATED");
- }
- EndPoint targets = (EndPoint) msg.obj;
- updateRunningAccountsH(targets);
- } else if (msg.what == MESSAGE_RELEASE_MESSAGES_FROM_QUEUE) {
- if (mUnreadyQueue != null) {
- for (Message m : mUnreadyQueue) {
- handleSyncMessage(m);
- }
- mUnreadyQueue = null;
- }
- } else if (tryEnqueueMessageUntilReadyToRun(msg)) {
- // No work to be done.
- } else {
- handleSyncMessage(msg);
- }
+ handleSyncMessage(msg);
} finally {
mSyncManagerWakeLock.release();
}
@@ -3001,6 +2927,13 @@
try {
mDataConnectionIsConnected = readDataConnectionState();
switch (msg.what) {
+ case MESSAGE_ACCOUNTS_UPDATED:
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Slog.v(TAG, "handleSyncHandlerMessage: MESSAGE_ACCOUNTS_UPDATED");
+ }
+ EndPoint targets = (EndPoint) msg.obj;
+ updateRunningAccountsH(targets);
+ break;
case MESSAGE_SCHEDULE_SYNC:
ScheduleSyncMessagePayload syncPayload =
(ScheduleSyncMessagePayload) msg.obj;
@@ -3069,7 +3002,7 @@
if (isLoggable) {
Slog.v(TAG, "syncFinished" + payload.activeSyncContext.mSyncOperation);
}
- mSyncJobService.callJobFinished(
+ SyncJobService.callJobFinished(
payload.activeSyncContext.mSyncOperation.jobId, false,
"sync finished");
runSyncFinishedOrCanceledH(payload.syncResult,
@@ -3119,7 +3052,7 @@
// which is a soft error.
SyncResult syncResult = new SyncResult();
syncResult.stats.numIoExceptions++;
- mSyncJobService.callJobFinished(
+ SyncJobService.callJobFinished(
currentSyncContext.mSyncOperation.jobId, false,
"service disconnected");
runSyncFinishedOrCanceledH(syncResult, currentSyncContext);
@@ -3138,7 +3071,7 @@
Log.w(TAG, String.format(
"Detected sync making no progress for %s. cancelling.",
monitoredSyncContext));
- mSyncJobService.callJobFinished(
+ SyncJobService.callJobFinished(
monitoredSyncContext.mSyncOperation.jobId, false,
"no network activity");
runSyncFinishedOrCanceledH(
@@ -3175,7 +3108,7 @@
private void deferSyncH(SyncOperation op, long delay, String why) {
mLogger.log("deferSyncH() ", (op.isPeriodic ? "periodic " : ""),
"sync. op=", op, " delay=", delay, " why=", why);
- mSyncJobService.callJobFinished(op.jobId, false, why);
+ SyncJobService.callJobFinished(op.jobId, false, why);
if (op.isPeriodic) {
scheduleSyncOperationH(op.createOneTimeSyncOperation(), delay);
} else {
@@ -3213,7 +3146,7 @@
// assume the clock is correct.
mSyncStorageEngine.setClockValid();
- mSyncJobService.markSyncStarted(op.jobId);
+ SyncJobService.markSyncStarted(op.jobId);
if (mStorageIsLow) {
deferSyncH(op, SYNC_DELAY_ON_LOW_STORAGE, "storage low");
@@ -3226,7 +3159,7 @@
List<SyncOperation> ops = getAllPendingSyncs();
for (SyncOperation syncOperation: ops) {
if (syncOperation.sourcePeriodicId == op.jobId) {
- mSyncJobService.callJobFinished(op.jobId, false,
+ SyncJobService.callJobFinished(op.jobId, false,
"periodic sync, pending");
return;
}
@@ -3235,7 +3168,7 @@
// executing according to some backoff criteria.
for (ActiveSyncContext asc: mActiveSyncContexts) {
if (asc.mSyncOperation.sourcePeriodicId == op.jobId) {
- mSyncJobService.callJobFinished(op.jobId, false,
+ SyncJobService.callJobFinished(op.jobId, false,
"periodic sync, already running");
return;
}
@@ -3272,13 +3205,13 @@
switch (syncOpState) {
case SYNC_OP_STATE_INVALID_NO_ACCOUNT_ACCESS:
case SYNC_OP_STATE_INVALID: {
- mSyncJobService.callJobFinished(op.jobId, false,
+ SyncJobService.callJobFinished(op.jobId, false,
"invalid op state: " + syncOpState);
} return;
}
if (!dispatchSyncOperation(op)) {
- mSyncJobService.callJobFinished(op.jobId, false, "dispatchSyncOperation() failed");
+ SyncJobService.callJobFinished(op.jobId, false, "dispatchSyncOperation() failed");
}
setAuthorityPendingState(op.target);
@@ -3306,9 +3239,7 @@
if (mLogger.enabled()) {
mLogger.log("updateRunningAccountsH: ", Arrays.toString(mRunningAccounts));
}
- if (mBootCompleted) {
- doDatabaseCleanup();
- }
+ removeStaleAccounts();
AccountAndUser[] accounts = mRunningAccounts;
for (ActiveSyncContext currentSyncContext : mActiveSyncContexts) {
@@ -3453,7 +3384,7 @@
if (op.sourcePeriodicId == syncOperation.jobId || op.jobId == syncOperation.jobId) {
ActiveSyncContext asc = findActiveSyncContextH(syncOperation.jobId);
if (asc != null) {
- mSyncJobService.callJobFinished(syncOperation.jobId, false,
+ SyncJobService.callJobFinished(syncOperation.jobId, false,
"removePeriodicSyncInternalH");
runSyncFinishedOrCanceledH(null, asc);
}
@@ -3662,7 +3593,7 @@
false /* no config settings */)) {
continue;
}
- mSyncJobService.callJobFinished(activeSyncContext.mSyncOperation.jobId, false,
+ SyncJobService.callJobFinished(activeSyncContext.mSyncOperation.jobId, false,
why);
runSyncFinishedOrCanceledH(null /* cancel => no result */, activeSyncContext);
}
diff --git a/services/core/java/com/android/server/content/SyncStorageEngine.java b/services/core/java/com/android/server/content/SyncStorageEngine.java
index 11f0701..391e3b0 100644
--- a/services/core/java/com/android/server/content/SyncStorageEngine.java
+++ b/services/core/java/com/android/server/content/SyncStorageEngine.java
@@ -19,6 +19,7 @@
import android.accounts.Account;
import android.accounts.AccountAndUser;
import android.accounts.AccountManager;
+import android.annotation.Nullable;
import android.app.backup.BackupManager;
import android.content.ComponentName;
import android.content.ContentResolver;
@@ -30,10 +31,6 @@
import android.content.SyncRequest;
import android.content.SyncStatusInfo;
import android.content.pm.PackageManager;
-import android.database.Cursor;
-import android.database.sqlite.SQLiteDatabase;
-import android.database.sqlite.SQLiteException;
-import android.database.sqlite.SQLiteQueryBuilder;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
@@ -510,10 +507,6 @@
readAccountInfoLocked();
readStatusLocked();
readStatisticsLocked();
- readAndDeleteLegacyAccountInfoLocked();
- writeAccountInfoLocked();
- writeStatusLocked();
- writeStatisticsLocked();
if (mLogger.enabled()) {
final int size = mAuthorities.size();
@@ -1013,7 +1006,7 @@
* Called when the set of account has changed, given the new array of
* active accounts.
*/
- public void doDatabaseCleanup(Account[] accounts, int userId) {
+ public void removeStaleAccounts(@Nullable Account[] accounts, int userId) {
synchronized (mAuthorities) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Slog.v(TAG, "Updating for new accounts...");
@@ -1022,8 +1015,9 @@
Iterator<AccountInfo> accIt = mAccounts.values().iterator();
while (accIt.hasNext()) {
AccountInfo acc = accIt.next();
- if (!ArrayUtils.contains(accounts, acc.accountAndUser.account)
- && acc.accountAndUser.userId == userId) {
+ if ((accounts == null) || (
+ (acc.accountAndUser.userId == userId)
+ && !ArrayUtils.contains(accounts, acc.accountAndUser.account))) {
// This account no longer exists...
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Slog.v(TAG, "Account removed: " + acc.accountAndUser);
@@ -1581,7 +1575,6 @@
readAccountInfoLocked();
readStatusLocked();
readStatisticsLocked();
- readAndDeleteLegacyAccountInfoLocked();
writeAccountInfoLocked();
writeStatusLocked();
writeStatisticsLocked();
@@ -1998,146 +1991,6 @@
}
}
- static int getIntColumn(Cursor c, String name) {
- return c.getInt(c.getColumnIndex(name));
- }
-
- static long getLongColumn(Cursor c, String name) {
- return c.getLong(c.getColumnIndex(name));
- }
-
- /**
- * TODO Remove it. It's super old code that was used to migrate the information from a sqlite
- * database that we used a long time ago, and is no longer relevant.
- */
- private void readAndDeleteLegacyAccountInfoLocked() {
- // Look for old database to initialize from.
- File file = mContext.getDatabasePath("syncmanager.db");
- if (!file.exists()) {
- return;
- }
- String path = file.getPath();
- SQLiteDatabase db = null;
- try {
- db = SQLiteDatabase.openDatabase(path, null,
- SQLiteDatabase.OPEN_READONLY);
- } catch (SQLiteException e) {
- }
-
- if (db != null) {
- final boolean hasType = db.getVersion() >= 11;
-
- // Copy in all of the status information, as well as accounts.
- if (Log.isLoggable(TAG_FILE, Log.VERBOSE)) {
- Slog.v(TAG_FILE, "Reading legacy sync accounts db");
- }
- SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
- qb.setTables("stats, status");
- HashMap<String,String> map = new HashMap<String,String>();
- map.put("_id", "status._id as _id");
- map.put("account", "stats.account as account");
- if (hasType) {
- map.put("account_type", "stats.account_type as account_type");
- }
- map.put("authority", "stats.authority as authority");
- map.put("totalElapsedTime", "totalElapsedTime");
- map.put("numSyncs", "numSyncs");
- map.put("numSourceLocal", "numSourceLocal");
- map.put("numSourcePoll", "numSourcePoll");
- map.put("numSourceServer", "numSourceServer");
- map.put("numSourceUser", "numSourceUser");
- map.put("lastSuccessSource", "lastSuccessSource");
- map.put("lastSuccessTime", "lastSuccessTime");
- map.put("lastFailureSource", "lastFailureSource");
- map.put("lastFailureTime", "lastFailureTime");
- map.put("lastFailureMesg", "lastFailureMesg");
- map.put("pending", "pending");
- qb.setProjectionMap(map);
- qb.appendWhere("stats._id = status.stats_id");
- Cursor c = qb.query(db, null, null, null, null, null, null);
- while (c.moveToNext()) {
- String accountName = c.getString(c.getColumnIndex("account"));
- String accountType = hasType
- ? c.getString(c.getColumnIndex("account_type")) : null;
- if (accountType == null) {
- accountType = "com.google";
- }
- String authorityName = c.getString(c.getColumnIndex("authority"));
- AuthorityInfo authority =
- this.getOrCreateAuthorityLocked(
- new EndPoint(new Account(accountName, accountType),
- authorityName,
- 0 /* legacy is single-user */)
- , -1,
- false);
- if (authority != null) {
- int i = mSyncStatus.size();
- boolean found = false;
- SyncStatusInfo st = null;
- while (i > 0) {
- i--;
- st = mSyncStatus.valueAt(i);
- if (st.authorityId == authority.ident) {
- found = true;
- break;
- }
- }
- if (!found) {
- st = new SyncStatusInfo(authority.ident);
- mSyncStatus.put(authority.ident, st);
- }
- st.totalStats.totalElapsedTime = getLongColumn(c, "totalElapsedTime");
- st.totalStats.numSyncs = getIntColumn(c, "numSyncs");
- st.totalStats.numSourceLocal = getIntColumn(c, "numSourceLocal");
- st.totalStats.numSourcePoll = getIntColumn(c, "numSourcePoll");
- st.totalStats.numSourceOther = getIntColumn(c, "numSourceServer");
- st.totalStats.numSourceUser = getIntColumn(c, "numSourceUser");
- st.totalStats.numSourcePeriodic = 0;
- st.lastSuccessSource = getIntColumn(c, "lastSuccessSource");
- st.lastSuccessTime = getLongColumn(c, "lastSuccessTime");
- st.lastFailureSource = getIntColumn(c, "lastFailureSource");
- st.lastFailureTime = getLongColumn(c, "lastFailureTime");
- st.lastFailureMesg = c.getString(c.getColumnIndex("lastFailureMesg"));
- st.pending = getIntColumn(c, "pending") != 0;
- }
- }
-
- c.close();
-
- // Retrieve the settings.
- qb = new SQLiteQueryBuilder();
- qb.setTables("settings");
- c = qb.query(db, null, null, null, null, null, null);
- while (c.moveToNext()) {
- String name = c.getString(c.getColumnIndex("name"));
- String value = c.getString(c.getColumnIndex("value"));
- if (name == null) continue;
- if (name.equals("listen_for_tickles")) {
- setMasterSyncAutomatically(value == null || Boolean.parseBoolean(value), 0,
- ContentResolver.SYNC_EXEMPTION_NONE, SyncLogger.CALLING_UID_SELF);
- } else if (name.startsWith("sync_provider_")) {
- String provider = name.substring("sync_provider_".length(),
- name.length());
- int i = mAuthorities.size();
- while (i > 0) {
- i--;
- AuthorityInfo authority = mAuthorities.valueAt(i);
- if (authority.target.provider.equals(provider)) {
- authority.enabled = value == null || Boolean.parseBoolean(value);
- authority.syncable = 1;
- }
- }
- }
- }
-
- c.close();
-
- db.close();
-
- (new File(path)).delete();
- }
- }
-
public static final int STATUS_FILE_END = 0;
public static final int STATUS_FILE_ITEM = 100;
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index 1c3342a..b97e904 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -400,6 +400,7 @@
} else if (mLightSensorEnabled) {
mLightSensorEnabled = false;
mAmbientLuxValid = !mResetAmbientLuxAfterWarmUpConfig;
+ mScreenAutoBrightness = -1;
mRecentLightSamples = 0;
mAmbientLightRingBuffer.clear();
mCurrentLightSensorRate = -1;
diff --git a/services/core/java/com/android/server/display/ColorFade.java b/services/core/java/com/android/server/display/ColorFade.java
index 4f53ed4..33525fd 100644
--- a/services/core/java/com/android/server/display/ColorFade.java
+++ b/services/core/java/com/android/server/display/ColorFade.java
@@ -46,6 +46,7 @@
import libcore.io.Streams;
import com.android.server.LocalServices;
+import com.android.server.policy.WindowManagerPolicy;
/**
* <p>
@@ -63,7 +64,7 @@
// The layer for the electron beam surface.
// This is currently hardcoded to be one layer above the boot animation.
- private static final int COLOR_FADE_LAYER = 0x40000001;
+ private static final int COLOR_FADE_LAYER = WindowManagerPolicy.COLOR_FADE_LAYER;
// The number of frames to draw when preparing the animation so that it will
// be ready to run smoothly. We use 3 frames because we are triple-buffered.
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 0a77269..75b3556 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -2165,6 +2165,24 @@
}
@Override
+ public boolean screenshot(int displayId, Surface outSurface) {
+ synchronized (mSyncRoot) {
+ final LogicalDisplay display = mLogicalDisplays.get(displayId);
+ if (display != null) {
+ final DisplayDevice device = display.getPrimaryDisplayDeviceLocked();
+ if (device != null) {
+ final IBinder token = device.getDisplayTokenLocked();
+ if (token != null) {
+ SurfaceControl.screenshot(token, outSurface);
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ @Override
public DisplayInfo getDisplayInfo(int displayId) {
return getDisplayInfoInternal(displayId, Process.myUid());
}
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 99412c5..b124ac7 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -791,9 +791,6 @@
&& mAutomaticBrightnessController != null;
final boolean userSetBrightnessChanged = updateUserSetScreenBrightness();
- if (userSetBrightnessChanged) {
- mTemporaryScreenBrightness = -1;
- }
// Use the temporary screen brightness if there isn't an override, either from
// WindowManager or based on the display state.
@@ -1514,11 +1511,13 @@
}
if (mCurrentScreenBrightnessSetting == mPendingScreenBrightnessSetting) {
mPendingScreenBrightnessSetting = -1;
+ mTemporaryScreenBrightness = -1;
return false;
}
mCurrentScreenBrightnessSetting = mPendingScreenBrightnessSetting;
mLastUserSetScreenBrightness = mPendingScreenBrightnessSetting;
mPendingScreenBrightnessSetting = -1;
+ mTemporaryScreenBrightness = -1;
return true;
}
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index b9a279a..21ae048 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -31,8 +31,6 @@
import android.os.PowerManager;
import android.os.SystemProperties;
import android.os.Trace;
-import android.text.TextUtils;
-import android.util.PathParser;
import android.util.Slog;
import android.util.SparseArray;
import android.view.Display;
@@ -404,8 +402,8 @@
&& SystemProperties.getBoolean(PROPERTY_EMULATOR_CIRCULAR, false))) {
mInfo.flags |= DisplayDeviceInfo.FLAG_ROUND;
}
- mInfo.displayCutout = DisplayCutout.fromResources(res, mInfo.width,
- mInfo.height);
+ mInfo.displayCutout = DisplayCutout.fromResourcesRectApproximation(res,
+ mInfo.width, mInfo.height);
mInfo.type = Display.TYPE_BUILT_IN;
mInfo.densityDpi = (int)(phys.density * 160 + 0.5f);
mInfo.xDpi = phys.xDpi;
diff --git a/services/core/java/com/android/server/fingerprint/EnrollClient.java b/services/core/java/com/android/server/fingerprint/EnrollClient.java
deleted file mode 100644
index c9efcf2..0000000
--- a/services/core/java/com/android/server/fingerprint/EnrollClient.java
+++ /dev/null
@@ -1,146 +0,0 @@
-/**
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.fingerprint;
-
-import android.content.Context;
-import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
-import android.hardware.fingerprint.FingerprintManager;
-import android.hardware.fingerprint.IFingerprintServiceReceiver;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.Slog;
-
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-
-import java.util.Arrays;
-
-/**
- * A class to keep track of the enrollment state for a given client.
- */
-public abstract class EnrollClient extends ClientMonitor {
- private static final long MS_PER_SEC = 1000;
- private static final int ENROLLMENT_TIMEOUT_MS = 60 * 1000; // 1 minute
- private byte[] mCryptoToken;
-
- public EnrollClient(Context context, long halDeviceId, IBinder token,
- IFingerprintServiceReceiver receiver, int userId, int groupId, byte [] cryptoToken,
- boolean restricted, String owner) {
- super(context, halDeviceId, token, receiver, userId, groupId, restricted, owner);
- mCryptoToken = Arrays.copyOf(cryptoToken, cryptoToken.length);
- }
-
- @Override
- public boolean onEnrollResult(int fingerId, int groupId, int remaining) {
- if (groupId != getGroupId()) {
- Slog.w(TAG, "groupId != getGroupId(), groupId: " + groupId +
- " getGroupId():" + getGroupId());
- }
- if (remaining == 0) {
- FingerprintUtils.getInstance().addFingerprintForUser(getContext(), fingerId,
- getTargetUserId());
- }
- return sendEnrollResult(fingerId, groupId, remaining);
- }
-
- /*
- * @return true if we're done.
- */
- private boolean sendEnrollResult(int fpId, int groupId, int remaining) {
- IFingerprintServiceReceiver receiver = getReceiver();
- if (receiver == null)
- return true; // client not listening
-
- vibrateSuccess();
- MetricsLogger.action(getContext(), MetricsEvent.ACTION_FINGERPRINT_ENROLL);
- try {
- receiver.onEnrollResult(getHalDeviceId(), fpId, groupId, remaining);
- return remaining == 0;
- } catch (RemoteException e) {
- Slog.w(TAG, "Failed to notify EnrollResult:", e);
- return true;
- }
- }
-
- @Override
- public int start() {
- IBiometricsFingerprint daemon = getFingerprintDaemon();
- if (daemon == null) {
- Slog.w(TAG, "enroll: no fingerprint HAL!");
- return ERROR_ESRCH;
- }
- final int timeout = (int) (ENROLLMENT_TIMEOUT_MS / MS_PER_SEC);
- try {
- final int result = daemon.enroll(mCryptoToken, getGroupId(), timeout);
- if (result != 0) {
- Slog.w(TAG, "startEnroll failed, result=" + result);
- MetricsLogger.histogram(getContext(), "fingerprintd_enroll_start_error", result);
- onError(FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */);
- return result;
- }
- } catch (RemoteException e) {
- Slog.e(TAG, "startEnroll failed", e);
- }
- return 0; // success
- }
-
- @Override
- public int stop(boolean initiatedByClient) {
- if (mAlreadyCancelled) {
- Slog.w(TAG, "stopEnroll: already cancelled!");
- return 0;
- }
- IBiometricsFingerprint daemon = getFingerprintDaemon();
- if (daemon == null) {
- Slog.w(TAG, "stopEnrollment: no fingerprint HAL!");
- return ERROR_ESRCH;
- }
- try {
- final int result = daemon.cancel();
- if (result != 0) {
- Slog.w(TAG, "startEnrollCancel failed, result = " + result);
- return result;
- }
- } catch (RemoteException e) {
- Slog.e(TAG, "stopEnrollment failed", e);
- }
- if (initiatedByClient) {
- onError(FingerprintManager.FINGERPRINT_ERROR_CANCELED, 0 /* vendorCode */);
- }
- mAlreadyCancelled = true;
- return 0;
- }
-
- @Override
- public boolean onRemoved(int fingerId, int groupId, int remaining) {
- if (DEBUG) Slog.w(TAG, "onRemoved() called for enroll!");
- return true; // Invalid for EnrollClient
- }
-
- @Override
- public boolean onEnumerationResult(int fingerId, int groupId, int remaining) {
- if (DEBUG) Slog.w(TAG, "onEnumerationResult() called for enroll!");
- return true; // Invalid for EnrollClient
- }
-
- @Override
- public boolean onAuthenticated(int fingerId, int groupId) {
- if (DEBUG) Slog.w(TAG, "onAuthenticated() called for enroll!");
- return true; // Invalid for EnrollClient
- }
-
-}
diff --git a/services/core/java/com/android/server/fingerprint/EnumerateClient.java b/services/core/java/com/android/server/fingerprint/EnumerateClient.java
deleted file mode 100644
index b6bbd1b..0000000
--- a/services/core/java/com/android/server/fingerprint/EnumerateClient.java
+++ /dev/null
@@ -1,118 +0,0 @@
-/**
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.fingerprint;
-
-import android.content.Context;
-import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
-import android.hardware.fingerprint.FingerprintManager;
-import android.hardware.fingerprint.IFingerprintServiceReceiver;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.Slog;
-import com.android.internal.logging.MetricsLogger;
-
-/**
- * A class to keep track of the enumeration state for a given client.
- */
-public abstract class EnumerateClient extends ClientMonitor {
- public EnumerateClient(Context context, long halDeviceId, IBinder token,
- IFingerprintServiceReceiver receiver, int groupId, int userId,
- boolean restricted, String owner) {
- super(context, halDeviceId, token, receiver, userId, groupId, restricted, owner);
- }
-
- @Override
- public int start() {
- IBiometricsFingerprint daemon = getFingerprintDaemon();
- // The fingerprint template ids will be removed when we get confirmation from the HAL
- try {
- final int result = daemon.enumerate();
- if (result != 0) {
- Slog.w(TAG, "start enumerate for user " + getTargetUserId()
- + " failed, result=" + result);
- MetricsLogger.histogram(getContext(), "fingerprintd_enum_start_error", result);
- onError(FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */);
- return result;
- }
- } catch (RemoteException e) {
- Slog.e(TAG, "startEnumeration failed", e);
- }
- return 0;
- }
-
- @Override
- public int stop(boolean initiatedByClient) {
- if (mAlreadyCancelled) {
- Slog.w(TAG, "stopEnumerate: already cancelled!");
- return 0;
- }
- IBiometricsFingerprint daemon = getFingerprintDaemon();
- if (daemon == null) {
- Slog.w(TAG, "stopEnumeration: no fingerprint HAL!");
- return ERROR_ESRCH;
- }
- try {
- final int result = daemon.cancel();
- if (result != 0) {
- Slog.w(TAG, "stop enumeration failed, result=" + result);
- return result;
- }
- } catch (RemoteException e) {
- Slog.e(TAG, "stopEnumeration failed", e);
- return ERROR_ESRCH;
- }
-
- // We don't actually stop enumerate, but inform the client that the cancel operation
- // succeeded so we can start the next operation.
- if (initiatedByClient) {
- onError(FingerprintManager.FINGERPRINT_ERROR_CANCELED, 0 /* vendorCode */);
- }
- mAlreadyCancelled = true;
- return 0; // success
- }
-
- @Override
- public boolean onEnumerationResult(int fingerId, int groupId, int remaining) {
- IFingerprintServiceReceiver receiver = getReceiver();
- if (receiver == null)
- return true; // client not listening
- try {
- receiver.onEnumerated(getHalDeviceId(), fingerId, groupId, remaining);
- } catch (RemoteException e) {
- Slog.w(TAG, "Failed to notify enumerated:", e);
- }
- return remaining == 0;
- }
-
- @Override
- public boolean onAuthenticated(int fingerId, int groupId) {
- if (DEBUG) Slog.w(TAG, "onAuthenticated() called for enumerate!");
- return true; // Invalid for Enumerate.
- }
-
- @Override
- public boolean onEnrollResult(int fingerId, int groupId, int rem) {
- if (DEBUG) Slog.w(TAG, "onEnrollResult() called for enumerate!");
- return true; // Invalid for Enumerate.
- }
-
- @Override
- public boolean onRemoved(int fingerId, int groupId, int remaining) {
- if (DEBUG) Slog.w(TAG, "onRemoved() called for enumerate!");
- return true; // Invalid for Enumerate.
- }
-}
diff --git a/services/core/java/com/android/server/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java
deleted file mode 100644
index 8e77820..0000000
--- a/services/core/java/com/android/server/fingerprint/FingerprintService.java
+++ /dev/null
@@ -1,1595 +0,0 @@
-/*
- * 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.
- */
-
-package com.android.server.fingerprint;
-
-import static android.Manifest.permission.INTERACT_ACROSS_USERS;
-import static android.Manifest.permission.MANAGE_FINGERPRINT;
-import static android.Manifest.permission.RESET_FINGERPRINT_LOCKOUT;
-import static android.Manifest.permission.USE_BIOMETRIC;
-import static android.Manifest.permission.USE_FINGERPRINT;
-import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE;
-
-import android.app.ActivityManager;
-import android.app.ActivityManager.RunningAppProcessInfo;
-import android.app.ActivityTaskManager;
-import android.app.AlarmManager;
-import android.app.AppOpsManager;
-import android.app.IActivityManager;
-import android.app.IActivityTaskManager;
-import android.app.PendingIntent;
-import android.app.SynchronousUserSwitchObserver;
-import android.app.TaskStackListener;
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.PackageManager;
-import android.content.pm.UserInfo;
-import android.hardware.biometrics.IBiometricPromptReceiver;
-import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
-import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprintClientCallback;
-import android.hardware.fingerprint.Fingerprint;
-import android.hardware.fingerprint.FingerprintManager;
-import android.hardware.fingerprint.IFingerprintClientActiveCallback;
-import android.hardware.fingerprint.IFingerprintService;
-import android.hardware.fingerprint.IFingerprintServiceLockoutResetCallback;
-import android.hardware.fingerprint.IFingerprintServiceReceiver;
-import android.os.Binder;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.DeadObjectException;
-import android.os.Environment;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.IHwBinder;
-import android.os.IRemoteCallback;
-import android.os.PowerManager;
-import android.os.PowerManager.WakeLock;
-import android.os.RemoteException;
-import android.os.SELinux;
-import android.os.ServiceManager;
-import android.os.SystemClock;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.security.KeyStore;
-import android.util.Slog;
-import android.util.SparseBooleanArray;
-import android.util.SparseIntArray;
-import android.util.proto.ProtoOutputStream;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.statusbar.IStatusBarService;
-import com.android.internal.util.DumpUtils;
-import com.android.server.SystemServerInitThreadPool;
-import com.android.server.SystemService;
-
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import java.io.File;
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.CopyOnWriteArrayList;
-
-/**
- * A service to manage multiple clients that want to access the fingerprint HAL API.
- * The service is responsible for maintaining a list of clients and dispatching all
- * fingerprint-related events.
- *
- * @hide
- */
-public class FingerprintService extends SystemService implements IHwBinder.DeathRecipient {
- static final String TAG = "FingerprintService";
- static final boolean DEBUG = true;
- private static final boolean CLEANUP_UNUSED_FP = true;
- private static final String FP_DATA_DIR = "fpdata";
- private static final int MSG_USER_SWITCHING = 10;
- private static final String ACTION_LOCKOUT_RESET =
- "com.android.server.fingerprint.ACTION_LOCKOUT_RESET";
- private static final String KEY_LOCKOUT_RESET_USER = "lockout_reset_user";
-
- private class PerformanceStats {
- int accept; // number of accepted fingerprints
- int reject; // number of rejected fingerprints
- int acquire; // total number of acquisitions. Should be >= accept+reject due to poor image
- // acquisition in some cases (too fast, too slow, dirty sensor, etc.)
- int lockout; // total number of lockouts
- int permanentLockout; // total number of permanent lockouts
- }
-
- private final ArrayList<FingerprintServiceLockoutResetMonitor> mLockoutMonitors =
- new ArrayList<>();
- private final CopyOnWriteArrayList<IFingerprintClientActiveCallback> mClientActiveCallbacks =
- new CopyOnWriteArrayList<>();
- private final Map<Integer, Long> mAuthenticatorIds =
- Collections.synchronizedMap(new HashMap<>());
- private final AppOpsManager mAppOps;
- private static final long FAIL_LOCKOUT_TIMEOUT_MS = 30*1000;
- private static final int MAX_FAILED_ATTEMPTS_LOCKOUT_TIMED = 5;
- private static final int MAX_FAILED_ATTEMPTS_LOCKOUT_PERMANENT = 20;
-
- private static final long CANCEL_TIMEOUT_LIMIT = 3000; // max wait for onCancel() from HAL,in ms
- private final String mKeyguardPackage;
- private int mCurrentUserId = UserHandle.USER_NULL;
- private final FingerprintUtils mFingerprintUtils = FingerprintUtils.getInstance();
- private Context mContext;
- private long mHalDeviceId;
- private SparseBooleanArray mTimedLockoutCleared;
- private SparseIntArray mFailedAttempts;
- @GuardedBy("this")
- private IBiometricsFingerprint mDaemon;
- private IStatusBarService mStatusBarService;
- private final IActivityManager mActivityManager;
- private final IActivityTaskManager mActivityTaskManager;
- private final PowerManager mPowerManager;
- private final AlarmManager mAlarmManager;
- private final UserManager mUserManager;
- private ClientMonitor mCurrentClient;
- private ClientMonitor mPendingClient;
- private PerformanceStats mPerformanceStats;
-
- private IBinder mToken = new Binder(); // used for internal FingerprintService enumeration
- private ArrayList<UserFingerprint> mUnknownFingerprints = new ArrayList<>(); // hw fingerprints
-
- private class UserFingerprint {
- Fingerprint f;
- int userId;
- public UserFingerprint(Fingerprint f, int userId) {
- this.f = f;
- this.userId = userId;
- }
- }
-
- // Normal fingerprint authentications are tracked by mPerformanceMap.
- private HashMap<Integer, PerformanceStats> mPerformanceMap = new HashMap<>();
-
- // Transactions that make use of CryptoObjects are tracked by mCryptoPerformaceMap.
- private HashMap<Integer, PerformanceStats> mCryptoPerformanceMap = new HashMap<>();
-
- private Handler mHandler = new Handler() {
- @Override
- public void handleMessage(android.os.Message msg) {
- switch (msg.what) {
- case MSG_USER_SWITCHING:
- handleUserSwitching(msg.arg1);
- break;
-
- default:
- Slog.w(TAG, "Unknown message:" + msg.what);
- }
- }
- };
-
- private final BroadcastReceiver mLockoutReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (ACTION_LOCKOUT_RESET.equals(intent.getAction())) {
- final int user = intent.getIntExtra(KEY_LOCKOUT_RESET_USER, 0);
- resetFailedAttemptsForUser(false /* clearAttemptCounter */, user);
- }
- }
- };
-
- private final Runnable mResetFailedAttemptsForCurrentUserRunnable = new Runnable() {
- @Override
- public void run() {
- resetFailedAttemptsForUser(true /* clearAttemptCounter */,
- ActivityManager.getCurrentUser());
- }
- };
-
- private final Runnable mResetClientState = new Runnable() {
- @Override
- public void run() {
- // Warning: if we get here, the driver never confirmed our call to cancel the current
- // operation (authenticate, enroll, remove, enumerate, etc), which is
- // really bad. The result will be a 3-second delay in starting each new client.
- // If you see this on a device, make certain the driver notifies with
- // {@link FingerprintManager#FINGERPRINT_ERROR_CANCEL} in response to cancel()
- // once it has successfully switched to the IDLE state in the fingerprint HAL.
- // Additionally,{@link FingerprintManager#FINGERPRINT_ERROR_CANCEL} should only be sent
- // in response to an actual cancel() call.
- Slog.w(TAG, "Client "
- + (mCurrentClient != null ? mCurrentClient.getOwnerString() : "null")
- + " failed to respond to cancel, starting client "
- + (mPendingClient != null ? mPendingClient.getOwnerString() : "null"));
-
- mCurrentClient = null;
- startClient(mPendingClient, false);
- }
- };
-
- private final TaskStackListener mTaskStackListener = new TaskStackListener() {
- @Override
- public void onTaskStackChanged() {
- try {
- if (!(mCurrentClient instanceof AuthenticationClient)) {
- return;
- }
- final String currentClient = mCurrentClient.getOwnerString();
- if (isKeyguard(currentClient)) {
- return; // Keyguard is always allowed
- }
- List<ActivityManager.RunningTaskInfo> runningTasks =
- mActivityTaskManager.getTasks(1);
- if (!runningTasks.isEmpty()) {
- final String topPackage = runningTasks.get(0).topActivity.getPackageName();
- if (!topPackage.contentEquals(currentClient)) {
- Slog.e(TAG, "Stopping background authentication, top: " + topPackage
- + " currentClient: " + currentClient);
- mCurrentClient.stop(false /* initiatedByClient */);
- }
- }
- } catch (RemoteException e) {
- Slog.e(TAG, "Unable to get running tasks", e);
- }
- }
- };
-
- public FingerprintService(Context context) {
- super(context);
- mContext = context;
- mKeyguardPackage = ComponentName.unflattenFromString(context.getResources().getString(
- com.android.internal.R.string.config_keyguardComponent)).getPackageName();
- mAppOps = context.getSystemService(AppOpsManager.class);
- mPowerManager = mContext.getSystemService(PowerManager.class);
- mAlarmManager = mContext.getSystemService(AlarmManager.class);
- mContext.registerReceiver(mLockoutReceiver, new IntentFilter(ACTION_LOCKOUT_RESET),
- RESET_FINGERPRINT_LOCKOUT, null /* handler */);
- mUserManager = UserManager.get(mContext);
- mTimedLockoutCleared = new SparseBooleanArray();
- mFailedAttempts = new SparseIntArray();
- mStatusBarService = IStatusBarService.Stub.asInterface(
- ServiceManager.getService(Context.STATUS_BAR_SERVICE));
- mActivityManager = ((ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE))
- .getService();
- mActivityTaskManager = ((ActivityTaskManager) context.getSystemService(
- Context.ACTIVITY_TASK_SERVICE)).getService();
- }
-
- @Override
- public void serviceDied(long cookie) {
- Slog.v(TAG, "fingerprint HAL died");
- MetricsLogger.count(mContext, "fingerprintd_died", 1);
- handleError(mHalDeviceId, FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE,
- 0 /*vendorCode */);
- }
-
- public synchronized IBiometricsFingerprint getFingerprintDaemon() {
- if (mDaemon == null) {
- Slog.v(TAG, "mDaemon was null, reconnect to fingerprint");
- try {
- mDaemon = IBiometricsFingerprint.getService();
- } catch (java.util.NoSuchElementException e) {
- // Service doesn't exist or cannot be opened. Logged below.
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to get biometric interface", e);
- }
- if (mDaemon == null) {
- Slog.w(TAG, "fingerprint HIDL not available");
- return null;
- }
-
- mDaemon.asBinder().linkToDeath(this, 0);
-
- try {
- mHalDeviceId = mDaemon.setNotify(mDaemonCallback);
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to open fingerprint HAL", e);
- mDaemon = null; // try again later!
- }
-
- if (DEBUG) Slog.v(TAG, "Fingerprint HAL id: " + mHalDeviceId);
- if (mHalDeviceId != 0) {
- loadAuthenticatorIds();
- updateActiveGroup(ActivityManager.getCurrentUser(), null);
- doFingerprintCleanupForUser(ActivityManager.getCurrentUser());
- } else {
- Slog.w(TAG, "Failed to open Fingerprint HAL!");
- MetricsLogger.count(mContext, "fingerprintd_openhal_error", 1);
- mDaemon = null;
- }
- }
- return mDaemon;
- }
-
- /** Populates existing authenticator ids. To be used only during the start of the service. */
- private void loadAuthenticatorIds() {
- // This operation can be expensive, so keep track of the elapsed time. Might need to move to
- // background if it takes too long.
- long t = System.currentTimeMillis();
- mAuthenticatorIds.clear();
- for (UserInfo user : UserManager.get(mContext).getUsers(true /* excludeDying */)) {
- int userId = getUserOrWorkProfileId(null, user.id);
- if (!mAuthenticatorIds.containsKey(userId)) {
- updateActiveGroup(userId, null);
- }
- }
-
- t = System.currentTimeMillis() - t;
- if (t > 1000) {
- Slog.w(TAG, "loadAuthenticatorIds() taking too long: " + t + "ms");
- }
- }
-
- /**
- * This method should be called upon connection to the daemon, and when user switches.
- * @param userId
- */
- private void doFingerprintCleanupForUser(int userId) {
- if (CLEANUP_UNUSED_FP) {
- enumerateUser(userId);
- }
- }
-
- private void clearEnumerateState() {
- if (DEBUG) Slog.v(TAG, "clearEnumerateState()");
- mUnknownFingerprints.clear();
- }
-
- private void enumerateUser(int userId) {
- if (DEBUG) Slog.v(TAG, "Enumerating user(" + userId + ")");
- boolean restricted = !hasPermission(MANAGE_FINGERPRINT);
- startEnumerate(mToken, userId, null, restricted, true /* internal */);
- }
-
- // Remove unknown fingerprints from hardware
- private void cleanupUnknownFingerprints() {
- if (!mUnknownFingerprints.isEmpty()) {
- UserFingerprint uf = mUnknownFingerprints.get(0);
- mUnknownFingerprints.remove(uf);
- boolean restricted = !hasPermission(MANAGE_FINGERPRINT);
- startRemove(mToken, uf.f.getFingerId(), uf.f.getGroupId(), uf.userId, null,
- restricted, true /* internal */);
- } else {
- clearEnumerateState();
- }
- }
-
- protected void handleEnumerate(long deviceId, int fingerId, int groupId, int remaining) {
- ClientMonitor client = mCurrentClient;
-
- if ( !(client instanceof InternalRemovalClient) && !(client instanceof EnumerateClient) ) {
- return;
- }
- client.onEnumerationResult(fingerId, groupId, remaining);
-
- // All fingerprints in hardware for this user were enumerated
- if (remaining == 0) {
- if (client instanceof InternalEnumerateClient) {
- List<Fingerprint> unknownFingerprints =
- ((InternalEnumerateClient) client).getUnknownFingerprints();
-
- if (!unknownFingerprints.isEmpty()) {
- Slog.w(TAG, "Adding " + unknownFingerprints.size() +
- " fingerprints for deletion");
- }
- for (Fingerprint f : unknownFingerprints) {
- mUnknownFingerprints.add(new UserFingerprint(f, client.getTargetUserId()));
- }
- removeClient(client);
- cleanupUnknownFingerprints();
- } else {
- removeClient(client);
- }
- }
- }
-
- protected void handleError(long deviceId, int error, int vendorCode) {
- ClientMonitor client = mCurrentClient;
- if (client instanceof InternalRemovalClient || client instanceof InternalEnumerateClient) {
- clearEnumerateState();
- }
- if (client != null && client.onError(error, vendorCode)) {
- removeClient(client);
- }
-
- if (DEBUG) Slog.v(TAG, "handleError(client="
- + (client != null ? client.getOwnerString() : "null") + ", error = " + error + ")");
- // This is the magic code that starts the next client when the old client finishes.
- if (error == FingerprintManager.FINGERPRINT_ERROR_CANCELED) {
- mHandler.removeCallbacks(mResetClientState);
- if (mPendingClient != null) {
- if (DEBUG) Slog.v(TAG, "start pending client " + mPendingClient.getOwnerString());
- startClient(mPendingClient, false);
- mPendingClient = null;
- }
- } else if (error == FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE) {
- // If we get HW_UNAVAILABLE, try to connect again later...
- Slog.w(TAG, "Got ERROR_HW_UNAVAILABLE; try reconnecting next client.");
- synchronized (this) {
- mDaemon = null;
- mHalDeviceId = 0;
- mCurrentUserId = UserHandle.USER_NULL;
- }
- }
- }
-
- protected void handleRemoved(long deviceId, int fingerId, int groupId, int remaining) {
- if (DEBUG) Slog.w(TAG, "Removed: fid=" + fingerId
- + ", gid=" + groupId
- + ", dev=" + deviceId
- + ", rem=" + remaining);
-
- ClientMonitor client = mCurrentClient;
- if (client != null && client.onRemoved(fingerId, groupId, remaining)) {
- removeClient(client);
- // When the last fingerprint of a group is removed, update the authenticator id
- if (!hasEnrolledFingerprints(groupId)) {
- updateActiveGroup(groupId, null);
- }
- }
- if (client instanceof InternalRemovalClient && !mUnknownFingerprints.isEmpty()) {
- cleanupUnknownFingerprints();
- } else if (client instanceof InternalRemovalClient){
- clearEnumerateState();
- }
- }
-
- protected void handleAuthenticated(long deviceId, int fingerId, int groupId,
- ArrayList<Byte> token) {
- ClientMonitor client = mCurrentClient;
- if (fingerId != 0) {
- // Ugh...
- final byte[] byteToken = new byte[token.size()];
- for (int i = 0; i < token.size(); i++) {
- byteToken[i] = token.get(i);
- }
- // Send to Keystore
- KeyStore.getInstance().addAuthToken(byteToken);
- }
- if (client != null && client.onAuthenticated(fingerId, groupId)) {
- removeClient(client);
- }
- if (fingerId != 0) {
- mPerformanceStats.accept++;
- } else {
- mPerformanceStats.reject++;
- }
- }
-
- protected void handleAcquired(long deviceId, int acquiredInfo, int vendorCode) {
- ClientMonitor client = mCurrentClient;
- if (client != null && client.onAcquired(acquiredInfo, vendorCode)) {
- removeClient(client);
- }
- if (mPerformanceStats != null && getLockoutMode() == AuthenticationClient.LOCKOUT_NONE
- && client instanceof AuthenticationClient) {
- // ignore enrollment acquisitions or acquisitions when we're locked out
- mPerformanceStats.acquire++;
- }
- }
-
- protected void handleEnrollResult(long deviceId, int fingerId, int groupId, int remaining) {
- ClientMonitor client = mCurrentClient;
- if (client != null && client.onEnrollResult(fingerId, groupId, remaining)) {
- removeClient(client);
- // When enrollment finishes, update this group's authenticator id, as the HAL has
- // already generated a new authenticator id when the new fingerprint is enrolled.
- updateActiveGroup(groupId, null);
- }
- }
-
- private void userActivity() {
- long now = SystemClock.uptimeMillis();
- mPowerManager.userActivity(now, PowerManager.USER_ACTIVITY_EVENT_TOUCH, 0);
- }
-
- void handleUserSwitching(int userId) {
- if (mCurrentClient instanceof InternalRemovalClient
- || mCurrentClient instanceof InternalEnumerateClient) {
- Slog.w(TAG, "User switched while performing cleanup");
- removeClient(mCurrentClient);
- clearEnumerateState();
- }
- updateActiveGroup(userId, null);
- doFingerprintCleanupForUser(userId);
- }
-
- private void removeClient(ClientMonitor client) {
- if (client != null) {
- client.destroy();
- if (client != mCurrentClient && mCurrentClient != null) {
- Slog.w(TAG, "Unexpected client: " + client.getOwnerString() + "expected: "
- + mCurrentClient != null ? mCurrentClient.getOwnerString() : "null");
- }
- }
- if (mCurrentClient != null) {
- if (DEBUG) Slog.v(TAG, "Done with client: " + client.getOwnerString());
- mCurrentClient = null;
- }
- if (mPendingClient == null) {
- notifyClientActiveCallbacks(false);
- }
- }
-
- private int getLockoutMode() {
- final int currentUser = ActivityManager.getCurrentUser();
- final int failedAttempts = mFailedAttempts.get(currentUser, 0);
- if (failedAttempts >= MAX_FAILED_ATTEMPTS_LOCKOUT_PERMANENT) {
- return AuthenticationClient.LOCKOUT_PERMANENT;
- } else if (failedAttempts > 0 &&
- mTimedLockoutCleared.get(currentUser, false) == false
- && (failedAttempts % MAX_FAILED_ATTEMPTS_LOCKOUT_TIMED == 0)) {
- return AuthenticationClient.LOCKOUT_TIMED;
- }
- return AuthenticationClient.LOCKOUT_NONE;
- }
-
- private void scheduleLockoutResetForUser(int userId) {
- mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP,
- SystemClock.elapsedRealtime() + FAIL_LOCKOUT_TIMEOUT_MS,
- getLockoutResetIntentForUser(userId));
- }
-
- private void cancelLockoutResetForUser(int userId) {
- mAlarmManager.cancel(getLockoutResetIntentForUser(userId));
- }
-
- private PendingIntent getLockoutResetIntentForUser(int userId) {
- return PendingIntent.getBroadcast(mContext, userId,
- new Intent(ACTION_LOCKOUT_RESET).putExtra(KEY_LOCKOUT_RESET_USER, userId),
- PendingIntent.FLAG_UPDATE_CURRENT);
- }
-
- public long startPreEnroll(IBinder token) {
- IBiometricsFingerprint daemon = getFingerprintDaemon();
- if (daemon == null) {
- Slog.w(TAG, "startPreEnroll: no fingerprint HAL!");
- return 0;
- }
- try {
- return daemon.preEnroll();
- } catch (RemoteException e) {
- Slog.e(TAG, "startPreEnroll failed", e);
- }
- return 0;
- }
-
- public int startPostEnroll(IBinder token) {
- IBiometricsFingerprint daemon = getFingerprintDaemon();
- if (daemon == null) {
- Slog.w(TAG, "startPostEnroll: no fingerprint HAL!");
- return 0;
- }
- try {
- return daemon.postEnroll();
- } catch (RemoteException e) {
- Slog.e(TAG, "startPostEnroll failed", e);
- }
- return 0;
- }
-
- /**
- * Calls fingerprint HAL to switch states to the new task. If there's already a current task,
- * it calls cancel() and sets mPendingClient to begin when the current task finishes
- * ({@link FingerprintManager#FINGERPRINT_ERROR_CANCELED}).
- * @param newClient the new client that wants to connect
- * @param initiatedByClient true for authenticate, remove and enroll
- */
- private void startClient(ClientMonitor newClient, boolean initiatedByClient) {
- ClientMonitor currentClient = mCurrentClient;
- if (currentClient != null) {
- if (DEBUG) Slog.v(TAG, "request stop current client " + currentClient.getOwnerString());
- if (currentClient instanceof InternalEnumerateClient ||
- currentClient instanceof InternalRemovalClient) {
- // This condition means we're currently running internal diagnostics to
- // remove extra fingerprints in the hardware and/or the software
- // TODO: design an escape hatch in case client never finishes
- if (newClient != null) {
- Slog.w(TAG, "Internal cleanup in progress but trying to start client "
- + newClient.getClass().getSuperclass().getSimpleName()
- + "(" + newClient.getOwnerString() + ")"
- + ", initiatedByClient = " + initiatedByClient);
- }
- }
- else {
- currentClient.stop(initiatedByClient);
- }
- mPendingClient = newClient;
- mHandler.removeCallbacks(mResetClientState);
- mHandler.postDelayed(mResetClientState, CANCEL_TIMEOUT_LIMIT);
- } else if (newClient != null) {
- mCurrentClient = newClient;
- if (DEBUG) Slog.v(TAG, "starting client "
- + newClient.getClass().getSuperclass().getSimpleName()
- + "(" + newClient.getOwnerString() + ")"
- + ", initiatedByClient = " + initiatedByClient);
- notifyClientActiveCallbacks(true);
-
- newClient.start();
- }
- }
-
- void startRemove(IBinder token, int fingerId, int groupId, int userId,
- IFingerprintServiceReceiver receiver, boolean restricted, boolean internal) {
- if (token == null) {
- Slog.w(TAG, "startRemove: token is null");
- return;
- }
- if (receiver == null) {
- Slog.w(TAG, "startRemove: receiver is null");
- return;
- }
-
- IBiometricsFingerprint daemon = getFingerprintDaemon();
- if (daemon == null) {
- Slog.w(TAG, "startRemove: no fingerprint HAL!");
- return;
- }
-
- if (internal) {
- Context context = getContext();
- InternalRemovalClient client = new InternalRemovalClient(context, mHalDeviceId,
- token, receiver, fingerId, groupId, userId, restricted,
- context.getOpPackageName()) {
- @Override
- public void notifyUserActivity() {
-
- }
- @Override
- public IBiometricsFingerprint getFingerprintDaemon() {
- return FingerprintService.this.getFingerprintDaemon();
- }
- };
- startClient(client, true);
- }
- else {
- RemovalClient client = new RemovalClient(getContext(), mHalDeviceId, token,
- receiver, fingerId, groupId, userId, restricted, token.toString()) {
- @Override
- public void notifyUserActivity() {
- FingerprintService.this.userActivity();
- }
-
- @Override
- public IBiometricsFingerprint getFingerprintDaemon() {
- return FingerprintService.this.getFingerprintDaemon();
- }
- };
- startClient(client, true);
- }
- }
-
- void startEnumerate(IBinder token, int userId,
- IFingerprintServiceReceiver receiver, boolean restricted, boolean internal) {
- IBiometricsFingerprint daemon = getFingerprintDaemon();
- if (daemon == null) {
- Slog.w(TAG, "startEnumerate: no fingerprint HAL!");
- return;
- }
- if (internal) {
- List<Fingerprint> enrolledList = getEnrolledFingerprints(userId);
- Context context = getContext();
- InternalEnumerateClient client = new InternalEnumerateClient(context, mHalDeviceId,
- token, receiver, userId, userId, restricted, context.getOpPackageName(),
- enrolledList) {
- @Override
- public void notifyUserActivity() {
-
- }
-
- @Override
- public IBiometricsFingerprint getFingerprintDaemon() {
- return FingerprintService.this.getFingerprintDaemon();
- }
- };
- startClient(client, true);
- }
- else {
- EnumerateClient client = new EnumerateClient(getContext(), mHalDeviceId, token,
- receiver, userId, userId, restricted, token.toString()) {
- @Override
- public void notifyUserActivity() {
- FingerprintService.this.userActivity();
- }
-
- @Override
- public IBiometricsFingerprint getFingerprintDaemon() {
- return FingerprintService.this.getFingerprintDaemon();
- }
- };
- startClient(client, true);
- }
- }
-
- public List<Fingerprint> getEnrolledFingerprints(int userId) {
- return mFingerprintUtils.getFingerprintsForUser(mContext, userId);
- }
-
- public boolean hasEnrolledFingerprints(int userId) {
- if (userId != UserHandle.getCallingUserId()) {
- checkPermission(INTERACT_ACROSS_USERS);
- }
- return mFingerprintUtils.getFingerprintsForUser(mContext, userId).size() > 0;
- }
-
- boolean hasPermission(String permission) {
- return getContext().checkCallingOrSelfPermission(permission)
- == PackageManager.PERMISSION_GRANTED;
- }
-
- void checkPermission(String permission) {
- getContext().enforceCallingOrSelfPermission(permission,
- "Must have " + permission + " permission.");
- }
-
- int getEffectiveUserId(int userId) {
- UserManager um = UserManager.get(mContext);
- if (um != null) {
- final long callingIdentity = Binder.clearCallingIdentity();
- userId = um.getCredentialOwnerProfile(userId);
- Binder.restoreCallingIdentity(callingIdentity);
- } else {
- Slog.e(TAG, "Unable to acquire UserManager");
- }
- return userId;
- }
-
- boolean isCurrentUserOrProfile(int userId) {
- UserManager um = UserManager.get(mContext);
- if (um == null) {
- Slog.e(TAG, "Unable to acquire UserManager");
- return false;
- }
-
- final long token = Binder.clearCallingIdentity();
- try {
- // Allow current user or profiles of the current user...
- for (int profileId : um.getEnabledProfileIds(ActivityManager.getCurrentUser())) {
- if (profileId == userId) {
- return true;
- }
- }
- } finally {
- Binder.restoreCallingIdentity(token);
- }
-
- return false;
- }
-
- private boolean isForegroundActivity(int uid, int pid) {
- try {
- List<RunningAppProcessInfo> procs =
- ActivityManager.getService().getRunningAppProcesses();
- int N = procs.size();
- for (int i = 0; i < N; i++) {
- RunningAppProcessInfo proc = procs.get(i);
- if (proc.pid == pid && proc.uid == uid
- && proc.importance <= IMPORTANCE_FOREGROUND_SERVICE) {
- return true;
- }
- }
- } catch (RemoteException e) {
- Slog.w(TAG, "am.getRunningAppProcesses() failed");
- }
- return false;
- }
-
- /**
- * @param opPackageName name of package for caller
- * @param requireForeground only allow this call while app is in the foreground
- * @return true if caller can use fingerprint API
- */
- private boolean canUseFingerprint(String opPackageName, boolean requireForeground, int uid,
- int pid, int userId) {
- if (getContext().checkCallingPermission(USE_FINGERPRINT)
- != PackageManager.PERMISSION_GRANTED) {
- checkPermission(USE_BIOMETRIC);
- }
-
- if (isKeyguard(opPackageName)) {
- return true; // Keyguard is always allowed
- }
- if (!isCurrentUserOrProfile(userId)) {
- Slog.w(TAG,"Rejecting " + opPackageName + " ; not a current user or profile");
- return false;
- }
- if (mAppOps.noteOp(AppOpsManager.OP_USE_FINGERPRINT, uid, opPackageName)
- != AppOpsManager.MODE_ALLOWED) {
- Slog.w(TAG, "Rejecting " + opPackageName + " ; permission denied");
- return false;
- }
- if (requireForeground && !(isForegroundActivity(uid, pid) || currentClient(opPackageName))){
- Slog.w(TAG, "Rejecting " + opPackageName + " ; not in foreground");
- return false;
- }
- return true;
- }
-
- /**
- * @param opPackageName package of the caller
- * @return true if this is the same client currently using fingerprint
- */
- private boolean currentClient(String opPackageName) {
- return mCurrentClient != null && mCurrentClient.getOwnerString().equals(opPackageName);
- }
-
- /**
- * @param clientPackage
- * @return true if this is keyguard package
- */
- private boolean isKeyguard(String clientPackage) {
- return mKeyguardPackage.equals(clientPackage);
- }
-
- private void addLockoutResetMonitor(FingerprintServiceLockoutResetMonitor monitor) {
- if (!mLockoutMonitors.contains(monitor)) {
- mLockoutMonitors.add(monitor);
- }
- }
-
- private void removeLockoutResetCallback(
- FingerprintServiceLockoutResetMonitor monitor) {
- mLockoutMonitors.remove(monitor);
- }
-
- private void notifyLockoutResetMonitors() {
- for (int i = 0; i < mLockoutMonitors.size(); i++) {
- mLockoutMonitors.get(i).sendLockoutReset();
- }
- }
-
- private void notifyClientActiveCallbacks(boolean isActive) {
- List<IFingerprintClientActiveCallback> callbacks = mClientActiveCallbacks;
- for (int i = 0; i < callbacks.size(); i++) {
- try {
- callbacks.get(i).onClientActiveChanged(isActive);
- } catch (RemoteException re) {
- // If the remote is dead, stop notifying it
- mClientActiveCallbacks.remove(callbacks.get(i));
- }
- }
- }
-
- private void startAuthentication(IBinder token, long opId, int callingUserId, int groupId,
- IFingerprintServiceReceiver receiver, int flags, boolean restricted,
- String opPackageName, Bundle bundle, IBiometricPromptReceiver dialogReceiver) {
- updateActiveGroup(groupId, opPackageName);
-
- if (DEBUG) Slog.v(TAG, "startAuthentication(" + opPackageName + ")");
-
- AuthenticationClient client = new AuthenticationClient(getContext(), mHalDeviceId, token,
- receiver, mCurrentUserId, groupId, opId, restricted, opPackageName, bundle,
- dialogReceiver, mStatusBarService) {
- @Override
- public void onStart() {
- try {
- mActivityTaskManager.registerTaskStackListener(mTaskStackListener);
- } catch (RemoteException e) {
- Slog.e(TAG, "Could not register task stack listener", e);
- }
- }
-
- @Override
- public void onStop() {
- try {
- mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
- } catch (RemoteException e) {
- Slog.e(TAG, "Could not unregister task stack listener", e);
- }
- }
-
- @Override
- public int handleFailedAttempt() {
- final int currentUser = ActivityManager.getCurrentUser();
- mFailedAttempts.put(currentUser, mFailedAttempts.get(currentUser, 0) + 1);
- mTimedLockoutCleared.put(ActivityManager.getCurrentUser(), false);
- final int lockoutMode = getLockoutMode();
- if (lockoutMode == AuthenticationClient.LOCKOUT_PERMANENT) {
- mPerformanceStats.permanentLockout++;
- } else if (lockoutMode == AuthenticationClient.LOCKOUT_TIMED) {
- mPerformanceStats.lockout++;
- }
-
- // Failing multiple times will continue to push out the lockout time
- if (lockoutMode != AuthenticationClient.LOCKOUT_NONE) {
- scheduleLockoutResetForUser(currentUser);
- return lockoutMode;
- }
- return AuthenticationClient.LOCKOUT_NONE;
- }
-
- @Override
- public void resetFailedAttempts() {
- FingerprintService.this.resetFailedAttemptsForUser(true /* clearAttemptCounter */,
- ActivityManager.getCurrentUser());
- }
-
- @Override
- public void notifyUserActivity() {
- FingerprintService.this.userActivity();
- }
-
- @Override
- public IBiometricsFingerprint getFingerprintDaemon() {
- return FingerprintService.this.getFingerprintDaemon();
- }
- };
-
- int lockoutMode = getLockoutMode();
- if (lockoutMode != AuthenticationClient.LOCKOUT_NONE) {
- Slog.v(TAG, "In lockout mode(" + lockoutMode +
- ") ; disallowing authentication");
- int errorCode = lockoutMode == AuthenticationClient.LOCKOUT_TIMED ?
- FingerprintManager.FINGERPRINT_ERROR_LOCKOUT :
- FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT;
- if (!client.onError(errorCode, 0 /* vendorCode */)) {
- Slog.w(TAG, "Cannot send permanent lockout message to client");
- }
- return;
- }
- startClient(client, true /* initiatedByClient */);
- }
-
- private void startEnrollment(IBinder token, byte [] cryptoToken, int userId,
- IFingerprintServiceReceiver receiver, int flags, boolean restricted,
- String opPackageName) {
- updateActiveGroup(userId, opPackageName);
-
- final int groupId = userId; // default group for fingerprint enrollment
-
- EnrollClient client = new EnrollClient(getContext(), mHalDeviceId, token, receiver,
- userId, groupId, cryptoToken, restricted, opPackageName) {
-
- @Override
- public IBiometricsFingerprint getFingerprintDaemon() {
- return FingerprintService.this.getFingerprintDaemon();
- }
-
- @Override
- public void notifyUserActivity() {
- FingerprintService.this.userActivity();
- }
- };
- startClient(client, true /* initiatedByClient */);
- }
-
- // attempt counter should only be cleared when Keyguard goes away or when
- // a fingerprint is successfully authenticated
- protected void resetFailedAttemptsForUser(boolean clearAttemptCounter, int userId) {
- if (DEBUG && getLockoutMode() != AuthenticationClient.LOCKOUT_NONE) {
- Slog.v(TAG, "Reset fingerprint lockout, clearAttemptCounter=" + clearAttemptCounter);
- }
- if (clearAttemptCounter) {
- mFailedAttempts.put(userId, 0);
- }
- mTimedLockoutCleared.put(userId, true);
- // If we're asked to reset failed attempts externally (i.e. from Keyguard),
- // the alarm might still be pending; remove it.
- cancelLockoutResetForUser(userId);
- notifyLockoutResetMonitors();
- }
-
- private class FingerprintServiceLockoutResetMonitor implements IBinder.DeathRecipient {
-
- private static final long WAKELOCK_TIMEOUT_MS = 2000;
- private final IFingerprintServiceLockoutResetCallback mCallback;
- private final WakeLock mWakeLock;
-
- public FingerprintServiceLockoutResetMonitor(
- IFingerprintServiceLockoutResetCallback callback) {
- mCallback = callback;
- mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
- "lockout reset callback");
- try {
- mCallback.asBinder().linkToDeath(FingerprintServiceLockoutResetMonitor.this, 0);
- } catch (RemoteException e) {
- Slog.w(TAG, "caught remote exception in linkToDeath", e);
- }
- }
-
- public void sendLockoutReset() {
- if (mCallback != null) {
- try {
- mWakeLock.acquire(WAKELOCK_TIMEOUT_MS);
- mCallback.onLockoutReset(mHalDeviceId, new IRemoteCallback.Stub() {
-
- @Override
- public void sendResult(Bundle data) throws RemoteException {
- releaseWakelock();
- }
- });
- } catch (DeadObjectException e) {
- Slog.w(TAG, "Death object while invoking onLockoutReset: ", e);
- mHandler.post(mRemoveCallbackRunnable);
- } catch (RemoteException e) {
- Slog.w(TAG, "Failed to invoke onLockoutReset: ", e);
- releaseWakelock();
- }
- }
- }
-
- private final Runnable mRemoveCallbackRunnable = new Runnable() {
- @Override
- public void run() {
- releaseWakelock();
- removeLockoutResetCallback(FingerprintServiceLockoutResetMonitor.this);
- }
- };
-
- @Override
- public void binderDied() {
- Slog.e(TAG, "Lockout reset callback binder died");
- mHandler.post(mRemoveCallbackRunnable);
- }
-
- private void releaseWakelock() {
- if (mWakeLock.isHeld()) {
- mWakeLock.release();
- }
- }
- }
-
- private IBiometricsFingerprintClientCallback mDaemonCallback =
- new IBiometricsFingerprintClientCallback.Stub() {
-
- @Override
- public void onEnrollResult(final long deviceId, final int fingerId, final int groupId,
- final int remaining) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- handleEnrollResult(deviceId, fingerId, groupId, remaining);
- }
- });
- }
-
- @Override
- public void onAcquired(final long deviceId, final int acquiredInfo, final int vendorCode) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- handleAcquired(deviceId, acquiredInfo, vendorCode);
- }
- });
- }
-
- @Override
- public void onAuthenticated(final long deviceId, final int fingerId, final int groupId,
- ArrayList<Byte> token) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- handleAuthenticated(deviceId, fingerId, groupId, token);
- }
- });
- }
-
- @Override
- public void onError(final long deviceId, final int error, final int vendorCode) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- handleError(deviceId, error, vendorCode);
- }
- });
- }
-
- @Override
- public void onRemoved(final long deviceId, final int fingerId, final int groupId, final int remaining) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- handleRemoved(deviceId, fingerId, groupId, remaining);
- }
- });
- }
-
- @Override
- public void onEnumerate(final long deviceId, final int fingerId, final int groupId,
- final int remaining) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- handleEnumerate(deviceId, fingerId, groupId, remaining);
- }
- });
- }
- };
-
- private final class FingerprintServiceWrapper extends IFingerprintService.Stub {
- @Override // Binder call
- public long preEnroll(IBinder token) {
- checkPermission(MANAGE_FINGERPRINT);
- return startPreEnroll(token);
- }
-
- @Override // Binder call
- public int postEnroll(IBinder token) {
- checkPermission(MANAGE_FINGERPRINT);
- return startPostEnroll(token);
- }
-
- @Override // Binder call
- public void enroll(final IBinder token, final byte[] cryptoToken, final int userId,
- final IFingerprintServiceReceiver receiver, final int flags,
- final String opPackageName) {
- checkPermission(MANAGE_FINGERPRINT);
- final int limit = mContext.getResources().getInteger(
- com.android.internal.R.integer.config_fingerprintMaxTemplatesPerUser);
-
- final int enrolled = FingerprintService.this.getEnrolledFingerprints(userId).size();
- if (enrolled >= limit) {
- Slog.w(TAG, "Too many fingerprints registered");
- return;
- }
-
- // Group ID is arbitrarily set to parent profile user ID. It just represents
- // the default fingerprints for the user.
- if (!isCurrentUserOrProfile(userId)) {
- return;
- }
-
- final boolean restricted = isRestricted();
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- startEnrollment(token, cryptoToken, userId, receiver, flags,
- restricted, opPackageName);
- }
- });
- }
-
- private boolean isRestricted() {
- // Only give privileged apps (like Settings) access to fingerprint info
- final boolean restricted = !hasPermission(MANAGE_FINGERPRINT);
- return restricted;
- }
-
- @Override // Binder call
- public void cancelEnrollment(final IBinder token) {
- checkPermission(MANAGE_FINGERPRINT);
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- ClientMonitor client = mCurrentClient;
- if (client instanceof EnrollClient && client.getToken() == token) {
- client.stop(client.getToken() == token);
- }
- }
- });
- }
-
- @Override // Binder call
- public void authenticate(final IBinder token, final long opId, final int groupId,
- final IFingerprintServiceReceiver receiver, final int flags,
- final String opPackageName, final Bundle bundle,
- final IBiometricPromptReceiver dialogReceiver) {
- final int callingUid = Binder.getCallingUid();
- final int callingPid = Binder.getCallingPid();
- final int callingUserId = UserHandle.getCallingUserId();
- final boolean restricted = isRestricted();
-
- if (!canUseFingerprint(opPackageName, true /* foregroundOnly */, callingUid, callingPid,
- callingUserId)) {
- if (DEBUG) Slog.v(TAG, "authenticate(): reject " + opPackageName);
- return;
- }
-
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- MetricsLogger.histogram(mContext, "fingerprint_token", opId != 0L ? 1 : 0);
-
- // Get performance stats object for this user.
- HashMap<Integer, PerformanceStats> pmap
- = (opId == 0) ? mPerformanceMap : mCryptoPerformanceMap;
- PerformanceStats stats = pmap.get(mCurrentUserId);
- if (stats == null) {
- stats = new PerformanceStats();
- pmap.put(mCurrentUserId, stats);
- }
- mPerformanceStats = stats;
-
- startAuthentication(token, opId, callingUserId, groupId, receiver,
- flags, restricted, opPackageName, bundle, dialogReceiver);
- }
- });
- }
-
- @Override // Binder call
- public void cancelAuthentication(final IBinder token, final String opPackageName) {
- final int callingUid = Binder.getCallingUid();
- final int callingPid = Binder.getCallingPid();
- final int callingUserId = UserHandle.getCallingUserId();
-
- if (!canUseFingerprint(opPackageName, true /* foregroundOnly */, callingUid, callingPid,
- callingUserId)) {
- if (DEBUG) Slog.v(TAG, "cancelAuthentication(): reject " + opPackageName);
- return;
- }
-
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- ClientMonitor client = mCurrentClient;
- if (client instanceof AuthenticationClient) {
- if (client.getToken() == token) {
- if (DEBUG) Slog.v(TAG, "stop client " + client.getOwnerString());
- client.stop(client.getToken() == token);
- } else {
- if (DEBUG) Slog.v(TAG, "can't stop client "
- + client.getOwnerString() + " since tokens don't match");
- }
- } else if (client != null) {
- if (DEBUG) Slog.v(TAG, "can't cancel non-authenticating client "
- + client.getOwnerString());
- }
- }
- });
- }
-
- @Override // Binder call
- public void setActiveUser(final int userId) {
- checkPermission(MANAGE_FINGERPRINT);
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- updateActiveGroup(userId, null);
- }
- });
- }
-
- @Override // Binder call
- public void remove(final IBinder token, final int fingerId, final int groupId,
- final int userId, final IFingerprintServiceReceiver receiver) {
- checkPermission(MANAGE_FINGERPRINT); // TODO: Maybe have another permission
- final boolean restricted = isRestricted();
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- startRemove(token, fingerId, groupId, userId, receiver,
- restricted, false /* internal */);
- }
- });
- }
-
- @Override // Binder call
- public void enumerate(final IBinder token, final int userId,
- final IFingerprintServiceReceiver receiver) {
- checkPermission(MANAGE_FINGERPRINT); // TODO: Maybe have another permission
- final boolean restricted = isRestricted();
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- startEnumerate(token, userId, receiver, restricted, false /* internal */);
- }
- });
- }
-
- @Override // Binder call
- public boolean isHardwareDetected(long deviceId, String opPackageName) {
- if (!canUseFingerprint(opPackageName, false /* foregroundOnly */,
- Binder.getCallingUid(), Binder.getCallingPid(),
- UserHandle.getCallingUserId())) {
- return false;
- }
-
- final long token = Binder.clearCallingIdentity();
- try {
- IBiometricsFingerprint daemon = getFingerprintDaemon();
- return daemon != null && mHalDeviceId != 0;
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
- @Override // Binder call
- public void rename(final int fingerId, final int groupId, final String name) {
- checkPermission(MANAGE_FINGERPRINT);
- if (!isCurrentUserOrProfile(groupId)) {
- return;
- }
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- mFingerprintUtils.renameFingerprintForUser(mContext, fingerId,
- groupId, name);
- }
- });
- }
-
- @Override // Binder call
- public List<Fingerprint> getEnrolledFingerprints(int userId, String opPackageName) {
- if (!canUseFingerprint(opPackageName, false /* foregroundOnly */,
- Binder.getCallingUid(), Binder.getCallingPid(),
- UserHandle.getCallingUserId())) {
- return Collections.emptyList();
- }
-
- return FingerprintService.this.getEnrolledFingerprints(userId);
- }
-
- @Override // Binder call
- public boolean hasEnrolledFingerprints(int userId, String opPackageName) {
- if (!canUseFingerprint(opPackageName, false /* foregroundOnly */,
- Binder.getCallingUid(), Binder.getCallingPid(),
- UserHandle.getCallingUserId())) {
- return false;
- }
-
- return FingerprintService.this.hasEnrolledFingerprints(userId);
- }
-
- @Override // Binder call
- public long getAuthenticatorId(String opPackageName) {
- // In this method, we're not checking whether the caller is permitted to use fingerprint
- // API because current authenticator ID is leaked (in a more contrived way) via Android
- // Keystore (android.security.keystore package): the user of that API can create a key
- // which requires fingerprint authentication for its use, and then query the key's
- // characteristics (hidden API) which returns, among other things, fingerprint
- // authenticator ID which was active at key creation time.
- //
- // Reason: The part of Android Keystore which runs inside an app's process invokes this
- // method in certain cases. Those cases are not always where the developer demonstrates
- // explicit intent to use fingerprint functionality. Thus, to avoiding throwing an
- // unexpected SecurityException this method does not check whether its caller is
- // permitted to use fingerprint API.
- //
- // The permission check should be restored once Android Keystore no longer invokes this
- // method from inside app processes.
-
- return FingerprintService.this.getAuthenticatorId(opPackageName);
- }
-
- @Override // Binder call
- protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
-
- final long ident = Binder.clearCallingIdentity();
- try {
- if (args.length > 0 && "--proto".equals(args[0])) {
- dumpProto(fd);
- } else {
- dumpInternal(pw);
- }
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
-
- @Override // Binder call
- public void resetTimeout(byte [] token) {
- checkPermission(RESET_FINGERPRINT_LOCKOUT);
- // TODO: confirm security token when we move timeout management into the HAL layer.
- mHandler.post(mResetFailedAttemptsForCurrentUserRunnable);
- }
-
- @Override
- public void addLockoutResetCallback(final IFingerprintServiceLockoutResetCallback callback)
- throws RemoteException {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- addLockoutResetMonitor(
- new FingerprintServiceLockoutResetMonitor(callback));
- }
- });
- }
-
- @Override
- public boolean isClientActive() {
- checkPermission(MANAGE_FINGERPRINT);
- synchronized(FingerprintService.this) {
- return (mCurrentClient != null) || (mPendingClient != null);
- }
- }
-
- @Override
- public void addClientActiveCallback(IFingerprintClientActiveCallback callback) {
- checkPermission(MANAGE_FINGERPRINT);
- mClientActiveCallbacks.add(callback);
- }
-
- @Override
- public void removeClientActiveCallback(IFingerprintClientActiveCallback callback) {
- checkPermission(MANAGE_FINGERPRINT);
- mClientActiveCallbacks.remove(callback);
- }
- }
-
- private void dumpInternal(PrintWriter pw) {
- JSONObject dump = new JSONObject();
- try {
- dump.put("service", "Fingerprint Manager");
-
- JSONArray sets = new JSONArray();
- for (UserInfo user : UserManager.get(getContext()).getUsers()) {
- final int userId = user.getUserHandle().getIdentifier();
- final int N = mFingerprintUtils.getFingerprintsForUser(mContext, userId).size();
- PerformanceStats stats = mPerformanceMap.get(userId);
- PerformanceStats cryptoStats = mCryptoPerformanceMap.get(userId);
- JSONObject set = new JSONObject();
- set.put("id", userId);
- set.put("count", N);
- set.put("accept", (stats != null) ? stats.accept : 0);
- set.put("reject", (stats != null) ? stats.reject : 0);
- set.put("acquire", (stats != null) ? stats.acquire : 0);
- set.put("lockout", (stats != null) ? stats.lockout : 0);
- set.put("permanentLockout", (stats != null) ? stats.permanentLockout : 0);
- // cryptoStats measures statistics about secure fingerprint transactions
- // (e.g. to unlock password storage, make secure purchases, etc.)
- set.put("acceptCrypto", (cryptoStats != null) ? cryptoStats.accept : 0);
- set.put("rejectCrypto", (cryptoStats != null) ? cryptoStats.reject : 0);
- set.put("acquireCrypto", (cryptoStats != null) ? cryptoStats.acquire : 0);
- set.put("lockoutCrypto", (cryptoStats != null) ? cryptoStats.lockout : 0);
- set.put("permanentLockoutCrypto",
- (cryptoStats != null) ? cryptoStats.permanentLockout : 0);
- sets.put(set);
- }
-
- dump.put("prints", sets);
- } catch (JSONException e) {
- Slog.e(TAG, "dump formatting failure", e);
- }
- pw.println(dump);
- }
-
- private void dumpProto(FileDescriptor fd) {
- final ProtoOutputStream proto = new ProtoOutputStream(fd);
- for (UserInfo user : UserManager.get(getContext()).getUsers()) {
- final int userId = user.getUserHandle().getIdentifier();
-
- final long userToken = proto.start(FingerprintServiceDumpProto.USERS);
-
- proto.write(FingerprintUserStatsProto.USER_ID, userId);
- proto.write(FingerprintUserStatsProto.NUM_FINGERPRINTS,
- mFingerprintUtils.getFingerprintsForUser(mContext, userId).size());
-
- // Normal fingerprint authentications (e.g. lockscreen)
- final PerformanceStats normal = mPerformanceMap.get(userId);
- if (normal != null) {
- final long countsToken = proto.start(FingerprintUserStatsProto.NORMAL);
- proto.write(PerformanceStatsProto.ACCEPT, normal.accept);
- proto.write(PerformanceStatsProto.REJECT, normal.reject);
- proto.write(PerformanceStatsProto.ACQUIRE, normal.acquire);
- proto.write(PerformanceStatsProto.LOCKOUT, normal.lockout);
- proto.write(PerformanceStatsProto.PERMANENT_LOCKOUT, normal.permanentLockout);
- proto.end(countsToken);
- }
-
- // Statistics about secure fingerprint transactions (e.g. to unlock password
- // storage, make secure purchases, etc.)
- final PerformanceStats crypto = mCryptoPerformanceMap.get(userId);
- if (crypto != null) {
- final long countsToken = proto.start(FingerprintUserStatsProto.CRYPTO);
- proto.write(PerformanceStatsProto.ACCEPT, crypto.accept);
- proto.write(PerformanceStatsProto.REJECT, crypto.reject);
- proto.write(PerformanceStatsProto.ACQUIRE, crypto.acquire);
- proto.write(PerformanceStatsProto.LOCKOUT, crypto.lockout);
- proto.write(PerformanceStatsProto.PERMANENT_LOCKOUT, crypto.permanentLockout);
- proto.end(countsToken);
- }
-
- proto.end(userToken);
- }
- proto.flush();
- mPerformanceMap.clear();
- mCryptoPerformanceMap.clear();
- }
-
- @Override
- public void onStart() {
- publishBinderService(Context.FINGERPRINT_SERVICE, new FingerprintServiceWrapper());
- SystemServerInitThreadPool.get().submit(this::getFingerprintDaemon, TAG + ".onStart");
- listenForUserSwitches();
- }
-
- private void updateActiveGroup(int userId, String clientPackage) {
- IBiometricsFingerprint daemon = getFingerprintDaemon();
-
- if (daemon != null) {
- try {
- userId = getUserOrWorkProfileId(clientPackage, userId);
- if (userId != mCurrentUserId) {
- int firstSdkInt = Build.VERSION.FIRST_SDK_INT;
- if (firstSdkInt < Build.VERSION_CODES.BASE) {
- Slog.e(TAG, "First SDK version " + firstSdkInt + " is invalid; must be " +
- "at least VERSION_CODES.BASE");
- }
- File baseDir;
- if (firstSdkInt <= Build.VERSION_CODES.O_MR1) {
- baseDir = Environment.getUserSystemDirectory(userId);
- } else {
- baseDir = Environment.getDataVendorDeDirectory(userId);
- }
-
- File fpDir = new File(baseDir, FP_DATA_DIR);
- if (!fpDir.exists()) {
- if (!fpDir.mkdir()) {
- Slog.v(TAG, "Cannot make directory: " + fpDir.getAbsolutePath());
- return;
- }
- // Calling mkdir() from this process will create a directory with our
- // permissions (inherited from the containing dir). This command fixes
- // the label.
- if (!SELinux.restorecon(fpDir)) {
- Slog.w(TAG, "Restorecons failed. Directory will have wrong label.");
- return;
- }
- }
-
- daemon.setActiveGroup(userId, fpDir.getAbsolutePath());
- mCurrentUserId = userId;
- }
- mAuthenticatorIds.put(userId,
- hasEnrolledFingerprints(userId) ? daemon.getAuthenticatorId() : 0L);
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to setActiveGroup():", e);
- }
- }
- }
-
- /**
- * @param clientPackage the package of the caller
- * @return the profile id
- */
- private int getUserOrWorkProfileId(String clientPackage, int userId) {
- if (!isKeyguard(clientPackage) && isWorkProfile(userId)) {
- return userId;
- }
- return getEffectiveUserId(userId);
- }
-
- /**
- * @param userId
- * @return true if this is a work profile
- */
- private boolean isWorkProfile(int userId) {
- UserInfo userInfo = null;
- final long token = Binder.clearCallingIdentity();
- try {
- userInfo = mUserManager.getUserInfo(userId);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- return userInfo != null && userInfo.isManagedProfile();
- }
-
- private void listenForUserSwitches() {
- try {
- ActivityManager.getService().registerUserSwitchObserver(
- new SynchronousUserSwitchObserver() {
- @Override
- public void onUserSwitching(int newUserId) throws RemoteException {
- mHandler.obtainMessage(MSG_USER_SWITCHING, newUserId, 0 /* unused */)
- .sendToTarget();
- }
- }, TAG);
- } catch (RemoteException e) {
- Slog.w(TAG, "Failed to listen for user switching event" ,e);
- }
- }
-
- /***
- * @param opPackageName the name of the calling package
- * @return authenticator id for the calling user
- */
- public long getAuthenticatorId(String opPackageName) {
- final int userId = getUserOrWorkProfileId(opPackageName, UserHandle.getCallingUserId());
- return mAuthenticatorIds.getOrDefault(userId, 0L);
- }
-}
diff --git a/services/core/java/com/android/server/fingerprint/FingerprintUtils.java b/services/core/java/com/android/server/fingerprint/FingerprintUtils.java
deleted file mode 100644
index 5fbd735..0000000
--- a/services/core/java/com/android/server/fingerprint/FingerprintUtils.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/**
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.fingerprint;
-
-import android.content.Context;
-import android.hardware.fingerprint.Fingerprint;
-import android.text.TextUtils;
-import android.util.SparseArray;
-
-import com.android.internal.annotations.GuardedBy;
-
-import java.util.List;
-
-/**
- * Utility class for dealing with fingerprints and fingerprint settings.
- */
-public class FingerprintUtils {
-
- private static final Object sInstanceLock = new Object();
- private static FingerprintUtils sInstance;
-
- @GuardedBy("this")
- private final SparseArray<FingerprintsUserState> mUsers = new SparseArray<>();
-
- public static FingerprintUtils getInstance() {
- synchronized (sInstanceLock) {
- if (sInstance == null) {
- sInstance = new FingerprintUtils();
- }
- }
- return sInstance;
- }
-
- private FingerprintUtils() {
- }
-
- public List<Fingerprint> getFingerprintsForUser(Context ctx, int userId) {
- return getStateForUser(ctx, userId).getFingerprints();
- }
-
- public void addFingerprintForUser(Context ctx, int fingerId, int userId) {
- getStateForUser(ctx, userId).addFingerprint(fingerId, userId);
- }
-
- public void removeFingerprintIdForUser(Context ctx, int fingerId, int userId) {
- getStateForUser(ctx, userId).removeFingerprint(fingerId);
- }
-
- public void renameFingerprintForUser(Context ctx, int fingerId, int userId, CharSequence name) {
- if (TextUtils.isEmpty(name)) {
- // Don't do the rename if it's empty
- return;
- }
- getStateForUser(ctx, userId).renameFingerprint(fingerId, name);
- }
-
- private FingerprintsUserState getStateForUser(Context ctx, int userId) {
- synchronized (this) {
- FingerprintsUserState state = mUsers.get(userId);
- if (state == null) {
- state = new FingerprintsUserState(ctx, userId);
- mUsers.put(userId, state);
- }
- return state;
- }
- }
-}
-
diff --git a/services/core/java/com/android/server/fingerprint/FingerprintsUserState.java b/services/core/java/com/android/server/fingerprint/FingerprintsUserState.java
deleted file mode 100644
index b0cde2d..0000000
--- a/services/core/java/com/android/server/fingerprint/FingerprintsUserState.java
+++ /dev/null
@@ -1,272 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.server.fingerprint;
-
-import android.content.Context;
-import android.hardware.fingerprint.Fingerprint;
-import android.os.AsyncTask;
-import android.os.Environment;
-import android.util.AtomicFile;
-import android.util.Slog;
-import android.util.Xml;
-
-import com.android.internal.annotations.GuardedBy;
-
-import libcore.io.IoUtils;
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Class managing the set of fingerprint per user across device reboots.
- */
-class FingerprintsUserState {
-
- private static final String TAG = "FingerprintState";
- private static final String FINGERPRINT_FILE = "settings_fingerprint.xml";
-
- private static final String TAG_FINGERPRINTS = "fingerprints";
- private static final String TAG_FINGERPRINT = "fingerprint";
- private static final String ATTR_NAME = "name";
- private static final String ATTR_GROUP_ID = "groupId";
- private static final String ATTR_FINGER_ID = "fingerId";
- private static final String ATTR_DEVICE_ID = "deviceId";
-
- private final File mFile;
-
- @GuardedBy("this")
- private final ArrayList<Fingerprint> mFingerprints = new ArrayList<Fingerprint>();
- private final Context mCtx;
-
- public FingerprintsUserState(Context ctx, int userId) {
- mFile = getFileForUser(userId);
- mCtx = ctx;
- synchronized (this) {
- readStateSyncLocked();
- }
- }
-
- public void addFingerprint(int fingerId, int groupId) {
- synchronized (this) {
- mFingerprints.add(new Fingerprint(getUniqueName(), groupId, fingerId, 0));
- scheduleWriteStateLocked();
- }
- }
-
- public void removeFingerprint(int fingerId) {
- synchronized (this) {
- for (int i = 0; i < mFingerprints.size(); i++) {
- if (mFingerprints.get(i).getFingerId() == fingerId) {
- mFingerprints.remove(i);
- scheduleWriteStateLocked();
- break;
- }
- }
- }
- }
-
- public void renameFingerprint(int fingerId, CharSequence name) {
- synchronized (this) {
- for (int i = 0; i < mFingerprints.size(); i++) {
- if (mFingerprints.get(i).getFingerId() == fingerId) {
- Fingerprint old = mFingerprints.get(i);
- mFingerprints.set(i, new Fingerprint(name, old.getGroupId(), old.getFingerId(),
- old.getDeviceId()));
- scheduleWriteStateLocked();
- break;
- }
- }
- }
- }
-
- public List<Fingerprint> getFingerprints() {
- synchronized (this) {
- return getCopy(mFingerprints);
- }
- }
-
- /**
- * Finds a unique name for the given fingerprint
- * @return unique name
- */
- private String getUniqueName() {
- int guess = 1;
- while (true) {
- // Not the most efficient algorithm in the world, but there shouldn't be more than 10
- String name = mCtx.getString(com.android.internal.R.string.fingerprint_name_template,
- guess);
- if (isUnique(name)) {
- return name;
- }
- guess++;
- }
- }
-
- private boolean isUnique(String name) {
- for (Fingerprint fp : mFingerprints) {
- if (fp.getName().equals(name)) {
- return false;
- }
- }
- return true;
- }
-
- private static File getFileForUser(int userId) {
- return new File(Environment.getUserSystemDirectory(userId), FINGERPRINT_FILE);
- }
-
- private final Runnable mWriteStateRunnable = new Runnable() {
- @Override
- public void run() {
- doWriteState();
- }
- };
-
- private void scheduleWriteStateLocked() {
- AsyncTask.execute(mWriteStateRunnable);
- }
-
- private ArrayList<Fingerprint> getCopy(ArrayList<Fingerprint> array) {
- ArrayList<Fingerprint> result = new ArrayList<Fingerprint>(array.size());
- for (int i = 0; i < array.size(); i++) {
- Fingerprint fp = array.get(i);
- result.add(new Fingerprint(fp.getName(), fp.getGroupId(), fp.getFingerId(),
- fp.getDeviceId()));
- }
- return result;
- }
-
- private void doWriteState() {
- AtomicFile destination = new AtomicFile(mFile);
-
- ArrayList<Fingerprint> fingerprints;
-
- synchronized (this) {
- fingerprints = getCopy(mFingerprints);
- }
-
- FileOutputStream out = null;
- try {
- out = destination.startWrite();
-
- XmlSerializer serializer = Xml.newSerializer();
- serializer.setOutput(out, "utf-8");
- serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
- serializer.startDocument(null, true);
- serializer.startTag(null, TAG_FINGERPRINTS);
-
- final int count = fingerprints.size();
- for (int i = 0; i < count; i++) {
- Fingerprint fp = fingerprints.get(i);
- serializer.startTag(null, TAG_FINGERPRINT);
- serializer.attribute(null, ATTR_FINGER_ID, Integer.toString(fp.getFingerId()));
- serializer.attribute(null, ATTR_NAME, fp.getName().toString());
- serializer.attribute(null, ATTR_GROUP_ID, Integer.toString(fp.getGroupId()));
- serializer.attribute(null, ATTR_DEVICE_ID, Long.toString(fp.getDeviceId()));
- serializer.endTag(null, TAG_FINGERPRINT);
- }
-
- serializer.endTag(null, TAG_FINGERPRINTS);
- serializer.endDocument();
- destination.finishWrite(out);
-
- // Any error while writing is fatal.
- } catch (Throwable t) {
- Slog.wtf(TAG, "Failed to write settings, restoring backup", t);
- destination.failWrite(out);
- throw new IllegalStateException("Failed to write fingerprints", t);
- } finally {
- IoUtils.closeQuietly(out);
- }
- }
-
- @GuardedBy("this")
- private void readStateSyncLocked() {
- FileInputStream in;
- if (!mFile.exists()) {
- return;
- }
- try {
- in = new FileInputStream(mFile);
- } catch (FileNotFoundException fnfe) {
- Slog.i(TAG, "No fingerprint state");
- return;
- }
- try {
- XmlPullParser parser = Xml.newPullParser();
- parser.setInput(in, null);
- parseStateLocked(parser);
-
- } catch (XmlPullParserException | IOException e) {
- throw new IllegalStateException("Failed parsing settings file: "
- + mFile , e);
- } finally {
- IoUtils.closeQuietly(in);
- }
- }
-
- @GuardedBy("this")
- private void parseStateLocked(XmlPullParser parser)
- throws IOException, XmlPullParserException {
- final int outerDepth = parser.getDepth();
- int type;
- while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
- && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
- if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
- continue;
- }
-
- String tagName = parser.getName();
- if (tagName.equals(TAG_FINGERPRINTS)) {
- parseFingerprintsLocked(parser);
- }
- }
- }
-
- @GuardedBy("this")
- private void parseFingerprintsLocked(XmlPullParser parser)
- throws IOException, XmlPullParserException {
-
- final int outerDepth = parser.getDepth();
- int type;
- while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
- && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
- if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
- continue;
- }
-
- String tagName = parser.getName();
- if (tagName.equals(TAG_FINGERPRINT)) {
- String name = parser.getAttributeValue(null, ATTR_NAME);
- String groupId = parser.getAttributeValue(null, ATTR_GROUP_ID);
- String fingerId = parser.getAttributeValue(null, ATTR_FINGER_ID);
- String deviceId = parser.getAttributeValue(null, ATTR_DEVICE_ID);
- mFingerprints.add(new Fingerprint(name, Integer.parseInt(groupId),
- Integer.parseInt(fingerId), Integer.parseInt(deviceId)));
- }
- }
- }
-
-}
diff --git a/services/core/java/com/android/server/fingerprint/InternalEnumerateClient.java b/services/core/java/com/android/server/fingerprint/InternalEnumerateClient.java
deleted file mode 100644
index 434db98..0000000
--- a/services/core/java/com/android/server/fingerprint/InternalEnumerateClient.java
+++ /dev/null
@@ -1,93 +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.fingerprint;
-
-import android.content.Context;
-import android.hardware.fingerprint.Fingerprint;
-import android.hardware.fingerprint.IFingerprintServiceReceiver;
-import android.os.IBinder;
-import android.util.Slog;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * An internal class to help clean up unknown fingerprints in the hardware and software
- */
-public abstract class InternalEnumerateClient extends EnumerateClient {
-
- private List<Fingerprint> mEnrolledList;
- private List<Fingerprint> mUnknownFingerprints = new ArrayList<>(); // list of fp to delete
-
- public InternalEnumerateClient(Context context, long halDeviceId, IBinder token,
- IFingerprintServiceReceiver receiver, int groupId, int userId,
- boolean restricted, String owner, List<Fingerprint> enrolledList) {
-
- super(context, halDeviceId, token, receiver, userId, groupId, restricted, owner);
- mEnrolledList = enrolledList;
- }
-
- private void handleEnumeratedFingerprint(int fingerId, int groupId, int remaining) {
-
- boolean matched = false;
- for (int i=0; i<mEnrolledList.size(); i++) {
- if (mEnrolledList.get(i).getFingerId() == fingerId) {
- mEnrolledList.remove(i);
- matched = true;
- break;
- }
- }
-
- // fingerId 0 means no fingerprints are in hardware
- if (!matched && fingerId != 0) {
- Fingerprint fingerprint = new Fingerprint("", groupId, fingerId, getHalDeviceId());
- mUnknownFingerprints.add(fingerprint);
- }
- }
-
- private void doFingerprintCleanup() {
-
- if (mEnrolledList == null) {
- return;
- }
-
- for (Fingerprint f : mEnrolledList) {
- Slog.e(TAG, "Internal Enumerate: Removing dangling enrolled fingerprint: "
- + f.getName() + " " + f.getFingerId() + " " + f.getGroupId()
- + " " + f.getDeviceId());
-
- FingerprintUtils.getInstance().removeFingerprintIdForUser(getContext(),
- f.getFingerId(), getTargetUserId());
- }
- mEnrolledList.clear();
- }
-
- public List<Fingerprint> getUnknownFingerprints() {
- return mUnknownFingerprints;
- }
-
- @Override
- public boolean onEnumerationResult(int fingerId, int groupId, int remaining) {
-
- handleEnumeratedFingerprint(fingerId, groupId, remaining);
- if (remaining == 0) {
- doFingerprintCleanup();
- }
-
- return remaining == 0;
- }
-
-}
diff --git a/services/core/java/com/android/server/fingerprint/InternalRemovalClient.java b/services/core/java/com/android/server/fingerprint/InternalRemovalClient.java
deleted file mode 100644
index 19f61fe..0000000
--- a/services/core/java/com/android/server/fingerprint/InternalRemovalClient.java
+++ /dev/null
@@ -1,33 +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.fingerprint;
-
-import android.content.Context;
-import android.os.IBinder;
-import android.hardware.fingerprint.IFingerprintServiceReceiver;
-import com.android.server.fingerprint.RemovalClient;
-
-public abstract class InternalRemovalClient extends RemovalClient {
-
- public InternalRemovalClient(Context context, long halDeviceId, IBinder token,
- IFingerprintServiceReceiver receiver, int fingerId, int groupId, int userId,
- boolean restricted, String owner) {
-
- super(context, halDeviceId, token, receiver, fingerId, groupId, userId, restricted, owner);
-
- }
-}
diff --git a/services/core/java/com/android/server/fingerprint/RemovalClient.java b/services/core/java/com/android/server/fingerprint/RemovalClient.java
deleted file mode 100644
index ffc8488..0000000
--- a/services/core/java/com/android/server/fingerprint/RemovalClient.java
+++ /dev/null
@@ -1,129 +0,0 @@
-/**
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.fingerprint;
-
-import android.content.Context;
-import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
-import android.hardware.fingerprint.FingerprintManager;
-import android.hardware.fingerprint.IFingerprintServiceReceiver;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.os.UserHandle;
-import android.util.Slog;
-import com.android.internal.logging.MetricsLogger;
-
-/**
- * A class to keep track of the remove state for a given client.
- */
-public abstract class RemovalClient extends ClientMonitor {
- private int mFingerId;
-
- public RemovalClient(Context context, long halDeviceId, IBinder token,
- IFingerprintServiceReceiver receiver, int fingerId, int groupId, int userId,
- boolean restricted, String owner) {
- super(context, halDeviceId, token, receiver, userId, groupId, restricted, owner);
- mFingerId = fingerId;
- }
-
- @Override
- public int start() {
- IBiometricsFingerprint daemon = getFingerprintDaemon();
- // The fingerprint template ids will be removed when we get confirmation from the HAL
- try {
- final int result = daemon.remove(getGroupId(), mFingerId);
- if (result != 0) {
- Slog.w(TAG, "startRemove with id = " + mFingerId + " failed, result=" + result);
- MetricsLogger.histogram(getContext(), "fingerprintd_remove_start_error", result);
- onError(FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */);
- return result;
- }
- } catch (RemoteException e) {
- Slog.e(TAG, "startRemove failed", e);
- }
- return 0;
- }
-
- @Override
- public int stop(boolean initiatedByClient) {
- if (mAlreadyCancelled) {
- Slog.w(TAG, "stopRemove: already cancelled!");
- return 0;
- }
- IBiometricsFingerprint daemon = getFingerprintDaemon();
- if (daemon == null) {
- Slog.w(TAG, "stopRemoval: no fingerprint HAL!");
- return ERROR_ESRCH;
- }
- try {
- final int result = daemon.cancel();
- if (result != 0) {
- Slog.w(TAG, "stopRemoval failed, result=" + result);
- return result;
- }
- if (DEBUG) Slog.w(TAG, "client " + getOwnerString() + " is no longer removing");
- } catch (RemoteException e) {
- Slog.e(TAG, "stopRemoval failed", e);
- return ERROR_ESRCH;
- }
- mAlreadyCancelled = true;
- return 0; // success
- }
-
- /*
- * @return true if we're done.
- */
- private boolean sendRemoved(int fingerId, int groupId, int remaining) {
- IFingerprintServiceReceiver receiver = getReceiver();
- try {
- if (receiver != null) {
- receiver.onRemoved(getHalDeviceId(), fingerId, groupId, remaining);
- }
- } catch (RemoteException e) {
- Slog.w(TAG, "Failed to notify Removed:", e);
- }
- return remaining == 0;
- }
-
- @Override
- public boolean onRemoved(int fingerId, int groupId, int remaining) {
- if (fingerId != 0) {
- FingerprintUtils.getInstance().removeFingerprintIdForUser(getContext(), fingerId,
- getTargetUserId());
- }
- return sendRemoved(fingerId, getGroupId(), remaining);
- }
-
- @Override
- public boolean onEnrollResult(int fingerId, int groupId, int rem) {
- if (DEBUG) Slog.w(TAG, "onEnrollResult() called for remove!");
- return true; // Invalid for Remove
- }
-
- @Override
- public boolean onAuthenticated(int fingerId, int groupId) {
- if (DEBUG) Slog.w(TAG, "onAuthenticated() called for remove!");
- return true; // Invalid for Remove.
- }
-
- @Override
- public boolean onEnumerationResult(int fingerId, int groupId, int remaining) {
- if (DEBUG) Slog.w(TAG, "onEnumerationResult() called for remove!");
- return true; // Invalid for Remove.
- }
-
-
-}
diff --git a/services/core/java/com/android/server/hdmi/Constants.java b/services/core/java/com/android/server/hdmi/Constants.java
index a2a55e5..4bbae80 100644
--- a/services/core/java/com/android/server/hdmi/Constants.java
+++ b/services/core/java/com/android/server/hdmi/Constants.java
@@ -16,6 +16,7 @@
package com.android.server.hdmi;
+import android.annotation.IntDef;
import android.hardware.hdmi.HdmiDeviceInfo;
/**
@@ -200,6 +201,21 @@
static final int UNKNOWN_VOLUME = -1;
+ // States of property PROPERTY_SYSTEM_AUDIO_CONTROL_ON_POWER_ON
+ // to decide if turn on the system audio control when power on the device
+ @IntDef({
+ ALWAYS_SYSTEM_AUDIO_CONTROL_ON_POWER_ON,
+ USE_LAST_STATE_SYSTEM_AUDIO_CONTROL_ON_POWER_ON,
+ NEVER_SYSTEM_AUDIO_CONTROL_ON_POWER_ON
+ })
+ @interface SystemAudioControlOnPowerOn {}
+ static final int ALWAYS_SYSTEM_AUDIO_CONTROL_ON_POWER_ON = 0;
+ static final int USE_LAST_STATE_SYSTEM_AUDIO_CONTROL_ON_POWER_ON = 1;
+ static final int NEVER_SYSTEM_AUDIO_CONTROL_ON_POWER_ON = 2;
+
+
+ static final String PROPERTY_PREFERRED_ADDRESS_AUDIO_SYSTEM =
+ "persist.sys.hdmi.addr.audiosystem";
static final String PROPERTY_PREFERRED_ADDRESS_PLAYBACK = "persist.sys.hdmi.addr.playback";
static final String PROPERTY_PREFERRED_ADDRESS_TV = "persist.sys.hdmi.addr.tv";
@@ -219,6 +235,24 @@
// when it's an active source. True by default.
static final String PROPERTY_KEEP_AWAKE = "persist.sys.hdmi.keep_awake";
+ // TODO(UI): Set this from UI to decide if turn on System Audio Mode when power on the device
+ /**
+ * Property to decide if turn on the system audio control when power on the device.
+ * Default is always turn on.
+ * State must be a valid {@link SystemAudioControlOnPowerOn} int.
+ */
+ static final String PROPERTY_SYSTEM_AUDIO_CONTROL_ON_POWER_ON =
+ "persist.sys.hdmi.system_audio_control_on_power_on";
+
+ /**
+ * Property to record last state of system audio control before device powered off.
+ * When {@link #PROPERTY_SYSTEM_AUDIO_CONTROL_ON_POWER_ON} is set to
+ * {@link #USE_LAST_STATE_SYSTEM_AUDIO_CONTROL_ON_POWER_ON}, restoring this state on power on.
+ * State must be true or false. Default true.
+ */
+ static final String PROPERTY_LAST_SYSTEM_AUDIO_CONTROL =
+ "persist.sys.hdmi.last_system_audio_control";
+
static final int RECORDING_TYPE_DIGITAL_RF = 1;
static final int RECORDING_TYPE_ANALOGUE_RF = 2;
static final int RECORDING_TYPE_EXTERNAL_PHYSICAL_ADDRESS = 3;
diff --git a/services/core/java/com/android/server/hdmi/DelayedMessageBuffer.java b/services/core/java/com/android/server/hdmi/DelayedMessageBuffer.java
index 2c1a7d5..15ec486 100644
--- a/services/core/java/com/android/server/hdmi/DelayedMessageBuffer.java
+++ b/services/core/java/com/android/server/hdmi/DelayedMessageBuffer.java
@@ -85,7 +85,7 @@
void processAllMessages() {
// Use the copied buffer.
- ArrayList<HdmiCecMessage> copiedBuffer = new ArrayList<HdmiCecMessage>(mBuffer);
+ ArrayList<HdmiCecMessage> copiedBuffer = new ArrayList<>(mBuffer);
mBuffer.clear();
for (HdmiCecMessage message : copiedBuffer) {
mDevice.onMessage(message);
@@ -104,7 +104,7 @@
* are associated with
*/
void processMessagesForDevice(int address) {
- ArrayList<HdmiCecMessage> copiedBuffer = new ArrayList<HdmiCecMessage>(mBuffer);
+ ArrayList<HdmiCecMessage> copiedBuffer = new ArrayList<>(mBuffer);
mBuffer.clear();
HdmiLogger.debug("Checking message for address:" + address);
for (HdmiCecMessage message : copiedBuffer) {
@@ -134,7 +134,7 @@
* @param address logical address of the device to be the active source
*/
void processActiveSource(int address) {
- ArrayList<HdmiCecMessage> copiedBuffer = new ArrayList<HdmiCecMessage>(mBuffer);
+ ArrayList<HdmiCecMessage> copiedBuffer = new ArrayList<>(mBuffer);
mBuffer.clear();
for (HdmiCecMessage message : copiedBuffer) {
if (message.getOpcode() == Constants.MESSAGE_ACTIVE_SOURCE
diff --git a/services/core/java/com/android/server/hdmi/DetectTvSystemAudioModeSupportAction.java b/services/core/java/com/android/server/hdmi/DetectTvSystemAudioModeSupportAction.java
new file mode 100644
index 0000000..0495ff8
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/DetectTvSystemAudioModeSupportAction.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.server.hdmi;
+
+import android.hardware.tv.cec.V1_0.SendMessageResult;
+
+import com.android.server.hdmi.HdmiCecLocalDeviceAudioSystem.TvSystemAudioModeSupportedCallback;
+
+/**
+ * Feature action that detects if TV supports system audio control.
+ */
+public class DetectTvSystemAudioModeSupportAction extends HdmiCecFeatureAction {
+
+ // State that waits for <Active Source> once send <Request Active Source>.
+ private static final int STATE_WAITING_FOR_FEATURE_ABORT = 1;
+
+ private TvSystemAudioModeSupportedCallback mCallback;
+ private int mState;
+
+ DetectTvSystemAudioModeSupportAction(HdmiCecLocalDevice source,
+ TvSystemAudioModeSupportedCallback callback) {
+ super(source);
+ mCallback = callback;
+ }
+
+ @Override
+ boolean start() {
+ mState = STATE_WAITING_FOR_FEATURE_ABORT;
+ addTimer(mState, HdmiConfig.TIMEOUT_MS);
+ sendSetSystemAudioMode();
+ return true;
+ }
+
+ @Override
+ boolean processCommand(HdmiCecMessage cmd) {
+ if (cmd.getOpcode() == Constants.MESSAGE_FEATURE_ABORT) {
+ if (mState != STATE_WAITING_FOR_FEATURE_ABORT) {
+ return false;
+ }
+ finishAction(false);
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ void handleTimerEvent(int state) {
+ if (mState != state) {
+ return;
+ }
+
+ switch (mState) {
+ case STATE_WAITING_FOR_FEATURE_ABORT:
+ finishAction(true);
+ break;
+ }
+ }
+
+ protected void sendSetSystemAudioMode() {
+ sendCommand(
+ HdmiCecMessageBuilder.buildSetSystemAudioMode(getSourceAddress(),Constants.ADDR_TV,
+ true),
+ result -> {
+ if (result != SendMessageResult.SUCCESS) {
+ finishAction(false);
+ }
+ });
+ }
+
+ private void finishAction(boolean supported) {
+ mCallback.onResult(supported);
+ audioSystem().setTvSystemAudioModeSupport(supported);
+ finish();
+ }
+}
diff --git a/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java b/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java
index db8dedb..b75e75f 100755
--- a/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java
+++ b/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java
@@ -386,6 +386,7 @@
return;
case STATE_WAITING_FOR_VENDOR_ID:
queryVendorId(address);
+ return;
default:
return;
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecFeatureAction.java b/services/core/java/com/android/server/hdmi/HdmiCecFeatureAction.java
index d26be57..11faa56 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecFeatureAction.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecFeatureAction.java
@@ -256,6 +256,10 @@
return (HdmiCecLocalDeviceTv) mSource;
}
+ protected final HdmiCecLocalDeviceAudioSystem audioSystem() {
+ return (HdmiCecLocalDeviceAudioSystem) mSource;
+ }
+
protected final int getSourceAddress() {
return mSource.getDeviceInfo().getLogicalAddress();
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
index 5681367..1dd10f5 100755
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
@@ -169,6 +169,8 @@
return new HdmiCecLocalDeviceTv(service);
case HdmiDeviceInfo.DEVICE_PLAYBACK:
return new HdmiCecLocalDevicePlayback(service);
+ case HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM:
+ return new HdmiCecLocalDeviceAudioSystem(service);
default:
return null;
}
@@ -264,14 +266,28 @@
return handleRoutingChange(message);
case Constants.MESSAGE_ROUTING_INFORMATION:
return handleRoutingInformation(message);
+ case Constants.MESSAGE_REQUEST_ARC_INITIATION:
+ return handleRequestArcInitiate(message);
+ case Constants.MESSAGE_REQUEST_ARC_TERMINATION:
+ return handleRequestArcTermination(message);
case Constants.MESSAGE_INITIATE_ARC:
return handleInitiateArc(message);
case Constants.MESSAGE_TERMINATE_ARC:
return handleTerminateArc(message);
+ case Constants.MESSAGE_REPORT_ARC_INITIATED:
+ return handleReportArcInitiate(message);
+ case Constants.MESSAGE_REPORT_ARC_TERMINATED:
+ return handleReportArcTermination(message);
+ case Constants.MESSAGE_SYSTEM_AUDIO_MODE_REQUEST:
+ return handleSystemAudioModeRequest(message);
case Constants.MESSAGE_SET_SYSTEM_AUDIO_MODE:
return handleSetSystemAudioMode(message);
case Constants.MESSAGE_SYSTEM_AUDIO_MODE_STATUS:
return handleSystemAudioModeStatus(message);
+ case Constants.MESSAGE_GIVE_SYSTEM_AUDIO_MODE_STATUS:
+ return handleGiveSystemAudioModeStatus(message);
+ case Constants.MESSAGE_GIVE_AUDIO_STATUS:
+ return handleGiveAudioStatus(message);
case Constants.MESSAGE_REPORT_AUDIO_STATUS:
return handleReportAudioStatus(message);
case Constants.MESSAGE_STANDBY:
@@ -419,10 +435,18 @@
return false;
}
+ protected boolean handleGiveSystemAudioModeStatus(HdmiCecMessage message) {
+ return false;
+ }
+
protected boolean handleSetSystemAudioMode(HdmiCecMessage message) {
return false;
}
+ protected boolean handleSystemAudioModeRequest(HdmiCecMessage message) {
+ return false;
+ }
+
protected boolean handleTerminateArc(HdmiCecMessage message) {
return false;
}
@@ -431,10 +455,30 @@
return false;
}
+ protected boolean handleRequestArcInitiate(HdmiCecMessage message) {
+ return false;
+ }
+
+ protected boolean handleRequestArcTermination(HdmiCecMessage message) {
+ return false;
+ }
+
+ protected boolean handleReportArcInitiate(HdmiCecMessage message) {
+ return false;
+ }
+
+ protected boolean handleReportArcTermination(HdmiCecMessage message) {
+ return false;
+ }
+
protected boolean handleReportAudioStatus(HdmiCecMessage message) {
return false;
}
+ protected boolean handleGiveAudioStatus(HdmiCecMessage message) {
+ return false;
+ }
+
@ServiceThreadOnly
protected boolean handleStandby(HdmiCecMessage message) {
assertRunOnServiceThread();
@@ -516,8 +560,7 @@
static boolean isPowerOffOrToggleCommand(HdmiCecMessage message) {
byte[] params = message.getParams();
return message.getOpcode() == Constants.MESSAGE_USER_CONTROL_PRESSED
- && (params[0] == HdmiCecKeycode.CEC_KEYCODE_POWER
- || params[0] == HdmiCecKeycode.CEC_KEYCODE_POWER_OFF_FUNCTION
+ && (params[0] == HdmiCecKeycode.CEC_KEYCODE_POWER_OFF_FUNCTION
|| params[0] == HdmiCecKeycode.CEC_KEYCODE_POWER_TOGGLE_FUNCTION);
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
new file mode 100644
index 0000000..b25287c
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
@@ -0,0 +1,326 @@
+/*
+ * 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.hdmi;
+
+import static com.android.server.hdmi.Constants.ALWAYS_SYSTEM_AUDIO_CONTROL_ON_POWER_ON;
+import static com.android.server.hdmi.Constants.PROPERTY_SYSTEM_AUDIO_CONTROL_ON_POWER_ON;
+import static com.android.server.hdmi.Constants.USE_LAST_STATE_SYSTEM_AUDIO_CONTROL_ON_POWER_ON;
+
+import android.hardware.hdmi.HdmiDeviceInfo;
+import android.media.AudioManager;
+import android.os.SystemProperties;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
+
+/**
+ * Represent a logical device of type {@link HdmiDeviceInfo#DEVICE_AUDIO_SYSTEM} residing in
+ * Android system.
+ */
+public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDevice {
+
+ private static final String TAG = "HdmiCecLocalDeviceAudioSystem";
+
+ // Whether System audio mode is activated or not.
+ // This becomes true only when all system audio sequences are finished.
+ @GuardedBy("mLock")
+ private boolean mSystemAudioActivated;
+
+ // Whether the System Audio Control feature is enabled or not. True by default.
+ @GuardedBy("mLock")
+ private boolean mSystemAudioControlFeatureEnabled;
+ protected Integer mSystemAudioSource;
+
+ private boolean mTvSystemAudioModeSupport;
+
+ protected HdmiCecLocalDeviceAudioSystem(HdmiControlService service) {
+ super(service, HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM);
+ mSystemAudioControlFeatureEnabled = true;
+ // TODO(amyjojo) make System Audio Control controllable by users
+ /*mSystemAudioControlFeatureEnabled =
+ mService.readBooleanSetting(Global.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED, true);*/
+ }
+
+ @Override
+ @ServiceThreadOnly
+ protected void onStandby(boolean initiatedByCec, int standbyAction) {
+ assertRunOnServiceThread();
+ mTvSystemAudioModeSupport = false;
+ // Record the last state of System Audio Control before going to standby
+ synchronized (mLock) {
+ SystemProperties.set(Constants.PROPERTY_LAST_SYSTEM_AUDIO_CONTROL,
+ mSystemAudioActivated ? "true" : "false");
+ }
+ if (setSystemAudioMode(false)) {
+ mService.sendCecCommand(
+ HdmiCecMessageBuilder.buildSetSystemAudioMode(
+ mAddress, Constants.ADDR_BROADCAST, false));
+ }
+ }
+
+ @Override
+ @ServiceThreadOnly
+ protected void onAddressAllocated(int logicalAddress, int reason) {
+ assertRunOnServiceThread();
+ mService.sendCecCommand(HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
+ mAddress, mService.getPhysicalAddress(), mDeviceType));
+ mService.sendCecCommand(HdmiCecMessageBuilder.buildDeviceVendorIdCommand(
+ mAddress, mService.getVendorId()));
+ int systemAudioControlOnPowerOnProp = SystemProperties.getInt(
+ PROPERTY_SYSTEM_AUDIO_CONTROL_ON_POWER_ON, ALWAYS_SYSTEM_AUDIO_CONTROL_ON_POWER_ON);
+ boolean lastSystemAudioControlStatus = SystemProperties.getBoolean(
+ Constants.PROPERTY_LAST_SYSTEM_AUDIO_CONTROL, true);
+ systemAudioControlOnPowerOn(systemAudioControlOnPowerOnProp, lastSystemAudioControlStatus);
+ startQueuedActions();
+ }
+
+ @VisibleForTesting
+ protected void systemAudioControlOnPowerOn(
+ int systemAudioOnPowerOnProp, boolean lastSystemAudioControlStatus) {
+ if ((systemAudioOnPowerOnProp ==
+ ALWAYS_SYSTEM_AUDIO_CONTROL_ON_POWER_ON) ||
+ ((systemAudioOnPowerOnProp ==
+ USE_LAST_STATE_SYSTEM_AUDIO_CONTROL_ON_POWER_ON) &&
+ lastSystemAudioControlStatus)) {
+ addAndStartAction(new SystemAudioInitiationActionFromAvr(this));
+ }
+ }
+
+ @Override
+ @ServiceThreadOnly
+ protected int getPreferredAddress() {
+ assertRunOnServiceThread();
+ return SystemProperties.getInt(Constants.PROPERTY_PREFERRED_ADDRESS_AUDIO_SYSTEM,
+ Constants.ADDR_UNREGISTERED);
+ }
+
+ @Override
+ @ServiceThreadOnly
+ protected void setPreferredAddress(int addr) {
+ assertRunOnServiceThread();
+ SystemProperties.set(Constants.PROPERTY_PREFERRED_ADDRESS_AUDIO_SYSTEM,
+ String.valueOf(addr));
+ }
+
+ @Override
+ @ServiceThreadOnly
+ protected boolean handleReportAudioStatus(HdmiCecMessage message) {
+ assertRunOnServiceThread();
+ // TODO(amyjojo): implement report audio status handler
+ HdmiLogger.debug(TAG + "Stub handleReportAudioStatus");
+ return true;
+ }
+
+ @Override
+ @ServiceThreadOnly
+ protected boolean handleInitiateArc(HdmiCecMessage message) {
+ assertRunOnServiceThread();
+ // TODO(amyjojo): implement initiate arc handler
+ HdmiLogger.debug(TAG + "Stub handleInitiateArc");
+ return true;
+ }
+
+ @Override
+ @ServiceThreadOnly
+ protected boolean handleReportArcInitiate(HdmiCecMessage message) {
+ assertRunOnServiceThread();
+ // TODO(amyjojo): implement report arc initiate handler
+ HdmiLogger.debug(TAG + "Stub handleReportArcInitiate");
+ return true;
+ }
+
+ @Override
+ @ServiceThreadOnly
+ protected boolean handleReportArcTermination(HdmiCecMessage message) {
+ assertRunOnServiceThread();
+ // TODO(amyjojo): implement report arc terminate handler
+ HdmiLogger.debug(TAG + "Stub handleReportArcTermination");
+ return true;
+ }
+
+ @Override
+ @ServiceThreadOnly
+ protected boolean handleGiveAudioStatus(HdmiCecMessage message) {
+ assertRunOnServiceThread();
+
+ reportAudioStatus(message);
+ return true;
+ }
+
+ @Override
+ @ServiceThreadOnly
+ protected boolean handleGiveSystemAudioModeStatus(HdmiCecMessage message) {
+ assertRunOnServiceThread();
+ mService.sendCecCommand(HdmiCecMessageBuilder
+ .buildReportSystemAudioMode(mAddress, message.getSource(), mSystemAudioActivated));
+ return true;
+ }
+
+ @Override
+ @ServiceThreadOnly
+ protected boolean handleRequestArcInitiate(HdmiCecMessage message) {
+ assertRunOnServiceThread();
+ // TODO(b/80296911): Check if ARC supported.
+
+ // TODO(b/80296911): Check if port is ready to accept.
+
+ // TODO(b/80296911): if both true, activate ARC functinality and
+ mService.sendCecCommand(HdmiCecMessageBuilder
+ .buildInitiateArc(mAddress, message.getSource()));
+ // TODO(b/80296911): else, send <Feature Abort>["Unrecongnized opcode"]
+
+ return true;
+ }
+
+ @Override
+ @ServiceThreadOnly
+ protected boolean handleRequestArcTermination(HdmiCecMessage message) {
+ assertRunOnServiceThread();
+ // TODO(b/80297105): Check if ARC supported.
+
+ // TODO(b/80297105): Check is currently in arc.
+
+ // TODO(b/80297105): If both true, deactivate ARC functionality and
+ mService.sendCecCommand(HdmiCecMessageBuilder
+ .buildTerminateArc(mAddress, message.getSource()));
+ // TODO(b/80297105): else, send <Feature Abort>["Unrecongnized opcode"]
+
+ return true;
+ }
+
+ @Override
+ @ServiceThreadOnly
+ protected boolean handleSystemAudioModeRequest(HdmiCecMessage message) {
+ assertRunOnServiceThread();
+ boolean systemAudioStatusOn = message.getParams().length != 0;
+ if (!setSystemAudioMode(systemAudioStatusOn)) {
+ mService.maySendFeatureAbortCommand(message, Constants.ABORT_REFUSED);
+ return true;
+ }
+
+ mService.sendCecCommand(HdmiCecMessageBuilder
+ .buildSetSystemAudioMode(mAddress, Constants.ADDR_BROADCAST, systemAudioStatusOn));
+ return true;
+ }
+
+ @Override
+ @ServiceThreadOnly
+ protected boolean handleSetSystemAudioMode(HdmiCecMessage message) {
+ assertRunOnServiceThread();
+ if (!setSystemAudioMode(HdmiUtils.parseCommandParamSystemAudioStatus(message))) {
+ mService.maySendFeatureAbortCommand(message, Constants.ABORT_REFUSED);
+ }
+ return true;
+ }
+
+ @Override
+ @ServiceThreadOnly
+ protected boolean handleSystemAudioModeStatus(HdmiCecMessage message) {
+ assertRunOnServiceThread();
+ if (!setSystemAudioMode(HdmiUtils.parseCommandParamSystemAudioStatus(message))) {
+ mService.maySendFeatureAbortCommand(message, Constants.ABORT_REFUSED);
+ }
+ return true;
+ }
+
+ private void reportAudioStatus(HdmiCecMessage message) {
+ assertRunOnServiceThread();
+
+ int volume = mService.getAudioManager().getStreamVolume(AudioManager.STREAM_MUSIC);
+ boolean mute = mService.getAudioManager().isStreamMute(AudioManager.STREAM_MUSIC);
+ int maxVolume = mService.getAudioManager().getStreamMaxVolume(AudioManager.STREAM_MUSIC);
+ int scaledVolume = VolumeControlAction.scaleToCecVolume(volume, maxVolume);
+
+ mService.sendCecCommand(HdmiCecMessageBuilder
+ .buildReportAudioStatus(mAddress, message.getSource(), scaledVolume, mute));
+ }
+
+ protected boolean setSystemAudioMode(boolean newSystemAudioMode) {
+ if (!isSystemAudioControlFeatureEnabled()) {
+ HdmiLogger.debug("Cannot turn " +
+ (newSystemAudioMode ? "on" : "off") + "system audio mode " +
+ "because the System Audio Control feature is disabled.");
+ return false;
+ }
+ HdmiLogger.debug("System Audio Mode change[old:%b new:%b]",
+ mSystemAudioActivated, newSystemAudioMode);
+ // Wake up device if System Audio Control is turned on but device is still on standby
+ if (newSystemAudioMode && mService.isPowerStandbyOrTransient()) {
+ mService.wakeUp();
+ // TODO(amyjojo): Switch to the corresponding input
+ }
+ // Mute device when feature is turned off and unmute device when feature is turned on
+ boolean currentMuteStatus =
+ mService.getAudioManager().isStreamMute(AudioManager.STREAM_MUSIC);
+ if (currentMuteStatus == newSystemAudioMode) {
+ mService.getAudioManager().adjustStreamVolume(AudioManager.STREAM_MUSIC,
+ newSystemAudioMode ? AudioManager.ADJUST_UNMUTE : AudioManager.ADJUST_MUTE, 0);
+ }
+ updateAudioManagerForSystemAudio(newSystemAudioMode);
+ synchronized (mLock) {
+ if (mSystemAudioActivated != newSystemAudioMode) {
+ mSystemAudioActivated = newSystemAudioMode;
+ mService.announceSystemAudioModeChange(newSystemAudioMode);
+ }
+ }
+ return true;
+ }
+
+ private void updateAudioManagerForSystemAudio(boolean on) {
+ int device = mService.getAudioManager().setHdmiSystemAudioSupported(on);
+ HdmiLogger.debug("[A]UpdateSystemAudio mode[on=%b] output=[%X]", on, device);
+ }
+
+ protected boolean isSystemAudioControlFeatureEnabled() {
+ synchronized (mLock) {
+ return mSystemAudioControlFeatureEnabled;
+ }
+ }
+
+ protected boolean isSystemAudioActivated() {
+ synchronized (mLock) {
+ return mSystemAudioActivated;
+ }
+ }
+
+ /** Reports if System Audio Mode is supported by the connected TV */
+ interface TvSystemAudioModeSupportedCallback {
+
+ /** {@code supported} is true if the TV is connected and supports System Audio Mode. */
+ void onResult(boolean supported);
+
+ }
+
+ /**
+ * Queries the connected TV to detect if System Audio Mode is supported by the TV.
+ *
+ * <p>This query may take up to 2 seconds to complete.
+ *
+ * <p>The result of the query may be cached until Audio device type is put in standby or loses
+ * its physical address.
+ */
+ void queryTvSystemAudioModeSupport(TvSystemAudioModeSupportedCallback callback) {
+ if (!mTvSystemAudioModeSupport) {
+ addAndStartAction(new DetectTvSystemAudioModeSupportAction(this, callback));
+ } else {
+ callback.onResult(true);
+ }
+ }
+
+ void setTvSystemAudioModeSupport(boolean supported) {
+ mTvSystemAudioModeSupport = supported;
+ }
+}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessage.java b/services/core/java/com/android/server/hdmi/HdmiCecMessage.java
index fd0ff9d..c005615 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecMessage.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecMessage.java
@@ -16,9 +16,11 @@
package com.android.server.hdmi;
+import android.annotation.Nullable;
import libcore.util.EmptyArray;
import java.util.Arrays;
+import java.util.Objects;
/**
* A class to encapsulate HDMI-CEC message used for the devices connected via
@@ -44,6 +46,27 @@
mParams = Arrays.copyOf(params, params.length);
}
+ @Override
+ public boolean equals(@Nullable Object message) {
+ if (message instanceof HdmiCecMessage) {
+ HdmiCecMessage that = (HdmiCecMessage) message;
+ return this.mSource == that.getSource() &&
+ this.mDestination == that.getDestination() &&
+ this.mOpcode == that.getOpcode() &&
+ Arrays.equals(this.mParams, that.getParams());
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(
+ mSource,
+ mDestination,
+ mOpcode,
+ Arrays.hashCode(mParams));
+ }
+
/**
* Return the source address field of the message. It is the logical address
* of the device which generated the message.
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java b/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java
index 9a51e3c..f1cb246 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java
@@ -211,6 +211,28 @@
}
/**
+ * Build <Initiate Arc>
+ *
+ * @param src source address of command
+ * @param dest destination address of command
+ * @return newly created {@link HdmiCecMessage}
+ */
+ static HdmiCecMessage buildInitiateArc(int src, int dest) {
+ return buildCommand(src, dest, Constants.MESSAGE_INITIATE_ARC);
+ }
+
+ /**
+ * Build <Terminate Arc>
+ *
+ * @param src source address of command
+ * @param dest destination address of command
+ * @return newly created {@link HdmiCecMessage}
+ */
+ static HdmiCecMessage buildTerminateArc(int src, int dest) {
+ return buildCommand(src, dest, Constants.MESSAGE_TERMINATE_ARC);
+ }
+
+ /**
* Build <Request Arc Termination>
*
* @param src source address of command
@@ -255,6 +277,16 @@
}
/**
+ * Build <Request Active Source> command.
+ *
+ * @param src source address of command
+ * @return newly created {@link HdmiCecMessage}
+ */
+ static HdmiCecMessage buildRequestActiveSource(int src) {
+ return buildCommand(src, Constants.ADDR_BROADCAST, Constants.MESSAGE_REQUEST_ACTIVE_SOURCE);
+ }
+
+ /**
* Build <Active Source> command.
*
* @param src source address of command
@@ -372,6 +404,34 @@
}
/**
+ * Build <Set System Audio Mode> command.
+ *
+ * @param src source address of command
+ * @param des destination address of command
+ * @param systemAudioStatus whether to set System Audio Mode on or off
+ * @return newly created {@link HdmiCecMessage}
+ */
+ static HdmiCecMessage buildSetSystemAudioMode(int src, int des, boolean systemAudioStatus) {
+ return buildCommandWithBooleanParam(src, des, Constants.MESSAGE_SET_SYSTEM_AUDIO_MODE,
+ systemAudioStatus
+ );
+ }
+
+ /**
+ * Build <Report System Audio Mode> command.
+ *
+ * @param src source address of command
+ * @param des destination address of command
+ * @param systemAudioStatus whether System Audio Mode is on or off
+ * @return newly created {@link HdmiCecMessage}
+ */
+ static HdmiCecMessage buildReportSystemAudioMode(int src, int des, boolean systemAudioStatus) {
+ return buildCommandWithBooleanParam(src, des, Constants.MESSAGE_SYSTEM_AUDIO_MODE_STATUS,
+ systemAudioStatus
+ );
+ }
+
+ /**
* Build <Give Audio Status> command.
*
* @param src source address of command
@@ -383,6 +443,21 @@
}
/**
+ * Build <Report Audio Status> command.
+ *
+ * @param src source address of command
+ * @param dest destination address of command
+ * @param volume volume level of current device in param
+ * @param mute mute status of current device in param
+ * @return newly created {@link HdmiCecMessage}
+ */
+ static HdmiCecMessage buildReportAudioStatus(int src, int dest, int volume, boolean mute) {
+ byte status = (byte) ((byte) (mute ? 1 << 7 : 0) | ((byte) volume & 0x7F));
+ byte[] params = new byte[] { status };
+ return buildCommand(src, dest, Constants.MESSAGE_REPORT_AUDIO_STATUS, params);
+ }
+
+ /**
* Build <User Control Pressed> command.
*
* @param src source address of command
@@ -592,6 +667,23 @@
return new HdmiCecMessage(src, dest, opcode, params);
}
+ /**
+ * Build a {@link HdmiCecMessage} with a boolean param and other given values.
+ *
+ * @param src source address of command
+ * @param des destination address of command
+ * @param opcode opcode for a message
+ * @param param boolean param for building the command
+ * @return newly created {@link HdmiCecMessage}
+ */
+ private static HdmiCecMessage buildCommandWithBooleanParam(int src, int des,
+ int opcode, boolean param) {
+ byte[] params = new byte[]{
+ param ? (byte) 0b1 : 0b0
+ };
+ return buildCommand(src, des, opcode, params);
+ }
+
private static byte[] physicalAddressToParam(int physicalAddress) {
return new byte[] {
(byte) ((physicalAddress >> 8) & 0xFF),
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index a1753e5..71ba03a 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -96,7 +96,7 @@
static final String PERMISSION = "android.permission.HDMI_CEC";
- // The reason code to initiate intializeCec().
+ // The reason code to initiate initializeCec().
static final int INITIATED_BY_ENABLE_CEC = 0;
static final int INITIATED_BY_BOOT_UP = 1;
static final int INITIATED_BY_SCREEN_ON = 2;
@@ -407,8 +407,9 @@
Slog.i(TAG, "Device does not support HDMI-CEC.");
return;
}
-
- mMhlController = HdmiMhlControllerStub.create(this);
+ if (mMhlController == null) {
+ mMhlController = HdmiMhlControllerStub.create(this);
+ }
if (!mMhlController.isReady()) {
Slog.i(TAG, "Device does not support MHL-control.");
}
@@ -440,6 +441,11 @@
mCecController = cecController;
}
+ @VisibleForTesting
+ void setHdmiMhlController(HdmiMhlControllerStub hdmiMhlController) {
+ mMhlController = hdmiMhlController;
+ }
+
@Override
public void onBootPhase(int phase) {
if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
@@ -598,7 +604,8 @@
}
@ServiceThreadOnly
- private void allocateLogicalAddress(final ArrayList<HdmiCecLocalDevice> allocatingDevices,
+ @VisibleForTesting
+ protected void allocateLogicalAddress(final ArrayList<HdmiCecLocalDevice> allocatingDevices,
final int initiatedBy) {
assertRunOnServiceThread();
mCecController.clearLogicalAddress();
@@ -665,7 +672,8 @@
// Initialize HDMI port information. Combine the information from CEC and MHL HAL and
// keep them in one place.
@ServiceThreadOnly
- private void initPortInfo() {
+ @VisibleForTesting
+ protected void initPortInfo() {
assertRunOnServiceThread();
HdmiPortInfo[] cecPortInfo = null;
@@ -1709,6 +1717,38 @@
}
@Override
+ public void reportAudioStatus(final int deviceType, final int volume, final int maxVolume,
+ final boolean isMute) {
+ enforceAccessPermission();
+ runOnServiceThread(new Runnable() {
+ @Override
+ public void run() {
+ HdmiCecLocalDevice device = mCecController.getLocalDevice(deviceType);
+ if (device == null) {
+ Slog.w(TAG, "Local device not available");
+ return;
+ }
+ if (audioSystem() == null) {
+ Slog.w(TAG, "audio system is not available");
+ return;
+ }
+ if (audioSystem().mSystemAudioSource == null) {
+ Slog.w(TAG, "audio system is not in system audio mode");
+ return;
+ }
+ int scaledVolume = VolumeControlAction.scaleToCecVolume(volume, maxVolume);
+
+ sendCecCommand(HdmiCecMessageBuilder
+ .buildReportAudioStatus(
+ device.getDeviceInfo().getLogicalAddress(),
+ audioSystem().mSystemAudioSource,
+ scaledVolume,
+ isMute));
+ }
+ });
+ }
+
+ @Override
protected void dump(FileDescriptor fd, final PrintWriter writer, String[] args) {
if (!DumpUtils.checkDumpPermission(getContext(), TAG, writer)) return;
final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ");
@@ -2001,6 +2041,10 @@
return mLocalDevices.contains(HdmiDeviceInfo.DEVICE_TV);
}
+ boolean isAudioSystemDevice() {
+ return mLocalDevices.contains(HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM);
+ }
+
boolean isTvDeviceEnabled() {
return isTvDevice() && tv() != null;
}
@@ -2010,6 +2054,11 @@
mCecController.getLocalDevice(HdmiDeviceInfo.DEVICE_PLAYBACK);
}
+ public HdmiCecLocalDeviceAudioSystem audioSystem() {
+ return (HdmiCecLocalDeviceAudioSystem) mCecController.getLocalDevice(
+ HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM);
+ }
+
AudioManager getAudioManager() {
return (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
}
@@ -2071,6 +2120,11 @@
return mWakeUpMessageReceived;
}
+ @VisibleForTesting
+ boolean isStandbyMessageReceived() {
+ return mStandbyMessageReceived;
+ }
+
@ServiceThreadOnly
private void onWakeUp() {
assertRunOnServiceThread();
@@ -2090,17 +2144,23 @@
}
@ServiceThreadOnly
- private void onStandby(final int standbyAction) {
+ @VisibleForTesting
+ protected void onStandby(final int standbyAction) {
assertRunOnServiceThread();
mPowerStatus = HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY;
invokeVendorCommandListenersOnControlStateChanged(false,
HdmiControlManager.CONTROL_STATE_CHANGED_REASON_STANDBY);
- if (!canGoToStandby()) {
+
+ final List<HdmiCecLocalDevice> devices = getAllLocalDevices();
+
+ if (!isStandbyMessageReceived() && !canGoToStandby()) {
mPowerStatus = HdmiControlManager.POWER_STATUS_STANDBY;
+ for (HdmiCecLocalDevice device : devices) {
+ device.onStandby(mStandbyMessageReceived, standbyAction);
+ }
return;
}
- final List<HdmiCecLocalDevice> devices = getAllLocalDevices();
disableDevices(new PendingActionClearedCallback() {
@Override
public void onCleared(HdmiCecLocalDevice device) {
@@ -2173,8 +2233,10 @@
device.onStandby(mStandbyMessageReceived, standbyAction);
}
mStandbyMessageReceived = false;
- mCecController.setOption(OptionKey.SYSTEM_CEC_CONTROL, false);
- mMhlController.setOption(OPTION_MHL_SERVICE_CONTROL, DISABLED);
+ if (!isAudioSystemDevice()) {
+ mCecController.setOption(OptionKey.SYSTEM_CEC_CONTROL, false);
+ mMhlController.setOption(OPTION_MHL_SERVICE_CONTROL, DISABLED);
+ }
}
private void addVendorCommandListener(IHdmiVendorCommandListener listener, int deviceType) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiLogger.java b/services/core/java/com/android/server/hdmi/HdmiLogger.java
index ebe52c0..2309293 100644
--- a/services/core/java/com/android/server/hdmi/HdmiLogger.java
+++ b/services/core/java/com/android/server/hdmi/HdmiLogger.java
@@ -17,7 +17,6 @@
package com.android.server.hdmi;
import android.annotation.Nullable;
-import android.os.Build;
import android.os.SystemClock;
import android.util.Pair;
import android.util.Slog;
@@ -41,7 +40,7 @@
final class HdmiLogger {
private static final String TAG = "HDMI";
// Logging duration for same error message.
- private static final long ERROR_LOG_DURATTION_MILLIS = 20 * 1000; // 20s
+ private static final long ERROR_LOG_DURATION_MILLIS = 20 * 1000; // 20s
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
@@ -134,6 +133,6 @@
}
private static boolean shouldLogNow(@Nullable Pair<Long, Integer> timing, long curTime) {
- return timing == null || curTime - timing.first > ERROR_LOG_DURATTION_MILLIS;
+ return timing == null || curTime - timing.first > ERROR_LOG_DURATION_MILLIS;
}
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiUtils.java b/services/core/java/com/android/server/hdmi/HdmiUtils.java
index 4ac3bba..2a8117f 100644
--- a/services/core/java/com/android/server/hdmi/HdmiUtils.java
+++ b/services/core/java/com/android/server/hdmi/HdmiUtils.java
@@ -114,7 +114,7 @@
*
* @param logicalAddress the logical address to verify
* @param deviceType the device type to check
- * @throw IllegalArgumentException
+ * @throws IllegalArgumentException
*/
static void verifyAddressType(int logicalAddress, int deviceType) {
int actualDeviceType = getTypeFromAddress(logicalAddress);
diff --git a/services/core/java/com/android/server/hdmi/PowerStatusMonitorAction.java b/services/core/java/com/android/server/hdmi/PowerStatusMonitorAction.java
index 6893012..a62d0b6 100644
--- a/services/core/java/com/android/server/hdmi/PowerStatusMonitorAction.java
+++ b/services/core/java/com/android/server/hdmi/PowerStatusMonitorAction.java
@@ -38,7 +38,7 @@
private static final int INVALID_POWER_STATUS = POWER_STATUS_UNKNOWN - 1;
// Monitoring interval (60s)
- private static final int MONITIROING_INTERNAL_MS = 60000;
+ private static final int MONITORING_INTERNAL_MS = 60000;
// Timeout once sending <Give Device Power Status>
private static final int REPORT_POWER_STATUS_TIMEOUT_MS = 5000;
@@ -132,7 +132,7 @@
mState = STATE_WAIT_FOR_REPORT_POWER_STATUS;
// Add both timers, monitoring and timeout.
- addTimer(STATE_WAIT_FOR_NEXT_MONITORING, MONITIROING_INTERNAL_MS);
+ addTimer(STATE_WAIT_FOR_NEXT_MONITORING, MONITORING_INTERNAL_MS);
addTimer(STATE_WAIT_FOR_REPORT_POWER_STATUS, REPORT_POWER_STATUS_TIMEOUT_MS);
}
diff --git a/services/core/java/com/android/server/hdmi/RequestArcAction.java b/services/core/java/com/android/server/hdmi/RequestArcAction.java
index 75a79cb..c70101c 100644
--- a/services/core/java/com/android/server/hdmi/RequestArcAction.java
+++ b/services/core/java/com/android/server/hdmi/RequestArcAction.java
@@ -35,7 +35,7 @@
*
* @param source {@link HdmiCecLocalDevice} instance
* @param avrAddress address of AV receiver. It should be AUDIO_SYSTEM type
- * @throw IllegalArugmentException if device type of sourceAddress and avrAddress
+ * @throws IllegalArgumentException if device type of sourceAddress and avrAddress
* is invalid
*/
RequestArcAction(HdmiCecLocalDevice source, int avrAddress) {
diff --git a/services/core/java/com/android/server/hdmi/SystemAudioAction.java b/services/core/java/com/android/server/hdmi/SystemAudioAction.java
index 449b208..a5477e8 100644
--- a/services/core/java/com/android/server/hdmi/SystemAudioAction.java
+++ b/services/core/java/com/android/server/hdmi/SystemAudioAction.java
@@ -60,7 +60,7 @@
* @param avrAddress logical address of AVR device
* @param targetStatus Whether to enable the system audio mode or not
* @param callback callback interface to be notified when it's done
- * @throw IllegalArugmentException if device type of sourceAddress and avrAddress is invalid
+ * @throws IllegalArgumentException if device type of sourceAddress and avrAddress is invalid
*/
SystemAudioAction(HdmiCecLocalDevice source, int avrAddress, boolean targetStatus,
IHdmiControlCallback callback) {
diff --git a/services/core/java/com/android/server/hdmi/SystemAudioActionFromAvr.java b/services/core/java/com/android/server/hdmi/SystemAudioActionFromAvr.java
index eb5119b..6ddff91 100644
--- a/services/core/java/com/android/server/hdmi/SystemAudioActionFromAvr.java
+++ b/services/core/java/com/android/server/hdmi/SystemAudioActionFromAvr.java
@@ -32,7 +32,7 @@
* @param avrAddress logical address of AVR device
* @param targetStatus Whether to enable the system audio mode or not
* @param callback callback interface to be notified when it's done
- * @throw IllegalArugmentException if device type of tvAddress and avrAddress is invalid
+ * @throws IllegalArgumentException if device type of tvAddress and avrAddress is invalid
*/
SystemAudioActionFromAvr(HdmiCecLocalDevice source, int avrAddress,
boolean targetStatus, IHdmiControlCallback callback) {
diff --git a/services/core/java/com/android/server/hdmi/SystemAudioActionFromTv.java b/services/core/java/com/android/server/hdmi/SystemAudioActionFromTv.java
index 02ecd13..5c0c272 100644
--- a/services/core/java/com/android/server/hdmi/SystemAudioActionFromTv.java
+++ b/services/core/java/com/android/server/hdmi/SystemAudioActionFromTv.java
@@ -32,7 +32,7 @@
* @param avrAddress logical address of AVR device
* @param targetStatus Whether to enable the system audio mode or not
* @param callback callback interface to be notified when it's done
- * @throw IllegalArugmentException if device type of tvAddress is invalid
+ * @throws IllegalArgumentException if device type of tvAddress is invalid
*/
SystemAudioActionFromTv(HdmiCecLocalDevice sourceAddress, int avrAddress,
boolean targetStatus, IHdmiControlCallback callback) {
diff --git a/services/core/java/com/android/server/hdmi/SystemAudioInitiationActionFromAvr.java b/services/core/java/com/android/server/hdmi/SystemAudioInitiationActionFromAvr.java
new file mode 100644
index 0000000..d4932f9
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/SystemAudioInitiationActionFromAvr.java
@@ -0,0 +1,136 @@
+/*
+ * 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.hdmi;
+
+import android.hardware.tv.cec.V1_0.SendMessageResult;
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * Feature action that handles System Audio Mode initiated by AVR devices.
+ */
+public class SystemAudioInitiationActionFromAvr extends HdmiCecFeatureAction {
+
+ // State that waits for <Active Source> once send <Request Active Source>.
+ private static final int STATE_WAITING_FOR_ACTIVE_SOURCE = 1;
+ @VisibleForTesting
+ static final int MAX_RETRY_COUNT = 5;
+
+ private int mSendRequestActiveSourceRetryCount = 0;
+ private int mSendSetSystemAudioModeRetryCount = 0;
+
+ SystemAudioInitiationActionFromAvr(HdmiCecLocalDevice source) {
+ super(source);
+ }
+
+ @Override
+ boolean start() {
+ if (audioSystem().mActiveSource.physicalAddress == Constants.INVALID_PHYSICAL_ADDRESS) {
+ mState = STATE_WAITING_FOR_ACTIVE_SOURCE;
+ addTimer(mState, HdmiConfig.TIMEOUT_MS);
+ sendRequestActiveSource();
+ } else {
+ queryTvSystemAudioModeSupport();
+ }
+ return true;
+ }
+
+ @Override
+ boolean processCommand(HdmiCecMessage cmd) {
+ switch (cmd.getOpcode()) {
+ case Constants.MESSAGE_ACTIVE_SOURCE:
+ // received <Active Source>
+ if (mState != STATE_WAITING_FOR_ACTIVE_SOURCE) {
+ return false;
+ }
+ mActionTimer.clearTimerMessage();
+ int physicalAddress = HdmiUtils.twoBytesToInt(cmd.getParams());
+ if (physicalAddress != getSourcePath()) {
+ audioSystem().setActiveSource(cmd.getSource(), physicalAddress);
+ }
+ queryTvSystemAudioModeSupport();
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ void handleTimerEvent(int state) {
+ if (mState != state) {
+ return;
+ }
+
+ switch (mState) {
+ case STATE_WAITING_FOR_ACTIVE_SOURCE:
+ handleActiveSourceTimeout();
+ break;
+ }
+ }
+
+ protected void sendRequestActiveSource() {
+ sendCommand(HdmiCecMessageBuilder.buildRequestActiveSource(getSourceAddress()),
+ result -> {
+ if (result != SendMessageResult.SUCCESS) {
+ if (mSendRequestActiveSourceRetryCount < MAX_RETRY_COUNT) {
+ mSendRequestActiveSourceRetryCount++;
+ sendRequestActiveSource();
+ } else {
+ audioSystem().setSystemAudioMode(false);
+ finish();
+ }
+ }
+ });
+ }
+
+ protected void sendSetSystemAudioMode(boolean on, int dest) {
+ sendCommand(HdmiCecMessageBuilder.buildSetSystemAudioMode(getSourceAddress(),
+ dest, on), result -> {
+ if (result != SendMessageResult.SUCCESS) {
+ if (mSendSetSystemAudioModeRetryCount < MAX_RETRY_COUNT) {
+ mSendSetSystemAudioModeRetryCount++;
+ sendSetSystemAudioMode(on, dest);
+ } else {
+ audioSystem().setSystemAudioMode(false);
+ finish();
+ }
+ }
+ });
+ }
+
+ private void handleActiveSourceTimeout() {
+ HdmiLogger.debug("Cannot get active source.");
+ audioSystem().setSystemAudioMode(false);
+ finish();
+ }
+
+ private void queryTvSystemAudioModeSupport() {
+ audioSystem().queryTvSystemAudioModeSupport(
+ supported -> {
+ if (supported) {
+ if (audioSystem().setSystemAudioMode(true)) {
+ sendSetSystemAudioMode(true, Constants.ADDR_BROADCAST);
+ }
+ finish();
+ } else {
+ audioSystem().setSystemAudioMode(false);
+ finish();
+ }
+ });
+ }
+
+ private void switchToRelevantInputForDeviceAt(int physicalAddress) {
+ // TODO(shubang): implement this method
+ }
+}
diff --git a/services/core/java/com/android/server/hdmi/SystemAudioStatusAction.java b/services/core/java/com/android/server/hdmi/SystemAudioStatusAction.java
index d41a36c..13f0f4ae 100644
--- a/services/core/java/com/android/server/hdmi/SystemAudioStatusAction.java
+++ b/services/core/java/com/android/server/hdmi/SystemAudioStatusAction.java
@@ -64,7 +64,7 @@
}
private void handleSendGiveAudioStatusFailure() {
- // Inform to all application that the audio status (volumn, mute) of
+ // Inform to all application that the audio status (volume, mute) of
// the audio amplifier is unknown.
tv().setAudioStatus(false, Constants.UNKNOWN_VOLUME);
diff --git a/services/core/java/com/android/server/input/InputForwarder.java b/services/core/java/com/android/server/input/InputForwarder.java
index 38a1cd7..00af839 100644
--- a/services/core/java/com/android/server/input/InputForwarder.java
+++ b/services/core/java/com/android/server/input/InputForwarder.java
@@ -19,7 +19,6 @@
import android.app.IInputForwarder;
import android.hardware.input.InputManagerInternal;
import android.view.InputEvent;
-import android.view.MotionEvent;
import com.android.server.LocalServices;
@@ -40,9 +39,7 @@
@Override
public boolean forwardEvent(InputEvent event) {
- if (event instanceof MotionEvent) {
- ((MotionEvent) event).setDisplayId(mDisplayId);
- }
+ event.setDisplayId(mDisplayId);
return mInputManagerInternal.injectInputEvent(event, INJECT_INPUT_EVENT_MODE_ASYNC);
}
}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index d515ce6..4edd48f 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -68,7 +68,6 @@
import android.os.WorkSource.WorkChain;
import android.provider.Settings;
import android.provider.Telephony.Carriers;
-import android.provider.Telephony.Sms.Intents;
import android.telephony.CarrierConfigManager;
import android.telephony.SubscriptionManager;
import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
@@ -254,7 +253,7 @@
// 1 second, or 1 Hz frequency.
private static final long LOCATION_UPDATE_MIN_TIME_INTERVAL_MILLIS = 1000;
// Default update duration in milliseconds for REQUEST_LOCATION.
- private static final long LOCATION_UPDATE_DURATION_MILLIS = 0;
+ private static final long LOCATION_UPDATE_DURATION_MILLIS = 10 * 1000;
/** simpler wrapper for ProviderRequest + Worksource */
private static class GpsRequest {
@@ -2402,28 +2401,7 @@
.addOnSubscriptionsChangedListener(mOnSubscriptionsChangedListener);
// listen for events
- IntentFilter intentFilter;
- if (native_is_agps_ril_supported()) {
- intentFilter = new IntentFilter();
- intentFilter.addAction(Intents.DATA_SMS_RECEIVED_ACTION);
- intentFilter.addDataScheme("sms");
- intentFilter.addDataAuthority("localhost", "7275");
- mContext.registerReceiver(mBroadcastReceiver, intentFilter, null, this);
-
- intentFilter = new IntentFilter();
- intentFilter.addAction(Intents.WAP_PUSH_RECEIVED_ACTION);
- try {
- intentFilter.addDataType("application/vnd.omaloc-supl-init");
- } catch (IntentFilter.MalformedMimeTypeException e) {
- Log.w(TAG, "Malformed SUPL init mime type");
- }
- mContext.registerReceiver(mBroadcastReceiver, intentFilter, null, this);
- } else if (DEBUG) {
- Log.d(TAG, "Skipped registration for SMS/WAP-PUSH messages because AGPS Ril in GPS"
- + " HAL is not supported");
- }
-
- intentFilter = new IntentFilter();
+ IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(ALARM_WAKEUP);
intentFilter.addAction(ALARM_TIMEOUT);
intentFilter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 36b04fc..adfa8d5 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -955,12 +955,18 @@
@GuardedBy("mSeparateChallengeLock")
private void setSeparateProfileChallengeEnabledLocked(@UserIdInt int userId, boolean enabled,
String managedUserPassword) {
+ final boolean old = getBoolean(SEPARATE_PROFILE_CHALLENGE_KEY, false, userId);
setBoolean(SEPARATE_PROFILE_CHALLENGE_KEY, enabled, userId);
- if (enabled) {
- mStorage.removeChildProfileLock(userId);
- removeKeystoreProfileKey(userId);
- } else {
- tieManagedProfileLockIfNecessary(userId, managedUserPassword);
+ try {
+ if (enabled) {
+ mStorage.removeChildProfileLock(userId);
+ removeKeystoreProfileKey(userId);
+ } else {
+ tieManagedProfileLockIfNecessary(userId, managedUserPassword);
+ }
+ } catch (IllegalStateException e) {
+ setBoolean(SEPARATE_PROFILE_CHALLENGE_KEY, old, userId);
+ throw e;
}
}
@@ -2006,7 +2012,7 @@
enforceShell();
final long origId = Binder.clearCallingIdentity();
try {
- (new LockSettingsShellCommand(mContext, new LockPatternUtils(mContext))).exec(
+ (new LockSettingsShellCommand(new LockPatternUtils(mContext))).exec(
this, in, out, err, args, callback, resultReceiver);
} finally {
Binder.restoreCallingIdentity(origId);
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java b/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java
index 4d2cf32..07f23ce 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java
@@ -21,13 +21,13 @@
import static com.android.internal.widget.LockPatternUtils.stringToPattern;
import android.app.ActivityManager;
-import android.content.Context;
-import android.os.RemoteException;
import android.os.ShellCommand;
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.LockPatternUtils.RequestThrottledException;
+import java.io.PrintWriter;
+
class LockSettingsShellCommand extends ShellCommand {
private static final String COMMAND_SET_PATTERN = "set-pattern";
@@ -38,20 +38,22 @@
private static final String COMMAND_SET_DISABLED = "set-disabled";
private static final String COMMAND_VERIFY = "verify";
private static final String COMMAND_GET_DISABLED = "get-disabled";
+ private static final String COMMAND_HELP = "help";
private int mCurrentUserId;
private final LockPatternUtils mLockPatternUtils;
- private final Context mContext;
private String mOld = "";
private String mNew = "";
- LockSettingsShellCommand(Context context, LockPatternUtils lockPatternUtils) {
- mContext = context;
+ LockSettingsShellCommand(LockPatternUtils lockPatternUtils) {
mLockPatternUtils = lockPatternUtils;
}
@Override
public int onCommand(String cmd) {
+ if (cmd == null) {
+ return handleDefaultCommands(cmd);
+ }
try {
mCurrentUserId = ActivityManager.getService().getCurrentUser().id;
@@ -84,6 +86,9 @@
case COMMAND_GET_DISABLED:
runGetDisabled();
break;
+ case COMMAND_HELP:
+ onHelp();
+ break;
default:
getErrPrintWriter().println("Unknown command: " + cmd);
break;
@@ -103,6 +108,43 @@
@Override
public void onHelp() {
+ try (final PrintWriter pw = getOutPrintWriter();) {
+ pw.println("lockSettings service commands:");
+ pw.println("");
+ pw.println("NOTE: when lock screen is set, all commands require the --old <CREDENTIAL>"
+ + " argument.");
+ pw.println("");
+ pw.println(" help");
+ pw.println(" Prints this help text.");
+ pw.println("");
+ pw.println(" get-disabled [--old <CREDENTIAL>] [--user USER_ID]");
+ pw.println(" Checks whether lock screen is disabled.");
+ pw.println("");
+ pw.println(" set-disabled [--old <CREDENTIAL>] [--user USER_ID] <true|false>");
+ pw.println(" When true, disables lock screen.");
+ pw.println("");
+ pw.println(" set-pattern [--old <CREDENTIAL>] [--user USER_ID] <PATTERN>");
+ pw.println(" Sets the lock screen as pattern, using the given PATTERN to unlock.");
+ pw.println("");
+ pw.println(" set-pin [--old <CREDENTIAL>] [--user USER_ID] <PIN>");
+ pw.println(" Sets the lock screen as PIN, using the given PIN to unlock.");
+ pw.println("");
+ pw.println(" set-pin [--old <CREDENTIAL>] [--user USER_ID] <PASSWORD>");
+ pw.println(" Sets the lock screen as password, using the given PASSOWRD to unlock.");
+ pw.println("");
+ pw.println(" sp [--old <CREDENTIAL>] [--user USER_ID]");
+ pw.println(" Gets whether synthetic password is enabled.");
+ pw.println("");
+ pw.println(" sp [--old <CREDENTIAL>] [--user USER_ID] <1|0>");
+ pw.println(" Enables / disables synthetic password.");
+ pw.println("");
+ pw.println(" clear [--old <CREDENTIAL>] [--user USER_ID]");
+ pw.println(" Clears the lock credentials.");
+ pw.println("");
+ pw.println(" verify [--old <CREDENTIAL>] [--user USER_ID]");
+ pw.println(" Verifies the lock credentials.");
+ pw.println("");
+ }
}
private void parseArgs() {
@@ -134,27 +176,27 @@
mLockPatternUtils.isSyntheticPasswordEnabled()));
}
- private void runSetPattern() throws RemoteException {
+ private void runSetPattern() {
mLockPatternUtils.saveLockPattern(stringToPattern(mNew), mOld, mCurrentUserId);
getOutPrintWriter().println("Pattern set to '" + mNew + "'");
}
- private void runSetPassword() throws RemoteException {
+ private void runSetPassword() {
mLockPatternUtils.saveLockPassword(mNew, mOld, PASSWORD_QUALITY_ALPHABETIC, mCurrentUserId);
getOutPrintWriter().println("Password set to '" + mNew + "'");
}
- private void runSetPin() throws RemoteException {
+ private void runSetPin() {
mLockPatternUtils.saveLockPassword(mNew, mOld, PASSWORD_QUALITY_NUMERIC, mCurrentUserId);
getOutPrintWriter().println("Pin set to '" + mNew + "'");
}
- private void runClear() throws RemoteException {
+ private void runClear() {
mLockPatternUtils.clearLock(mOld, mCurrentUserId);
getOutPrintWriter().println("Lock credential cleared");
}
- private void runSetDisabled() throws RemoteException {
+ private void runSetDisabled() {
final boolean disabled = Boolean.parseBoolean(mNew);
mLockPatternUtils.setLockScreenDisabled(disabled, mCurrentUserId);
getOutPrintWriter().println("Lock screen disabled set to " + disabled);
@@ -165,7 +207,7 @@
getOutPrintWriter().println(isLockScreenDisabled);
}
- private boolean checkCredential() throws RemoteException {
+ private boolean checkCredential() {
final boolean havePassword = mLockPatternUtils.isLockPasswordEnabled(mCurrentUserId);
final boolean havePattern = mLockPatternUtils.isLockPatternEnabled(mCurrentUserId);
if (havePassword || havePattern) {
@@ -186,6 +228,9 @@
mLockPatternUtils.reportFailedPasswordAttempt(mCurrentUserId);
}
getOutPrintWriter().println("Old password '" + mOld + "' didn't match");
+ } else {
+ // Resets the counter for failed password attempts to 0.
+ mLockPatternUtils.reportSuccessfulPasswordAttempt(mCurrentUserId);
}
return result;
} catch (RequestThrottledException e) {
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index 442354b..a49cf44 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -481,7 +481,7 @@
mAudioManagerInternal.adjustStreamVolumeForUid(stream, direction, flags,
packageName, uid);
}
- } catch (IllegalArgumentException e) {
+ } catch (IllegalArgumentException | SecurityException e) {
Log.e(TAG, "Cannot adjust volume: direction=" + direction + ", stream="
+ stream + ", flags=" + flags + ", packageName=" + packageName
+ ", uid=" + uid + ", useSuggested=" + useSuggested
diff --git a/services/core/java/com/android/server/notification/GroupHelper.java b/services/core/java/com/android/server/notification/GroupHelper.java
index ce805aad..b1cd627 100644
--- a/services/core/java/com/android/server/notification/GroupHelper.java
+++ b/services/core/java/com/android/server/notification/GroupHelper.java
@@ -32,16 +32,17 @@
private static final String TAG = "GroupHelper";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
- protected static final int AUTOGROUP_AT_COUNT = 4;
protected static final String AUTOGROUP_KEY = "ranker_group";
private final Callback mCallback;
+ private final int mAutoGroupAtCount;
// Map of user : <Map of package : notification keys>. Only contains notifications that are not
// grouped by the app (aka no group or sort key).
Map<Integer, Map<String, LinkedHashSet<String>>> mUngroupedNotifications = new HashMap<>();
- public GroupHelper(Callback callback) {;
+ public GroupHelper(int autoGroupAtCount, Callback callback) {
+ mAutoGroupAtCount = autoGroupAtCount;
mCallback = callback;
}
@@ -68,7 +69,7 @@
notificationsForPackage.add(sbn.getKey());
ungroupedNotificationsByUser.put(sbn.getPackageName(), notificationsForPackage);
- if (notificationsForPackage.size() >= AUTOGROUP_AT_COUNT
+ if (notificationsForPackage.size() >= mAutoGroupAtCount
|| autogroupSummaryExists) {
notificationsToGroup.addAll(notificationsForPackage);
}
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index 5eb7397..af4f426 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -54,6 +54,7 @@
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
+import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
import android.util.proto.ProtoOutputStream;
@@ -116,9 +117,12 @@
// contains connections to all connected services, including app services
// and system services
private final ArrayList<ManagedServiceInfo> mServices = new ArrayList<>();
- // things that will be put into mServices as soon as they're ready
- private final ArrayList<String> mServicesBinding = new ArrayList<>();
- private final ArraySet<String> mServicesRebinding = new ArraySet<>();
+ /**
+ * The services that have been bound by us. If the service is also connected, it will also
+ * be in {@link #mServices}.
+ */
+ private final ArrayList<Pair<ComponentName, Integer>> mServicesBound = new ArrayList<>();
+ private final ArraySet<Pair<ComponentName, Integer>> mServicesRebinding = new ArraySet<>();
// lists the component names of all enabled (and therefore potentially connected)
// app services for current profiles.
@@ -917,13 +921,13 @@
final boolean isSystem) {
if (DEBUG) Slog.v(TAG, "registerService: " + name + " u=" + userid);
- final String servicesBindingTag = name.toString() + "/" + userid;
- if (mServicesBinding.contains(servicesBindingTag)) {
- Slog.v(TAG, "Not registering " + name + " as bind is already in progress");
+ final Pair<ComponentName, Integer> servicesBindingTag = Pair.create(name, userid);
+ if (mServicesBound.contains(servicesBindingTag)) {
+ Slog.v(TAG, "Not registering " + name + " is already bound");
// stop registering this thing already! we're working on it
return;
}
- mServicesBinding.add(servicesBindingTag);
+ mServicesBound.add(servicesBindingTag);
final int N = mServices.size();
for (int i = N - 1; i >= 0; i--) {
@@ -934,11 +938,7 @@
Slog.v(TAG, " disconnecting old " + getCaption() + ": " + info.service);
removeServiceLocked(i);
if (info.connection != null) {
- try {
- mContext.unbindService(info.connection);
- } catch (IllegalArgumentException e) {
- Slog.e(TAG, "failed to unbind " + name, e);
- }
+ unbindService(info.connection, info.component, info.userid);
}
}
}
@@ -969,11 +969,11 @@
@Override
public void onServiceConnected(ComponentName name, IBinder binder) {
+ Slog.v(TAG, getCaption() + " service connected: " + name);
boolean added = false;
ManagedServiceInfo info = null;
synchronized (mMutex) {
mServicesRebinding.remove(servicesBindingTag);
- mServicesBinding.remove(servicesBindingTag);
try {
mService = asInterface(binder);
info = newServiceInfo(mService, name,
@@ -991,7 +991,6 @@
@Override
public void onServiceDisconnected(ComponentName name) {
- mServicesBinding.remove(servicesBindingTag);
Slog.v(TAG, getCaption() + " connection lost: " + name);
}
@@ -999,12 +998,7 @@
public void onBindingDied(ComponentName name) {
Slog.w(TAG, getCaption() + " binding died: " + name);
synchronized (mMutex) {
- mServicesBinding.remove(servicesBindingTag);
- try {
- mContext.unbindService(this);
- } catch (IllegalArgumentException e) {
- Slog.e(TAG, "failed to unbind " + name, e);
- }
+ unbindService(this, name, userid);
if (!mServicesRebinding.contains(servicesBindingTag)) {
mServicesRebinding.add(servicesBindingTag);
mHandler.postDelayed(new Runnable() {
@@ -1024,12 +1018,12 @@
serviceConnection,
BIND_AUTO_CREATE | BIND_FOREGROUND_SERVICE | BIND_ALLOW_WHITELIST_MANAGEMENT,
new UserHandle(userid))) {
- mServicesBinding.remove(servicesBindingTag);
+ mServicesBound.remove(servicesBindingTag);
Slog.w(TAG, "Unable to bind " + getCaption() + " service: " + intent);
return;
}
} catch (SecurityException ex) {
- mServicesBinding.remove(servicesBindingTag);
+ mServicesBound.remove(servicesBindingTag);
Slog.e(TAG, "Unable to bind " + getCaption() + " service: " + intent, ex);
}
}
@@ -1050,13 +1044,7 @@
if (name.equals(info.component) && info.userid == userid) {
removeServiceLocked(i);
if (info.connection != null) {
- try {
- mContext.unbindService(info.connection);
- } catch (IllegalArgumentException ex) {
- // something happened to the service: we think we have a connection
- // but it's bogus.
- Slog.e(TAG, getCaption() + " " + name + " could not be unbound: " + ex);
- }
+ unbindService(info.connection, info.component, info.userid);
}
}
}
@@ -1121,7 +1109,18 @@
private void unregisterServiceImpl(IInterface service, int userid) {
ManagedServiceInfo info = removeServiceImpl(service, userid);
if (info != null && info.connection != null && !info.isGuest(this)) {
- mContext.unbindService(info.connection);
+ unbindService(info.connection, info.component, info.userid);
+ }
+ }
+
+ private void unbindService(ServiceConnection connection, ComponentName component, int userId) {
+ try {
+ mContext.unbindService(connection);
+ } catch (IllegalArgumentException e) {
+ Slog.e(TAG, getCaption() + " " + component + " could not be unbound", e);
+ }
+ synchronized (mMutex) {
+ mServicesBound.remove(Pair.create(component, userId));
}
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 32f1744..507d0c4 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -329,6 +329,8 @@
private long[] mFallbackVibrationPattern;
private boolean mUseAttentionLight;
+ boolean mHasLight = true;
+ boolean mLightEnabled;
boolean mSystemReady;
private boolean mDisableNotificationEffects;
@@ -343,9 +345,9 @@
private int mInterruptionFilter = NotificationListenerService.INTERRUPTION_FILTER_UNKNOWN;
// for enabling and disabling notification pulse behavior
- private boolean mScreenOn = true;
+ boolean mScreenOn = true;
protected boolean mInCall = false;
- private boolean mNotificationPulseEnabled;
+ boolean mNotificationPulseEnabled;
private Uri mInCallNotificationUri;
private AudioAttributes mInCallNotificationAudioAttributes;
@@ -384,6 +386,7 @@
private static final String ATTR_VERSION = "version";
private RankingHelper mRankingHelper;
+ private PreferencesHelper mPreferencesHelper;
private final UserProfiles mUserProfiles = new UserProfiles();
private NotificationListeners mListeners;
@@ -400,6 +403,7 @@
private SnoozeHelper mSnoozeHelper;
private GroupHelper mGroupHelper;
+ private int mAutoGroupAtCount;
private boolean mIsTelevision;
private MetricsLogger mMetricsLogger;
@@ -477,7 +481,7 @@
String defaultDndAccess = getContext().getResources().getString(
com.android.internal.R.string.config_defaultDndAccessPackages);
- if (defaultListenerAccess != null) {
+ if (defaultDndAccess != null) {
for (String whitelisted :
defaultDndAccess.split(ManagedServices.ENABLED_SERVICES_SEPARATOR)) {
try {
@@ -522,8 +526,8 @@
while (XmlUtils.nextElementWithin(parser, outerDepth)) {
if (ZenModeConfig.ZEN_TAG.equals(parser.getName())) {
mZenModeHelper.readXml(parser, forRestore);
- } else if (RankingHelper.TAG_RANKING.equals(parser.getName())){
- mRankingHelper.readXml(parser, forRestore);
+ } else if (PreferencesHelper.TAG_RANKING.equals(parser.getName())){
+ mPreferencesHelper.readXml(parser, forRestore);
}
if (mListeners.getConfig().xmlTag.equals(parser.getName())) {
mListeners.readXml(parser, mAllowedManagedServicePackages);
@@ -605,7 +609,7 @@
out.startTag(null, TAG_NOTIFICATION_POLICY);
out.attribute(null, ATTR_VERSION, Integer.toString(DB_VERSION));
mZenModeHelper.writeXml(out, forBackup, null);
- mRankingHelper.writeXml(out, forBackup);
+ mPreferencesHelper.writeXml(out, forBackup);
mListeners.writeXml(out, forBackup);
mAssistants.writeXml(out, forBackup);
mConditionProviders.writeXml(out, forBackup);
@@ -946,7 +950,7 @@
// update system notification channels
SystemNotificationChannels.createAll(context);
mZenModeHelper.updateDefaultZenRules();
- mRankingHelper.onLocaleChanged(context, ActivityManager.getCurrentUser());
+ mPreferencesHelper.onLocaleChanged(context, ActivityManager.getCurrentUser());
}
}
};
@@ -1089,7 +1093,8 @@
mListeners.onPackagesChanged(removingPackage, pkgList, uidList);
mAssistants.onPackagesChanged(removingPackage, pkgList, uidList);
mConditionProviders.onPackagesChanged(removingPackage, pkgList, uidList);
- mRankingHelper.onPackagesChanged(removingPackage, changeUserId, pkgList, uidList);
+ mPreferencesHelper.onPackagesChanged(
+ removingPackage, changeUserId, pkgList, uidList);
savePolicyFile();
}
}
@@ -1149,7 +1154,7 @@
final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_NULL);
mUserProfiles.updateCache(context);
mZenModeHelper.onUserRemoved(user);
- mRankingHelper.onUserRemoved(user);
+ mPreferencesHelper.onUserRemoved(user);
mListeners.onUserRemoved(user);
mConditionProviders.onUserRemoved(user);
mAssistants.onUserRemoved(user);
@@ -1195,7 +1200,8 @@
ContentResolver resolver = getContext().getContentResolver();
if (uri == null || NOTIFICATION_LIGHT_PULSE_URI.equals(uri)) {
boolean pulseEnabled = Settings.System.getIntForUser(resolver,
- Settings.System.NOTIFICATION_LIGHT_PULSE, 0, UserHandle.USER_CURRENT) != 0;
+ Settings.System.NOTIFICATION_LIGHT_PULSE, 0, UserHandle.USER_CURRENT)
+ != 0;
if (mNotificationPulseEnabled != pulseEnabled) {
mNotificationPulseEnabled = pulseEnabled;
updateNotificationPulse();
@@ -1206,7 +1212,7 @@
Settings.Global.MAX_NOTIFICATION_ENQUEUE_RATE, mMaxPackageEnqueueRate);
}
if (uri == null || NOTIFICATION_BADGING_URI.equals(uri)) {
- mRankingHelper.updateBadgingEnabled();
+ mPreferencesHelper.updateBadgingEnabled();
}
}
}
@@ -1328,6 +1334,9 @@
}
@VisibleForTesting
+ void setPreferencesHelper(PreferencesHelper prefHelper) { mPreferencesHelper = prefHelper; }
+
+ @VisibleForTesting
void setRankingHandler(RankingHandler rankingHandler) {
mRankingHandler = rankingHandler;
}
@@ -1415,9 +1424,13 @@
mRankingHandler.requestSort();
}
});
- mRankingHelper = new RankingHelper(getContext(),
+ mPreferencesHelper = new PreferencesHelper(getContext(),
mPackageManagerClient,
mRankingHandler,
+ mZenModeHelper);
+ mRankingHelper = new RankingHelper(getContext(),
+ mRankingHandler,
+ mPreferencesHelper,
mZenModeHelper,
mUsageStats,
extractorNames);
@@ -1457,6 +1470,8 @@
mInCallNotificationVolume = resources.getFloat(R.dimen.config_inCallNotificationVolume);
mUseAttentionLight = resources.getBoolean(R.bool.config_useAttentionLight);
+ mHasLight =
+ resources.getBoolean(com.android.internal.R.bool.config_intrusiveNotificationLed);
// Don't start allowing notifications until the setup wizard has run once.
// After that, including subsequent boots, init with notifications turned on.
@@ -1565,7 +1580,9 @@
}
private GroupHelper getGroupHelper() {
- return new GroupHelper(new GroupHelper.Callback() {
+ mAutoGroupAtCount =
+ getContext().getResources().getInteger(R.integer.config_autoGroupAtCount);
+ return new GroupHelper(mAutoGroupAtCount, new GroupHelper.Callback() {
@Override
public void addAutoGroup(String key) {
synchronized (mNotificationLock) {
@@ -1668,14 +1685,14 @@
}
}
final NotificationChannel preUpdate =
- mRankingHelper.getNotificationChannel(pkg, uid, channel.getId(), true);
+ mPreferencesHelper.getNotificationChannel(pkg, uid, channel.getId(), true);
- mRankingHelper.updateNotificationChannel(pkg, uid, channel, true);
+ mPreferencesHelper.updateNotificationChannel(pkg, uid, channel, true);
maybeNotifyChannelOwner(pkg, uid, preUpdate, channel);
if (!fromListener) {
final NotificationChannel modifiedChannel =
- mRankingHelper.getNotificationChannel(pkg, uid, channel.getId(), false);
+ mPreferencesHelper.getNotificationChannel(pkg, uid, channel.getId(), false);
mListeners.notifyNotificationChannelChanged(
pkg, UserHandle.getUserHandleForUid(uid),
modifiedChannel, NOTIFICATION_CHANNEL_OR_GROUP_UPDATED);
@@ -1712,8 +1729,8 @@
Preconditions.checkNotNull(pkg);
final NotificationChannelGroup preUpdate =
- mRankingHelper.getNotificationChannelGroup(group.getId(), pkg, uid);
- mRankingHelper.createNotificationChannelGroup(pkg, uid, group,
+ mPreferencesHelper.getNotificationChannelGroup(group.getId(), pkg, uid);
+ mPreferencesHelper.createNotificationChannelGroup(pkg, uid, group,
fromApp);
if (!fromApp) {
maybeNotifyChannelGroupOwner(pkg, uid, preUpdate, group);
@@ -2116,7 +2133,7 @@
public void setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled) {
enforceSystemOrSystemUI("setNotificationsEnabledForPackage");
- mRankingHelper.setEnabled(pkg, uid, enabled);
+ mPreferencesHelper.setEnabled(pkg, uid, enabled);
// Now, cancel any outstanding notifications that are part of a just-disabled app
if (!enabled) {
cancelAllNotificationsInt(MY_UID, MY_PID, pkg, null, 0, 0, true,
@@ -2154,7 +2171,7 @@
String pkg, int uid, boolean enabled) {
setNotificationsEnabledForPackage(pkg, uid, enabled);
- mRankingHelper.setAppImportanceLocked(pkg, uid);
+ mPreferencesHelper.setAppImportanceLocked(pkg, uid);
}
/**
@@ -2172,25 +2189,25 @@
public boolean areNotificationsEnabledForPackage(String pkg, int uid) {
checkCallerIsSystemOrSameApp(pkg);
- return mRankingHelper.getImportance(pkg, uid) != IMPORTANCE_NONE;
+ return mPreferencesHelper.getImportance(pkg, uid) != IMPORTANCE_NONE;
}
@Override
public int getPackageImportance(String pkg) {
checkCallerIsSystemOrSameApp(pkg);
- return mRankingHelper.getImportance(pkg, Binder.getCallingUid());
+ return mPreferencesHelper.getImportance(pkg, Binder.getCallingUid());
}
@Override
public boolean canShowBadge(String pkg, int uid) {
checkCallerIsSystem();
- return mRankingHelper.canShowBadge(pkg, uid);
+ return mPreferencesHelper.canShowBadge(pkg, uid);
}
@Override
public void setShowBadge(String pkg, int uid, boolean showBadge) {
checkCallerIsSystem();
- mRankingHelper.setShowBadge(pkg, uid, showBadge);
+ mPreferencesHelper.setShowBadge(pkg, uid, showBadge);
savePolicyFile();
}
@@ -2222,12 +2239,12 @@
for (int i = 0; i < channelsSize; i++) {
final NotificationChannel channel = channels.get(i);
Preconditions.checkNotNull(channel, "channel in list is null");
- mRankingHelper.createNotificationChannel(pkg, uid, channel,
+ mPreferencesHelper.createNotificationChannel(pkg, uid, channel,
true /* fromTargetApp */, mConditionProviders.isPackageOrComponentAllowed(
pkg, UserHandle.getUserId(uid)));
mListeners.notifyNotificationChannelChanged(pkg,
UserHandle.getUserHandleForUid(uid),
- mRankingHelper.getNotificationChannel(pkg, uid, channel.getId(), false),
+ mPreferencesHelper.getNotificationChannel(pkg, uid, channel.getId(), false),
NOTIFICATION_CHANNEL_OR_GROUP_ADDED);
}
savePolicyFile();
@@ -2250,7 +2267,7 @@
@Override
public NotificationChannel getNotificationChannel(String pkg, String channelId) {
checkCallerIsSystemOrSameApp(pkg);
- return mRankingHelper.getNotificationChannel(
+ return mPreferencesHelper.getNotificationChannel(
pkg, Binder.getCallingUid(), channelId, false /* includeDeleted */);
}
@@ -2258,7 +2275,7 @@
public NotificationChannel getNotificationChannelForPackage(String pkg, int uid,
String channelId, boolean includeDeleted) {
checkCallerIsSystem();
- return mRankingHelper.getNotificationChannel(pkg, uid, channelId, includeDeleted);
+ return mPreferencesHelper.getNotificationChannel(pkg, uid, channelId, includeDeleted);
}
@Override
@@ -2270,10 +2287,10 @@
}
cancelAllNotificationsInt(MY_UID, MY_PID, pkg, channelId, 0, 0, true,
UserHandle.getUserId(callingUid), REASON_CHANNEL_BANNED, null);
- mRankingHelper.deleteNotificationChannel(pkg, callingUid, channelId);
+ mPreferencesHelper.deleteNotificationChannel(pkg, callingUid, channelId);
mListeners.notifyNotificationChannelChanged(pkg,
UserHandle.getUserHandleForUid(callingUid),
- mRankingHelper.getNotificationChannel(pkg, callingUid, channelId, true),
+ mPreferencesHelper.getNotificationChannel(pkg, callingUid, channelId, true),
NOTIFICATION_CHANNEL_OR_GROUP_DELETED);
savePolicyFile();
}
@@ -2281,7 +2298,7 @@
@Override
public NotificationChannelGroup getNotificationChannelGroup(String pkg, String groupId) {
checkCallerIsSystemOrSameApp(pkg);
- return mRankingHelper.getNotificationChannelGroupWithChannels(
+ return mPreferencesHelper.getNotificationChannelGroupWithChannels(
pkg, Binder.getCallingUid(), groupId, false);
}
@@ -2289,7 +2306,7 @@
public ParceledListSlice<NotificationChannelGroup> getNotificationChannelGroups(
String pkg) {
checkCallerIsSystemOrSameApp(pkg);
- return mRankingHelper.getNotificationChannelGroups(
+ return mPreferencesHelper.getNotificationChannelGroups(
pkg, Binder.getCallingUid(), false, false);
}
@@ -2299,10 +2316,10 @@
final int callingUid = Binder.getCallingUid();
NotificationChannelGroup groupToDelete =
- mRankingHelper.getNotificationChannelGroup(groupId, pkg, callingUid);
+ mPreferencesHelper.getNotificationChannelGroup(groupId, pkg, callingUid);
if (groupToDelete != null) {
List<NotificationChannel> deletedChannels =
- mRankingHelper.deleteNotificationChannelGroup(pkg, callingUid, groupId);
+ mPreferencesHelper.deleteNotificationChannelGroup(pkg, callingUid, groupId);
for (int i = 0; i < deletedChannels.size(); i++) {
final NotificationChannel deletedChannel = deletedChannels.get(i);
cancelAllNotificationsInt(MY_UID, MY_PID, pkg, deletedChannel.getId(), 0, 0,
@@ -2333,47 +2350,47 @@
public ParceledListSlice<NotificationChannel> getNotificationChannelsForPackage(String pkg,
int uid, boolean includeDeleted) {
enforceSystemOrSystemUI("getNotificationChannelsForPackage");
- return mRankingHelper.getNotificationChannels(pkg, uid, includeDeleted);
+ return mPreferencesHelper.getNotificationChannels(pkg, uid, includeDeleted);
}
@Override
public int getNumNotificationChannelsForPackage(String pkg, int uid,
boolean includeDeleted) {
enforceSystemOrSystemUI("getNumNotificationChannelsForPackage");
- return mRankingHelper.getNotificationChannels(pkg, uid, includeDeleted)
+ return mPreferencesHelper.getNotificationChannels(pkg, uid, includeDeleted)
.getList().size();
}
@Override
public boolean onlyHasDefaultChannel(String pkg, int uid) {
enforceSystemOrSystemUI("onlyHasDefaultChannel");
- return mRankingHelper.onlyHasDefaultChannel(pkg, uid);
+ return mPreferencesHelper.onlyHasDefaultChannel(pkg, uid);
}
@Override
public int getDeletedChannelCount(String pkg, int uid) {
enforceSystemOrSystemUI("getDeletedChannelCount");
- return mRankingHelper.getDeletedChannelCount(pkg, uid);
+ return mPreferencesHelper.getDeletedChannelCount(pkg, uid);
}
@Override
public int getBlockedChannelCount(String pkg, int uid) {
enforceSystemOrSystemUI("getBlockedChannelCount");
- return mRankingHelper.getBlockedChannelCount(pkg, uid);
+ return mPreferencesHelper.getBlockedChannelCount(pkg, uid);
}
@Override
public ParceledListSlice<NotificationChannelGroup> getNotificationChannelGroupsForPackage(
String pkg, int uid, boolean includeDeleted) {
checkCallerIsSystem();
- return mRankingHelper.getNotificationChannelGroups(pkg, uid, includeDeleted, true);
+ return mPreferencesHelper.getNotificationChannelGroups(pkg, uid, includeDeleted, true);
}
@Override
public NotificationChannelGroup getPopulatedNotificationChannelGroupForPackage(
String pkg, int uid, String groupId, boolean includeDeleted) {
enforceSystemOrSystemUI("getPopulatedNotificationChannelGroupForPackage");
- return mRankingHelper.getNotificationChannelGroupWithChannels(
+ return mPreferencesHelper.getNotificationChannelGroupWithChannels(
pkg, uid, groupId, includeDeleted);
}
@@ -2381,13 +2398,13 @@
public NotificationChannelGroup getNotificationChannelGroupForPackage(
String groupId, String pkg, int uid) {
enforceSystemOrSystemUI("getNotificationChannelGroupForPackage");
- return mRankingHelper.getNotificationChannelGroup(groupId, pkg, uid);
+ return mPreferencesHelper.getNotificationChannelGroup(groupId, pkg, uid);
}
@Override
public ParceledListSlice<NotificationChannel> getNotificationChannels(String pkg) {
checkCallerIsSystemOrSameApp(pkg);
- return mRankingHelper.getNotificationChannels(
+ return mPreferencesHelper.getNotificationChannels(
pkg, Binder.getCallingUid(), false /* includeDeleted */);
}
@@ -2404,12 +2421,12 @@
@Override
public int getBlockedAppCount(int userId) {
checkCallerIsSystem();
- return mRankingHelper.getBlockedAppCount(userId);
+ return mPreferencesHelper.getBlockedAppCount(userId);
}
@Override
public boolean areChannelsBypassingDnd() {
- return mRankingHelper.areChannelsBypassingDnd();
+ return mPreferencesHelper.areChannelsBypassingDnd();
}
@Override
@@ -2432,7 +2449,7 @@
// Reset notification preferences
if (!fromApp) {
- mRankingHelper.onPackagesChanged(
+ mPreferencesHelper.onPackagesChanged(
true, UserHandle.getCallingUserId(), packages, uids);
}
@@ -3125,14 +3142,19 @@
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpAndUsageStatsPermission(getContext(), TAG, pw)) return;
final DumpFilter filter = DumpFilter.parseFromArguments(args);
- if (filter.stats) {
- dumpJson(pw, filter);
- } else if (filter.proto) {
- dumpProto(fd, filter);
- } else if (filter.criticalPriority) {
- dumpNotificationRecords(pw, filter);
- } else {
- dumpImpl(pw, filter);
+ final long token = Binder.clearCallingIdentity();
+ try {
+ if (filter.stats) {
+ dumpJson(pw, filter);
+ } else if (filter.proto) {
+ dumpProto(fd, filter);
+ } else if (filter.criticalPriority) {
+ dumpNotificationRecords(pw, filter);
+ } else {
+ dumpImpl(pw, filter);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
}
}
@@ -3499,7 +3521,7 @@
Preconditions.checkNotNull(user);
verifyPrivilegedListener(token, user);
- return mRankingHelper.getNotificationChannels(pkg, getUidForPackageAndUser(pkg, user),
+ return mPreferencesHelper.getNotificationChannels(pkg, getUidForPackageAndUser(pkg, user),
false /* includeDeleted */);
}
@@ -3512,7 +3534,7 @@
verifyPrivilegedListener(token, user);
List<NotificationChannelGroup> groups = new ArrayList<>();
- groups.addAll(mRankingHelper.getNotificationChannelGroups(
+ groups.addAll(mPreferencesHelper.getNotificationChannelGroups(
pkg, getUidForPackageAndUser(pkg, user)));
return new ParceledListSlice<>(groups);
}
@@ -3693,10 +3715,10 @@
JSONObject dump = new JSONObject();
try {
dump.put("service", "Notification Manager");
- dump.put("bans", mRankingHelper.dumpBansJson(filter));
- dump.put("ranking", mRankingHelper.dumpJson(filter));
+ dump.put("bans", mPreferencesHelper.dumpBansJson(filter));
+ dump.put("ranking", mPreferencesHelper.dumpJson(filter));
dump.put("stats", mUsageStats.dumpJson(filter));
- dump.put("channels", mRankingHelper.dumpChannelsJson(filter));
+ dump.put("channels", mPreferencesHelper.dumpChannelsJson(filter));
} catch (JSONException e) {
e.printStackTrace();
}
@@ -3769,6 +3791,7 @@
long rankingToken = proto.start(NotificationServiceDumpProto.RANKING_CONFIG);
mRankingHelper.dump(proto, filter);
+ mPreferencesHelper.dump(proto, filter);
proto.end(rankingToken);
}
@@ -3835,6 +3858,7 @@
pw.println(" ");
}
pw.println(" mUseAttentionLight=" + mUseAttentionLight);
+ pw.println(" mHasLight=" + mHasLight);
pw.println(" mNotificationPulseEnabled=" + mNotificationPulseEnabled);
pw.println(" mSoundNotificationKey=" + mSoundNotificationKey);
pw.println(" mVibrateNotificationKey=" + mVibrateNotificationKey);
@@ -3876,6 +3900,9 @@
pw.println("\n Ranking Config:");
mRankingHelper.dump(pw, " ", filter);
+ pw.println("\n Notification Preferences:");
+ mPreferencesHelper.dump(pw, " ", filter);
+
pw.println("\n Notification listeners:");
mListeners.dump(pw, filter);
pw.print(" mListenerHints: "); pw.println(mListenerHints);
@@ -3939,7 +3966,7 @@
@Override
public NotificationChannel getNotificationChannel(String pkg, int uid, String
channelId) {
- return mRankingHelper.getNotificationChannel(pkg, uid, channelId, false);
+ return mPreferencesHelper.getNotificationChannel(pkg, uid, channelId, false);
}
@Override
@@ -4035,7 +4062,7 @@
if (mIsTelevision && (new Notification.TvExtender(notification)).getChannelId() != null) {
channelId = (new Notification.TvExtender(notification)).getChannelId();
}
- final NotificationChannel channel = mRankingHelper.getNotificationChannel(pkg,
+ final NotificationChannel channel = mPreferencesHelper.getNotificationChannel(pkg,
notificationUid, channelId, false /* includeDeleted */);
if (channel == null) {
final String noChannelStr = "No Channel found for "
@@ -4050,7 +4077,7 @@
+ ", notificationUid=" + notificationUid
+ ", notification=" + notification;
Log.e(TAG, noChannelStr);
- boolean appNotificationsOff = mRankingHelper.getImportance(pkg, notificationUid)
+ boolean appNotificationsOff = mPreferencesHelper.getImportance(pkg, notificationUid)
== NotificationManager.IMPORTANCE_NONE;
if (!appNotificationsOff) {
@@ -4065,7 +4092,7 @@
pkg, opPkg, id, tag, notificationUid, callingPid, notification,
user, null, System.currentTimeMillis());
final NotificationRecord r = new NotificationRecord(getContext(), n, channel);
- r.setIsAppImportanceLocked(mRankingHelper.getIsAppImportanceLocked(pkg, callingUid));
+ r.setIsAppImportanceLocked(mPreferencesHelper.getIsAppImportanceLocked(pkg, callingUid));
if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
final boolean fgServiceShown = channel.isFgServiceShown();
@@ -4084,7 +4111,7 @@
channel.unlockFields(NotificationChannel.USER_LOCKED_IMPORTANCE);
channel.setFgServiceShown(true);
}
- mRankingHelper.updateNotificationChannel(pkg, notificationUid, channel, false);
+ mPreferencesHelper.updateNotificationChannel(pkg, notificationUid, channel, false);
r.updateNotificationChannel(channel);
}
} else if (!fgServiceShown && !TextUtils.isEmpty(channelId)
@@ -4259,8 +4286,8 @@
return isPackageSuspended;
}
final boolean isBlocked =
- mRankingHelper.isGroupBlocked(pkg, callingUid, r.getChannel().getGroup())
- || mRankingHelper.getImportance(pkg, callingUid)
+ mPreferencesHelper.isGroupBlocked(pkg, callingUid, r.getChannel().getGroup())
+ || mPreferencesHelper.getImportance(pkg, callingUid)
== NotificationManager.IMPORTANCE_NONE
|| r.getChannel().getImportance() == NotificationManager.IMPORTANCE_NONE;
if (isBlocked) {
@@ -4532,6 +4559,15 @@
@GuardedBy("mNotificationLock")
@VisibleForTesting
protected boolean isVisuallyInterruptive(NotificationRecord old, NotificationRecord r) {
+ // Ignore summary updates because we don't display most of the information.
+ if (r.sbn.isGroup() && r.sbn.getNotification().isGroupSummary()) {
+ if (DEBUG_INTERRUPTIVENESS) {
+ Log.v(TAG, "INTERRUPTIVENESS: "
+ + r.getKey() + " is not interruptive: summary");
+ }
+ return false;
+ }
+
if (old == null) {
if (DEBUG_INTERRUPTIVENESS) {
Log.v(TAG, "INTERRUPTIVENESS: "
@@ -4569,15 +4605,6 @@
return false;
}
- // Ignore summary updates because we don't display most of the information.
- if (r.sbn.isGroup() && r.sbn.getNotification().isGroupSummary()) {
- if (DEBUG_INTERRUPTIVENESS) {
- Log.v(TAG, "INTERRUPTIVENESS: "
- + r.getKey() + " is not interruptive: summary");
- }
- return false;
- }
-
final String oldTitle = String.valueOf(oldN.extras.get(Notification.EXTRA_TITLE));
final String newTitle = String.valueOf(newN.extras.get(Notification.EXTRA_TITLE));
if (!Objects.equals(oldTitle, newTitle)) {
@@ -4817,6 +4844,8 @@
buzz = playVibration(record, vibration, hasValidSound);
}
+ } else if ((record.getFlags() & Notification.FLAG_INSISTENT) != 0) {
+ hasValidSound = false;
}
}
}
@@ -4832,8 +4861,7 @@
// light
// release the light
boolean wasShowLights = mLights.remove(key);
- if (record.getLight() != null && aboveThreshold
- && ((record.getSuppressedVisualEffects() & SUPPRESSED_EFFECT_LIGHTS) == 0)) {
+ if (canShowLightsLocked(record, aboveThreshold)) {
mLights.add(key);
updateLightsLocked();
if (mUseAttentionLight) {
@@ -4844,7 +4872,19 @@
updateLightsLocked();
}
if (buzz || beep || blink) {
- record.setInterruptive(true);
+ // Ignore summary updates because we don't display most of the information.
+ if (record.sbn.isGroup() && record.sbn.getNotification().isGroupSummary()) {
+ if (DEBUG_INTERRUPTIVENESS) {
+ Log.v(TAG, "INTERRUPTIVENESS: "
+ + record.getKey() + " is not interruptive: summary");
+ }
+ } else {
+ if (DEBUG_INTERRUPTIVENESS) {
+ Log.v(TAG, "INTERRUPTIVENESS: "
+ + record.getKey() + " is interruptive: alerted");
+ }
+ record.setInterruptive(true);
+ }
MetricsLogger.action(record.getLogMaker()
.setCategory(MetricsEvent.NOTIFICATION_ALERT)
.setType(MetricsEvent.TYPE_OPEN)
@@ -4854,11 +4894,49 @@
}
@GuardedBy("mNotificationLock")
+ boolean canShowLightsLocked(final NotificationRecord record, boolean aboveThreshold) {
+ // device lacks light
+ if (!mHasLight) {
+ return false;
+ }
+ // user turned lights off globally
+ if (!mNotificationPulseEnabled) {
+ return false;
+ }
+ // the notification/channel has no light
+ if (record.getLight() == null) {
+ return false;
+ }
+ // unimportant notification
+ if (!aboveThreshold) {
+ return false;
+ }
+ // suppressed due to DND
+ if ((record.getSuppressedVisualEffects() & SUPPRESSED_EFFECT_LIGHTS) != 0) {
+ return false;
+ }
+ // Suppressed because it's a silent update
+ final Notification notification = record.getNotification();
+ if (record.isUpdate && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0) {
+ return false;
+ }
+ // Suppressed because another notification in its group handles alerting
+ if (record.sbn.isGroup() && record.getNotification().suppressAlertingDueToGrouping()) {
+ return false;
+ }
+ // not if in call or the screen's on
+ if (mInCall || mScreenOn) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @GuardedBy("mNotificationLock")
boolean shouldMuteNotificationLocked(final NotificationRecord record) {
// Suppressed because it's a silent update
final Notification notification = record.getNotification();
- if(record.isUpdate
- && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0) {
+ if (record.isUpdate && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0) {
return true;
}
@@ -4971,27 +5049,31 @@
}
protected void playInCallNotification() {
- new Thread() {
- @Override
- public void run() {
- final long identity = Binder.clearCallingIdentity();
- try {
- final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
- if (player != null) {
- if (mCallNotificationToken != null) {
- player.stop(mCallNotificationToken);
+ if (mAudioManager.getRingerModeInternal() == AudioManager.RINGER_MODE_NORMAL
+ && Settings.Secure.getInt(getContext().getContentResolver(),
+ Settings.Secure.IN_CALL_NOTIFICATION_ENABLED, 1) != 0) {
+ new Thread() {
+ @Override
+ public void run() {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
+ if (player != null) {
+ if (mCallNotificationToken != null) {
+ player.stop(mCallNotificationToken);
+ }
+ mCallNotificationToken = new Binder();
+ player.play(mCallNotificationToken, mInCallNotificationUri,
+ mInCallNotificationAudioAttributes,
+ mInCallNotificationVolume, false);
}
- mCallNotificationToken = new Binder();
- player.play(mCallNotificationToken, mInCallNotificationUri,
- mInCallNotificationAudioAttributes,
- mInCallNotificationVolume, false);
+ } catch (RemoteException e) {
+ } finally {
+ Binder.restoreCallingIdentity(identity);
}
- } catch (RemoteException e) {
- } finally {
- Binder.restoreCallingIdentity(identity);
}
- }
- }.start();
+ }.start();
+ }
}
@GuardedBy("mToastQueue")
@@ -5180,6 +5262,7 @@
ArrayList<ArrayList<SnoozeCriterion>> snoozeCriteriaBefore = new ArrayList<>(N);
ArrayList<Integer> userSentimentBefore = new ArrayList<>(N);
ArrayList<Integer> suppressVisuallyBefore = new ArrayList<>(N);
+ ArrayList<ArrayList<Notification.Action>> smartActionsBefore = new ArrayList<>(N);
for (int i = 0; i < N; i++) {
final NotificationRecord r = mNotificationList.get(i);
orderBefore.add(r.getKey());
@@ -5191,6 +5274,7 @@
snoozeCriteriaBefore.add(r.getSnoozeCriteria());
userSentimentBefore.add(r.getUserSentiment());
suppressVisuallyBefore.add(r.getSuppressedVisualEffects());
+ smartActionsBefore.add(r.getSmartActions());
mRankingHelper.extractSignals(r);
}
mRankingHelper.sort(mNotificationList);
@@ -5205,7 +5289,8 @@
|| !Objects.equals(snoozeCriteriaBefore.get(i), r.getSnoozeCriteria())
|| !Objects.equals(userSentimentBefore.get(i), r.getUserSentiment())
|| !Objects.equals(suppressVisuallyBefore.get(i),
- r.getSuppressedVisualEffects())) {
+ r.getSuppressedVisualEffects())
+ || !Objects.equals(smartActionsBefore.get(i), r.getSmartActions())) {
mHandler.scheduleSendRankingUpdate();
return;
}
@@ -6188,6 +6273,7 @@
Bundle showBadge = new Bundle();
Bundle userSentiment = new Bundle();
Bundle hidden = new Bundle();
+ Bundle smartActions = new Bundle();
for (int i = 0; i < N; i++) {
NotificationRecord record = mNotificationList.get(i);
if (!isVisibleToListener(record.sbn, info)) {
@@ -6215,6 +6301,7 @@
showBadge.putBoolean(key, record.canShowBadge());
userSentiment.putInt(key, record.getUserSentiment());
hidden.putBoolean(key, record.isHidden());
+ smartActions.putParcelableArrayList(key, record.getSmartActions());
}
final int M = keys.size();
String[] keysAr = keys.toArray(new String[M]);
@@ -6225,7 +6312,8 @@
}
return new NotificationRankingUpdate(keysAr, interceptedKeysAr, visibilityOverrides,
suppressedVisualEffects, importanceAr, explanation, overrideGroupKeys,
- channels, overridePeople, snoozeCriteria, showBadge, userSentiment, hidden);
+ channels, overridePeople, snoozeCriteria, showBadge, userSentiment, hidden,
+ smartActions);
}
boolean hasCompanionDevice(ManagedServiceInfo info) {
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 39d0bf5..0154c72 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -128,6 +128,11 @@
// The most recent update time, or the creation time if no updates.
private long mUpdateTimeMs;
+ // The most recent interruption time, or the creation time if no updates. Differs from the
+ // above value because updates are filtered based on whether they actually interrupted the
+ // user
+ private long mInterruptionTimeMs;
+
// Is this record an update of an old record?
public boolean isUpdate;
private int mPackagePriority;
@@ -154,6 +159,7 @@
private Light mLight;
private String mGroupLogTag;
private String mChannelIdLogTag;
+ private ArrayList<Notification.Action> mSmartActions;
private final List<Adjustment> mAdjustments;
private final NotificationStats mStats;
@@ -180,6 +186,7 @@
mRankingTimeMs = calculateRankingTimeMs(0L);
mCreationTimeMs = sbn.getPostTime();
mUpdateTimeMs = mCreationTimeMs;
+ mInterruptionTimeMs = mCreationTimeMs;
mContext = context;
stats = new NotificationUsageStats.SingleNotificationStats();
mChannel = channel;
@@ -437,6 +444,8 @@
pw.println(prefix + "fullscreenIntent=" + notification.fullScreenIntent);
pw.println(prefix + "contentIntent=" + notification.contentIntent);
pw.println(prefix + "deleteIntent=" + notification.deleteIntent);
+ pw.println(prefix + "number=" + notification.number);
+ pw.println(prefix + "groupAlertBehavior=" + notification.getGroupAlertBehavior());
pw.print(prefix + "tickerText=");
if (!TextUtils.isEmpty(notification.tickerText)) {
@@ -525,6 +534,7 @@
pw.println(prefix + "mCreationTimeMs=" + mCreationTimeMs);
pw.println(prefix + "mVisibleSinceMs=" + mVisibleSinceMs);
pw.println(prefix + "mUpdateTimeMs=" + mUpdateTimeMs);
+ pw.println(prefix + "mInterruptionTimeMs=" + mInterruptionTimeMs);
pw.println(prefix + "mSuppressedVisualEffects= " + mSuppressedVisualEffects);
if (mPreChannelsNotification) {
pw.println(prefix + String.format("defaults=0x%08x flags=0x%08x",
@@ -621,6 +631,9 @@
Adjustment.KEY_USER_SENTIMENT, USER_SENTIMENT_NEUTRAL));
}
}
+ if (signals.containsKey(Adjustment.KEY_SMART_ACTIONS)) {
+ setSmartActions(signals.getParcelableArrayList(Adjustment.KEY_SMART_ACTIONS));
+ }
}
}
}
@@ -786,6 +799,10 @@
return mVisibleSinceMs == 0 ? 0 : (int) (now - mVisibleSinceMs);
}
+ public int getInterruptionMs(long now) {
+ return (int) (now - mInterruptionTimeMs);
+ }
+
/**
* Set the visibility of the notification.
*/
@@ -844,7 +861,7 @@
public void setSeen() {
mStats.setSeen();
if (mTextChanged) {
- mIsInterruptive = true;
+ setInterruptive(true);
}
}
@@ -940,6 +957,17 @@
public void setInterruptive(boolean interruptive) {
mIsInterruptive = interruptive;
+ final long now = System.currentTimeMillis();
+ mInterruptionTimeMs = interruptive ? now : mInterruptionTimeMs;
+
+ if (interruptive) {
+ MetricsLogger.action(getLogMaker()
+ .setCategory(MetricsEvent.NOTIFICATION_INTERRUPTION)
+ .setType(MetricsEvent.TYPE_OPEN)
+ .addTaggedData(MetricsEvent.NOTIFICATION_SINCE_INTERRUPTION_MILLIS,
+ getInterruptionMs(now)));
+ MetricsLogger.histogram(mContext, "note_interruptive", getInterruptionMs(now));
+ }
}
public void setTextChanged(boolean textChanged) {
@@ -1025,6 +1053,14 @@
mHasSeenSmartReplies = hasSeenSmartReplies;
}
+ public void setSmartActions(ArrayList<Notification.Action> smartActions) {
+ mSmartActions = smartActions;
+ }
+
+ public ArrayList<Notification.Action> getSmartActions() {
+ return mSmartActions;
+ }
+
/**
* @return all {@link Uri} that should have permission granted to whoever
* will be rendering it. This list has already been vetted to only
@@ -1116,7 +1152,9 @@
sbn.getNotification().isGroupSummary() ? 1 : 0)
.addTaggedData(MetricsEvent.NOTIFICATION_SINCE_CREATE_MILLIS, getLifespanMs(now))
.addTaggedData(MetricsEvent.NOTIFICATION_SINCE_UPDATE_MILLIS, getFreshnessMs(now))
- .addTaggedData(MetricsEvent.NOTIFICATION_SINCE_VISIBLE_MILLIS, getExposureMs(now));
+ .addTaggedData(MetricsEvent.NOTIFICATION_SINCE_VISIBLE_MILLIS, getExposureMs(now))
+ .addTaggedData(MetricsEvent.NOTIFICATION_SINCE_INTERRUPTION_MILLIS,
+ getInterruptionMs(now));
}
public LogMaker getLogMaker() {
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
new file mode 100644
index 0000000..dfc61d9
--- /dev/null
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -0,0 +1,1373 @@
+/**
+ * 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.notification;
+
+import static android.app.NotificationManager.IMPORTANCE_NONE;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationChannelGroup;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ParceledListSlice;
+import android.metrics.LogMaker;
+import android.os.Build;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.service.notification.NotificationListenerService;
+import android.service.notification.RankingHelperProto;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.Slog;
+import android.util.SparseBooleanArray;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.util.Preconditions;
+import com.android.internal.util.XmlUtils;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
+
+public class PreferencesHelper implements RankingConfig {
+ private static final String TAG = "NotificationPrefHelper";
+ private static final int XML_VERSION = 1;
+
+ @VisibleForTesting
+ static final String TAG_RANKING = "ranking";
+ private static final String TAG_PACKAGE = "package";
+ private static final String TAG_CHANNEL = "channel";
+ private static final String TAG_GROUP = "channelGroup";
+
+ private static final String ATT_VERSION = "version";
+ private static final String ATT_NAME = "name";
+ private static final String ATT_UID = "uid";
+ private static final String ATT_ID = "id";
+ private static final String ATT_PRIORITY = "priority";
+ private static final String ATT_VISIBILITY = "visibility";
+ private static final String ATT_IMPORTANCE = "importance";
+ private static final String ATT_SHOW_BADGE = "show_badge";
+ private static final String ATT_APP_USER_LOCKED_FIELDS = "app_user_locked_fields";
+
+ private static final int DEFAULT_PRIORITY = Notification.PRIORITY_DEFAULT;
+ private static final int DEFAULT_VISIBILITY = NotificationManager.VISIBILITY_NO_OVERRIDE;
+ private static final int DEFAULT_IMPORTANCE = NotificationManager.IMPORTANCE_UNSPECIFIED;
+ private static final boolean DEFAULT_SHOW_BADGE = true;
+ /**
+ * Default value for what fields are user locked. See {@link LockableAppFields} for all lockable
+ * fields.
+ */
+ private static final int DEFAULT_LOCKED_APP_FIELDS = 0;
+
+ /**
+ * All user-lockable fields for a given application.
+ */
+ @IntDef({LockableAppFields.USER_LOCKED_IMPORTANCE})
+ public @interface LockableAppFields {
+ int USER_LOCKED_IMPORTANCE = 0x00000001;
+ }
+
+ // pkg|uid => PackagePreferences
+ private final ArrayMap<String, PackagePreferences> mPackagePreferencess = new ArrayMap<>();
+ // pkg => PackagePreferences
+ private final ArrayMap<String, PackagePreferences> mRestoredWithoutUids = new ArrayMap<>();
+
+
+ private final Context mContext;
+ private final PackageManager mPm;
+ private final RankingHandler mRankingHandler;
+ private final ZenModeHelper mZenModeHelper;
+
+ private SparseBooleanArray mBadgingEnabled;
+ private boolean mAreChannelsBypassingDnd;
+
+
+ public PreferencesHelper(Context context, PackageManager pm, RankingHandler rankingHandler,
+ ZenModeHelper zenHelper) {
+ mContext = context;
+ mZenModeHelper = zenHelper;
+ mRankingHandler = rankingHandler;
+ mPm = pm;
+
+ updateBadgingEnabled();
+
+ mAreChannelsBypassingDnd = (mZenModeHelper.getNotificationPolicy().state &
+ NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND) == 1;
+ updateChannelsBypassingDnd();
+
+ }
+
+ public void readXml(XmlPullParser parser, boolean forRestore)
+ throws XmlPullParserException, IOException {
+ int type = parser.getEventType();
+ if (type != XmlPullParser.START_TAG) return;
+ String tag = parser.getName();
+ if (!TAG_RANKING.equals(tag)) return;
+ // Clobber groups and channels with the xml, but don't delete other data that wasn't present
+ // at the time of serialization.
+ mRestoredWithoutUids.clear();
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
+ tag = parser.getName();
+ if (type == XmlPullParser.END_TAG && TAG_RANKING.equals(tag)) {
+ return;
+ }
+ if (type == XmlPullParser.START_TAG) {
+ if (TAG_PACKAGE.equals(tag)) {
+ int uid = XmlUtils.readIntAttribute(parser, ATT_UID,
+ PackagePreferences.UNKNOWN_UID);
+ String name = parser.getAttributeValue(null, ATT_NAME);
+ if (!TextUtils.isEmpty(name)) {
+ if (forRestore) {
+ try {
+ //TODO: http://b/22388012
+ uid = mPm.getPackageUidAsUser(name,
+ UserHandle.USER_SYSTEM);
+ } catch (PackageManager.NameNotFoundException e) {
+ // noop
+ }
+ }
+
+ PackagePreferences r = getOrCreatePackagePreferences(name, uid,
+ XmlUtils.readIntAttribute(
+ parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE),
+ XmlUtils.readIntAttribute(parser, ATT_PRIORITY, DEFAULT_PRIORITY),
+ XmlUtils.readIntAttribute(
+ parser, ATT_VISIBILITY, DEFAULT_VISIBILITY),
+ XmlUtils.readBooleanAttribute(
+ parser, ATT_SHOW_BADGE, DEFAULT_SHOW_BADGE));
+ r.importance = XmlUtils.readIntAttribute(
+ parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE);
+ r.priority = XmlUtils.readIntAttribute(
+ parser, ATT_PRIORITY, DEFAULT_PRIORITY);
+ r.visibility = XmlUtils.readIntAttribute(
+ parser, ATT_VISIBILITY, DEFAULT_VISIBILITY);
+ r.showBadge = XmlUtils.readBooleanAttribute(
+ parser, ATT_SHOW_BADGE, DEFAULT_SHOW_BADGE);
+ r.lockedAppFields = XmlUtils.readIntAttribute(parser,
+ ATT_APP_USER_LOCKED_FIELDS, DEFAULT_LOCKED_APP_FIELDS);
+
+ final int innerDepth = parser.getDepth();
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG
+ || parser.getDepth() > innerDepth)) {
+ if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+ continue;
+ }
+
+ String tagName = parser.getName();
+ // Channel groups
+ if (TAG_GROUP.equals(tagName)) {
+ String id = parser.getAttributeValue(null, ATT_ID);
+ CharSequence groupName = parser.getAttributeValue(null, ATT_NAME);
+ if (!TextUtils.isEmpty(id)) {
+ NotificationChannelGroup group
+ = new NotificationChannelGroup(id, groupName);
+ group.populateFromXml(parser);
+ r.groups.put(id, group);
+ }
+ }
+ // Channels
+ if (TAG_CHANNEL.equals(tagName)) {
+ String id = parser.getAttributeValue(null, ATT_ID);
+ String channelName = parser.getAttributeValue(null, ATT_NAME);
+ int channelImportance = XmlUtils.readIntAttribute(
+ parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE);
+ if (!TextUtils.isEmpty(id) && !TextUtils.isEmpty(channelName)) {
+ NotificationChannel channel = new NotificationChannel(id,
+ channelName, channelImportance);
+ if (forRestore) {
+ channel.populateFromXmlForRestore(parser, mContext);
+ } else {
+ channel.populateFromXml(parser);
+ }
+ r.channels.put(id, channel);
+ }
+ }
+ }
+
+ try {
+ deleteDefaultChannelIfNeeded(r);
+ } catch (PackageManager.NameNotFoundException e) {
+ Slog.e(TAG, "deleteDefaultChannelIfNeeded - Exception: " + e);
+ }
+ }
+ }
+ }
+ }
+ throw new IllegalStateException("Failed to reach END_DOCUMENT");
+ }
+
+ private PackagePreferences getPackagePreferences(String pkg, int uid) {
+ final String key = packagePreferencesKey(pkg, uid);
+ synchronized (mPackagePreferencess) {
+ return mPackagePreferencess.get(key);
+ }
+ }
+
+ private PackagePreferences getOrCreatePackagePreferences(String pkg, int uid) {
+ return getOrCreatePackagePreferences(pkg, uid,
+ DEFAULT_IMPORTANCE, DEFAULT_PRIORITY, DEFAULT_VISIBILITY, DEFAULT_SHOW_BADGE);
+ }
+
+ private PackagePreferences getOrCreatePackagePreferences(String pkg, int uid, int importance,
+ int priority, int visibility, boolean showBadge) {
+ final String key = packagePreferencesKey(pkg, uid);
+ synchronized (mPackagePreferencess) {
+ PackagePreferences
+ r = (uid == PackagePreferences.UNKNOWN_UID) ? mRestoredWithoutUids.get(pkg)
+ : mPackagePreferencess.get(key);
+ if (r == null) {
+ r = new PackagePreferences();
+ r.pkg = pkg;
+ r.uid = uid;
+ r.importance = importance;
+ r.priority = priority;
+ r.visibility = visibility;
+ r.showBadge = showBadge;
+
+ try {
+ createDefaultChannelIfNeeded(r);
+ } catch (PackageManager.NameNotFoundException e) {
+ Slog.e(TAG, "createDefaultChannelIfNeeded - Exception: " + e);
+ }
+
+ if (r.uid == PackagePreferences.UNKNOWN_UID) {
+ mRestoredWithoutUids.put(pkg, r);
+ } else {
+ mPackagePreferencess.put(key, r);
+ }
+ }
+ return r;
+ }
+ }
+
+ private boolean shouldHaveDefaultChannel(PackagePreferences r) throws
+ PackageManager.NameNotFoundException {
+ final int userId = UserHandle.getUserId(r.uid);
+ final ApplicationInfo applicationInfo =
+ mPm.getApplicationInfoAsUser(r.pkg, 0, userId);
+ if (applicationInfo.targetSdkVersion >= Build.VERSION_CODES.O) {
+ // O apps should not have the default channel.
+ return false;
+ }
+
+ // Otherwise, this app should have the default channel.
+ return true;
+ }
+
+ private void deleteDefaultChannelIfNeeded(PackagePreferences r) throws
+ PackageManager.NameNotFoundException {
+ if (!r.channels.containsKey(NotificationChannel.DEFAULT_CHANNEL_ID)) {
+ // Not present
+ return;
+ }
+
+ if (shouldHaveDefaultChannel(r)) {
+ // Keep the default channel until upgraded.
+ return;
+ }
+
+ // Remove Default Channel.
+ r.channels.remove(NotificationChannel.DEFAULT_CHANNEL_ID);
+ }
+
+ private void createDefaultChannelIfNeeded(PackagePreferences r) throws
+ PackageManager.NameNotFoundException {
+ if (r.channels.containsKey(NotificationChannel.DEFAULT_CHANNEL_ID)) {
+ r.channels.get(NotificationChannel.DEFAULT_CHANNEL_ID).setName(mContext.getString(
+ com.android.internal.R.string.default_notification_channel_label));
+ return;
+ }
+
+ if (!shouldHaveDefaultChannel(r)) {
+ // Keep the default channel until upgraded.
+ return;
+ }
+
+ // Create Default Channel
+ NotificationChannel channel;
+ channel = new NotificationChannel(
+ NotificationChannel.DEFAULT_CHANNEL_ID,
+ mContext.getString(R.string.default_notification_channel_label),
+ r.importance);
+ channel.setBypassDnd(r.priority == Notification.PRIORITY_MAX);
+ channel.setLockscreenVisibility(r.visibility);
+ if (r.importance != NotificationManager.IMPORTANCE_UNSPECIFIED) {
+ channel.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE);
+ }
+ if (r.priority != DEFAULT_PRIORITY) {
+ channel.lockFields(NotificationChannel.USER_LOCKED_PRIORITY);
+ }
+ if (r.visibility != DEFAULT_VISIBILITY) {
+ channel.lockFields(NotificationChannel.USER_LOCKED_VISIBILITY);
+ }
+ r.channels.put(channel.getId(), channel);
+ }
+
+ public void writeXml(XmlSerializer out, boolean forBackup) throws IOException {
+ out.startTag(null, TAG_RANKING);
+ out.attribute(null, ATT_VERSION, Integer.toString(XML_VERSION));
+
+ synchronized (mPackagePreferencess) {
+ final int N = mPackagePreferencess.size();
+ for (int i = 0; i < N; i++) {
+ final PackagePreferences r = mPackagePreferencess.valueAt(i);
+ //TODO: http://b/22388012
+ if (forBackup && UserHandle.getUserId(r.uid) != UserHandle.USER_SYSTEM) {
+ continue;
+ }
+ final boolean hasNonDefaultSettings =
+ r.importance != DEFAULT_IMPORTANCE
+ || r.priority != DEFAULT_PRIORITY
+ || r.visibility != DEFAULT_VISIBILITY
+ || r.showBadge != DEFAULT_SHOW_BADGE
+ || r.lockedAppFields != DEFAULT_LOCKED_APP_FIELDS
+ || r.channels.size() > 0
+ || r.groups.size() > 0;
+ if (hasNonDefaultSettings) {
+ out.startTag(null, TAG_PACKAGE);
+ out.attribute(null, ATT_NAME, r.pkg);
+ if (r.importance != DEFAULT_IMPORTANCE) {
+ out.attribute(null, ATT_IMPORTANCE, Integer.toString(r.importance));
+ }
+ if (r.priority != DEFAULT_PRIORITY) {
+ out.attribute(null, ATT_PRIORITY, Integer.toString(r.priority));
+ }
+ if (r.visibility != DEFAULT_VISIBILITY) {
+ out.attribute(null, ATT_VISIBILITY, Integer.toString(r.visibility));
+ }
+ out.attribute(null, ATT_SHOW_BADGE, Boolean.toString(r.showBadge));
+ out.attribute(null, ATT_APP_USER_LOCKED_FIELDS,
+ Integer.toString(r.lockedAppFields));
+
+ if (!forBackup) {
+ out.attribute(null, ATT_UID, Integer.toString(r.uid));
+ }
+
+ for (NotificationChannelGroup group : r.groups.values()) {
+ group.writeXml(out);
+ }
+
+ for (NotificationChannel channel : r.channels.values()) {
+ if (forBackup) {
+ if (!channel.isDeleted()) {
+ channel.writeXmlForBackup(out, mContext);
+ }
+ } else {
+ channel.writeXml(out);
+ }
+ }
+
+ out.endTag(null, TAG_PACKAGE);
+ }
+ }
+ }
+ out.endTag(null, TAG_RANKING);
+ }
+
+ /**
+ * Gets importance.
+ */
+ @Override
+ public int getImportance(String packageName, int uid) {
+ return getOrCreatePackagePreferences(packageName, uid).importance;
+ }
+
+
+ /**
+ * Returns whether the importance of the corresponding notification is user-locked and shouldn't
+ * be adjusted by an assistant (via means of a blocking helper, for example). For the channel
+ * locking field, see {@link NotificationChannel#USER_LOCKED_IMPORTANCE}.
+ */
+ public boolean getIsAppImportanceLocked(String packageName, int uid) {
+ int userLockedFields = getOrCreatePackagePreferences(packageName, uid).lockedAppFields;
+ return (userLockedFields & LockableAppFields.USER_LOCKED_IMPORTANCE) != 0;
+ }
+
+ @Override
+ public boolean canShowBadge(String packageName, int uid) {
+ return getOrCreatePackagePreferences(packageName, uid).showBadge;
+ }
+
+ @Override
+ public void setShowBadge(String packageName, int uid, boolean showBadge) {
+ getOrCreatePackagePreferences(packageName, uid).showBadge = showBadge;
+ updateConfig();
+ }
+
+ @Override
+ public boolean isGroupBlocked(String packageName, int uid, String groupId) {
+ if (groupId == null) {
+ return false;
+ }
+ PackagePreferences r = getOrCreatePackagePreferences(packageName, uid);
+ NotificationChannelGroup group = r.groups.get(groupId);
+ if (group == null) {
+ return false;
+ }
+ return group.isBlocked();
+ }
+
+ int getPackagePriority(String pkg, int uid) {
+ return getOrCreatePackagePreferences(pkg, uid).priority;
+ }
+
+ int getPackageVisibility(String pkg, int uid) {
+ return getOrCreatePackagePreferences(pkg, uid).visibility;
+ }
+
+ @Override
+ public void createNotificationChannelGroup(String pkg, int uid, NotificationChannelGroup group,
+ boolean fromTargetApp) {
+ Preconditions.checkNotNull(pkg);
+ Preconditions.checkNotNull(group);
+ Preconditions.checkNotNull(group.getId());
+ Preconditions.checkNotNull(!TextUtils.isEmpty(group.getName()));
+ PackagePreferences r = getOrCreatePackagePreferences(pkg, uid);
+ if (r == null) {
+ throw new IllegalArgumentException("Invalid package");
+ }
+ final NotificationChannelGroup oldGroup = r.groups.get(group.getId());
+ if (!group.equals(oldGroup)) {
+ // will log for new entries as well as name/description changes
+ MetricsLogger.action(getChannelGroupLog(group.getId(), pkg));
+ }
+ if (oldGroup != null) {
+ group.setChannels(oldGroup.getChannels());
+
+ if (fromTargetApp) {
+ group.setBlocked(oldGroup.isBlocked());
+ }
+ }
+ r.groups.put(group.getId(), group);
+ }
+
+ @Override
+ public void createNotificationChannel(String pkg, int uid, NotificationChannel channel,
+ boolean fromTargetApp, boolean hasDndAccess) {
+ Preconditions.checkNotNull(pkg);
+ Preconditions.checkNotNull(channel);
+ Preconditions.checkNotNull(channel.getId());
+ Preconditions.checkArgument(!TextUtils.isEmpty(channel.getName()));
+ PackagePreferences r = getOrCreatePackagePreferences(pkg, uid);
+ if (r == null) {
+ throw new IllegalArgumentException("Invalid package");
+ }
+ if (channel.getGroup() != null && !r.groups.containsKey(channel.getGroup())) {
+ throw new IllegalArgumentException("NotificationChannelGroup doesn't exist");
+ }
+ if (NotificationChannel.DEFAULT_CHANNEL_ID.equals(channel.getId())) {
+ throw new IllegalArgumentException("Reserved id");
+ }
+ NotificationChannel existing = r.channels.get(channel.getId());
+ // Keep most of the existing settings
+ if (existing != null && fromTargetApp) {
+ if (existing.isDeleted()) {
+ existing.setDeleted(false);
+
+ // log a resurrected channel as if it's new again
+ MetricsLogger.action(getChannelLog(channel, pkg).setType(
+ com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_OPEN));
+ }
+
+ existing.setName(channel.getName().toString());
+ existing.setDescription(channel.getDescription());
+ existing.setBlockableSystem(channel.isBlockableSystem());
+ if (existing.getGroup() == null) {
+ existing.setGroup(channel.getGroup());
+ }
+
+ // Apps are allowed to downgrade channel importance if the user has not changed any
+ // fields on this channel yet.
+ if (existing.getUserLockedFields() == 0 &&
+ channel.getImportance() < existing.getImportance()) {
+ existing.setImportance(channel.getImportance());
+ }
+
+ // system apps and dnd access apps can bypass dnd if the user hasn't changed any
+ // fields on the channel yet
+ if (existing.getUserLockedFields() == 0 && hasDndAccess) {
+ boolean bypassDnd = channel.canBypassDnd();
+ existing.setBypassDnd(bypassDnd);
+
+ if (bypassDnd != mAreChannelsBypassingDnd) {
+ updateChannelsBypassingDnd();
+ }
+ }
+
+ updateConfig();
+ return;
+ }
+ if (channel.getImportance() < IMPORTANCE_NONE
+ || channel.getImportance() > NotificationManager.IMPORTANCE_MAX) {
+ throw new IllegalArgumentException("Invalid importance level");
+ }
+
+ // Reset fields that apps aren't allowed to set.
+ if (fromTargetApp && !hasDndAccess) {
+ channel.setBypassDnd(r.priority == Notification.PRIORITY_MAX);
+ }
+ if (fromTargetApp) {
+ channel.setLockscreenVisibility(r.visibility);
+ }
+ clearLockedFields(channel);
+ if (channel.getLockscreenVisibility() == Notification.VISIBILITY_PUBLIC) {
+ channel.setLockscreenVisibility(
+ NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE);
+ }
+ if (!r.showBadge) {
+ channel.setShowBadge(false);
+ }
+
+ r.channels.put(channel.getId(), channel);
+ if (channel.canBypassDnd() != mAreChannelsBypassingDnd) {
+ updateChannelsBypassingDnd();
+ }
+ MetricsLogger.action(getChannelLog(channel, pkg).setType(
+ com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_OPEN));
+ }
+
+ void clearLockedFields(NotificationChannel channel) {
+ channel.unlockFields(channel.getUserLockedFields());
+ }
+
+ @Override
+ public void updateNotificationChannel(String pkg, int uid, NotificationChannel updatedChannel,
+ boolean fromUser) {
+ Preconditions.checkNotNull(updatedChannel);
+ Preconditions.checkNotNull(updatedChannel.getId());
+ PackagePreferences r = getOrCreatePackagePreferences(pkg, uid);
+ if (r == null) {
+ throw new IllegalArgumentException("Invalid package");
+ }
+ NotificationChannel channel = r.channels.get(updatedChannel.getId());
+ if (channel == null || channel.isDeleted()) {
+ throw new IllegalArgumentException("Channel does not exist");
+ }
+ if (updatedChannel.getLockscreenVisibility() == Notification.VISIBILITY_PUBLIC) {
+ updatedChannel.setLockscreenVisibility(
+ NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE);
+ }
+ if (!fromUser) {
+ updatedChannel.unlockFields(updatedChannel.getUserLockedFields());
+ }
+ if (fromUser) {
+ updatedChannel.lockFields(channel.getUserLockedFields());
+ lockFieldsForUpdate(channel, updatedChannel);
+ }
+ r.channels.put(updatedChannel.getId(), updatedChannel);
+
+ if (NotificationChannel.DEFAULT_CHANNEL_ID.equals(updatedChannel.getId())) {
+ // copy settings to app level so they are inherited by new channels
+ // when the app migrates
+ r.importance = updatedChannel.getImportance();
+ r.priority = updatedChannel.canBypassDnd()
+ ? Notification.PRIORITY_MAX : Notification.PRIORITY_DEFAULT;
+ r.visibility = updatedChannel.getLockscreenVisibility();
+ r.showBadge = updatedChannel.canShowBadge();
+ }
+
+ if (!channel.equals(updatedChannel)) {
+ // only log if there are real changes
+ MetricsLogger.action(getChannelLog(updatedChannel, pkg));
+ }
+
+ if (updatedChannel.canBypassDnd() != mAreChannelsBypassingDnd) {
+ updateChannelsBypassingDnd();
+ }
+ updateConfig();
+ }
+
+ @Override
+ public NotificationChannel getNotificationChannel(String pkg, int uid, String channelId,
+ boolean includeDeleted) {
+ Preconditions.checkNotNull(pkg);
+ PackagePreferences r = getOrCreatePackagePreferences(pkg, uid);
+ if (r == null) {
+ return null;
+ }
+ if (channelId == null) {
+ channelId = NotificationChannel.DEFAULT_CHANNEL_ID;
+ }
+ final NotificationChannel nc = r.channels.get(channelId);
+ if (nc != null && (includeDeleted || !nc.isDeleted())) {
+ return nc;
+ }
+ return null;
+ }
+
+ @Override
+ public void deleteNotificationChannel(String pkg, int uid, String channelId) {
+ PackagePreferences r = getPackagePreferences(pkg, uid);
+ if (r == null) {
+ return;
+ }
+ NotificationChannel channel = r.channels.get(channelId);
+ if (channel != null) {
+ channel.setDeleted(true);
+ LogMaker lm = getChannelLog(channel, pkg);
+ lm.setType(com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_CLOSE);
+ MetricsLogger.action(lm);
+
+ if (mAreChannelsBypassingDnd && channel.canBypassDnd()) {
+ updateChannelsBypassingDnd();
+ }
+ }
+ }
+
+ @Override
+ @VisibleForTesting
+ public void permanentlyDeleteNotificationChannel(String pkg, int uid, String channelId) {
+ Preconditions.checkNotNull(pkg);
+ Preconditions.checkNotNull(channelId);
+ PackagePreferences r = getPackagePreferences(pkg, uid);
+ if (r == null) {
+ return;
+ }
+ r.channels.remove(channelId);
+ }
+
+ @Override
+ public void permanentlyDeleteNotificationChannels(String pkg, int uid) {
+ Preconditions.checkNotNull(pkg);
+ PackagePreferences r = getPackagePreferences(pkg, uid);
+ if (r == null) {
+ return;
+ }
+ int N = r.channels.size() - 1;
+ for (int i = N; i >= 0; i--) {
+ String key = r.channels.keyAt(i);
+ if (!NotificationChannel.DEFAULT_CHANNEL_ID.equals(key)) {
+ r.channels.remove(key);
+ }
+ }
+ }
+
+ public NotificationChannelGroup getNotificationChannelGroupWithChannels(String pkg,
+ int uid, String groupId, boolean includeDeleted) {
+ Preconditions.checkNotNull(pkg);
+ PackagePreferences r = getPackagePreferences(pkg, uid);
+ if (r == null || groupId == null || !r.groups.containsKey(groupId)) {
+ return null;
+ }
+ NotificationChannelGroup group = r.groups.get(groupId).clone();
+ group.setChannels(new ArrayList<>());
+ int N = r.channels.size();
+ for (int i = 0; i < N; i++) {
+ final NotificationChannel nc = r.channels.valueAt(i);
+ if (includeDeleted || !nc.isDeleted()) {
+ if (groupId.equals(nc.getGroup())) {
+ group.addChannel(nc);
+ }
+ }
+ }
+ return group;
+ }
+
+ public NotificationChannelGroup getNotificationChannelGroup(String groupId, String pkg,
+ int uid) {
+ Preconditions.checkNotNull(pkg);
+ PackagePreferences r = getPackagePreferences(pkg, uid);
+ if (r == null) {
+ return null;
+ }
+ return r.groups.get(groupId);
+ }
+
+ @Override
+ public ParceledListSlice<NotificationChannelGroup> getNotificationChannelGroups(String pkg,
+ int uid, boolean includeDeleted, boolean includeNonGrouped) {
+ Preconditions.checkNotNull(pkg);
+ Map<String, NotificationChannelGroup> groups = new ArrayMap<>();
+ PackagePreferences r = getPackagePreferences(pkg, uid);
+ if (r == null) {
+ return ParceledListSlice.emptyList();
+ }
+ NotificationChannelGroup nonGrouped = new NotificationChannelGroup(null, null);
+ int N = r.channels.size();
+ for (int i = 0; i < N; i++) {
+ final NotificationChannel nc = r.channels.valueAt(i);
+ if (includeDeleted || !nc.isDeleted()) {
+ if (nc.getGroup() != null) {
+ if (r.groups.get(nc.getGroup()) != null) {
+ NotificationChannelGroup ncg = groups.get(nc.getGroup());
+ if (ncg == null) {
+ ncg = r.groups.get(nc.getGroup()).clone();
+ ncg.setChannels(new ArrayList<>());
+ groups.put(nc.getGroup(), ncg);
+
+ }
+ ncg.addChannel(nc);
+ }
+ } else {
+ nonGrouped.addChannel(nc);
+ }
+ }
+ }
+ if (includeNonGrouped && nonGrouped.getChannels().size() > 0) {
+ groups.put(null, nonGrouped);
+ }
+ return new ParceledListSlice<>(new ArrayList<>(groups.values()));
+ }
+
+ public List<NotificationChannel> deleteNotificationChannelGroup(String pkg, int uid,
+ String groupId) {
+ List<NotificationChannel> deletedChannels = new ArrayList<>();
+ PackagePreferences r = getPackagePreferences(pkg, uid);
+ if (r == null || TextUtils.isEmpty(groupId)) {
+ return deletedChannels;
+ }
+
+ r.groups.remove(groupId);
+
+ int N = r.channels.size();
+ for (int i = 0; i < N; i++) {
+ final NotificationChannel nc = r.channels.valueAt(i);
+ if (groupId.equals(nc.getGroup())) {
+ nc.setDeleted(true);
+ deletedChannels.add(nc);
+ }
+ }
+ return deletedChannels;
+ }
+
+ @Override
+ public Collection<NotificationChannelGroup> getNotificationChannelGroups(String pkg,
+ int uid) {
+ PackagePreferences r = getPackagePreferences(pkg, uid);
+ if (r == null) {
+ return new ArrayList<>();
+ }
+ return r.groups.values();
+ }
+
+ @Override
+ public ParceledListSlice<NotificationChannel> getNotificationChannels(String pkg, int uid,
+ boolean includeDeleted) {
+ Preconditions.checkNotNull(pkg);
+ List<NotificationChannel> channels = new ArrayList<>();
+ PackagePreferences r = getPackagePreferences(pkg, uid);
+ if (r == null) {
+ return ParceledListSlice.emptyList();
+ }
+ int N = r.channels.size();
+ for (int i = 0; i < N; i++) {
+ final NotificationChannel nc = r.channels.valueAt(i);
+ if (includeDeleted || !nc.isDeleted()) {
+ channels.add(nc);
+ }
+ }
+ return new ParceledListSlice<>(channels);
+ }
+
+ /**
+ * True for pre-O apps that only have the default channel, or pre O apps that have no
+ * channels yet. This method will create the default channel for pre-O apps that don't have it.
+ * Should never be true for O+ targeting apps, but that's enforced on boot/when an app
+ * upgrades.
+ */
+ public boolean onlyHasDefaultChannel(String pkg, int uid) {
+ PackagePreferences r = getOrCreatePackagePreferences(pkg, uid);
+ if (r.channels.size() == 1
+ && r.channels.containsKey(NotificationChannel.DEFAULT_CHANNEL_ID)) {
+ return true;
+ }
+ return false;
+ }
+
+ public int getDeletedChannelCount(String pkg, int uid) {
+ Preconditions.checkNotNull(pkg);
+ int deletedCount = 0;
+ PackagePreferences r = getPackagePreferences(pkg, uid);
+ if (r == null) {
+ return deletedCount;
+ }
+ int N = r.channels.size();
+ for (int i = 0; i < N; i++) {
+ final NotificationChannel nc = r.channels.valueAt(i);
+ if (nc.isDeleted()) {
+ deletedCount++;
+ }
+ }
+ return deletedCount;
+ }
+
+ public int getBlockedChannelCount(String pkg, int uid) {
+ Preconditions.checkNotNull(pkg);
+ int blockedCount = 0;
+ PackagePreferences r = getPackagePreferences(pkg, uid);
+ if (r == null) {
+ return blockedCount;
+ }
+ int N = r.channels.size();
+ for (int i = 0; i < N; i++) {
+ final NotificationChannel nc = r.channels.valueAt(i);
+ if (!nc.isDeleted() && IMPORTANCE_NONE == nc.getImportance()) {
+ blockedCount++;
+ }
+ }
+ return blockedCount;
+ }
+
+ public int getBlockedAppCount(int userId) {
+ int count = 0;
+ synchronized (mPackagePreferencess) {
+ final int N = mPackagePreferencess.size();
+ for (int i = 0; i < N; i++) {
+ final PackagePreferences r = mPackagePreferencess.valueAt(i);
+ if (userId == UserHandle.getUserId(r.uid)
+ && r.importance == IMPORTANCE_NONE) {
+ count++;
+ }
+ }
+ }
+ return count;
+ }
+
+ public void updateChannelsBypassingDnd() {
+ synchronized (mPackagePreferencess) {
+ final int numPackagePreferencess = mPackagePreferencess.size();
+ for (int PackagePreferencesIndex = 0; PackagePreferencesIndex < numPackagePreferencess;
+ PackagePreferencesIndex++) {
+ final PackagePreferences r = mPackagePreferencess.valueAt(PackagePreferencesIndex);
+ final int numChannels = r.channels.size();
+
+ for (int channelIndex = 0; channelIndex < numChannels; channelIndex++) {
+ NotificationChannel channel = r.channels.valueAt(channelIndex);
+ if (!channel.isDeleted() && channel.canBypassDnd()) {
+ // If any channel bypasses DND, synchronize state and return early.
+ if (!mAreChannelsBypassingDnd) {
+ mAreChannelsBypassingDnd = true;
+ updateZenPolicy(true);
+ }
+ return;
+ }
+ }
+ }
+ }
+
+ // If no channels bypass DND, update the zen policy once to disable DND bypass.
+ if (mAreChannelsBypassingDnd) {
+ mAreChannelsBypassingDnd = false;
+ updateZenPolicy(false);
+ }
+ }
+
+ public void updateZenPolicy(boolean areChannelsBypassingDnd) {
+ NotificationManager.Policy policy = mZenModeHelper.getNotificationPolicy();
+ mZenModeHelper.setNotificationPolicy(new NotificationManager.Policy(
+ policy.priorityCategories, policy.priorityCallSenders,
+ policy.priorityMessageSenders, policy.suppressedVisualEffects,
+ (areChannelsBypassingDnd ? NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND
+ : 0)));
+ }
+
+ public boolean areChannelsBypassingDnd() {
+ return mAreChannelsBypassingDnd;
+ }
+
+ /**
+ * Sets importance.
+ */
+ @Override
+ public void setImportance(String pkgName, int uid, int importance) {
+ getOrCreatePackagePreferences(pkgName, uid).importance = importance;
+ updateConfig();
+ }
+
+ public void setEnabled(String packageName, int uid, boolean enabled) {
+ boolean wasEnabled = getImportance(packageName, uid) != IMPORTANCE_NONE;
+ if (wasEnabled == enabled) {
+ return;
+ }
+ setImportance(packageName, uid,
+ enabled ? DEFAULT_IMPORTANCE : IMPORTANCE_NONE);
+ }
+
+ /**
+ * Sets whether any notifications from the app, represented by the given {@code pkgName} and
+ * {@code uid}, have their importance locked by the user. Locked notifications don't get
+ * considered for sentiment adjustments (and thus never show a blocking helper).
+ */
+ public void setAppImportanceLocked(String packageName, int uid) {
+ PackagePreferences PackagePreferences = getOrCreatePackagePreferences(packageName, uid);
+ if ((PackagePreferences.lockedAppFields & LockableAppFields.USER_LOCKED_IMPORTANCE) != 0) {
+ return;
+ }
+
+ PackagePreferences.lockedAppFields =
+ PackagePreferences.lockedAppFields | LockableAppFields.USER_LOCKED_IMPORTANCE;
+ updateConfig();
+ }
+
+ @VisibleForTesting
+ void lockFieldsForUpdate(NotificationChannel original, NotificationChannel update) {
+ if (original.canBypassDnd() != update.canBypassDnd()) {
+ update.lockFields(NotificationChannel.USER_LOCKED_PRIORITY);
+ }
+ if (original.getLockscreenVisibility() != update.getLockscreenVisibility()) {
+ update.lockFields(NotificationChannel.USER_LOCKED_VISIBILITY);
+ }
+ if (original.getImportance() != update.getImportance()) {
+ update.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE);
+ }
+ if (original.shouldShowLights() != update.shouldShowLights()
+ || original.getLightColor() != update.getLightColor()) {
+ update.lockFields(NotificationChannel.USER_LOCKED_LIGHTS);
+ }
+ if (!Objects.equals(original.getSound(), update.getSound())) {
+ update.lockFields(NotificationChannel.USER_LOCKED_SOUND);
+ }
+ if (!Arrays.equals(original.getVibrationPattern(), update.getVibrationPattern())
+ || original.shouldVibrate() != update.shouldVibrate()) {
+ update.lockFields(NotificationChannel.USER_LOCKED_VIBRATION);
+ }
+ if (original.canShowBadge() != update.canShowBadge()) {
+ update.lockFields(NotificationChannel.USER_LOCKED_SHOW_BADGE);
+ }
+ }
+
+ public void dump(PrintWriter pw, String prefix,
+ @NonNull NotificationManagerService.DumpFilter filter) {
+ pw.print(prefix);
+ pw.println("per-package config:");
+
+ pw.println("PackagePreferencess:");
+ synchronized (mPackagePreferencess) {
+ dumpPackagePreferencess(pw, prefix, filter, mPackagePreferencess);
+ }
+ pw.println("Restored without uid:");
+ dumpPackagePreferencess(pw, prefix, filter, mRestoredWithoutUids);
+ }
+
+ public void dump(ProtoOutputStream proto,
+ @NonNull NotificationManagerService.DumpFilter filter) {
+ synchronized (mPackagePreferencess) {
+ dumpPackagePreferencess(proto, RankingHelperProto.RECORDS, filter,
+ mPackagePreferencess);
+ }
+ dumpPackagePreferencess(proto, RankingHelperProto.RECORDS_RESTORED_WITHOUT_UID, filter,
+ mRestoredWithoutUids);
+ }
+
+ private static void dumpPackagePreferencess(PrintWriter pw, String prefix,
+ @NonNull NotificationManagerService.DumpFilter filter,
+ ArrayMap<String, PackagePreferences> PackagePreferencess) {
+ final int N = PackagePreferencess.size();
+ for (int i = 0; i < N; i++) {
+ final PackagePreferences r = PackagePreferencess.valueAt(i);
+ if (filter.matches(r.pkg)) {
+ pw.print(prefix);
+ pw.print(" AppSettings: ");
+ pw.print(r.pkg);
+ pw.print(" (");
+ pw.print(r.uid == PackagePreferences.UNKNOWN_UID ? "UNKNOWN_UID"
+ : Integer.toString(r.uid));
+ pw.print(')');
+ if (r.importance != DEFAULT_IMPORTANCE) {
+ pw.print(" importance=");
+ pw.print(NotificationListenerService.Ranking.importanceToString(r.importance));
+ }
+ if (r.priority != DEFAULT_PRIORITY) {
+ pw.print(" priority=");
+ pw.print(Notification.priorityToString(r.priority));
+ }
+ if (r.visibility != DEFAULT_VISIBILITY) {
+ pw.print(" visibility=");
+ pw.print(Notification.visibilityToString(r.visibility));
+ }
+ pw.print(" showBadge=");
+ pw.print(Boolean.toString(r.showBadge));
+ pw.println();
+ for (NotificationChannel channel : r.channels.values()) {
+ pw.print(prefix);
+ channel.dump(pw, " ", filter.redact);
+ }
+ for (NotificationChannelGroup group : r.groups.values()) {
+ pw.print(prefix);
+ pw.print(" ");
+ pw.print(" ");
+ pw.println(group);
+ }
+ }
+ }
+ }
+
+ private static void dumpPackagePreferencess(ProtoOutputStream proto, long fieldId,
+ @NonNull NotificationManagerService.DumpFilter filter,
+ ArrayMap<String, PackagePreferences> PackagePreferencess) {
+ final int N = PackagePreferencess.size();
+ long fToken;
+ for (int i = 0; i < N; i++) {
+ final PackagePreferences r = PackagePreferencess.valueAt(i);
+ if (filter.matches(r.pkg)) {
+ fToken = proto.start(fieldId);
+
+ proto.write(RankingHelperProto.RecordProto.PACKAGE, r.pkg);
+ proto.write(RankingHelperProto.RecordProto.UID, r.uid);
+ proto.write(RankingHelperProto.RecordProto.IMPORTANCE, r.importance);
+ proto.write(RankingHelperProto.RecordProto.PRIORITY, r.priority);
+ proto.write(RankingHelperProto.RecordProto.VISIBILITY, r.visibility);
+ proto.write(RankingHelperProto.RecordProto.SHOW_BADGE, r.showBadge);
+
+ for (NotificationChannel channel : r.channels.values()) {
+ channel.writeToProto(proto, RankingHelperProto.RecordProto.CHANNELS);
+ }
+ for (NotificationChannelGroup group : r.groups.values()) {
+ group.writeToProto(proto, RankingHelperProto.RecordProto.CHANNEL_GROUPS);
+ }
+
+ proto.end(fToken);
+ }
+ }
+ }
+
+ public JSONObject dumpJson(NotificationManagerService.DumpFilter filter) {
+ JSONObject ranking = new JSONObject();
+ JSONArray PackagePreferencess = new JSONArray();
+ try {
+ ranking.put("noUid", mRestoredWithoutUids.size());
+ } catch (JSONException e) {
+ // pass
+ }
+ synchronized (mPackagePreferencess) {
+ final int N = mPackagePreferencess.size();
+ for (int i = 0; i < N; i++) {
+ final PackagePreferences r = mPackagePreferencess.valueAt(i);
+ if (filter == null || filter.matches(r.pkg)) {
+ JSONObject PackagePreferences = new JSONObject();
+ try {
+ PackagePreferences.put("userId", UserHandle.getUserId(r.uid));
+ PackagePreferences.put("packageName", r.pkg);
+ if (r.importance != DEFAULT_IMPORTANCE) {
+ PackagePreferences.put("importance",
+ NotificationListenerService.Ranking.importanceToString(
+ r.importance));
+ }
+ if (r.priority != DEFAULT_PRIORITY) {
+ PackagePreferences.put("priority",
+ Notification.priorityToString(r.priority));
+ }
+ if (r.visibility != DEFAULT_VISIBILITY) {
+ PackagePreferences.put("visibility",
+ Notification.visibilityToString(r.visibility));
+ }
+ if (r.showBadge != DEFAULT_SHOW_BADGE) {
+ PackagePreferences.put("showBadge", Boolean.valueOf(r.showBadge));
+ }
+ JSONArray channels = new JSONArray();
+ for (NotificationChannel channel : r.channels.values()) {
+ channels.put(channel.toJson());
+ }
+ PackagePreferences.put("channels", channels);
+ JSONArray groups = new JSONArray();
+ for (NotificationChannelGroup group : r.groups.values()) {
+ groups.put(group.toJson());
+ }
+ PackagePreferences.put("groups", groups);
+ } catch (JSONException e) {
+ // pass
+ }
+ PackagePreferencess.put(PackagePreferences);
+ }
+ }
+ }
+ try {
+ ranking.put("PackagePreferencess", PackagePreferencess);
+ } catch (JSONException e) {
+ // pass
+ }
+ return ranking;
+ }
+
+ /**
+ * Dump only the ban information as structured JSON for the stats collector.
+ *
+ * This is intentionally redundant with {#link dumpJson} because the old
+ * scraper will expect this format.
+ *
+ * @param filter
+ * @return
+ */
+ public JSONArray dumpBansJson(NotificationManagerService.DumpFilter filter) {
+ JSONArray bans = new JSONArray();
+ Map<Integer, String> packageBans = getPackageBans();
+ for (Map.Entry<Integer, String> ban : packageBans.entrySet()) {
+ final int userId = UserHandle.getUserId(ban.getKey());
+ final String packageName = ban.getValue();
+ if (filter == null || filter.matches(packageName)) {
+ JSONObject banJson = new JSONObject();
+ try {
+ banJson.put("userId", userId);
+ banJson.put("packageName", packageName);
+ } catch (JSONException e) {
+ e.printStackTrace();
+ }
+ bans.put(banJson);
+ }
+ }
+ return bans;
+ }
+
+ public Map<Integer, String> getPackageBans() {
+ synchronized (mPackagePreferencess) {
+ final int N = mPackagePreferencess.size();
+ ArrayMap<Integer, String> packageBans = new ArrayMap<>(N);
+ for (int i = 0; i < N; i++) {
+ final PackagePreferences r = mPackagePreferencess.valueAt(i);
+ if (r.importance == IMPORTANCE_NONE) {
+ packageBans.put(r.uid, r.pkg);
+ }
+ }
+
+ return packageBans;
+ }
+ }
+
+ /**
+ * Dump only the channel information as structured JSON for the stats collector.
+ *
+ * This is intentionally redundant with {#link dumpJson} because the old
+ * scraper will expect this format.
+ *
+ * @param filter
+ * @return
+ */
+ public JSONArray dumpChannelsJson(NotificationManagerService.DumpFilter filter) {
+ JSONArray channels = new JSONArray();
+ Map<String, Integer> packageChannels = getPackageChannels();
+ for (Map.Entry<String, Integer> channelCount : packageChannels.entrySet()) {
+ final String packageName = channelCount.getKey();
+ if (filter == null || filter.matches(packageName)) {
+ JSONObject channelCountJson = new JSONObject();
+ try {
+ channelCountJson.put("packageName", packageName);
+ channelCountJson.put("channelCount", channelCount.getValue());
+ } catch (JSONException e) {
+ e.printStackTrace();
+ }
+ channels.put(channelCountJson);
+ }
+ }
+ return channels;
+ }
+
+ private Map<String, Integer> getPackageChannels() {
+ ArrayMap<String, Integer> packageChannels = new ArrayMap<>();
+ synchronized (mPackagePreferencess) {
+ for (int i = 0; i < mPackagePreferencess.size(); i++) {
+ final PackagePreferences r = mPackagePreferencess.valueAt(i);
+ int channelCount = 0;
+ for (int j = 0; j < r.channels.size(); j++) {
+ if (!r.channels.valueAt(j).isDeleted()) {
+ channelCount++;
+ }
+ }
+ packageChannels.put(r.pkg, channelCount);
+ }
+ }
+ return packageChannels;
+ }
+
+ public void onUserRemoved(int userId) {
+ synchronized (mPackagePreferencess) {
+ int N = mPackagePreferencess.size();
+ for (int i = N - 1; i >= 0; i--) {
+ PackagePreferences PackagePreferences = mPackagePreferencess.valueAt(i);
+ if (UserHandle.getUserId(PackagePreferences.uid) == userId) {
+ mPackagePreferencess.removeAt(i);
+ }
+ }
+ }
+ }
+
+ protected void onLocaleChanged(Context context, int userId) {
+ synchronized (mPackagePreferencess) {
+ int N = mPackagePreferencess.size();
+ for (int i = 0; i < N; i++) {
+ PackagePreferences PackagePreferences = mPackagePreferencess.valueAt(i);
+ if (UserHandle.getUserId(PackagePreferences.uid) == userId) {
+ if (PackagePreferences.channels.containsKey(
+ NotificationChannel.DEFAULT_CHANNEL_ID)) {
+ PackagePreferences.channels.get(
+ NotificationChannel.DEFAULT_CHANNEL_ID).setName(
+ context.getResources().getString(
+ R.string.default_notification_channel_label));
+ }
+ }
+ }
+ }
+ }
+
+ public void onPackagesChanged(boolean removingPackage, int changeUserId, String[] pkgList,
+ int[] uidList) {
+ if (pkgList == null || pkgList.length == 0) {
+ return; // nothing to do
+ }
+ boolean updated = false;
+ if (removingPackage) {
+ // Remove notification settings for uninstalled package
+ int size = Math.min(pkgList.length, uidList.length);
+ for (int i = 0; i < size; i++) {
+ final String pkg = pkgList[i];
+ final int uid = uidList[i];
+ synchronized (mPackagePreferencess) {
+ mPackagePreferencess.remove(packagePreferencesKey(pkg, uid));
+ }
+ mRestoredWithoutUids.remove(pkg);
+ updated = true;
+ }
+ } else {
+ for (String pkg : pkgList) {
+ // Package install
+ final PackagePreferences r = mRestoredWithoutUids.get(pkg);
+ if (r != null) {
+ try {
+ r.uid = mPm.getPackageUidAsUser(r.pkg, changeUserId);
+ mRestoredWithoutUids.remove(pkg);
+ synchronized (mPackagePreferencess) {
+ mPackagePreferencess.put(packagePreferencesKey(r.pkg, r.uid), r);
+ }
+ updated = true;
+ } catch (PackageManager.NameNotFoundException e) {
+ // noop
+ }
+ }
+ // Package upgrade
+ try {
+ PackagePreferences fullPackagePreferences = getPackagePreferences(pkg,
+ mPm.getPackageUidAsUser(pkg, changeUserId));
+ if (fullPackagePreferences != null) {
+ createDefaultChannelIfNeeded(fullPackagePreferences);
+ deleteDefaultChannelIfNeeded(fullPackagePreferences);
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ }
+ }
+ }
+
+ if (updated) {
+ updateConfig();
+ }
+ }
+
+ private LogMaker getChannelLog(NotificationChannel channel, String pkg) {
+ return new LogMaker(
+ com.android.internal.logging.nano.MetricsProto.MetricsEvent
+ .ACTION_NOTIFICATION_CHANNEL)
+ .setType(com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_UPDATE)
+ .setPackageName(pkg)
+ .addTaggedData(
+ com.android.internal.logging.nano.MetricsProto.MetricsEvent
+ .FIELD_NOTIFICATION_CHANNEL_ID,
+ channel.getId())
+ .addTaggedData(
+ com.android.internal.logging.nano.MetricsProto.MetricsEvent
+ .FIELD_NOTIFICATION_CHANNEL_IMPORTANCE,
+ channel.getImportance());
+ }
+
+ private LogMaker getChannelGroupLog(String groupId, String pkg) {
+ return new LogMaker(
+ com.android.internal.logging.nano.MetricsProto.MetricsEvent
+ .ACTION_NOTIFICATION_CHANNEL_GROUP)
+ .setType(com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_UPDATE)
+ .addTaggedData(
+ com.android.internal.logging.nano.MetricsProto.MetricsEvent
+ .FIELD_NOTIFICATION_CHANNEL_GROUP_ID,
+ groupId)
+ .setPackageName(pkg);
+ }
+
+
+ public void updateBadgingEnabled() {
+ if (mBadgingEnabled == null) {
+ mBadgingEnabled = new SparseBooleanArray();
+ }
+ boolean changed = false;
+ // update the cached values
+ for (int index = 0; index < mBadgingEnabled.size(); index++) {
+ int userId = mBadgingEnabled.keyAt(index);
+ final boolean oldValue = mBadgingEnabled.get(userId);
+ final boolean newValue = Settings.Secure.getIntForUser(mContext.getContentResolver(),
+ Settings.Secure.NOTIFICATION_BADGING,
+ DEFAULT_SHOW_BADGE ? 1 : 0, userId) != 0;
+ mBadgingEnabled.put(userId, newValue);
+ changed |= oldValue != newValue;
+ }
+ if (changed) {
+ updateConfig();
+ }
+ }
+
+ public boolean badgingEnabled(UserHandle userHandle) {
+ int userId = userHandle.getIdentifier();
+ if (userId == UserHandle.USER_ALL) {
+ return false;
+ }
+ if (mBadgingEnabled.indexOfKey(userId) < 0) {
+ mBadgingEnabled.put(userId,
+ Settings.Secure.getIntForUser(mContext.getContentResolver(),
+ Settings.Secure.NOTIFICATION_BADGING,
+ DEFAULT_SHOW_BADGE ? 1 : 0, userId) != 0);
+ }
+ return mBadgingEnabled.get(userId, DEFAULT_SHOW_BADGE);
+ }
+
+ private void updateConfig() {
+ mRankingHandler.requestSort();
+ }
+
+ private static String packagePreferencesKey(String pkg, int uid) {
+ return pkg + "|" + uid;
+ }
+
+ private static class PackagePreferences {
+ static int UNKNOWN_UID = UserHandle.USER_NULL;
+
+ String pkg;
+ int uid = UNKNOWN_UID;
+ int importance = DEFAULT_IMPORTANCE;
+ int priority = DEFAULT_PRIORITY;
+ int visibility = DEFAULT_VISIBILITY;
+ boolean showBadge = DEFAULT_SHOW_BADGE;
+ int lockedAppFields = DEFAULT_LOCKED_APP_FIELDS;
+
+ ArrayMap<String, NotificationChannel> channels = new ArrayMap<>();
+ Map<String, NotificationChannelGroup> groups = new ConcurrentHashMap<>();
+ }
+}
diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java
index 61b5415..f5e58ea 100644
--- a/services/core/java/com/android/server/notification/RankingHelper.java
+++ b/services/core/java/com/android/server/notification/RankingHelper.java
@@ -15,123 +15,37 @@
*/
package com.android.server.notification;
-import static android.app.NotificationManager.IMPORTANCE_NONE;
-
-import android.annotation.IntDef;
import android.annotation.NonNull;
-import android.app.Notification;
-import android.app.NotificationChannel;
-import android.app.NotificationChannelGroup;
import android.app.NotificationManager;
import android.content.Context;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.ParceledListSlice;
-import android.metrics.LogMaker;
-import android.os.Build;
-import android.os.UserHandle;
-import android.provider.Settings.Secure;
-import android.service.notification.NotificationListenerService.Ranking;
import android.service.notification.RankingHelperProto;
-import android.service.notification.RankingHelperProto.RecordProto;
-import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Slog;
-import android.util.SparseBooleanArray;
import android.util.proto.ProtoOutputStream;
-import com.android.internal.R;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto;
-import com.android.internal.util.Preconditions;
-import com.android.internal.util.XmlUtils;
-
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
-
-import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Objects;
-import java.util.concurrent.ConcurrentHashMap;
-public class RankingHelper implements RankingConfig {
+public class RankingHelper {
private static final String TAG = "RankingHelper";
- private static final int XML_VERSION = 1;
-
- static final String TAG_RANKING = "ranking";
- private static final String TAG_PACKAGE = "package";
- private static final String TAG_CHANNEL = "channel";
- private static final String TAG_GROUP = "channelGroup";
-
- private static final String ATT_VERSION = "version";
- private static final String ATT_NAME = "name";
- private static final String ATT_UID = "uid";
- private static final String ATT_ID = "id";
- private static final String ATT_PRIORITY = "priority";
- private static final String ATT_VISIBILITY = "visibility";
- private static final String ATT_IMPORTANCE = "importance";
- private static final String ATT_SHOW_BADGE = "show_badge";
- private static final String ATT_APP_USER_LOCKED_FIELDS = "app_user_locked_fields";
-
- private static final int DEFAULT_PRIORITY = Notification.PRIORITY_DEFAULT;
- private static final int DEFAULT_VISIBILITY = NotificationManager.VISIBILITY_NO_OVERRIDE;
- private static final int DEFAULT_IMPORTANCE = NotificationManager.IMPORTANCE_UNSPECIFIED;
- private static final boolean DEFAULT_SHOW_BADGE = true;
- /**
- * Default value for what fields are user locked. See {@link LockableAppFields} for all lockable
- * fields.
- */
- private static final int DEFAULT_LOCKED_APP_FIELDS = 0;
-
- /**
- * All user-lockable fields for a given application.
- */
- @IntDef({LockableAppFields.USER_LOCKED_IMPORTANCE})
- public @interface LockableAppFields {
- int USER_LOCKED_IMPORTANCE = 0x00000001;
- }
-
private final NotificationSignalExtractor[] mSignalExtractors;
private final NotificationComparator mPreliminaryComparator;
private final GlobalSortKeyComparator mFinalComparator = new GlobalSortKeyComparator();
- private final ArrayMap<String, Record> mRecords = new ArrayMap<>(); // pkg|uid => Record
private final ArrayMap<String, NotificationRecord> mProxyByGroupTmp = new ArrayMap<>();
- private final ArrayMap<String, Record> mRestoredWithoutUids = new ArrayMap<>(); // pkg => Record
private final Context mContext;
private final RankingHandler mRankingHandler;
- private final PackageManager mPm;
- private SparseBooleanArray mBadgingEnabled;
- private boolean mAreChannelsBypassingDnd;
- private ZenModeHelper mZenModeHelper;
- public RankingHelper(Context context, PackageManager pm, RankingHandler rankingHandler,
+ public RankingHelper(Context context, RankingHandler rankingHandler, RankingConfig config,
ZenModeHelper zenHelper, NotificationUsageStats usageStats, String[] extractorNames) {
mContext = context;
mRankingHandler = rankingHandler;
- mPm = pm;
- mZenModeHelper= zenHelper;
-
mPreliminaryComparator = new NotificationComparator(mContext);
- updateBadgingEnabled();
-
final int N = extractorNames.length;
mSignalExtractors = new NotificationSignalExtractor[N];
for (int i = 0; i < N; i++) {
@@ -140,7 +54,7 @@
NotificationSignalExtractor extractor =
(NotificationSignalExtractor) extractorClass.newInstance();
extractor.initialize(mContext, usageStats);
- extractor.setConfig(this);
+ extractor.setConfig(config);
extractor.setZenHelper(zenHelper);
mSignalExtractors[i] = extractor;
} catch (ClassNotFoundException e) {
@@ -151,10 +65,6 @@
Slog.w(TAG, "Problem accessing extractor " + extractorNames[i] + ".", e);
}
}
-
- mAreChannelsBypassingDnd = (mZenModeHelper.getNotificationPolicy().state &
- NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND) == 1;
- updateChannelsBypassingDnd();
}
@SuppressWarnings("unchecked")
@@ -184,279 +94,6 @@
}
}
- public void readXml(XmlPullParser parser, boolean forRestore)
- throws XmlPullParserException, IOException {
- int type = parser.getEventType();
- if (type != XmlPullParser.START_TAG) return;
- String tag = parser.getName();
- if (!TAG_RANKING.equals(tag)) return;
- // Clobber groups and channels with the xml, but don't delete other data that wasn't present
- // at the time of serialization.
- mRestoredWithoutUids.clear();
- while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
- tag = parser.getName();
- if (type == XmlPullParser.END_TAG && TAG_RANKING.equals(tag)) {
- return;
- }
- if (type == XmlPullParser.START_TAG) {
- if (TAG_PACKAGE.equals(tag)) {
- int uid = XmlUtils.readIntAttribute(parser, ATT_UID, Record.UNKNOWN_UID);
- String name = parser.getAttributeValue(null, ATT_NAME);
- if (!TextUtils.isEmpty(name)) {
- if (forRestore) {
- try {
- //TODO: http://b/22388012
- uid = mPm.getPackageUidAsUser(name, UserHandle.USER_SYSTEM);
- } catch (NameNotFoundException e) {
- // noop
- }
- }
-
- Record r = getOrCreateRecord(name, uid,
- XmlUtils.readIntAttribute(
- parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE),
- XmlUtils.readIntAttribute(parser, ATT_PRIORITY, DEFAULT_PRIORITY),
- XmlUtils.readIntAttribute(
- parser, ATT_VISIBILITY, DEFAULT_VISIBILITY),
- XmlUtils.readBooleanAttribute(
- parser, ATT_SHOW_BADGE, DEFAULT_SHOW_BADGE));
- r.importance = XmlUtils.readIntAttribute(
- parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE);
- r.priority = XmlUtils.readIntAttribute(
- parser, ATT_PRIORITY, DEFAULT_PRIORITY);
- r.visibility = XmlUtils.readIntAttribute(
- parser, ATT_VISIBILITY, DEFAULT_VISIBILITY);
- r.showBadge = XmlUtils.readBooleanAttribute(
- parser, ATT_SHOW_BADGE, DEFAULT_SHOW_BADGE);
- r.lockedAppFields = XmlUtils.readIntAttribute(parser,
- ATT_APP_USER_LOCKED_FIELDS, DEFAULT_LOCKED_APP_FIELDS);
-
- final int innerDepth = parser.getDepth();
- while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
- && (type != XmlPullParser.END_TAG
- || parser.getDepth() > innerDepth)) {
- if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
- continue;
- }
-
- String tagName = parser.getName();
- // Channel groups
- if (TAG_GROUP.equals(tagName)) {
- String id = parser.getAttributeValue(null, ATT_ID);
- CharSequence groupName = parser.getAttributeValue(null, ATT_NAME);
- if (!TextUtils.isEmpty(id)) {
- NotificationChannelGroup group
- = new NotificationChannelGroup(id, groupName);
- group.populateFromXml(parser);
- r.groups.put(id, group);
- }
- }
- // Channels
- if (TAG_CHANNEL.equals(tagName)) {
- String id = parser.getAttributeValue(null, ATT_ID);
- String channelName = parser.getAttributeValue(null, ATT_NAME);
- int channelImportance = XmlUtils.readIntAttribute(
- parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE);
- if (!TextUtils.isEmpty(id) && !TextUtils.isEmpty(channelName)) {
- NotificationChannel channel = new NotificationChannel(id,
- channelName, channelImportance);
- if (forRestore) {
- channel.populateFromXmlForRestore(parser, mContext);
- } else {
- channel.populateFromXml(parser);
- }
- r.channels.put(id, channel);
- }
- }
- }
-
- try {
- deleteDefaultChannelIfNeeded(r);
- } catch (NameNotFoundException e) {
- Slog.e(TAG, "deleteDefaultChannelIfNeeded - Exception: " + e);
- }
- }
- }
- }
- }
- throw new IllegalStateException("Failed to reach END_DOCUMENT");
- }
-
- private static String recordKey(String pkg, int uid) {
- return pkg + "|" + uid;
- }
-
- private Record getRecord(String pkg, int uid) {
- final String key = recordKey(pkg, uid);
- synchronized (mRecords) {
- return mRecords.get(key);
- }
- }
-
- private Record getOrCreateRecord(String pkg, int uid) {
- return getOrCreateRecord(pkg, uid,
- DEFAULT_IMPORTANCE, DEFAULT_PRIORITY, DEFAULT_VISIBILITY, DEFAULT_SHOW_BADGE);
- }
-
- private Record getOrCreateRecord(String pkg, int uid, int importance, int priority,
- int visibility, boolean showBadge) {
- final String key = recordKey(pkg, uid);
- synchronized (mRecords) {
- Record r = (uid == Record.UNKNOWN_UID) ? mRestoredWithoutUids.get(pkg) : mRecords.get(
- key);
- if (r == null) {
- r = new Record();
- r.pkg = pkg;
- r.uid = uid;
- r.importance = importance;
- r.priority = priority;
- r.visibility = visibility;
- r.showBadge = showBadge;
-
- try {
- createDefaultChannelIfNeeded(r);
- } catch (NameNotFoundException e) {
- Slog.e(TAG, "createDefaultChannelIfNeeded - Exception: " + e);
- }
-
- if (r.uid == Record.UNKNOWN_UID) {
- mRestoredWithoutUids.put(pkg, r);
- } else {
- mRecords.put(key, r);
- }
- }
- return r;
- }
- }
-
- private boolean shouldHaveDefaultChannel(Record r) throws NameNotFoundException {
- final int userId = UserHandle.getUserId(r.uid);
- final ApplicationInfo applicationInfo = mPm.getApplicationInfoAsUser(r.pkg, 0, userId);
- if (applicationInfo.targetSdkVersion >= Build.VERSION_CODES.O) {
- // O apps should not have the default channel.
- return false;
- }
-
- // Otherwise, this app should have the default channel.
- return true;
- }
-
- private void deleteDefaultChannelIfNeeded(Record r) throws NameNotFoundException {
- if (!r.channels.containsKey(NotificationChannel.DEFAULT_CHANNEL_ID)) {
- // Not present
- return;
- }
-
- if (shouldHaveDefaultChannel(r)) {
- // Keep the default channel until upgraded.
- return;
- }
-
- // Remove Default Channel.
- r.channels.remove(NotificationChannel.DEFAULT_CHANNEL_ID);
- }
-
- private void createDefaultChannelIfNeeded(Record r) throws NameNotFoundException {
- if (r.channels.containsKey(NotificationChannel.DEFAULT_CHANNEL_ID)) {
- r.channels.get(NotificationChannel.DEFAULT_CHANNEL_ID).setName(
- mContext.getString(R.string.default_notification_channel_label));
- return;
- }
-
- if (!shouldHaveDefaultChannel(r)) {
- // Keep the default channel until upgraded.
- return;
- }
-
- // Create Default Channel
- NotificationChannel channel;
- channel = new NotificationChannel(
- NotificationChannel.DEFAULT_CHANNEL_ID,
- mContext.getString(R.string.default_notification_channel_label),
- r.importance);
- channel.setBypassDnd(r.priority == Notification.PRIORITY_MAX);
- channel.setLockscreenVisibility(r.visibility);
- if (r.importance != NotificationManager.IMPORTANCE_UNSPECIFIED) {
- channel.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE);
- }
- if (r.priority != DEFAULT_PRIORITY) {
- channel.lockFields(NotificationChannel.USER_LOCKED_PRIORITY);
- }
- if (r.visibility != DEFAULT_VISIBILITY) {
- channel.lockFields(NotificationChannel.USER_LOCKED_VISIBILITY);
- }
- r.channels.put(channel.getId(), channel);
- }
-
- public void writeXml(XmlSerializer out, boolean forBackup) throws IOException {
- out.startTag(null, TAG_RANKING);
- out.attribute(null, ATT_VERSION, Integer.toString(XML_VERSION));
-
- synchronized (mRecords) {
- final int N = mRecords.size();
- for (int i = 0; i < N; i++) {
- final Record r = mRecords.valueAt(i);
- //TODO: http://b/22388012
- if (forBackup && UserHandle.getUserId(r.uid) != UserHandle.USER_SYSTEM) {
- continue;
- }
- final boolean hasNonDefaultSettings =
- r.importance != DEFAULT_IMPORTANCE
- || r.priority != DEFAULT_PRIORITY
- || r.visibility != DEFAULT_VISIBILITY
- || r.showBadge != DEFAULT_SHOW_BADGE
- || r.lockedAppFields != DEFAULT_LOCKED_APP_FIELDS
- || r.channels.size() > 0
- || r.groups.size() > 0;
- if (hasNonDefaultSettings) {
- out.startTag(null, TAG_PACKAGE);
- out.attribute(null, ATT_NAME, r.pkg);
- if (r.importance != DEFAULT_IMPORTANCE) {
- out.attribute(null, ATT_IMPORTANCE, Integer.toString(r.importance));
- }
- if (r.priority != DEFAULT_PRIORITY) {
- out.attribute(null, ATT_PRIORITY, Integer.toString(r.priority));
- }
- if (r.visibility != DEFAULT_VISIBILITY) {
- out.attribute(null, ATT_VISIBILITY, Integer.toString(r.visibility));
- }
- out.attribute(null, ATT_SHOW_BADGE, Boolean.toString(r.showBadge));
- out.attribute(null, ATT_APP_USER_LOCKED_FIELDS,
- Integer.toString(r.lockedAppFields));
-
- if (!forBackup) {
- out.attribute(null, ATT_UID, Integer.toString(r.uid));
- }
-
- for (NotificationChannelGroup group : r.groups.values()) {
- group.writeXml(out);
- }
-
- for (NotificationChannel channel : r.channels.values()) {
- if (forBackup) {
- if (!channel.isDeleted()) {
- channel.writeXmlForBackup(out, mContext);
- }
- } else {
- channel.writeXml(out);
- }
- }
-
- out.endTag(null, TAG_PACKAGE);
- }
- }
- }
- out.endTag(null, TAG_RANKING);
- }
-
- private void updateConfig() {
- final int N = mSignalExtractors.length;
- for (int i = 0; i < N; i++) {
- mSignalExtractors[i].setConfig(this);
- }
- mRankingHandler.requestSort();
- }
-
public void sort(ArrayList<NotificationRecord> notificationList) {
final int N = notificationList.size();
// clear global sort keys
@@ -521,562 +158,6 @@
return Collections.binarySearch(notificationList, target, mFinalComparator);
}
- /**
- * Gets importance.
- */
- @Override
- public int getImportance(String packageName, int uid) {
- return getOrCreateRecord(packageName, uid).importance;
- }
-
-
- /**
- * Returns whether the importance of the corresponding notification is user-locked and shouldn't
- * be adjusted by an assistant (via means of a blocking helper, for example). For the channel
- * locking field, see {@link NotificationChannel#USER_LOCKED_IMPORTANCE}.
- */
- public boolean getIsAppImportanceLocked(String packageName, int uid) {
- int userLockedFields = getOrCreateRecord(packageName, uid).lockedAppFields;
- return (userLockedFields & LockableAppFields.USER_LOCKED_IMPORTANCE) != 0;
- }
-
- @Override
- public boolean canShowBadge(String packageName, int uid) {
- return getOrCreateRecord(packageName, uid).showBadge;
- }
-
- @Override
- public void setShowBadge(String packageName, int uid, boolean showBadge) {
- getOrCreateRecord(packageName, uid).showBadge = showBadge;
- updateConfig();
- }
-
- @Override
- public boolean isGroupBlocked(String packageName, int uid, String groupId) {
- if (groupId == null) {
- return false;
- }
- Record r = getOrCreateRecord(packageName, uid);
- NotificationChannelGroup group = r.groups.get(groupId);
- if (group == null) {
- return false;
- }
- return group.isBlocked();
- }
-
- int getPackagePriority(String pkg, int uid) {
- return getOrCreateRecord(pkg, uid).priority;
- }
-
- int getPackageVisibility(String pkg, int uid) {
- return getOrCreateRecord(pkg, uid).visibility;
- }
-
- @Override
- public void createNotificationChannelGroup(String pkg, int uid, NotificationChannelGroup group,
- boolean fromTargetApp) {
- Preconditions.checkNotNull(pkg);
- Preconditions.checkNotNull(group);
- Preconditions.checkNotNull(group.getId());
- Preconditions.checkNotNull(!TextUtils.isEmpty(group.getName()));
- Record r = getOrCreateRecord(pkg, uid);
- if (r == null) {
- throw new IllegalArgumentException("Invalid package");
- }
- final NotificationChannelGroup oldGroup = r.groups.get(group.getId());
- if (!group.equals(oldGroup)) {
- // will log for new entries as well as name/description changes
- MetricsLogger.action(getChannelGroupLog(group.getId(), pkg));
- }
- if (oldGroup != null) {
- group.setChannels(oldGroup.getChannels());
-
- if (fromTargetApp) {
- group.setBlocked(oldGroup.isBlocked());
- }
- }
- r.groups.put(group.getId(), group);
- }
-
- @Override
- public void createNotificationChannel(String pkg, int uid, NotificationChannel channel,
- boolean fromTargetApp, boolean hasDndAccess) {
- Preconditions.checkNotNull(pkg);
- Preconditions.checkNotNull(channel);
- Preconditions.checkNotNull(channel.getId());
- Preconditions.checkArgument(!TextUtils.isEmpty(channel.getName()));
- Record r = getOrCreateRecord(pkg, uid);
- if (r == null) {
- throw new IllegalArgumentException("Invalid package");
- }
- if (channel.getGroup() != null && !r.groups.containsKey(channel.getGroup())) {
- throw new IllegalArgumentException("NotificationChannelGroup doesn't exist");
- }
- if (NotificationChannel.DEFAULT_CHANNEL_ID.equals(channel.getId())) {
- throw new IllegalArgumentException("Reserved id");
- }
- NotificationChannel existing = r.channels.get(channel.getId());
- // Keep most of the existing settings
- if (existing != null && fromTargetApp) {
- if (existing.isDeleted()) {
- existing.setDeleted(false);
-
- // log a resurrected channel as if it's new again
- MetricsLogger.action(getChannelLog(channel, pkg).setType(
- MetricsProto.MetricsEvent.TYPE_OPEN));
- }
-
- existing.setName(channel.getName().toString());
- existing.setDescription(channel.getDescription());
- existing.setBlockableSystem(channel.isBlockableSystem());
- if (existing.getGroup() == null) {
- existing.setGroup(channel.getGroup());
- }
-
- // Apps are allowed to downgrade channel importance if the user has not changed any
- // fields on this channel yet.
- if (existing.getUserLockedFields() == 0 &&
- channel.getImportance() < existing.getImportance()) {
- existing.setImportance(channel.getImportance());
- }
-
- // system apps and dnd access apps can bypass dnd if the user hasn't changed any
- // fields on the channel yet
- if (existing.getUserLockedFields() == 0 && hasDndAccess) {
- boolean bypassDnd = channel.canBypassDnd();
- existing.setBypassDnd(bypassDnd);
-
- if (bypassDnd != mAreChannelsBypassingDnd) {
- updateChannelsBypassingDnd();
- }
- }
-
- updateConfig();
- return;
- }
- if (channel.getImportance() < IMPORTANCE_NONE
- || channel.getImportance() > NotificationManager.IMPORTANCE_MAX) {
- throw new IllegalArgumentException("Invalid importance level");
- }
-
- // Reset fields that apps aren't allowed to set.
- if (fromTargetApp && !hasDndAccess) {
- channel.setBypassDnd(r.priority == Notification.PRIORITY_MAX);
- }
- if (fromTargetApp) {
- channel.setLockscreenVisibility(r.visibility);
- }
- clearLockedFields(channel);
- if (channel.getLockscreenVisibility() == Notification.VISIBILITY_PUBLIC) {
- channel.setLockscreenVisibility(Ranking.VISIBILITY_NO_OVERRIDE);
- }
- if (!r.showBadge) {
- channel.setShowBadge(false);
- }
-
- r.channels.put(channel.getId(), channel);
- if (channel.canBypassDnd() != mAreChannelsBypassingDnd) {
- updateChannelsBypassingDnd();
- }
- MetricsLogger.action(getChannelLog(channel, pkg).setType(
- MetricsProto.MetricsEvent.TYPE_OPEN));
- }
-
- void clearLockedFields(NotificationChannel channel) {
- channel.unlockFields(channel.getUserLockedFields());
- }
-
- @Override
- public void updateNotificationChannel(String pkg, int uid, NotificationChannel updatedChannel,
- boolean fromUser) {
- Preconditions.checkNotNull(updatedChannel);
- Preconditions.checkNotNull(updatedChannel.getId());
- Record r = getOrCreateRecord(pkg, uid);
- if (r == null) {
- throw new IllegalArgumentException("Invalid package");
- }
- NotificationChannel channel = r.channels.get(updatedChannel.getId());
- if (channel == null || channel.isDeleted()) {
- throw new IllegalArgumentException("Channel does not exist");
- }
- if (updatedChannel.getLockscreenVisibility() == Notification.VISIBILITY_PUBLIC) {
- updatedChannel.setLockscreenVisibility(Ranking.VISIBILITY_NO_OVERRIDE);
- }
- if (!fromUser) {
- updatedChannel.unlockFields(updatedChannel.getUserLockedFields());
- }
- if (fromUser) {
- updatedChannel.lockFields(channel.getUserLockedFields());
- lockFieldsForUpdate(channel, updatedChannel);
- }
- r.channels.put(updatedChannel.getId(), updatedChannel);
-
- if (NotificationChannel.DEFAULT_CHANNEL_ID.equals(updatedChannel.getId())) {
- // copy settings to app level so they are inherited by new channels
- // when the app migrates
- r.importance = updatedChannel.getImportance();
- r.priority = updatedChannel.canBypassDnd()
- ? Notification.PRIORITY_MAX : Notification.PRIORITY_DEFAULT;
- r.visibility = updatedChannel.getLockscreenVisibility();
- r.showBadge = updatedChannel.canShowBadge();
- }
-
- if (!channel.equals(updatedChannel)) {
- // only log if there are real changes
- MetricsLogger.action(getChannelLog(updatedChannel, pkg));
- }
-
- if (updatedChannel.canBypassDnd() != mAreChannelsBypassingDnd) {
- updateChannelsBypassingDnd();
- }
- updateConfig();
- }
-
- @Override
- public NotificationChannel getNotificationChannel(String pkg, int uid, String channelId,
- boolean includeDeleted) {
- Preconditions.checkNotNull(pkg);
- Record r = getOrCreateRecord(pkg, uid);
- if (r == null) {
- return null;
- }
- if (channelId == null) {
- channelId = NotificationChannel.DEFAULT_CHANNEL_ID;
- }
- final NotificationChannel nc = r.channels.get(channelId);
- if (nc != null && (includeDeleted || !nc.isDeleted())) {
- return nc;
- }
- return null;
- }
-
- @Override
- public void deleteNotificationChannel(String pkg, int uid, String channelId) {
- Record r = getRecord(pkg, uid);
- if (r == null) {
- return;
- }
- NotificationChannel channel = r.channels.get(channelId);
- if (channel != null) {
- channel.setDeleted(true);
- LogMaker lm = getChannelLog(channel, pkg);
- lm.setType(MetricsProto.MetricsEvent.TYPE_CLOSE);
- MetricsLogger.action(lm);
-
- if (mAreChannelsBypassingDnd && channel.canBypassDnd()) {
- updateChannelsBypassingDnd();
- }
- }
- }
-
- @Override
- @VisibleForTesting
- public void permanentlyDeleteNotificationChannel(String pkg, int uid, String channelId) {
- Preconditions.checkNotNull(pkg);
- Preconditions.checkNotNull(channelId);
- Record r = getRecord(pkg, uid);
- if (r == null) {
- return;
- }
- r.channels.remove(channelId);
- }
-
- @Override
- public void permanentlyDeleteNotificationChannels(String pkg, int uid) {
- Preconditions.checkNotNull(pkg);
- Record r = getRecord(pkg, uid);
- if (r == null) {
- return;
- }
- int N = r.channels.size() - 1;
- for (int i = N; i >= 0; i--) {
- String key = r.channels.keyAt(i);
- if (!NotificationChannel.DEFAULT_CHANNEL_ID.equals(key)) {
- r.channels.remove(key);
- }
- }
- }
-
- public NotificationChannelGroup getNotificationChannelGroupWithChannels(String pkg,
- int uid, String groupId, boolean includeDeleted) {
- Preconditions.checkNotNull(pkg);
- Record r = getRecord(pkg, uid);
- if (r == null || groupId == null || !r.groups.containsKey(groupId)) {
- return null;
- }
- NotificationChannelGroup group = r.groups.get(groupId).clone();
- group.setChannels(new ArrayList<>());
- int N = r.channels.size();
- for (int i = 0; i < N; i++) {
- final NotificationChannel nc = r.channels.valueAt(i);
- if (includeDeleted || !nc.isDeleted()) {
- if (groupId.equals(nc.getGroup())) {
- group.addChannel(nc);
- }
- }
- }
- return group;
- }
-
- public NotificationChannelGroup getNotificationChannelGroup(String groupId, String pkg,
- int uid) {
- Preconditions.checkNotNull(pkg);
- Record r = getRecord(pkg, uid);
- if (r == null) {
- return null;
- }
- return r.groups.get(groupId);
- }
-
- @Override
- public ParceledListSlice<NotificationChannelGroup> getNotificationChannelGroups(String pkg,
- int uid, boolean includeDeleted, boolean includeNonGrouped) {
- Preconditions.checkNotNull(pkg);
- Map<String, NotificationChannelGroup> groups = new ArrayMap<>();
- Record r = getRecord(pkg, uid);
- if (r == null) {
- return ParceledListSlice.emptyList();
- }
- NotificationChannelGroup nonGrouped = new NotificationChannelGroup(null, null);
- int N = r.channels.size();
- for (int i = 0; i < N; i++) {
- final NotificationChannel nc = r.channels.valueAt(i);
- if (includeDeleted || !nc.isDeleted()) {
- if (nc.getGroup() != null) {
- if (r.groups.get(nc.getGroup()) != null) {
- NotificationChannelGroup ncg = groups.get(nc.getGroup());
- if (ncg == null) {
- ncg = r.groups.get(nc.getGroup()).clone();
- ncg.setChannels(new ArrayList<>());
- groups.put(nc.getGroup(), ncg);
-
- }
- ncg.addChannel(nc);
- }
- } else {
- nonGrouped.addChannel(nc);
- }
- }
- }
- if (includeNonGrouped && nonGrouped.getChannels().size() > 0) {
- groups.put(null, nonGrouped);
- }
- return new ParceledListSlice<>(new ArrayList<>(groups.values()));
- }
-
- public List<NotificationChannel> deleteNotificationChannelGroup(String pkg, int uid,
- String groupId) {
- List<NotificationChannel> deletedChannels = new ArrayList<>();
- Record r = getRecord(pkg, uid);
- if (r == null || TextUtils.isEmpty(groupId)) {
- return deletedChannels;
- }
-
- r.groups.remove(groupId);
-
- int N = r.channels.size();
- for (int i = 0; i < N; i++) {
- final NotificationChannel nc = r.channels.valueAt(i);
- if (groupId.equals(nc.getGroup())) {
- nc.setDeleted(true);
- deletedChannels.add(nc);
- }
- }
- return deletedChannels;
- }
-
- @Override
- public Collection<NotificationChannelGroup> getNotificationChannelGroups(String pkg,
- int uid) {
- Record r = getRecord(pkg, uid);
- if (r == null) {
- return new ArrayList<>();
- }
- return r.groups.values();
- }
-
- @Override
- public ParceledListSlice<NotificationChannel> getNotificationChannels(String pkg, int uid,
- boolean includeDeleted) {
- Preconditions.checkNotNull(pkg);
- List<NotificationChannel> channels = new ArrayList<>();
- Record r = getRecord(pkg, uid);
- if (r == null) {
- return ParceledListSlice.emptyList();
- }
- int N = r.channels.size();
- for (int i = 0; i < N; i++) {
- final NotificationChannel nc = r.channels.valueAt(i);
- if (includeDeleted || !nc.isDeleted()) {
- channels.add(nc);
- }
- }
- return new ParceledListSlice<>(channels);
- }
-
- /**
- * True for pre-O apps that only have the default channel, or pre O apps that have no
- * channels yet. This method will create the default channel for pre-O apps that don't have it.
- * Should never be true for O+ targeting apps, but that's enforced on boot/when an app
- * upgrades.
- */
- public boolean onlyHasDefaultChannel(String pkg, int uid) {
- Record r = getOrCreateRecord(pkg, uid);
- if (r.channels.size() == 1
- && r.channels.containsKey(NotificationChannel.DEFAULT_CHANNEL_ID)) {
- return true;
- }
- return false;
- }
-
- public int getDeletedChannelCount(String pkg, int uid) {
- Preconditions.checkNotNull(pkg);
- int deletedCount = 0;
- Record r = getRecord(pkg, uid);
- if (r == null) {
- return deletedCount;
- }
- int N = r.channels.size();
- for (int i = 0; i < N; i++) {
- final NotificationChannel nc = r.channels.valueAt(i);
- if (nc.isDeleted()) {
- deletedCount++;
- }
- }
- return deletedCount;
- }
-
- public int getBlockedChannelCount(String pkg, int uid) {
- Preconditions.checkNotNull(pkg);
- int blockedCount = 0;
- Record r = getRecord(pkg, uid);
- if (r == null) {
- return blockedCount;
- }
- int N = r.channels.size();
- for (int i = 0; i < N; i++) {
- final NotificationChannel nc = r.channels.valueAt(i);
- if (!nc.isDeleted() && IMPORTANCE_NONE == nc.getImportance()) {
- blockedCount++;
- }
- }
- return blockedCount;
- }
-
- public int getBlockedAppCount(int userId) {
- int count = 0;
- synchronized (mRecords) {
- final int N = mRecords.size();
- for (int i = 0; i < N; i++) {
- final Record r = mRecords.valueAt(i);
- if (userId == UserHandle.getUserId(r.uid)
- && r.importance == IMPORTANCE_NONE) {
- count++;
- }
- }
- }
- return count;
- }
-
- public void updateChannelsBypassingDnd() {
- synchronized (mRecords) {
- final int numRecords = mRecords.size();
- for (int recordIndex = 0; recordIndex < numRecords; recordIndex++) {
- final Record r = mRecords.valueAt(recordIndex);
- final int numChannels = r.channels.size();
-
- for (int channelIndex = 0; channelIndex < numChannels; channelIndex++) {
- NotificationChannel channel = r.channels.valueAt(channelIndex);
- if (!channel.isDeleted() && channel.canBypassDnd()) {
- if (!mAreChannelsBypassingDnd) {
- mAreChannelsBypassingDnd = true;
- updateZenPolicy(true);
- }
- return;
- }
- }
- }
- }
-
- if (mAreChannelsBypassingDnd) {
- mAreChannelsBypassingDnd = false;
- updateZenPolicy(false);
- }
- }
-
- public void updateZenPolicy(boolean areChannelsBypassingDnd) {
- NotificationManager.Policy policy = mZenModeHelper.getNotificationPolicy();
- mZenModeHelper.setNotificationPolicy(new NotificationManager.Policy(
- policy.priorityCategories, policy.priorityCallSenders,
- policy.priorityMessageSenders, policy.suppressedVisualEffects,
- (areChannelsBypassingDnd ? NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND
- : 0)));
- }
-
- public boolean areChannelsBypassingDnd() {
- return mAreChannelsBypassingDnd;
- }
-
- /**
- * Sets importance.
- */
- @Override
- public void setImportance(String pkgName, int uid, int importance) {
- getOrCreateRecord(pkgName, uid).importance = importance;
- updateConfig();
- }
-
- public void setEnabled(String packageName, int uid, boolean enabled) {
- boolean wasEnabled = getImportance(packageName, uid) != IMPORTANCE_NONE;
- if (wasEnabled == enabled) {
- return;
- }
- setImportance(packageName, uid,
- enabled ? DEFAULT_IMPORTANCE : IMPORTANCE_NONE);
- }
-
- /**
- * Sets whether any notifications from the app, represented by the given {@code pkgName} and
- * {@code uid}, have their importance locked by the user. Locked notifications don't get
- * considered for sentiment adjustments (and thus never show a blocking helper).
- */
- public void setAppImportanceLocked(String packageName, int uid) {
- Record record = getOrCreateRecord(packageName, uid);
- if ((record.lockedAppFields & LockableAppFields.USER_LOCKED_IMPORTANCE) != 0) {
- return;
- }
-
- record.lockedAppFields = record.lockedAppFields | LockableAppFields.USER_LOCKED_IMPORTANCE;
- updateConfig();
- }
-
- @VisibleForTesting
- void lockFieldsForUpdate(NotificationChannel original, NotificationChannel update) {
- if (original.canBypassDnd() != update.canBypassDnd()) {
- update.lockFields(NotificationChannel.USER_LOCKED_PRIORITY);
- }
- if (original.getLockscreenVisibility() != update.getLockscreenVisibility()) {
- update.lockFields(NotificationChannel.USER_LOCKED_VISIBILITY);
- }
- if (original.getImportance() != update.getImportance()) {
- update.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE);
- }
- if (original.shouldShowLights() != update.shouldShowLights()
- || original.getLightColor() != update.getLightColor()) {
- update.lockFields(NotificationChannel.USER_LOCKED_LIGHTS);
- }
- if (!Objects.equals(original.getSound(), update.getSound())) {
- update.lockFields(NotificationChannel.USER_LOCKED_SOUND);
- }
- if (!Arrays.equals(original.getVibrationPattern(), update.getVibrationPattern())
- || original.shouldVibrate() != update.shouldVibrate()) {
- update.lockFields(NotificationChannel.USER_LOCKED_VIBRATION);
- }
- if (original.canShowBadge() != update.canShowBadge()) {
- update.lockFields(NotificationChannel.USER_LOCKED_SHOW_BADGE);
- }
- }
-
public void dump(PrintWriter pw, String prefix,
@NonNull NotificationManagerService.DumpFilter filter) {
final int N = mSignalExtractors.length;
@@ -1088,16 +169,6 @@
pw.print(" ");
pw.println(mSignalExtractors[i].getClass().getSimpleName());
}
-
- pw.print(prefix);
- pw.println("per-package config:");
-
- pw.println("Records:");
- synchronized (mRecords) {
- dumpRecords(pw, prefix, filter, mRecords);
- }
- pw.println("Restored without uid:");
- dumpRecords(pw, prefix, filter, mRestoredWithoutUids);
}
public void dump(ProtoOutputStream proto,
@@ -1105,375 +176,7 @@
final int N = mSignalExtractors.length;
for (int i = 0; i < N; i++) {
proto.write(RankingHelperProto.NOTIFICATION_SIGNAL_EXTRACTORS,
- mSignalExtractors[i].getClass().getSimpleName());
- }
- synchronized (mRecords) {
- dumpRecords(proto, RankingHelperProto.RECORDS, filter, mRecords);
- }
- dumpRecords(proto, RankingHelperProto.RECORDS_RESTORED_WITHOUT_UID, filter,
- mRestoredWithoutUids);
- }
-
- private static void dumpRecords(ProtoOutputStream proto, long fieldId,
- @NonNull NotificationManagerService.DumpFilter filter,
- ArrayMap<String, Record> records) {
- final int N = records.size();
- long fToken;
- for (int i = 0; i < N; i++) {
- final Record r = records.valueAt(i);
- if (filter.matches(r.pkg)) {
- fToken = proto.start(fieldId);
-
- proto.write(RecordProto.PACKAGE, r.pkg);
- proto.write(RecordProto.UID, r.uid);
- proto.write(RecordProto.IMPORTANCE, r.importance);
- proto.write(RecordProto.PRIORITY, r.priority);
- proto.write(RecordProto.VISIBILITY, r.visibility);
- proto.write(RecordProto.SHOW_BADGE, r.showBadge);
-
- for (NotificationChannel channel : r.channels.values()) {
- channel.writeToProto(proto, RecordProto.CHANNELS);
- }
- for (NotificationChannelGroup group : r.groups.values()) {
- group.writeToProto(proto, RecordProto.CHANNEL_GROUPS);
- }
-
- proto.end(fToken);
- }
+ mSignalExtractors[i].getClass().getSimpleName());
}
}
-
- private static void dumpRecords(PrintWriter pw, String prefix,
- @NonNull NotificationManagerService.DumpFilter filter,
- ArrayMap<String, Record> records) {
- final int N = records.size();
- for (int i = 0; i < N; i++) {
- final Record r = records.valueAt(i);
- if (filter.matches(r.pkg)) {
- pw.print(prefix);
- pw.print(" AppSettings: ");
- pw.print(r.pkg);
- pw.print(" (");
- pw.print(r.uid == Record.UNKNOWN_UID ? "UNKNOWN_UID" : Integer.toString(r.uid));
- pw.print(')');
- if (r.importance != DEFAULT_IMPORTANCE) {
- pw.print(" importance=");
- pw.print(Ranking.importanceToString(r.importance));
- }
- if (r.priority != DEFAULT_PRIORITY) {
- pw.print(" priority=");
- pw.print(Notification.priorityToString(r.priority));
- }
- if (r.visibility != DEFAULT_VISIBILITY) {
- pw.print(" visibility=");
- pw.print(Notification.visibilityToString(r.visibility));
- }
- pw.print(" showBadge=");
- pw.print(Boolean.toString(r.showBadge));
- pw.println();
- for (NotificationChannel channel : r.channels.values()) {
- pw.print(prefix);
- pw.print(" ");
- pw.print(" ");
- pw.println(channel);
- }
- for (NotificationChannelGroup group : r.groups.values()) {
- pw.print(prefix);
- pw.print(" ");
- pw.print(" ");
- pw.println(group);
- }
- }
- }
- }
-
- public JSONObject dumpJson(NotificationManagerService.DumpFilter filter) {
- JSONObject ranking = new JSONObject();
- JSONArray records = new JSONArray();
- try {
- ranking.put("noUid", mRestoredWithoutUids.size());
- } catch (JSONException e) {
- // pass
- }
- synchronized (mRecords) {
- final int N = mRecords.size();
- for (int i = 0; i < N; i++) {
- final Record r = mRecords.valueAt(i);
- if (filter == null || filter.matches(r.pkg)) {
- JSONObject record = new JSONObject();
- try {
- record.put("userId", UserHandle.getUserId(r.uid));
- record.put("packageName", r.pkg);
- if (r.importance != DEFAULT_IMPORTANCE) {
- record.put("importance", Ranking.importanceToString(r.importance));
- }
- if (r.priority != DEFAULT_PRIORITY) {
- record.put("priority", Notification.priorityToString(r.priority));
- }
- if (r.visibility != DEFAULT_VISIBILITY) {
- record.put("visibility", Notification.visibilityToString(r.visibility));
- }
- if (r.showBadge != DEFAULT_SHOW_BADGE) {
- record.put("showBadge", Boolean.valueOf(r.showBadge));
- }
- JSONArray channels = new JSONArray();
- for (NotificationChannel channel : r.channels.values()) {
- channels.put(channel.toJson());
- }
- record.put("channels", channels);
- JSONArray groups = new JSONArray();
- for (NotificationChannelGroup group : r.groups.values()) {
- groups.put(group.toJson());
- }
- record.put("groups", groups);
- } catch (JSONException e) {
- // pass
- }
- records.put(record);
- }
- }
- }
- try {
- ranking.put("records", records);
- } catch (JSONException e) {
- // pass
- }
- return ranking;
- }
-
- /**
- * Dump only the ban information as structured JSON for the stats collector.
- *
- * This is intentionally redundant with {#link dumpJson} because the old
- * scraper will expect this format.
- *
- * @param filter
- * @return
- */
- public JSONArray dumpBansJson(NotificationManagerService.DumpFilter filter) {
- JSONArray bans = new JSONArray();
- Map<Integer, String> packageBans = getPackageBans();
- for(Entry<Integer, String> ban : packageBans.entrySet()) {
- final int userId = UserHandle.getUserId(ban.getKey());
- final String packageName = ban.getValue();
- if (filter == null || filter.matches(packageName)) {
- JSONObject banJson = new JSONObject();
- try {
- banJson.put("userId", userId);
- banJson.put("packageName", packageName);
- } catch (JSONException e) {
- e.printStackTrace();
- }
- bans.put(banJson);
- }
- }
- return bans;
- }
-
- public Map<Integer, String> getPackageBans() {
- synchronized (mRecords) {
- final int N = mRecords.size();
- ArrayMap<Integer, String> packageBans = new ArrayMap<>(N);
- for (int i = 0; i < N; i++) {
- final Record r = mRecords.valueAt(i);
- if (r.importance == IMPORTANCE_NONE) {
- packageBans.put(r.uid, r.pkg);
- }
- }
-
- return packageBans;
- }
- }
-
- /**
- * Dump only the channel information as structured JSON for the stats collector.
- *
- * This is intentionally redundant with {#link dumpJson} because the old
- * scraper will expect this format.
- *
- * @param filter
- * @return
- */
- public JSONArray dumpChannelsJson(NotificationManagerService.DumpFilter filter) {
- JSONArray channels = new JSONArray();
- Map<String, Integer> packageChannels = getPackageChannels();
- for(Entry<String, Integer> channelCount : packageChannels.entrySet()) {
- final String packageName = channelCount.getKey();
- if (filter == null || filter.matches(packageName)) {
- JSONObject channelCountJson = new JSONObject();
- try {
- channelCountJson.put("packageName", packageName);
- channelCountJson.put("channelCount", channelCount.getValue());
- } catch (JSONException e) {
- e.printStackTrace();
- }
- channels.put(channelCountJson);
- }
- }
- return channels;
- }
-
- private Map<String, Integer> getPackageChannels() {
- ArrayMap<String, Integer> packageChannels = new ArrayMap<>();
- synchronized (mRecords) {
- for (int i = 0; i < mRecords.size(); i++) {
- final Record r = mRecords.valueAt(i);
- int channelCount = 0;
- for (int j = 0; j < r.channels.size(); j++) {
- if (!r.channels.valueAt(j).isDeleted()) {
- channelCount++;
- }
- }
- packageChannels.put(r.pkg, channelCount);
- }
- }
- return packageChannels;
- }
-
- public void onUserRemoved(int userId) {
- synchronized (mRecords) {
- int N = mRecords.size();
- for (int i = N - 1; i >= 0 ; i--) {
- Record record = mRecords.valueAt(i);
- if (UserHandle.getUserId(record.uid) == userId) {
- mRecords.removeAt(i);
- }
- }
- }
- }
-
- protected void onLocaleChanged(Context context, int userId) {
- synchronized (mRecords) {
- int N = mRecords.size();
- for (int i = 0; i < N; i++) {
- Record record = mRecords.valueAt(i);
- if (UserHandle.getUserId(record.uid) == userId) {
- if (record.channels.containsKey(NotificationChannel.DEFAULT_CHANNEL_ID)) {
- record.channels.get(NotificationChannel.DEFAULT_CHANNEL_ID).setName(
- context.getResources().getString(
- R.string.default_notification_channel_label));
- }
- }
- }
- }
- }
-
- public void onPackagesChanged(boolean removingPackage, int changeUserId, String[] pkgList,
- int[] uidList) {
- if (pkgList == null || pkgList.length == 0) {
- return; // nothing to do
- }
- boolean updated = false;
- if (removingPackage) {
- // Remove notification settings for uninstalled package
- int size = Math.min(pkgList.length, uidList.length);
- for (int i = 0; i < size; i++) {
- final String pkg = pkgList[i];
- final int uid = uidList[i];
- synchronized (mRecords) {
- mRecords.remove(recordKey(pkg, uid));
- }
- mRestoredWithoutUids.remove(pkg);
- updated = true;
- }
- } else {
- for (String pkg : pkgList) {
- // Package install
- final Record r = mRestoredWithoutUids.get(pkg);
- if (r != null) {
- try {
- r.uid = mPm.getPackageUidAsUser(r.pkg, changeUserId);
- mRestoredWithoutUids.remove(pkg);
- synchronized (mRecords) {
- mRecords.put(recordKey(r.pkg, r.uid), r);
- }
- updated = true;
- } catch (NameNotFoundException e) {
- // noop
- }
- }
- // Package upgrade
- try {
- Record fullRecord = getRecord(pkg,
- mPm.getPackageUidAsUser(pkg, changeUserId));
- if (fullRecord != null) {
- createDefaultChannelIfNeeded(fullRecord);
- deleteDefaultChannelIfNeeded(fullRecord);
- }
- } catch (NameNotFoundException e) {}
- }
- }
-
- if (updated) {
- updateConfig();
- }
- }
-
- private LogMaker getChannelLog(NotificationChannel channel, String pkg) {
- return new LogMaker(MetricsProto.MetricsEvent.ACTION_NOTIFICATION_CHANNEL)
- .setType(MetricsProto.MetricsEvent.TYPE_UPDATE)
- .setPackageName(pkg)
- .addTaggedData(MetricsProto.MetricsEvent.FIELD_NOTIFICATION_CHANNEL_ID,
- channel.getId())
- .addTaggedData(MetricsProto.MetricsEvent.FIELD_NOTIFICATION_CHANNEL_IMPORTANCE,
- channel.getImportance());
- }
-
- private LogMaker getChannelGroupLog(String groupId, String pkg) {
- return new LogMaker(MetricsProto.MetricsEvent.ACTION_NOTIFICATION_CHANNEL_GROUP)
- .setType(MetricsProto.MetricsEvent.TYPE_UPDATE)
- .addTaggedData(MetricsProto.MetricsEvent.FIELD_NOTIFICATION_CHANNEL_GROUP_ID,
- groupId)
- .setPackageName(pkg);
- }
-
- public void updateBadgingEnabled() {
- if (mBadgingEnabled == null) {
- mBadgingEnabled = new SparseBooleanArray();
- }
- boolean changed = false;
- // update the cached values
- for (int index = 0; index < mBadgingEnabled.size(); index++) {
- int userId = mBadgingEnabled.keyAt(index);
- final boolean oldValue = mBadgingEnabled.get(userId);
- final boolean newValue = Secure.getIntForUser(mContext.getContentResolver(),
- Secure.NOTIFICATION_BADGING,
- DEFAULT_SHOW_BADGE ? 1 : 0, userId) != 0;
- mBadgingEnabled.put(userId, newValue);
- changed |= oldValue != newValue;
- }
- if (changed) {
- updateConfig();
- }
- }
-
- public boolean badgingEnabled(UserHandle userHandle) {
- int userId = userHandle.getIdentifier();
- if (userId == UserHandle.USER_ALL) {
- return false;
- }
- if (mBadgingEnabled.indexOfKey(userId) < 0) {
- mBadgingEnabled.put(userId,
- Secure.getIntForUser(mContext.getContentResolver(),
- Secure.NOTIFICATION_BADGING,
- DEFAULT_SHOW_BADGE ? 1 : 0, userId) != 0);
- }
- return mBadgingEnabled.get(userId, DEFAULT_SHOW_BADGE);
- }
-
-
- private static class Record {
- static int UNKNOWN_UID = UserHandle.USER_NULL;
-
- String pkg;
- int uid = UNKNOWN_UID;
- int importance = DEFAULT_IMPORTANCE;
- int priority = DEFAULT_PRIORITY;
- int visibility = DEFAULT_VISIBILITY;
- boolean showBadge = DEFAULT_SHOW_BADGE;
- int lockedAppFields = DEFAULT_LOCKED_APP_FIELDS;
-
- ArrayMap<String, NotificationChannel> channels = new ArrayMap<>();
- Map<String, NotificationChannelGroup> groups = new ConcurrentHashMap<>();
- }
}
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 669d556..1954ed4 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -1182,11 +1182,19 @@
}
private void showZenUpgradeNotification(int zen) {
+ final boolean isWatch = mContext.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_WATCH);
final boolean showNotification = mIsBootComplete
&& zen != Global.ZEN_MODE_OFF
+ && !isWatch
&& Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.SHOW_ZEN_UPGRADE_NOTIFICATION, 0) != 0;
+ if (isWatch) {
+ Settings.Global.putInt(mContext.getContentResolver(),
+ Global.SHOW_ZEN_UPGRADE_NOTIFICATION, 0);
+ }
+
if (showNotification) {
mNotificationManager.notify(TAG, SystemMessage.NOTE_ZEN_UPGRADE,
createZenUpgradeNotification());
diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
index 0774672..d6ab5f7 100644
--- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java
+++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
@@ -489,7 +489,7 @@
PinnerService pinnerService = LocalServices.getService(PinnerService.class);
if (pinnerService != null) {
Log.i(TAG, "Pinning optimized code " + updatedPackages);
- pinnerService.update(updatedPackages);
+ pinnerService.update(updatedPackages, false /* force */);
}
}
diff --git a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
index 079f3ea..65ccecd 100644
--- a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
+++ b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
@@ -21,7 +21,6 @@
import android.annotation.UserIdInt;
import android.app.ActivityManagerInternal;
import android.app.ActivityOptions;
-import android.app.ActivityTaskManagerInternal;
import android.app.AppOpsManager;
import android.app.IApplicationThread;
import android.content.ComponentName;
@@ -42,6 +41,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
import com.android.server.LocalServices;
+import com.android.server.wm.ActivityTaskManagerInternal;
import java.util.ArrayList;
import java.util.List;
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index f0807b9..72f11f7 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -34,6 +34,7 @@
import com.android.internal.os.BackgroundThread;
import com.android.server.SystemService;
+import dalvik.system.BlockGuard;
import dalvik.system.VMRuntime;
import java.io.FileDescriptor;
@@ -239,6 +240,11 @@
long[] ceDataInodes, String[] codePaths, PackageStats stats)
throws InstallerException {
if (!checkBeforeRemote()) return;
+ if (codePaths != null) {
+ for (String codePath : codePaths) {
+ BlockGuard.getVmPolicy().onPathAccess(codePath);
+ }
+ }
try {
final long[] res = mInstalld.getAppSize(uuid, packageNames, userId, flags,
appId, ceDataInodes, codePaths);
@@ -296,6 +302,9 @@
@Nullable String profileName, @Nullable String dexMetadataPath,
@Nullable String compilationReason) throws InstallerException {
assertValidInstructionSet(instructionSet);
+ BlockGuard.getVmPolicy().onPathAccess(apkPath);
+ BlockGuard.getVmPolicy().onPathAccess(outputPath);
+ BlockGuard.getVmPolicy().onPathAccess(dexMetadataPath);
if (!checkBeforeRemote()) return;
try {
mInstalld.dexopt(apkPath, uid, pkgName, instructionSet, dexoptNeeded, outputPath,
@@ -319,6 +328,7 @@
public boolean dumpProfiles(int uid, String packageName, String profileName, String codePath)
throws InstallerException {
if (!checkBeforeRemote()) return false;
+ BlockGuard.getVmPolicy().onPathAccess(codePath);
try {
return mInstalld.dumpProfiles(uid, packageName, profileName, codePath);
} catch (Exception e) {
@@ -339,6 +349,8 @@
public void idmap(String targetApkPath, String overlayApkPath, int uid)
throws InstallerException {
if (!checkBeforeRemote()) return;
+ BlockGuard.getVmPolicy().onPathAccess(targetApkPath);
+ BlockGuard.getVmPolicy().onPathAccess(overlayApkPath);
try {
mInstalld.idmap(targetApkPath, overlayApkPath, uid);
} catch (Exception e) {
@@ -348,6 +360,7 @@
public void removeIdmap(String overlayApkPath) throws InstallerException {
if (!checkBeforeRemote()) return;
+ BlockGuard.getVmPolicy().onPathAccess(overlayApkPath);
try {
mInstalld.removeIdmap(overlayApkPath);
} catch (Exception e) {
@@ -358,6 +371,7 @@
public void rmdex(String codePath, String instructionSet) throws InstallerException {
assertValidInstructionSet(instructionSet);
if (!checkBeforeRemote()) return;
+ BlockGuard.getVmPolicy().onPathAccess(codePath);
try {
mInstalld.rmdex(codePath, instructionSet);
} catch (Exception e) {
@@ -367,6 +381,7 @@
public void rmPackageDir(String packageDir) throws InstallerException {
if (!checkBeforeRemote()) return;
+ BlockGuard.getVmPolicy().onPathAccess(packageDir);
try {
mInstalld.rmPackageDir(packageDir);
} catch (Exception e) {
@@ -439,6 +454,7 @@
public void linkNativeLibraryDirectory(String uuid, String packageName, String nativeLibPath32,
int userId) throws InstallerException {
if (!checkBeforeRemote()) return;
+ BlockGuard.getVmPolicy().onPathAccess(nativeLibPath32);
try {
mInstalld.linkNativeLibraryDirectory(uuid, packageName, nativeLibPath32, userId);
} catch (Exception e) {
@@ -459,6 +475,8 @@
public void linkFile(String relativePath, String fromBase, String toBase)
throws InstallerException {
if (!checkBeforeRemote()) return;
+ BlockGuard.getVmPolicy().onPathAccess(fromBase);
+ BlockGuard.getVmPolicy().onPathAccess(toBase);
try {
mInstalld.linkFile(relativePath, fromBase, toBase);
} catch (Exception e) {
@@ -469,6 +487,8 @@
public void moveAb(String apkPath, String instructionSet, String outputPath)
throws InstallerException {
if (!checkBeforeRemote()) return;
+ BlockGuard.getVmPolicy().onPathAccess(apkPath);
+ BlockGuard.getVmPolicy().onPathAccess(outputPath);
try {
mInstalld.moveAb(apkPath, instructionSet, outputPath);
} catch (Exception e) {
@@ -479,6 +499,8 @@
public void deleteOdex(String apkPath, String instructionSet, String outputPath)
throws InstallerException {
if (!checkBeforeRemote()) return;
+ BlockGuard.getVmPolicy().onPathAccess(apkPath);
+ BlockGuard.getVmPolicy().onPathAccess(outputPath);
try {
mInstalld.deleteOdex(apkPath, instructionSet, outputPath);
} catch (Exception e) {
@@ -489,6 +511,7 @@
public void installApkVerity(String filePath, FileDescriptor verityInput, int contentSize)
throws InstallerException {
if (!checkBeforeRemote()) return;
+ BlockGuard.getVmPolicy().onPathAccess(filePath);
try {
mInstalld.installApkVerity(filePath, verityInput, contentSize);
} catch (Exception e) {
@@ -499,6 +522,7 @@
public void assertFsverityRootHashMatches(String filePath, @NonNull byte[] expectedHash)
throws InstallerException {
if (!checkBeforeRemote()) return;
+ BlockGuard.getVmPolicy().onPathAccess(filePath);
try {
mInstalld.assertFsverityRootHashMatches(filePath, expectedHash);
} catch (Exception e) {
@@ -512,6 +536,7 @@
assertValidInstructionSet(isas[i]);
}
if (!checkBeforeRemote()) return false;
+ BlockGuard.getVmPolicy().onPathAccess(apkPath);
try {
return mInstalld.reconcileSecondaryDexFile(apkPath, packageName, uid, isas,
volumeUuid, flags);
@@ -523,6 +548,7 @@
public byte[] hashSecondaryDexFile(String dexPath, String packageName, int uid,
@Nullable String volumeUuid, int flags) throws InstallerException {
if (!checkBeforeRemote()) return new byte[0];
+ BlockGuard.getVmPolicy().onPathAccess(dexPath);
try {
return mInstalld.hashSecondaryDexFile(dexPath, packageName, uid, volumeUuid, flags);
} catch (Exception e) {
@@ -571,6 +597,8 @@
public boolean prepareAppProfile(String pkg, @UserIdInt int userId, @AppIdInt int appId,
String profileName, String codePath, String dexMetadataPath) throws InstallerException {
if (!checkBeforeRemote()) return false;
+ BlockGuard.getVmPolicy().onPathAccess(codePath);
+ BlockGuard.getVmPolicy().onPathAccess(dexMetadataPath);
try {
return mInstalld.prepareAppProfile(pkg, userId, appId, profileName, codePath,
dexMetadataPath);
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index a52470d..9323040 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -20,7 +20,6 @@
import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
-import android.app.ActivityTaskManagerInternal;
import android.app.AppGlobals;
import android.app.IApplicationThread;
import android.app.PendingIntent;
@@ -64,6 +63,7 @@
import com.android.internal.util.Preconditions;
import com.android.server.LocalServices;
import com.android.server.SystemService;
+import com.android.server.wm.ActivityTaskManagerInternal;
import java.util.Collections;
import java.util.List;
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index fa934fe..b92d52c 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -685,13 +685,14 @@
// inserted above to hold the session active.
try {
final Int64Ref last = new Int64Ref(0);
- FileUtils.copy(incomingFd.getFileDescriptor(), targetFd, (long progress) -> {
- if (params.sizeBytes > 0) {
- final long delta = progress - last.value;
- last.value = progress;
- addClientProgress((float) delta / (float) params.sizeBytes);
- }
- }, null, lengthBytes);
+ FileUtils.copy(incomingFd.getFileDescriptor(), targetFd, lengthBytes, null,
+ Runnable::run, (long progress) -> {
+ if (params.sizeBytes > 0) {
+ final long delta = progress - last.value;
+ last.value = progress;
+ addClientProgress((float) delta / (float) params.sizeBytes);
+ }
+ });
} finally {
IoUtils.closeQuietly(targetFd);
IoUtils.closeQuietly(incomingFd);
@@ -930,6 +931,10 @@
@GuardedBy("mLock")
private void commitLocked()
throws PackageManagerException {
+ if (mRelinquished) {
+ Slog.d(TAG, "Ignoring commit after previous commit relinquished control");
+ return;
+ }
if (mDestroyed) {
throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, "Session destroyed");
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index e5e8648..965f7bd 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -60,7 +60,6 @@
import static android.content.pm.PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE;
import static android.content.pm.PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE;
import static android.content.pm.PackageManager.INSTALL_INTERNAL;
-import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK;
import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK;
@@ -91,12 +90,10 @@
import static android.os.storage.StorageManager.FLAG_STORAGE_DE;
import static android.system.OsConstants.O_CREAT;
import static android.system.OsConstants.O_RDWR;
-
import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_MANAGED_PROFILE;
import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_PARENT;
import static com.android.internal.content.NativeLibraryHelper.LIB64_DIR_NAME;
import static com.android.internal.content.NativeLibraryHelper.LIB_DIR_NAME;
-import static com.android.internal.util.ArrayUtils.appendInt;
import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
import static com.android.server.pm.InstructionSets.getDexCodeInstructionSet;
import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets;
@@ -123,7 +120,6 @@
import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
-import android.app.ActivityTaskManagerInternal;
import android.app.AppOpsManager;
import android.app.IActivityManager;
import android.app.ResourcesManager;
@@ -173,10 +169,10 @@
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.LegacyPackageDeleteObserver;
import android.content.pm.PackageManagerInternal;
+import android.content.pm.PackageManagerInternal.CheckPermissionDelegate;
import android.content.pm.PackageManagerInternal.PackageListObserver;
import android.content.pm.PackageParser;
import android.content.pm.PackageParser.ActivityIntentInfo;
-import android.content.pm.PackageParser.Package;
import android.content.pm.PackageParser.PackageLite;
import android.content.pm.PackageParser.PackageParserException;
import android.content.pm.PackageParser.ParseFlags;
@@ -323,11 +319,7 @@
import com.android.server.pm.permission.PermissionsState.PermissionState;
import com.android.server.security.VerityUtils;
import com.android.server.storage.DeviceStorageMonitorInternal;
-
-import dalvik.system.CloseGuard;
-import dalvik.system.VMRuntime;
-
-import libcore.io.IoUtils;
+import com.android.server.wm.ActivityTaskManagerInternal;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -373,6 +365,10 @@
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Predicate;
+import dalvik.system.CloseGuard;
+import dalvik.system.VMRuntime;
+import libcore.io.IoUtils;
+
/**
* Keep track of all those APKs everywhere.
* <p>
@@ -434,7 +430,6 @@
private static final boolean DEBUG_ABI_SELECTION = false;
private static final boolean DEBUG_INSTANT = Build.IS_DEBUGGABLE;
- private static final boolean DEBUG_TRIAGED_MISSING = false;
private static final boolean DEBUG_APP_DATA = false;
/** REMOVE. According to Svet, this was only used to reset permissions during development. */
@@ -452,10 +447,6 @@
private static final int SHELL_UID = Process.SHELL_UID;
private static final int SE_UID = Process.SE_UID;
- // Suffix used during package installation when copying/moving
- // package apks to install directory.
- private static final String INSTALL_PACKAGE_SUFFIX = "-";
-
static final int SCAN_NO_DEX = 1<<0;
static final int SCAN_UPDATE_SIGNATURE = 1<<1;
static final int SCAN_NEW_INSTALL = 1<<2;
@@ -693,11 +684,7 @@
// as the lock for the global state. Methods that must be called with
// this lock held have the prefix "LP".
@GuardedBy("mPackages")
- final ArrayMap<String, PackageParser.Package> mPackages =
- new ArrayMap<String, PackageParser.Package>();
-
- final ArrayMap<String, Set<String>> mKnownCodebase =
- new ArrayMap<String, Set<String>>();
+ final ArrayMap<String, PackageParser.Package> mPackages = new ArrayMap<>();
// Keys are isolated uids and values are the uid of the application
// that created the isolated proccess.
@@ -796,18 +783,15 @@
for (PackageParser.Package p : allPackages) {
if (targetPackageName.equals(p.mOverlayTarget) && p.mOverlayIsStatic) {
if (overlayPackages == null) {
- overlayPackages = new ArrayList<PackageParser.Package>();
+ overlayPackages = new ArrayList<>();
}
overlayPackages.add(p);
}
}
if (overlayPackages != null) {
- Comparator<PackageParser.Package> cmp = new Comparator<PackageParser.Package>() {
- public int compare(PackageParser.Package p1, PackageParser.Package p2) {
- return p1.mOverlayPriority - p2.mOverlayPriority;
- }
- };
- Collections.sort(overlayPackages, cmp);
+ Comparator<PackageParser.Package> cmp =
+ Comparator.comparingInt(p -> p.mOverlayPriority);
+ overlayPackages.sort(cmp);
}
return overlayPackages;
}
@@ -821,7 +805,7 @@
for (PackageParser.Package overlayPackage : overlayPackages) {
if (targetPath == null) {
if (overlayPathList == null) {
- overlayPathList = new ArrayList<String>();
+ overlayPathList = new ArrayList<>();
}
overlayPathList.add(overlayPackage.baseCodePath);
continue;
@@ -837,7 +821,7 @@
UserHandle.getSharedAppGid(
UserHandle.getUserGid(UserHandle.USER_SYSTEM)));
if (overlayPathList == null) {
- overlayPathList = new ArrayList<String>();
+ overlayPathList = new ArrayList<>();
}
overlayPathList.add(overlayPackage.baseCodePath);
} catch (InstallerException e) {
@@ -879,7 +863,7 @@
for (PackageParser.Package p : mPackages.values()) {
if (p.mOverlayIsStatic) {
if (mOverlayPackages == null) {
- mOverlayPackages = new ArrayList<PackageParser.Package>();
+ mOverlayPackages = new ArrayList<>();
}
mOverlayPackages.add(p);
}
@@ -940,24 +924,22 @@
// Mapping from provider base names (first directory in content URI codePath)
// to the provider information.
- final ArrayMap<String, PackageParser.Provider> mProvidersByAuthority =
- new ArrayMap<String, PackageParser.Provider>();
+ final ArrayMap<String, PackageParser.Provider> mProvidersByAuthority = new ArrayMap<>();
// Mapping from instrumentation class names to info about them.
final ArrayMap<ComponentName, PackageParser.Instrumentation> mInstrumentation =
- new ArrayMap<ComponentName, PackageParser.Instrumentation>();
+ new ArrayMap<>();
// Packages whose data we have transfered into another package, thus
// should no longer exist.
- final ArraySet<String> mTransferedPackages = new ArraySet<String>();
+ final ArraySet<String> mTransferedPackages = new ArraySet<>();
// Broadcast actions that are only available to the system.
@GuardedBy("mProtectedBroadcasts")
final ArraySet<String> mProtectedBroadcasts = new ArraySet<>();
/** List of packages waiting for verification. */
- final SparseArray<PackageVerificationState> mPendingVerification
- = new SparseArray<PackageVerificationState>();
+ final SparseArray<PackageVerificationState> mPendingVerification = new SparseArray<>();
final PackageInstallerService mInstallerService;
@@ -1008,7 +990,7 @@
final ResolveInfo mInstantAppInstallerInfo = new ResolveInfo();
final SparseArray<IntentFilterVerificationState> mIntentFilterVerificationStates
- = new SparseArray<IntentFilterVerificationState>();
+ = new SparseArray<>();
// TODO remove this and go through mPermissonManager directly
final DefaultPermissionGrantPolicy mDefaultPermissionPolicy;
@@ -1038,7 +1020,6 @@
pkg = _pkg;
replacing = _replacing;
userId = _userId;
- replacing = _replacing;
verifierUid = _verifierUid;
}
}
@@ -1050,10 +1031,13 @@
void receiveVerificationResponse(int verificationId);
}
+ @GuardedBy("mPackages")
+ private CheckPermissionDelegate mCheckPermissionDelegate;
+
private class IntentVerifierProxy implements IntentFilterVerifier<ActivityIntentInfo> {
private Context mContext;
private ComponentName mIntentFilterVerifierComponent;
- private ArrayList<Integer> mCurrentIntentFilterVerifications = new ArrayList<Integer>();
+ private ArrayList<Integer> mCurrentIntentFilterVerifications = new ArrayList<>();
public IntentVerifierProxy(Context context, ComponentName verifierComponent) {
mContext = context;
@@ -1143,7 +1127,7 @@
mIntentFilterVerificationStates.remove(verificationId);
final String packageName = ivs.getPackageName();
- IntentFilterVerificationInfo ivi = null;
+ IntentFilterVerificationInfo ivi;
synchronized (mPackages) {
ivi = mSettings.getIntentFilterVerificationLPr(packageName);
@@ -1248,7 +1232,7 @@
final SparseArray<ArrayMap<String, ArrayList<String>>> mUidMap;
public PendingPackageBroadcasts() {
- mUidMap = new SparseArray<ArrayMap<String, ArrayList<String>>>(2);
+ mUidMap = new SparseArray<>(2);
}
public ArrayList<String> get(int userId, String packageName) {
@@ -1300,7 +1284,7 @@
private ArrayMap<String, ArrayList<String>> getOrAllocate(int userId) {
ArrayMap<String, ArrayList<String>> map = mUidMap.get(userId);
if (map == null) {
- map = new ArrayMap<String, ArrayList<String>>();
+ map = new ArrayMap<>();
mUidMap.put(userId, map);
}
return map;
@@ -1315,11 +1299,9 @@
static final int SEND_PENDING_BROADCAST = 1;
static final int MCS_BOUND = 3;
- static final int END_COPY = 4;
static final int INIT_COPY = 5;
static final int MCS_UNBIND = 6;
static final int START_CLEANING_PACKAGE = 7;
- static final int FIND_INSTALL_LOC = 8;
static final int POST_INSTALL = 9;
static final int MCS_RECONNECT = 10;
static final int MCS_GIVE_UP = 11;
@@ -1344,7 +1326,7 @@
static UserManagerService sUserManager;
// Stores a list of users whose package restrictions file needs to be updated
- private ArraySet<Integer> mDirtyUsers = new ArraySet<Integer>();
+ private ArraySet<Integer> mDirtyUsers = new ArraySet<>();
final private DefaultContainerConnection mDefContainerConn =
new DefaultContainerConnection();
@@ -1373,7 +1355,7 @@
}
}
- final SparseArray<PostInstallData> mRunningInstalls = new SparseArray<PostInstallData>();
+ final SparseArray<PostInstallData> mRunningInstalls = new SparseArray<>();
int mNextInstallToken = 1; // nonzero; will be wrapped back to 1 when ++ overflows
// XML tags for backup/restore of various bits of state
@@ -1420,7 +1402,7 @@
class PackageHandler extends Handler {
private boolean mBound = false;
final ArrayList<HandlerParams> mPendingInstalls =
- new ArrayList<HandlerParams>();
+ new ArrayList<>();
private boolean connectToService() {
if (DEBUG_INSTALL) Log.i(TAG, "Trying to bind to DefaultContainerService");
@@ -1626,9 +1608,6 @@
int uids[];
Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
synchronized (mPackages) {
- if (mPendingBroadcasts == null) {
- return;
- }
size = mPendingBroadcasts.size();
if (size <= 0) {
// Nothing to be done. Just return
@@ -1903,12 +1882,7 @@
private PermissionCallback mPermissionCallback = new PermissionCallback() {
@Override
public void onGidsChanged(int appId, int userId) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- killUid(appId, userId, KILL_APP_REASON_GIDS_CHANGED);
- }
- });
+ mHandler.post(() -> killUid(appId, userId, KILL_APP_REASON_GIDS_CHANGED));
}
@Override
public void onPermissionGranted(int uid, int userId) {
@@ -2548,13 +2522,11 @@
requestCopyPreoptedFiles();
}
- String customResolverActivity = Resources.getSystem().getString(
+ String customResolverActivityName = Resources.getSystem().getString(
R.string.config_customResolverActivity);
- if (TextUtils.isEmpty(customResolverActivity)) {
- customResolverActivity = null;
- } else {
+ if (!TextUtils.isEmpty(customResolverActivityName)) {
mCustomResolverComponentName = ComponentName.unflattenFromString(
- customResolverActivity);
+ customResolverActivityName);
}
long startTime = SystemClock.uptimeMillis();
@@ -2592,6 +2564,8 @@
mIsPreNMR1Upgrade = mIsUpgrade && ver.sdkVersion < Build.VERSION_CODES.N_MR1;
+ int preUpgradeSdkVersion = ver.sdkVersion;
+
// save off the names of pre-existing system packages prior to scanning; we don't
// want to automatically grant runtime permissions for new system apps
if (mPromoteSystemApps) {
@@ -3185,6 +3159,58 @@
checkDefaultBrowser();
+ // If a granted permission is split, all new permissions should be granted too
+ if (mIsUpgrade) {
+ final int callingUid = getCallingUid();
+
+ final int numSplitPerms = PackageParser.SPLIT_PERMISSIONS.length;
+ for (int splitPermNum = 0; splitPermNum < numSplitPerms; splitPermNum++) {
+ final PackageParser.SplitPermissionInfo splitPerm =
+ PackageParser.SPLIT_PERMISSIONS[splitPermNum];
+ final String rootPerm = splitPerm.rootPerm;
+
+ if (preUpgradeSdkVersion >= splitPerm.targetSdk) {
+ continue;
+ }
+
+ final int numPackages = mPackages.size();
+ for (int packageNum = 0; packageNum < numPackages; packageNum++) {
+ final PackageParser.Package pkg = mPackages.valueAt(packageNum);
+
+ if (pkg.applicationInfo.targetSdkVersion >= splitPerm.targetSdk
+ || !pkg.requestedPermissions.contains(rootPerm)) {
+ continue;
+ }
+
+ final int userId = UserHandle.getUserId(pkg.applicationInfo.uid);
+ final String pkgName = pkg.packageName;
+
+ if (checkPermission(rootPerm, pkgName, userId) == PERMISSION_DENIED) {
+ continue;
+ }
+
+ final String[] newPerms = splitPerm.newPerms;
+
+ final int numNewPerms = newPerms.length;
+ for (int newPermNum = 0; newPermNum < numNewPerms; newPermNum++) {
+ final String newPerm = newPerms[newPermNum];
+ if (checkPermission(newPerm, pkgName, userId) == PERMISSION_GRANTED) {
+ continue;
+ }
+
+ if (DEBUG_PERMISSIONS) {
+ Slog.v(TAG, "Granting " + newPerm + " to " + pkgName
+ + " as the root permission " + rootPerm
+ + " is already granted");
+ }
+
+ mPermissionManager.grantRuntimePermission(newPerm, pkgName, true,
+ callingUid, userId, null);
+ }
+ }
+ }
+ }
+
// clear only after permissions and other defaults have been updated
mExistingSystemPackages.clear();
mPromoteSystemApps = false;
@@ -3404,7 +3430,7 @@
}
}
if (ret != PackageManager.INSTALL_SUCCEEDED) {
- if (dstCodePath == null || !dstCodePath.exists()) {
+ if (!dstCodePath.exists()) {
return null;
}
removeCodePathLI(dstCodePath);
@@ -3755,7 +3781,7 @@
for (ActivityIntentInfo filter : a.intents) {
if (hasValidDomains(filter)) {
if (domains == null) {
- domains = new ArraySet<String>();
+ domains = new ArraySet<>();
}
domains.addAll(filter.getHostsList());
}
@@ -3822,7 +3848,7 @@
PackageManager.MATCH_ALL, userId);
final int count = list.size();
- List<String> result = new ArrayList<String>(count);
+ List<String> result = new ArrayList<>(count);
for (int i=0; i<count; i++) {
ResolveInfo info = list.get(i);
if (info.activityInfo == null
@@ -3877,16 +3903,6 @@
}
}
- static int[] appendInts(int[] cur, int[] add) {
- if (add == null) return cur;
- if (cur == null) return add;
- final int N = add.length;
- for (int i=0; i<N; i++) {
- cur = appendInt(cur, add[i]);
- }
- return cur;
- }
-
/**
* Returns whether or not a full application can see an instant application.
* <p>
@@ -3950,7 +3966,7 @@
? EMPTY_INT_ARRAY : permissionsState.computeGids(userId);
// Compute granted permissions only if package has requested permissions
final Set<String> permissions = ArrayUtils.isEmpty(p.requestedPermissions)
- ? Collections.<String>emptySet() : permissionsState.getPermissions(userId);
+ ? Collections.emptySet() : permissionsState.getPermissions(userId);
PackageInfo packageInfo = PackageParser.generatePackageInfo(p, gids, flags,
ps.firstInstallTime, ps.lastUpdateTime, permissions, state, userId);
@@ -4790,22 +4806,6 @@
*/
private int updateFlagsForPackage(int flags, int userId, Object cookie) {
final boolean isCallerSystemUser = UserHandle.getCallingUserId() == UserHandle.USER_SYSTEM;
- boolean triaged = true;
- if ((flags & (PackageManager.GET_ACTIVITIES | PackageManager.GET_RECEIVERS
- | PackageManager.GET_SERVICES | PackageManager.GET_PROVIDERS)) != 0) {
- // Caller is asking for component details, so they'd better be
- // asking for specific encryption matching behavior, or be triaged
- if ((flags & (PackageManager.MATCH_DIRECT_BOOT_UNAWARE
- | PackageManager.MATCH_DIRECT_BOOT_AWARE
- | PackageManager.MATCH_DEBUG_TRIAGED_MISSING)) == 0) {
- triaged = false;
- }
- }
- if ((flags & (PackageManager.MATCH_UNINSTALLED_PACKAGES
- | PackageManager.MATCH_SYSTEM_ONLY
- | PackageManager.MATCH_DEBUG_TRIAGED_MISSING)) == 0) {
- triaged = false;
- }
if ((flags & PackageManager.MATCH_ANY_USER) != 0) {
// require the permission to be held; the calling uid and given user id referring
// to the same user is not sufficient
@@ -4822,10 +4822,6 @@
// MATCH_UNINSTALLED_PACKAGES to query apps in other profiles. b/31000380
flags |= PackageManager.MATCH_ANY_USER;
}
- if (DEBUG_TRIAGED_MISSING && (Binder.getCallingUid() == Process.SYSTEM_UID) && !triaged) {
- Log.w(TAG, "Caller hasn't been triaged for missing apps; they asked about " + cookie
- + " with flags 0x" + Integer.toHexString(flags), new Throwable());
- }
return updateFlags(flags, userId);
}
@@ -4840,25 +4836,6 @@
* Update given flags when being used to request {@link ComponentInfo}.
*/
private int updateFlagsForComponent(int flags, int userId, Object cookie) {
- if (cookie instanceof Intent) {
- if ((((Intent) cookie).getFlags() & Intent.FLAG_DEBUG_TRIAGED_MISSING) != 0) {
- flags |= PackageManager.MATCH_DEBUG_TRIAGED_MISSING;
- }
- }
-
- boolean triaged = true;
- // Caller is asking for component details, so they'd better be
- // asking for specific encryption matching behavior, or be triaged
- if ((flags & (PackageManager.MATCH_DIRECT_BOOT_UNAWARE
- | PackageManager.MATCH_DIRECT_BOOT_AWARE
- | PackageManager.MATCH_DEBUG_TRIAGED_MISSING)) == 0) {
- triaged = false;
- }
- if (DEBUG_TRIAGED_MISSING && (Binder.getCallingUid() == Process.SYSTEM_UID) && !triaged) {
- Log.w(TAG, "Caller hasn't been triaged for missing apps; they asked about " + cookie
- + " with flags 0x" + Integer.toHexString(flags), new Throwable());
- }
-
return updateFlags(flags, userId);
}
@@ -5340,11 +5317,35 @@
@Override
public int checkPermission(String permName, String pkgName, int userId) {
+ final CheckPermissionDelegate checkPermissionDelegate;
+ synchronized (mPackages) {
+ if (mCheckPermissionDelegate == null) {
+ return checkPermissionImpl(permName, pkgName, userId);
+ }
+ checkPermissionDelegate = mCheckPermissionDelegate;
+ }
+ return checkPermissionDelegate.checkPermission(permName, pkgName, userId,
+ PackageManagerService.this::checkPermissionImpl);
+ }
+
+ private int checkPermissionImpl(String permName, String pkgName, int userId) {
return mPermissionManager.checkPermission(permName, pkgName, getCallingUid(), userId);
}
@Override
public int checkUidPermission(String permName, int uid) {
+ final CheckPermissionDelegate checkPermissionDelegate;
+ synchronized (mPackages) {
+ if (mCheckPermissionDelegate == null) {
+ return checkUidPermissionImpl(permName, uid);
+ }
+ checkPermissionDelegate = mCheckPermissionDelegate;
+ }
+ return checkPermissionDelegate.checkUidPermission(permName, uid,
+ PackageManagerService.this::checkUidPermissionImpl);
+ }
+
+ private int checkUidPermissionImpl(String permName, int uid) {
synchronized (mPackages) {
final String[] packageNames = getPackagesForUid(uid);
final PackageParser.Package pkg = (packageNames != null && packageNames.length > 0)
@@ -5766,7 +5767,7 @@
final int callingUserId = UserHandle.getUserId(callingUid);
synchronized (mPackages) {
if (canViewInstantApps(callingUid, callingUserId)) {
- return new ArrayList<String>(mPackages.keySet());
+ return new ArrayList<>(mPackages.keySet());
}
final String instantAppPkgName = getInstantAppPackageName(callingUid);
final List<String> result = new ArrayList<>();
@@ -6638,7 +6639,7 @@
flags = updateFlagsForResolve(flags, userId, intent, filterCallingUid, resolveForStart,
comp != null || pkgName != null /*onlyExposedExplicitly*/);
if (comp != null) {
- final List<ResolveInfo> list = new ArrayList<ResolveInfo>(1);
+ final List<ResolveInfo> list = new ArrayList<>(1);
final ActivityInfo ai = getActivityInfo(comp, flags, userId);
if (ai != null) {
// When specifying an explicit component, we prevent the activity from being
@@ -6694,7 +6695,7 @@
ResolveInfo xpResolveInfo = querySkipCurrentProfileIntents(matchingFilters, intent,
resolvedType, flags, userId);
if (xpResolveInfo != null) {
- List<ResolveInfo> xpResult = new ArrayList<ResolveInfo>(1);
+ List<ResolveInfo> xpResult = new ArrayList<>(1);
xpResult.add(xpResolveInfo);
return applyPostResolutionFilter(
filterIfNotSystemUser(xpResult, userId), instantAppPkgName,
@@ -7119,12 +7120,12 @@
candidates.size());
}
- ArrayList<ResolveInfo> result = new ArrayList<ResolveInfo>();
- ArrayList<ResolveInfo> alwaysList = new ArrayList<ResolveInfo>();
- ArrayList<ResolveInfo> undefinedList = new ArrayList<ResolveInfo>();
- ArrayList<ResolveInfo> alwaysAskList = new ArrayList<ResolveInfo>();
- ArrayList<ResolveInfo> neverList = new ArrayList<ResolveInfo>();
- ArrayList<ResolveInfo> matchAllList = new ArrayList<ResolveInfo>();
+ final ArrayList<ResolveInfo> result = new ArrayList<>();
+ final ArrayList<ResolveInfo> alwaysList = new ArrayList<>();
+ final ArrayList<ResolveInfo> undefinedList = new ArrayList<>();
+ final ArrayList<ResolveInfo> alwaysAskList = new ArrayList<>();
+ final ArrayList<ResolveInfo> neverList = new ArrayList<>();
+ final ArrayList<ResolveInfo> matchAllList = new ArrayList<>();
synchronized (mPackages) {
final int count = candidates.size();
@@ -7599,7 +7600,7 @@
}
}
if (comp != null) {
- final List<ResolveInfo> list = new ArrayList<ResolveInfo>(1);
+ final List<ResolveInfo> list = new ArrayList<>(1);
final ActivityInfo ai = getReceiverInfo(comp, flags, userId);
if (ai != null) {
// When specifying an explicit component, we prevent the activity from being
@@ -7713,7 +7714,7 @@
}
}
if (comp != null) {
- final List<ResolveInfo> list = new ArrayList<ResolveInfo>(1);
+ final List<ResolveInfo> list = new ArrayList<>(1);
final ServiceInfo si = getServiceInfo(comp, flags, userId);
if (si != null) {
// When specifying an explicit component, we prevent the service from being
@@ -7831,7 +7832,7 @@
}
}
if (comp != null) {
- final List<ResolveInfo> list = new ArrayList<ResolveInfo>(1);
+ final List<ResolveInfo> list = new ArrayList<>(1);
final ProviderInfo pi = getProviderInfo(comp, flags, userId);
if (pi != null) {
// When specifying an explicit component, we prevent the provider from being
@@ -8030,7 +8031,7 @@
// writer
synchronized (mPackages) {
- ArrayList<PackageInfo> list = new ArrayList<PackageInfo>();
+ ArrayList<PackageInfo> list = new ArrayList<>();
boolean[] tmpBools = new boolean[permissions.length];
if (listUninstalled) {
for (PackageSetting ps : mSettings.mPackages.values()) {
@@ -8047,7 +8048,7 @@
}
}
- return new ParceledListSlice<PackageInfo>(list);
+ return new ParceledListSlice<>(list);
}
}
@@ -8248,7 +8249,7 @@
}
private @NonNull List<ApplicationInfo> getPersistentApplicationsInternal(int flags) {
- final ArrayList<ApplicationInfo> finalList = new ArrayList<ApplicationInfo>();
+ final ArrayList<ApplicationInfo> finalList = new ArrayList<>();
// reader
synchronized (mPackages) {
@@ -8377,7 +8378,7 @@
continue;
}
if (finalList == null) {
- finalList = new ArrayList<ProviderInfo>(3);
+ finalList = new ArrayList<>(3);
}
ProviderInfo info = PackageParser.generateProviderInfo(p, flags,
ps.readUserState(userId), userId);
@@ -8389,8 +8390,8 @@
}
if (finalList != null) {
- Collections.sort(finalList, mProviderInitOrderSorter);
- return new ParceledListSlice<ProviderInfo>(finalList);
+ finalList.sort(mProviderInitOrderSorter);
+ return new ParceledListSlice<>(finalList);
}
return ParceledListSlice.emptyList();
@@ -8426,7 +8427,7 @@
private @NonNull List<InstrumentationInfo> queryInstrumentationInternal(String targetPackage,
int flags) {
- ArrayList<InstrumentationInfo> finalList = new ArrayList<InstrumentationInfo>();
+ ArrayList<InstrumentationInfo> finalList = new ArrayList<>();
// reader
synchronized (mPackages) {
@@ -8496,10 +8497,8 @@
renameStaticSharedLibraryPackage(parseResult.pkg);
}
try {
- if (errorCode == PackageManager.INSTALL_SUCCEEDED) {
- scanPackageChildLI(parseResult.pkg, parseFlags, scanFlags,
- currentTime, null);
- }
+ scanPackageChildLI(parseResult.pkg, parseFlags, scanFlags,
+ currentTime, null);
} catch (PackageManagerException e) {
errorCode = e.error;
Slog.w(TAG, "Failed to scan " + parseResult.scanFile + ": " + e.getMessage());
@@ -8701,7 +8700,7 @@
* match one in a trusted source, and should be done separately.
*/
private boolean canSkipFullApkVerification(String apkPath) {
- byte[] rootHashObserved = null;
+ final byte[] rootHashObserved;
try {
rootHashObserved = VerityUtils.generateFsverityRootHash(apkPath);
if (rootHashObserved == null) {
@@ -8967,7 +8966,7 @@
* @param message used as message if SecurityException is thrown
* @throws SecurityException if the caller is not system or root
*/
- private static final void enforceSystemOrRoot(String message) {
+ private static void enforceSystemOrRoot(String message) {
final int uid = Binder.getCallingUid();
if (uid != Process.SYSTEM_UID && uid != Process.ROOT_UID) {
throw new SecurityException(message);
@@ -9232,6 +9231,16 @@
}
@GuardedBy("mPackages")
+ public CheckPermissionDelegate getCheckPermissionDelegateLocked() {
+ return mCheckPermissionDelegate;
+ }
+
+ @GuardedBy("mPackages")
+ public void setCheckPermissionDelegateLocked(CheckPermissionDelegate delegate) {
+ mCheckPermissionDelegate = delegate;
+ }
+
+ @GuardedBy("mPackages")
private void notifyPackageUseLocked(String packageName, int reason) {
final PackageParser.Package p = mPackages.get(packageName);
if (p == null) {
@@ -9370,7 +9379,7 @@
}
public ArraySet<String> getOptimizablePackages() {
- ArraySet<String> pkgs = new ArraySet<String>();
+ ArraySet<String> pkgs = new ArraySet<>();
synchronized (mPackages) {
for (PackageParser.Package p : mPackages.values()) {
if (PackageDexOptimizer.canOptimizePackage(p)) {
@@ -12395,7 +12404,8 @@
if (DEBUG_REMOVE) Log.d(TAG, " Activities: " + r);
}
- mPermissionManager.removeAllPermissions(pkg, chatty);
+ final ArrayList<String> allPackageNames = new ArrayList<>(mPackages.keySet());
+ mPermissionManager.removeAllPermissions(pkg, allPackageNames, mPermissionCallback, chatty);
N = pkg.instrumentation.size();
r = null;
@@ -12946,9 +12956,7 @@
if ((mFlags&PackageManager.GET_RESOLVED_FILTER) != 0) {
res.filter = info;
}
- if (info != null) {
- res.handleAllWebDataURI = info.handleAllWebDataURI();
- }
+ res.handleAllWebDataURI = info.handleAllWebDataURI();
res.priority = info.getPriority();
res.preferredOrder = activity.owner.mPreferredOrder;
//System.out.println("Result: " + res.activityInfo.className +
@@ -12970,7 +12978,7 @@
@Override
protected void sortResults(List<ResolveInfo> results) {
- Collections.sort(results, mResolvePrioritySorter);
+ results.sort(mResolvePrioritySorter);
}
@Override
@@ -13003,7 +13011,7 @@
// Keys are String (activity class name), values are Activity.
private final ArrayMap<ComponentName, PackageParser.Activity> mActivities
- = new ArrayMap<ComponentName, PackageParser.Activity>();
+ = new ArrayMap<>();
private int mFlags;
}
@@ -13033,8 +13041,7 @@
mFlags = flags;
final boolean defaultOnly = (flags&PackageManager.MATCH_DEFAULT_ONLY) != 0;
final int N = packageServices.size();
- ArrayList<PackageParser.ServiceIntentInfo[]> listCut =
- new ArrayList<PackageParser.ServiceIntentInfo[]>(N);
+ ArrayList<PackageParser.ServiceIntentInfo[]> listCut = new ArrayList<>(N);
ArrayList<PackageParser.ServiceIntentInfo> intentFilters;
for (int i = 0; i < N; ++i) {
@@ -13187,7 +13194,7 @@
@Override
protected void sortResults(List<ResolveInfo> results) {
- Collections.sort(results, mResolvePrioritySorter);
+ results.sort(mResolvePrioritySorter);
}
@Override
@@ -13236,8 +13243,7 @@
// }
// Keys are String (activity class name), values are Activity.
- private final ArrayMap<ComponentName, PackageParser.Service> mServices
- = new ArrayMap<ComponentName, PackageParser.Service>();
+ private final ArrayMap<ComponentName, PackageParser.Service> mServices = new ArrayMap<>();
private int mFlags;
}
@@ -13269,8 +13275,7 @@
mFlags = flags;
final boolean defaultOnly = (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0;
final int N = packageProviders.size();
- ArrayList<PackageParser.ProviderIntentInfo[]> listCut =
- new ArrayList<PackageParser.ProviderIntentInfo[]>(N);
+ ArrayList<PackageParser.ProviderIntentInfo[]> listCut = new ArrayList<>(N);
ArrayList<PackageParser.ProviderIntentInfo> intentFilters;
for (int i = 0; i < N; ++i) {
@@ -13430,7 +13435,7 @@
@Override
protected void sortResults(List<ResolveInfo> results) {
- Collections.sort(results, mResolvePrioritySorter);
+ results.sort(mResolvePrioritySorter);
}
@Override
@@ -13463,7 +13468,7 @@
}
private final ArrayMap<ComponentName, PackageParser.Provider> mProviders
- = new ArrayMap<ComponentName, PackageParser.Provider>();
+ = new ArrayMap<>();
private int mFlags;
}
@@ -13548,78 +13553,69 @@
}
}
- private static final Comparator<ResolveInfo> mResolvePrioritySorter =
- new Comparator<ResolveInfo>() {
- public int compare(ResolveInfo r1, ResolveInfo r2) {
- int v1 = r1.priority;
- int v2 = r2.priority;
- //System.out.println("Comparing: q1=" + q1 + " q2=" + q2);
- if (v1 != v2) {
- return (v1 > v2) ? -1 : 1;
- }
- v1 = r1.preferredOrder;
- v2 = r2.preferredOrder;
- if (v1 != v2) {
- return (v1 > v2) ? -1 : 1;
- }
- if (r1.isDefault != r2.isDefault) {
- return r1.isDefault ? -1 : 1;
- }
- v1 = r1.match;
- v2 = r2.match;
- //System.out.println("Comparing: m1=" + m1 + " m2=" + m2);
- if (v1 != v2) {
- return (v1 > v2) ? -1 : 1;
- }
- if (r1.system != r2.system) {
- return r1.system ? -1 : 1;
- }
- if (r1.activityInfo != null) {
- return r1.activityInfo.packageName.compareTo(r2.activityInfo.packageName);
- }
- if (r1.serviceInfo != null) {
- return r1.serviceInfo.packageName.compareTo(r2.serviceInfo.packageName);
- }
- if (r1.providerInfo != null) {
- return r1.providerInfo.packageName.compareTo(r2.providerInfo.packageName);
- }
- return 0;
+ private static final Comparator<ResolveInfo> mResolvePrioritySorter = (r1, r2) -> {
+ int v1 = r1.priority;
+ int v2 = r2.priority;
+ //System.out.println("Comparing: q1=" + q1 + " q2=" + q2);
+ if (v1 != v2) {
+ return (v1 > v2) ? -1 : 1;
}
+ v1 = r1.preferredOrder;
+ v2 = r2.preferredOrder;
+ if (v1 != v2) {
+ return (v1 > v2) ? -1 : 1;
+ }
+ if (r1.isDefault != r2.isDefault) {
+ return r1.isDefault ? -1 : 1;
+ }
+ v1 = r1.match;
+ v2 = r2.match;
+ //System.out.println("Comparing: m1=" + m1 + " m2=" + m2);
+ if (v1 != v2) {
+ return (v1 > v2) ? -1 : 1;
+ }
+ if (r1.system != r2.system) {
+ return r1.system ? -1 : 1;
+ }
+ if (r1.activityInfo != null) {
+ return r1.activityInfo.packageName.compareTo(r2.activityInfo.packageName);
+ }
+ if (r1.serviceInfo != null) {
+ return r1.serviceInfo.packageName.compareTo(r2.serviceInfo.packageName);
+ }
+ if (r1.providerInfo != null) {
+ return r1.providerInfo.packageName.compareTo(r2.providerInfo.packageName);
+ }
+ return 0;
};
- private static final Comparator<ProviderInfo> mProviderInitOrderSorter =
- new Comparator<ProviderInfo>() {
- public int compare(ProviderInfo p1, ProviderInfo p2) {
- final int v1 = p1.initOrder;
- final int v2 = p2.initOrder;
- return (v1 > v2) ? -1 : ((v1 < v2) ? 1 : 0);
- }
+ private static final Comparator<ProviderInfo> mProviderInitOrderSorter = (p1, p2) -> {
+ final int v1 = p1.initOrder;
+ final int v2 = p2.initOrder;
+ return (v1 > v2) ? -1 : ((v1 < v2) ? 1 : 0);
};
@Override
public void sendPackageBroadcast(final String action, final String pkg, final Bundle extras,
final int flags, final String targetPkg, final IIntentReceiver finishedReceiver,
final int[] userIds, int[] instantUserIds) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- try {
- final IActivityManager am = ActivityManager.getService();
- if (am == null) return;
- final int[] resolvedUserIds;
- if (userIds == null) {
- resolvedUserIds = am.getRunningUserIds();
- } else {
- resolvedUserIds = userIds;
- }
- doSendBroadcast(am, action, pkg, extras, flags, targetPkg, finishedReceiver,
- resolvedUserIds, false);
- if (instantUserIds != null && instantUserIds != EMPTY_INT_ARRAY) {
- doSendBroadcast(am, action, pkg, extras, flags, targetPkg, finishedReceiver,
- instantUserIds, true);
- }
- } catch (RemoteException ex) {
+ mHandler.post(() -> {
+ try {
+ final IActivityManager am = ActivityManager.getService();
+ if (am == null) return;
+ final int[] resolvedUserIds;
+ if (userIds == null) {
+ resolvedUserIds = am.getRunningUserIds();
+ } else {
+ resolvedUserIds = userIds;
}
+ doSendBroadcast(am, action, pkg, extras, flags, targetPkg, finishedReceiver,
+ resolvedUserIds, false);
+ if (instantUserIds != null && instantUserIds != EMPTY_INT_ARRAY) {
+ doSendBroadcast(am, action, pkg, extras, flags, targetPkg, finishedReceiver,
+ instantUserIds, true);
+ }
+ } catch (RemoteException ex) {
}
});
}
@@ -14018,6 +14014,68 @@
return false;
}
+ @Override
+ public void setSystemAppHiddenUntilInstalled(String packageName, boolean hidden) {
+ enforceSystemOrPhoneCaller("setSystemAppHiddenUntilInstalled");
+ synchronized (mPackages) {
+ final PackageSetting pkgSetting = mSettings.mPackages.get(packageName);
+ if (pkgSetting == null || !pkgSetting.isSystem()) {
+ return;
+ }
+ PackageParser.Package pkg = pkgSetting.pkg;
+ if (pkg != null && pkg.applicationInfo != null) {
+ pkg.applicationInfo.hiddenUntilInstalled = hidden;
+ }
+ final PackageSetting disabledPs = mSettings.getDisabledSystemPkgLPr(packageName);
+ if (disabledPs == null) {
+ return;
+ }
+ pkg = disabledPs.pkg;
+ if (pkg != null && pkg.applicationInfo != null) {
+ pkg.applicationInfo.hiddenUntilInstalled = hidden;
+ }
+ }
+ }
+
+ @Override
+ public boolean setSystemAppInstallState(String packageName, boolean installed, int userId) {
+ enforceSystemOrPhoneCaller("setSystemAppInstallState");
+ synchronized (mPackages) {
+ final PackageSetting pkgSetting = mSettings.mPackages.get(packageName);
+ // The target app should always be in system
+ if (pkgSetting == null || !pkgSetting.isSystem()) {
+ return false;
+ }
+ // Check if the install state is the same
+ if (pkgSetting.getInstalled(userId) == installed) {
+ return false;
+ }
+ }
+
+ final long callingId = Binder.clearCallingIdentity();
+ try {
+ if (installed) {
+ // install the app from uninstalled state
+ installExistingPackageAsUser(
+ packageName,
+ userId,
+ 0 /*installFlags*/,
+ PackageManager.INSTALL_REASON_DEVICE_SETUP);
+ return true;
+ }
+
+ // uninstall the app from installed state
+ deletePackageVersioned(
+ new VersionedPackage(packageName, PackageManager.VERSION_CODE_HIGHEST),
+ new LegacyPackageDeleteObserver(null).getBinder(),
+ userId,
+ PackageManager.DELETE_SYSTEM_APP);
+ return true;
+ } finally {
+ Binder.restoreCallingIdentity(callingId);
+ }
+ }
+
private void sendApplicationHiddenForUser(String packageName, PackageSetting pkgSetting,
int userId) {
final PackageRemovedInfo info = new PackageRemovedInfo(this);
@@ -14082,10 +14140,16 @@
@Override
public int installExistingPackageAsUser(String packageName, int userId, int installFlags,
int installReason) {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES,
- null);
- PackageSetting pkgSetting;
final int callingUid = Binder.getCallingUid();
+ if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES)
+ != PackageManager.PERMISSION_GRANTED
+ && mContext.checkCallingOrSelfPermission(
+ android.Manifest.permission.INSTALL_EXISTING_PACKAGES)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("Neither user " + callingUid + " nor current process has "
+ + android.Manifest.permission.INSTALL_PACKAGES + ".");
+ }
+ PackageSetting pkgSetting;
mPermissionManager.enforceCrossUserPermission(callingUid, userId,
true /* requireFullPermission */, true /* checkShell */,
"installExistingPackage for user " + userId);
@@ -14286,25 +14350,22 @@
} else {
action = Intent.ACTION_MY_PACKAGE_UNSUSPENDED;
}
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- try {
- final IActivityManager am = ActivityManager.getService();
- if (am == null) {
- Slog.wtf(TAG, "IActivityManager null. Cannot send MY_PACKAGE_ "
- + (suspended ? "" : "UN") + "SUSPENDED broadcasts");
- return;
- }
- final int[] targetUserIds = new int[] {userId};
- for (String packageName : affectedPackages) {
- doSendBroadcast(am, action, null, intentExtras,
- Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND, packageName, null,
- targetUserIds, false);
- }
- } catch (RemoteException ex) {
- // Shouldn't happen as AMS is in the same process.
+ mHandler.post(() -> {
+ try {
+ final IActivityManager am = ActivityManager.getService();
+ if (am == null) {
+ Slog.wtf(TAG, "IActivityManager null. Cannot send MY_PACKAGE_ "
+ + (suspended ? "" : "UN") + "SUSPENDED broadcasts");
+ return;
}
+ final int[] targetUserIds = new int[] {userId};
+ for (String packageName : affectedPackages) {
+ doSendBroadcast(am, action, null, intentExtras,
+ Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND, packageName, null,
+ targetUserIds, false);
+ }
+ } catch (RemoteException ex) {
+ // Shouldn't happen as AMS is in the same process.
}
});
}
@@ -14552,7 +14613,7 @@
}
final int N = pkgInfo.verifiers.length;
- final List<ComponentName> sufficientVerifiers = new ArrayList<ComponentName>(N + 1);
+ final List<ComponentName> sufficientVerifiers = new ArrayList<>(N + 1);
for (int i = 0; i < N; i++) {
final VerifierInfo verifierInfo = pkgInfo.verifiers[i];
@@ -15062,36 +15123,33 @@
// are coherent. In the non-restore case, the app has already completed install
// and been launched through some other means, so it is not in a problematic
// state for observers to see the FIRST_LAUNCH signal.
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- for (int i = 0; i < mRunningInstalls.size(); i++) {
- final PostInstallData data = mRunningInstalls.valueAt(i);
- if (data.res.returnCode != PackageManager.INSTALL_SUCCEEDED) {
- continue;
- }
- if (packageName.equals(data.res.pkg.applicationInfo.packageName)) {
- // right package; but is it for the right user?
- for (int uIndex = 0; uIndex < data.res.newUsers.length; uIndex++) {
- if (userId == data.res.newUsers[uIndex]) {
- if (DEBUG_BACKUP) {
- Slog.i(TAG, "Package " + packageName
- + " being restored so deferring FIRST_LAUNCH");
- }
- return;
+ mHandler.post(() -> {
+ for (int i = 0; i < mRunningInstalls.size(); i++) {
+ final PostInstallData data = mRunningInstalls.valueAt(i);
+ if (data.res.returnCode != PackageManager.INSTALL_SUCCEEDED) {
+ continue;
+ }
+ if (packageName.equals(data.res.pkg.applicationInfo.packageName)) {
+ // right package; but is it for the right user?
+ for (int uIndex = 0; uIndex < data.res.newUsers.length; uIndex++) {
+ if (userId == data.res.newUsers[uIndex]) {
+ if (DEBUG_BACKUP) {
+ Slog.i(TAG, "Package " + packageName
+ + " being restored so deferring FIRST_LAUNCH");
}
+ return;
}
}
}
- // didn't find it, so not being restored
- if (DEBUG_BACKUP) {
- Slog.i(TAG, "Package " + packageName + " sending normal FIRST_LAUNCH");
- }
- final boolean isInstantApp = isInstantApp(packageName, userId);
- final int[] userIds = isInstantApp ? EMPTY_INT_ARRAY : new int[] { userId };
- final int[] instantUserIds = isInstantApp ? new int[] { userId } : EMPTY_INT_ARRAY;
- sendFirstLaunchBroadcast(packageName, installerPackage, userIds, instantUserIds);
}
+ // didn't find it, so not being restored
+ if (DEBUG_BACKUP) {
+ Slog.i(TAG, "Package " + packageName + " sending normal FIRST_LAUNCH");
+ }
+ final boolean isInstantApp = isInstantApp(packageName, userId);
+ final int[] userIds = isInstantApp ? EMPTY_INT_ARRAY : new int[] { userId };
+ final int[] instantUserIds = isInstantApp ? new int[] { userId } : EMPTY_INT_ARRAY;
+ sendFirstLaunchBroadcast(packageName, installerPackage, userIds, instantUserIds);
});
}
@@ -15773,7 +15831,7 @@
* Rename package into final resting place. All paths on the given
* scanned package should be updated to reflect the rename.
*/
- abstract boolean doRename(int status, PackageParser.Package pkg, String oldCodePath);
+ abstract boolean doRename(int status, PackageParser.Package pkg);
abstract int doPostInstall(int status, int uid);
/** @see PackageSettingBase#codePathString */
@@ -15920,8 +15978,7 @@
}
};
- int ret = PackageManager.INSTALL_SUCCEEDED;
- ret = imcs.copyPackage(origin.file.getAbsolutePath(), target);
+ int ret = imcs.copyPackage(origin.file.getAbsolutePath(), target);
if (ret != PackageManager.INSTALL_SUCCEEDED) {
Slog.e(TAG, "Failed to copy package");
return ret;
@@ -15950,7 +16007,7 @@
return status;
}
- boolean doRename(int status, PackageParser.Package pkg, String oldCodePath) {
+ boolean doRename(int status, PackageParser.Package pkg) {
if (status != PackageManager.INSTALL_SUCCEEDED) {
cleanUp();
return false;
@@ -16066,17 +16123,6 @@
}
/**
- * Extract the StorageManagerService "container ID" from the full code path of an
- * .apk.
- */
- static String cidFromCodePath(String fullCodePath) {
- int eidx = fullCodePath.lastIndexOf("/");
- String subStr1 = fullCodePath.substring(0, eidx);
- int sidx = subStr1.lastIndexOf("/");
- return subStr1.substring(sidx+1, eidx);
- }
-
- /**
* Logic to handle movement of existing installed applications.
*/
class MoveInstallArgs extends InstallArgs {
@@ -16120,7 +16166,7 @@
return status;
}
- boolean doRename(int status, PackageParser.Package pkg, String oldCodePath) {
+ boolean doRename(int status, PackageParser.Package pkg) {
if (status != PackageManager.INSTALL_SUCCEEDED) {
cleanUp(move.toUuid);
return false;
@@ -16187,51 +16233,6 @@
}
}
- static String getAsecPackageName(String packageCid) {
- int idx = packageCid.lastIndexOf("-");
- if (idx == -1) {
- return packageCid;
- }
- return packageCid.substring(0, idx);
- }
-
- // Utility method used to create code paths based on package name and available index.
- private static String getNextCodePath(String oldCodePath, String prefix, String suffix) {
- String idxStr = "";
- int idx = 1;
- // Fall back to default value of idx=1 if prefix is not
- // part of oldCodePath
- if (oldCodePath != null) {
- String subStr = oldCodePath;
- // Drop the suffix right away
- if (suffix != null && subStr.endsWith(suffix)) {
- subStr = subStr.substring(0, subStr.length() - suffix.length());
- }
- // If oldCodePath already contains prefix find out the
- // ending index to either increment or decrement.
- int sidx = subStr.lastIndexOf(prefix);
- if (sidx != -1) {
- subStr = subStr.substring(sidx + prefix.length());
- if (subStr != null) {
- if (subStr.startsWith(INSTALL_PACKAGE_SUFFIX)) {
- subStr = subStr.substring(INSTALL_PACKAGE_SUFFIX.length());
- }
- try {
- idx = Integer.parseInt(subStr);
- if (idx <= 1) {
- idx++;
- } else {
- idx--;
- }
- } catch(NumberFormatException e) {
- }
- }
- }
- }
- idxStr = INSTALL_PACKAGE_SUFFIX + Integer.toString(idx);
- return prefix + idxStr;
- }
-
private File getNextCodePath(File targetDir, String packageName) {
File result;
SecureRandom random = new SecureRandom();
@@ -16442,7 +16443,7 @@
// don't allow a system upgrade unless the upgrade hash matches
if (oldPackage.restrictUpdateHash != null && oldPackage.isSystem()) {
- byte[] digestBytes = null;
+ final byte[] digestBytes;
try {
final MessageDigest digest = MessageDigest.getInstance("SHA-512");
updateDigest(digest, new File(pkg.baseCodePath));
@@ -16567,18 +16568,10 @@
boolean sysPkg = (isSystemApp(oldPackage));
if (sysPkg) {
// Set the system/privileged/oem/vendor/product flags as needed
- final boolean privileged =
- (oldPackage.applicationInfo.privateFlags
- & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0;
- final boolean oem =
- (oldPackage.applicationInfo.privateFlags
- & ApplicationInfo.PRIVATE_FLAG_OEM) != 0;
- final boolean vendor =
- (oldPackage.applicationInfo.privateFlags
- & ApplicationInfo.PRIVATE_FLAG_VENDOR) != 0;
- final boolean product =
- (oldPackage.applicationInfo.privateFlags
- & ApplicationInfo.PRIVATE_FLAG_PRODUCT) != 0;
+ final boolean privileged = isPrivilegedApp(oldPackage);
+ final boolean oem = isOemApp(oldPackage);
+ final boolean vendor = isVendorApp(oldPackage);
+ final boolean product = isProductApp(oldPackage);
final @ParseFlags int systemParseFlags = parseFlags;
final @ScanFlags int systemScanFlags = scanFlags
| SCAN_AS_SYSTEM
@@ -16629,7 +16622,7 @@
Slog.i(TAG, "upgrading pkg " + deletedPackage + " is ASEC-hosted -> UNAVAILABLE");
}
final int[] uidArray = new int[] { deletedPackage.applicationInfo.uid };
- final ArrayList<String> pkgList = new ArrayList<String>(1);
+ final ArrayList<String> pkgList = new ArrayList<>(1);
pkgList.add(deletedPackage.applicationInfo.packageName);
sendResourcesChangedBroadcast(false, true, pkgList, uidArray, null);
}
@@ -17054,17 +17047,91 @@
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
+ private static class InstallRequest {
+ final InstallArgs args;
+ final PackageInstalledInfo res;
+
+ private InstallRequest(InstallArgs args, PackageInstalledInfo res) {
+ this.args = args;
+ this.res = res;
+ }
+ }
private void installPackageTracedLI(InstallArgs args, PackageInstalledInfo res) {
try {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installPackage");
- installPackageLI(args, res);
+ installPackagesLI(Collections.singletonList(new InstallRequest(args, res)));
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}
- private void installPackageLI(InstallArgs args, PackageInstalledInfo res) {
+ private static class CommitRequest {
+ final Map<String, ReconciledPackage> reconciledPackages;
+
+ private CommitRequest(Map<String, ReconciledPackage> reconciledPackages) {
+ this.reconciledPackages = reconciledPackages;
+ }
+ }
+ private static class ReconcileRequest {
+ final Map<String, ScanResult> scannedPackages;
+
+ private ReconcileRequest(Map<String, ScanResult> scannedPackages) {
+ this.scannedPackages = scannedPackages;
+ }
+ }
+ private static class ReconcileFailure extends PackageManagerException {
+ public ReconcileFailure(String message) {
+ super("Invalid reconcile request: " + message);
+ }
+ };
+
+ /**
+ * A container of all data needed to commit a package to in-memory data structures and to disk.
+ * Ideally most of the data contained in this class will move into a PackageSetting it contains.
+ */
+ private static class ReconciledPackage {}
+
+ @GuardedBy("mPackages")
+ private static Map<String, ReconciledPackage> reconcilePackagesLocked(
+ final ReconcileRequest request) throws ReconcileFailure {
+ return Collections.emptyMap();
+ }
+
+ @GuardedBy("mPackages")
+ private boolean commitPackagesLocked(final CommitRequest request) {
+ return true;
+ }
+
+ @GuardedBy("mInstallLock")
+ private void installPackagesLI(List<InstallRequest> requests) {
+ Map<String, ScanResult> scans = new ArrayMap<>(requests.size());
+ for (InstallRequest request : requests) {
+ // TODO(b/109941548): remove this once we've pulled everything from it and into scan,
+ // reconcile or commit.
+ preparePackageLI(request.args, request.res);
+
+ // TODO(b/109941548): scan package and get result
+ }
+ ReconcileRequest reconcileRequest = new ReconcileRequest(scans);
+ Map<String, ReconciledPackage> reconciledPackages;
+ try {
+ reconciledPackages = reconcilePackagesLocked(reconcileRequest);
+ } catch (ReconcileFailure e) {
+ // TODO(b/109941548): set install args error
+ return;
+ }
+ CommitRequest request = new CommitRequest(reconciledPackages);
+ if (!commitPackagesLocked(request)) {
+ // TODO(b/109941548): set install args error
+ return;
+ }
+ // TODO(b/109941548) post-commit actions (dex-opt, etc.)
+ }
+
+ @Deprecated
+ @GuardedBy("mInstallLock")
+ private void preparePackageLI(InstallArgs args, PackageInstalledInfo res) {
final int installFlags = args.installFlags;
final String installerPackageName = args.installerPackageName;
final String volumeUuid = args.volumeUuid;
@@ -17236,7 +17303,6 @@
// Get rid of all references to package scan path via parser.
pp = null;
- String oldCodePath = null;
boolean systemApp = false;
synchronized (mPackages) {
// Check if installing already existing package
@@ -17348,7 +17414,6 @@
}
}
- oldCodePath = mSettings.mPackages.get(pkgName).codePathString;
if (ps.pkg != null && ps.pkg.applicationInfo != null) {
systemApp = (ps.pkg.applicationInfo.flags &
ApplicationInfo.FLAG_SYSTEM) != 0;
@@ -17498,7 +17563,7 @@
}
}
- if (!args.doRename(res.returnCode, pkg, oldCodePath)) {
+ if (!args.doRename(res.returnCode, pkg)) {
res.setError(INSTALL_FAILED_INSUFFICIENT_STORAGE, "Failed rename");
return;
}
@@ -17855,11 +17920,8 @@
}
private void deleteTempPackageFiles() {
- final FilenameFilter filter = new FilenameFilter() {
- public boolean accept(File dir, String name) {
- return name.startsWith("vmdl") && name.endsWith(".tmp");
- }
- };
+ final FilenameFilter filter =
+ (dir, name) -> name.startsWith("vmdl") && name.endsWith(".tmp");
for (File file : sDrmAppPrivateInstallDir.listFiles(filter)) {
file.delete();
}
@@ -18196,8 +18258,8 @@
return PackageManager.DELETE_FAILED_DEVICE_POLICY_MANAGER;
}
- PackageSetting uninstalledPs = null;
- PackageParser.Package pkg = null;
+ PackageSetting uninstalledPs;
+ PackageParser.Package pkg;
// for the uninstall-updates case and restricted profiles, remember the per-
// user handle installed state
@@ -18507,13 +18569,10 @@
if (userIdToKill == UserHandle.USER_ALL
|| userIdToKill >= UserHandle.USER_SYSTEM) {
// If gids changed for this user, kill all affected packages.
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- // This has to happen with no lock held.
- killApplication(deletedPs.name, deletedPs.appId,
- KILL_APP_REASON_GIDS_CHANGED);
- }
+ mHandler.post(() -> {
+ // This has to happen with no lock held.
+ killApplication(deletedPs.name, deletedPs.appId,
+ KILL_APP_REASON_GIDS_CHANGED);
});
break;
}
@@ -19455,12 +19514,8 @@
case PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED: {
writeRuntimePermissions = true;
final int appId = ps.appId;
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- killUid(appId, userId, KILL_APP_REASON_PERMISSIONS_REVOKED);
- }
- });
+ mHandler.post(
+ () -> killUid(appId, userId, KILL_APP_REASON_PERMISSIONS_REVOKED));
} break;
}
}
@@ -19538,33 +19593,31 @@
}
// Queue up an async operation since the package deletion may take a little while.
- mHandler.post(new Runnable() {
- public void run() {
- final PackageSetting ps = pkg == null ? null : (PackageSetting) pkg.mExtras;
- boolean doClearData = true;
- if (ps != null) {
- final boolean targetIsInstantApp =
- ps.getInstantApp(UserHandle.getUserId(callingUid));
- doClearData = !targetIsInstantApp
- || hasAccessInstantApps == PackageManager.PERMISSION_GRANTED;
+ mHandler.post(() -> {
+ final PackageSetting ps = pkg == null ? null : (PackageSetting) pkg.mExtras;
+ boolean doClearData = true;
+ if (ps != null) {
+ final boolean targetIsInstantApp =
+ ps.getInstantApp(UserHandle.getUserId(callingUid));
+ doClearData = !targetIsInstantApp
+ || hasAccessInstantApps == PackageManager.PERMISSION_GRANTED;
+ }
+ if (doClearData) {
+ synchronized (mInstallLock) {
+ final int flags = StorageManager.FLAG_STORAGE_DE
+ | StorageManager.FLAG_STORAGE_CE;
+ // We're only clearing cache files, so we don't care if the
+ // app is unfrozen and still able to run
+ clearAppDataLIF(pkg, userId, flags | Installer.FLAG_CLEAR_CACHE_ONLY);
+ clearAppDataLIF(pkg, userId, flags | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
}
- if (doClearData) {
- synchronized (mInstallLock) {
- final int flags = StorageManager.FLAG_STORAGE_DE
- | StorageManager.FLAG_STORAGE_CE;
- // We're only clearing cache files, so we don't care if the
- // app is unfrozen and still able to run
- clearAppDataLIF(pkg, userId, flags | Installer.FLAG_CLEAR_CACHE_ONLY);
- clearAppDataLIF(pkg, userId, flags | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
- }
- clearExternalStorageDataSync(packageName, userId, false);
- }
- if (observer != null) {
- try {
- observer.onRemoveCompleted(packageName, true);
- } catch (RemoteException e) {
- Log.i(TAG, "Observer no longer exists.");
- }
+ clearExternalStorageDataSync(packageName, userId, false);
+ }
+ if (observer != null) {
+ try {
+ observer.onRemoveCompleted(packageName, true);
+ } catch (RemoteException e) {
+ Log.i(TAG, "Observer no longer exists.");
}
}
});
@@ -19847,7 +19900,7 @@
(pa.mPref.mComponent.getPackageName().equals(packageName)
&& pa.mPref.mAlways)) {
if (removed == null) {
- removed = new ArrayList<PreferredActivity>();
+ removed = new ArrayList<>();
}
removed.add(pa);
}
@@ -20011,7 +20064,7 @@
// Mark entry for removal only if it matches the package name.
if (ppa.mComponent.getPackageName().equals(packageName)) {
if (removed == null) {
- removed = new ArrayList<PersistentPreferredActivity>();
+ removed = new ArrayList<>();
}
removed.add(ppa);
}
@@ -20050,7 +20103,6 @@
}
return;
}
-Slog.v(TAG, ":: restoreFromXml() : got to tag " + parser.getName());
// this is supposed to be TAG_PREFERRED_BACKUP
if (!expectedStartTag.equals(parser.getName())) {
if (DEBUG_BACKUP) {
@@ -20061,12 +20113,11 @@
// skip interfering stuff, then we're aligned with the backing implementation
while ((type = parser.next()) == XmlPullParser.TEXT) { }
-Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
functor.apply(parser, userId);
}
private interface BlobXmlRestorer {
- public void apply(XmlPullParser parser, int userId) throws IOException, XmlPullParserException;
+ void apply(XmlPullParser parser, int userId) throws IOException, XmlPullParserException;
}
/**
@@ -20114,15 +20165,11 @@
final XmlPullParser parser = Xml.newPullParser();
parser.setInput(new ByteArrayInputStream(backup), StandardCharsets.UTF_8.name());
restoreFromXml(parser, userId, TAG_PREFERRED_BACKUP,
- new BlobXmlRestorer() {
- @Override
- public void apply(XmlPullParser parser, int userId)
- throws XmlPullParserException, IOException {
- synchronized (mPackages) {
- mSettings.readPreferredActivitiesLPw(parser, userId);
- }
+ (readParser, readUserId) -> {
+ synchronized (mPackages) {
+ mSettings.readPreferredActivitiesLPw(readParser, readUserId);
}
- } );
+ });
} catch (Exception e) {
if (DEBUG_BACKUP) {
Slog.e(TAG, "Exception restoring preferred activities: " + e.getMessage());
@@ -20175,15 +20222,11 @@
final XmlPullParser parser = Xml.newPullParser();
parser.setInput(new ByteArrayInputStream(backup), StandardCharsets.UTF_8.name());
restoreFromXml(parser, userId, TAG_DEFAULT_APPS,
- new BlobXmlRestorer() {
- @Override
- public void apply(XmlPullParser parser, int userId)
- throws XmlPullParserException, IOException {
- synchronized (mPackages) {
- mSettings.readDefaultAppsLPw(parser, userId);
- }
+ (parser1, userId1) -> {
+ synchronized (mPackages) {
+ mSettings.readDefaultAppsLPw(parser1, userId1);
}
- } );
+ });
} catch (Exception e) {
if (DEBUG_BACKUP) {
Slog.e(TAG, "Exception restoring default apps: " + e.getMessage());
@@ -20231,16 +20274,12 @@
final XmlPullParser parser = Xml.newPullParser();
parser.setInput(new ByteArrayInputStream(backup), StandardCharsets.UTF_8.name());
restoreFromXml(parser, userId, TAG_INTENT_FILTER_VERIFICATION,
- new BlobXmlRestorer() {
- @Override
- public void apply(XmlPullParser parser, int userId)
- throws XmlPullParserException, IOException {
- synchronized (mPackages) {
- mSettings.readAllDomainVerificationsLPr(parser, userId);
- mSettings.writeLPr();
- }
+ (parser1, userId1) -> {
+ synchronized (mPackages) {
+ mSettings.readAllDomainVerificationsLPr(parser1, userId1);
+ mSettings.writeLPr();
}
- } );
+ });
} catch (Exception e) {
if (DEBUG_BACKUP) {
Slog.e(TAG, "Exception restoring preferred activities: " + e.getMessage());
@@ -20288,15 +20327,11 @@
final XmlPullParser parser = Xml.newPullParser();
parser.setInput(new ByteArrayInputStream(backup), StandardCharsets.UTF_8.name());
restoreFromXml(parser, userId, TAG_PERMISSION_BACKUP,
- new BlobXmlRestorer() {
- @Override
- public void apply(XmlPullParser parser, int userId)
- throws XmlPullParserException, IOException {
- synchronized (mPackages) {
- processRestoredPermissionGrantsLPr(parser, userId);
- }
+ (parser1, userId1) -> {
+ synchronized (mPackages) {
+ processRestoredPermissionGrantsLPr(parser1, userId1);
}
- } );
+ });
} catch (Exception e) {
if (DEBUG_BACKUP) {
Slog.e(TAG, "Exception restoring preferred activities: " + e.getMessage());
@@ -20484,7 +20519,7 @@
CrossProfileIntentResolver resolver =
mSettings.editCrossProfileIntentResolverLPw(sourceUserId);
ArraySet<CrossProfileIntentFilter> set =
- new ArraySet<CrossProfileIntentFilter>(resolver.filterSet());
+ new ArraySet<>(resolver.filterSet());
for (CrossProfileIntentFilter filter : set) {
if (filter.getOwnerPackage().equals(ownerPackage)) {
resolver.removeFilter(filter);
@@ -20589,9 +20624,7 @@
allHomeCandidates.clear();
if (list != null) {
- for (ResolveInfo ri : list) {
- allHomeCandidates.add(ri);
- }
+ allHomeCandidates.addAll(list);
}
return (preferred == null || preferred.activityInfo == null)
? null
@@ -20721,7 +20754,6 @@
boolean isApp = (className == null);
final boolean isCallerInstantApp = (getInstantAppPackageName(callingUid) != null);
String componentName = isApp ? packageName : className;
- int packageUid = -1;
ArrayList<String> components;
// reader
@@ -20961,7 +20993,7 @@
components = mPendingBroadcasts.get(userId, packageName);
final boolean newPackage = components == null;
if (newPackage) {
- components = new ArrayList<String>();
+ components = new ArrayList<>();
}
if (!components.contains(componentName)) {
components.add(componentName);
@@ -20985,7 +21017,7 @@
long callingId = Binder.clearCallingIdentity();
try {
if (sendNow) {
- packageUid = UserHandle.getUid(userId, pkgSetting.appId);
+ int packageUid = UserHandle.getUid(userId, pkgSetting.appId);
sendPackageChangedBroadcast(packageName,
(flags&PackageManager.DONT_KILL_APP) != 0, components, packageUid);
}
@@ -21148,6 +21180,8 @@
CarrierAppUtils.disableCarrierAppsUntilPrivileged(mContext.getOpPackageName(), this,
mContext.getContentResolver(), UserHandle.USER_SYSTEM);
+ disableSkuSpecificApps();
+
// Read the compatibilty setting when the system is ready.
boolean compatibilityModeEnabled = android.provider.Settings.Global.getInt(
mContext.getContentResolver(),
@@ -21168,7 +21202,7 @@
// possible for the user flow to never be able to return to that
// situation so here we do a sanity check to make sure we haven't
// left any junk around.
- ArrayList<PreferredActivity> removed = new ArrayList<PreferredActivity>();
+ ArrayList<PreferredActivity> removed = new ArrayList<>();
for (int i=0; i<mSettings.mPreferredActivities.size(); i++) {
PreferredIntentResolver pir = mSettings.mPreferredActivities.valueAt(i);
removed.clear();
@@ -21296,16 +21330,16 @@
}
static String arrayToString(int[] array) {
- StringBuffer buf = new StringBuffer(128);
- buf.append('[');
+ StringBuilder stringBuilder = new StringBuilder(128);
+ stringBuilder.append('[');
if (array != null) {
for (int i=0; i<array.length; i++) {
- if (i > 0) buf.append(", ");
- buf.append(array[i]);
+ if (i > 0) stringBuilder.append(", ");
+ stringBuilder.append(array[i]);
}
}
- buf.append(']');
- return buf.toString();
+ stringBuilder.append(']');
+ return stringBuilder.toString();
}
@Override
@@ -21938,6 +21972,28 @@
}
}
+ //TODO: b/111402650
+ private void disableSkuSpecificApps() {
+ if (!mIsUpgrade && !mFirstBoot) {
+ return;
+ }
+ String apkList[] = mContext.getResources().getStringArray(
+ R.array.config_disableApksUnlessMatchedSku_apk_list);
+ String skuArray[] = mContext.getResources().getStringArray(
+ R.array.config_disableApkUnlessMatchedSku_skus_list);
+ if (ArrayUtils.isEmpty(apkList)) {
+ return;
+ }
+ String sku = SystemProperties.get("ro.boot.hardware.sku");
+ if (!TextUtils.isEmpty(sku) && ArrayUtils.contains(skuArray, sku)) {
+ return;
+ }
+ for (String packageName : apkList) {
+ setSystemAppHiddenUntilInstalled(packageName, true);
+ setSystemAppInstallState(packageName, false, ActivityManager.getCurrentUser());
+ }
+ }
+
private void dumpProto(FileDescriptor fd) {
final ProtoOutputStream proto = new ProtoOutputStream(fd);
@@ -22016,7 +22072,7 @@
ipw.println();
ipw.println("Dexopt state:");
ipw.increaseIndent();
- Collection<PackageParser.Package> packages = null;
+ Collection<PackageParser.Package> packages;
if (packageName != null) {
PackageParser.Package targetPackage = mPackages.get(packageName);
if (targetPackage != null) {
@@ -22043,7 +22099,7 @@
ipw.println();
ipw.println("Compiler stats:");
ipw.increaseIndent();
- Collection<PackageParser.Package> packages = null;
+ Collection<PackageParser.Package> packages;
if (packageName != null) {
PackageParser.Package targetPackage = mPackages.get(packageName);
if (targetPackage != null) {
@@ -22078,9 +22134,7 @@
ArraySet<String> result = new ArraySet<>();
if (iviList.size() > 0) {
for (IntentFilterVerificationInfo ivi : iviList) {
- for (String host : ivi.getDomains()) {
- result.add(host);
- }
+ result.addAll(ivi.getDomains());
}
}
if (filters != null && filters.size() > 0) {
@@ -22172,12 +22226,7 @@
}
private void loadPrivatePackages(final VolumeInfo vol) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- loadPrivatePackagesInner(vol);
- }
- });
+ mHandler.post(() -> loadPrivatePackagesInner(vol));
}
private void loadPrivatePackagesInner(VolumeInfo vol) {
@@ -22268,12 +22317,7 @@
}
private void unloadPrivatePackages(final VolumeInfo vol) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- unloadPrivatePackagesInner(vol);
- }
- });
+ mHandler.post(() -> unloadPrivatePackagesInner(vol));
}
private void unloadPrivatePackagesInner(VolumeInfo vol) {
@@ -22328,23 +22372,6 @@
}
}
- private void assertPackageKnown(String volumeUuid, String packageName)
- throws PackageManagerException {
- synchronized (mPackages) {
- // Normalize package name to handle renamed packages
- packageName = normalizePackageNameLPr(packageName);
-
- final PackageSetting ps = mSettings.mPackages.get(packageName);
- if (ps == null) {
- throw new PackageManagerException("Package " + packageName + " is unknown");
- } else if (!TextUtils.equals(volumeUuid, ps.volumeUuid)) {
- throw new PackageManagerException(
- "Package " + packageName + " found on unknown volume " + volumeUuid
- + "; expected volume " + ps.volumeUuid);
- }
- }
- }
-
private void assertPackageKnownAndInstalled(String volumeUuid, String packageName, int userId)
throws PackageManagerException {
synchronized (mPackages) {
@@ -22457,7 +22484,7 @@
* <p>
* Verifies that directories exist and that ownership and labeling is
* correct for all installed apps.
- * @returns list of skipped non-core packages (if {@code onlyCoreApps} is true)
+ * @return list of skipped non-core packages (if {@code onlyCoreApps} is true)
*/
private List<String> reconcileAppsDataLI(String volumeUuid, int userId, int flags,
boolean migrateAppData, boolean onlyCoreApps) {
@@ -22827,10 +22854,7 @@
@Override
protected void finalize() throws Throwable {
try {
- if (mCloseGuard != null) {
- mCloseGuard.warnIfOpen();
- }
-
+ mCloseGuard.warnIfOpen();
close();
} finally {
super.finalize();
@@ -22874,15 +22898,12 @@
final int callingUid = Binder.getCallingUid();
final UserHandle user = new UserHandle(UserHandle.getUserId(callingUid));
final int moveId = mNextMoveId.getAndIncrement();
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- try {
- movePackageInternal(packageName, volumeUuid, moveId, callingUid, user);
- } catch (PackageManagerException e) {
- Slog.w(TAG, "Failed to move " + packageName, e);
- mMoveCallbacks.notifyStatusChanged(moveId, e.error);
- }
+ mHandler.post(() -> {
+ try {
+ movePackageInternal(packageName, volumeUuid, moveId, callingUid, user);
+ } catch (PackageManagerException e) {
+ Slog.w(TAG, "Failed to move " + packageName, e);
+ mMoveCallbacks.notifyStatusChanged(moveId, e.error);
}
});
return moveId;
@@ -23082,24 +23103,21 @@
final MoveInfo move;
if (moveCompleteApp) {
// Kick off a thread to report progress estimates
- new Thread() {
- @Override
- public void run() {
- while (true) {
- try {
- if (installedLatch.await(1, TimeUnit.SECONDS)) {
- break;
- }
- } catch (InterruptedException ignored) {
+ new Thread(() -> {
+ while (true) {
+ try {
+ if (installedLatch.await(1, TimeUnit.SECONDS)) {
+ break;
}
-
- final long deltaFreeBytes = startFreeBytes - measurePath.getUsableSpace();
- final int progress = 10 + (int) MathUtils.constrain(
- ((deltaFreeBytes * 80) / sizeBytes), 0, 80);
- mMoveCallbacks.notifyStatusChanged(moveId, progress);
+ } catch (InterruptedException ignored) {
}
+
+ final long deltaFreeBytes = startFreeBytes - measurePath.getUsableSpace();
+ final int progress = 10 + (int) MathUtils.constrain(
+ ((deltaFreeBytes * 80) / sizeBytes), 0, 80);
+ mMoveCallbacks.notifyStatusChanged(moveId, progress);
}
- }.start();
+ }).start();
final String dataAppName = codeFile.getName();
move = new MoveInfo(moveId, currentVolumeUuid, volumeUuid, packageName,
@@ -23253,12 +23271,9 @@
if (DEBUG_CLEAN_APKS) {
Slog.i(TAG, " Removing package " + packageName);
}
- mHandler.post(new Runnable() {
- public void run() {
- deletePackageX(packageName, PackageManager.VERSION_CODE_HIGHEST,
- userHandle, 0);
- } //end run
- });
+ //end run
+ mHandler.post(() -> deletePackageX(packageName, PackageManager.VERSION_CODE_HIGHEST,
+ userHandle, 0));
}
}
}
@@ -23505,12 +23520,8 @@
// TODO Implement atomic delete if package is unused
// It is currently possible that the package will be deleted even if it is installed
// after this method returns.
- mHandler.post(new Runnable() {
- public void run() {
- deletePackageX(packageName, PackageManager.VERSION_CODE_HIGHEST,
- 0, PackageManager.DELETE_ALL_USERS);
- }
- });
+ mHandler.post(() -> deletePackageX(packageName, PackageManager.VERSION_CODE_HIGHEST,
+ 0, PackageManager.DELETE_ALL_USERS));
}
}
@@ -24186,7 +24197,7 @@
}
@Override
- public boolean isLegacySystemApp(Package pkg) {
+ public boolean isLegacySystemApp(PackageParser.Package pkg) {
synchronized (mPackages) {
final PackageSetting ps = (PackageSetting) pkg.mExtras;
return mPromoteSystemApps
@@ -24327,6 +24338,20 @@
PackageManagerService.this.notifyPackageUseLocked(packageName, reason);
}
}
+
+ @Override
+ public CheckPermissionDelegate getCheckPermissionDelegate() {
+ synchronized (mPackages) {
+ return PackageManagerService.this.getCheckPermissionDelegateLocked();
+ }
+ }
+
+ @Override
+ public void setCheckPermissionDelegate(CheckPermissionDelegate delegate) {
+ synchronized (mPackages) {
+ PackageManagerService.this.setCheckPermissionDelegateLocked(delegate);
+ }
+ }
}
@Override
@@ -24362,11 +24387,9 @@
String[] packageNames, int userId) {
enforceSystemOrPhoneCaller("grantDefaultPermissionsToEnabledTelephonyDataServices");
synchronized (mPackages) {
- Binder.withCleanCallingIdentity( () -> {
- mDefaultPermissionPolicy.
- grantDefaultPermissionsToEnabledTelephonyDataServices(
- packageNames, userId);
- });
+ Binder.withCleanCallingIdentity( () -> mDefaultPermissionPolicy.
+ grantDefaultPermissionsToEnabledTelephonyDataServices(
+ packageNames, userId));
}
}
@@ -24375,11 +24398,9 @@
String[] packageNames, int userId) {
enforceSystemOrPhoneCaller("revokeDefaultPermissionsFromDisabledTelephonyDataServices");
synchronized (mPackages) {
- Binder.withCleanCallingIdentity( () -> {
- mDefaultPermissionPolicy.
- revokeDefaultPermissionsFromDisabledTelephonyDataServices(
- packageNames, userId);
- });
+ Binder.withCleanCallingIdentity( () -> mDefaultPermissionPolicy.
+ revokeDefaultPermissionsFromDisabledTelephonyDataServices(
+ packageNames, userId));
}
}
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index d963afb..d17697b 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -4121,7 +4121,8 @@
continue;
}
final boolean shouldInstall = ps.isSystem() &&
- !ArrayUtils.contains(disallowedPackages, ps.name);
+ !ArrayUtils.contains(disallowedPackages, ps.name) &&
+ !ps.pkg.applicationInfo.hiddenUntilInstalled;
// Only system apps are initially installed.
ps.setInstalled(shouldInstall, userHandle);
if (!shouldInstall) {
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 407cb93..46935f0 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -31,7 +31,6 @@
import android.app.IStopUserCallback;
import android.app.KeyguardManager;
import android.app.PendingIntent;
-import android.app.admin.DevicePolicyManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -72,7 +71,6 @@
import android.os.UserManagerInternal;
import android.os.UserManagerInternal.UserRestrictionsListener;
import android.os.storage.StorageManager;
-import android.provider.Settings;
import android.security.GateKeeper;
import android.service.gatekeeper.IGateKeeperService;
import android.util.AtomicFile;
@@ -82,7 +80,6 @@
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
-import android.util.SparseLongArray;
import android.util.TimeUtils;
import android.util.Xml;
@@ -101,8 +98,7 @@
import com.android.server.SystemService;
import com.android.server.am.UserState;
import com.android.server.storage.DeviceStorageMonitorInternal;
-
-import libcore.io.IoUtils;
+import com.android.server.wm.ActivityTaskManagerInternal;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -125,6 +121,8 @@
import java.util.List;
import java.util.Objects;
+import libcore.io.IoUtils;
+
/**
* Service for {@link UserManager}.
*
@@ -2968,9 +2966,9 @@
new Thread() {
@Override
public void run() {
- // Clean up any ActivityManager state
- LocalServices.getService(ActivityManagerInternal.class)
- .onUserRemoved(userHandle);
+ // Clean up any ActivityTaskManager state
+ LocalServices.getService(ActivityTaskManagerInternal.class)
+ .onUserStopped(userHandle);
removeUserState(userHandle);
}
}.start();
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index 1ae59cb..50e6f8d 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -1193,12 +1193,27 @@
}
}
- private void grantRuntimePermissions(PackageParser.Package pkg, Set<String> permissions,
- boolean systemFixed, boolean ignoreSystemPackage, int userId) {
+ private void grantRuntimePermissions(PackageParser.Package pkg,
+ Set<String> permissionsWithoutSplits, boolean systemFixed, boolean ignoreSystemPackage,
+ int userId) {
if (pkg.requestedPermissions.isEmpty()) {
return;
}
+ final ArraySet<String> permissions = new ArraySet<>(permissionsWithoutSplits);
+
+ // Automatically attempt to grant split permissions to older APKs
+ final int numSplitPerms = PackageParser.SPLIT_PERMISSIONS.length;
+ for (int splitPermNum = 0; splitPermNum < numSplitPerms; splitPermNum++) {
+ final PackageParser.SplitPermissionInfo splitPerm =
+ PackageParser.SPLIT_PERMISSIONS[splitPermNum];
+
+ if (pkg.applicationInfo.targetSdkVersion < splitPerm.targetSdk
+ && permissionsWithoutSplits.contains(splitPerm.rootPerm)) {
+ Collections.addAll(permissions, splitPerm.newPerms);
+ }
+ }
+
List<String> requestedPermissions = pkg.requestedPermissions;
Set<String> grantablePermissions = null;
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerInternal.java b/services/core/java/com/android/server/pm/permission/PermissionManagerInternal.java
index a042fed..80a5fbb6 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerInternal.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerInternal.java
@@ -21,19 +21,11 @@
import android.content.pm.PackageParser;
import android.content.pm.PermissionGroupInfo;
import android.content.pm.PermissionInfo;
-import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageManager.PermissionInfoFlags;
-import android.content.pm.PackageParser.Permission;
-
-import com.android.server.pm.SharedUserSetting;
-import com.android.server.pm.permission.PermissionManagerInternal.PermissionCallback;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.Iterator;
import java.util.List;
-import java.util.Map;
-import java.util.Set;
/**
* Internal interfaces to be used by other components within the system server.
@@ -115,7 +107,11 @@
*/
public abstract void addAllPermissions(@NonNull PackageParser.Package pkg, boolean chatty);
public abstract void addAllPermissionGroups(@NonNull PackageParser.Package pkg, boolean chatty);
- public abstract void removeAllPermissions(@NonNull PackageParser.Package pkg, boolean chatty);
+ public abstract void removeAllPermissions(
+ @NonNull PackageParser.Package pkg,
+ @NonNull List<String> allPackageNames,
+ @Nullable PermissionCallback permissionCallback,
+ boolean chatty);
public abstract boolean addDynamicPermission(@NonNull PermissionInfo info, boolean async,
int callingUid, @Nullable PermissionCallback callback);
public abstract void removeDynamicPermission(@NonNull String permName, int callingUid,
@@ -189,4 +185,4 @@
/** HACK HACK methods to allow for partial migration of data to the PermissionManager class */
public abstract @Nullable BasePermission getPermissionTEMP(@NonNull String permName);
-}
\ No newline at end of file
+}
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 7f2d5c3..8aed957 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -31,6 +31,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.PackageParser;
@@ -38,6 +39,7 @@
import android.content.pm.PermissionGroupInfo;
import android.content.pm.PermissionInfo;
import android.metrics.LogMaker;
+import android.os.AsyncTask;
import android.os.Binder;
import android.os.Build;
import android.os.Handler;
@@ -476,8 +478,9 @@
" to " + newPermissionGroupName);
try {
- revokeRuntimePermission(permissionName, packageName, false,
- Process.SYSTEM_UID, userId, permissionCallback);
+ revokeRuntimePermission(permissionName, packageName,
+ mSettings.getPermission(permissionName), false,
+ Process.SYSTEM_UID, userId, permissionCallback, false);
} catch (IllegalArgumentException e) {
Slog.e(TAG, "Could not revoke " + permissionName + " from "
+ packageName, e);
@@ -570,9 +573,59 @@
}
- private void removeAllPermissions(PackageParser.Package pkg, boolean chatty) {
+ private void revokeAllPermissions(
+ @NonNull List<BasePermission> bps,
+ @NonNull List<String> allPackageNames,
+ @Nullable PermissionCallback permissionCallback) {
+ AsyncTask.execute(() -> {
+ final int numRemovedPermissions = bps.size();
+ for (int permissionNum = 0; permissionNum < numRemovedPermissions; permissionNum++) {
+ final int[] userIds = mUserManagerInt.getUserIds();
+ final int numUserIds = userIds.length;
+
+ final int numPackages = allPackageNames.size();
+ for (int packageNum = 0; packageNum < numPackages; packageNum++) {
+ final String packageName = allPackageNames.get(packageNum);
+ final ApplicationInfo applicationInfo = mPackageManagerInt.getApplicationInfo(
+ packageName, 0, Process.SYSTEM_UID, UserHandle.USER_SYSTEM);
+ if (applicationInfo != null
+ && applicationInfo.targetSdkVersion < Build.VERSION_CODES.M) {
+ continue;
+ }
+ for (int userIdNum = 0; userIdNum < numUserIds; userIdNum++) {
+ final int userId = userIds[userIdNum];
+ final String permissionName = bps.get(permissionNum).getName();
+ if (checkPermission(permissionName, packageName, UserHandle.USER_SYSTEM,
+ userId) == PackageManager.PERMISSION_GRANTED) {
+ try {
+ revokeRuntimePermission(
+ permissionName,
+ packageName,
+ bps.get(permissionNum),
+ false,
+ Process.SYSTEM_UID,
+ userId,
+ permissionCallback,
+ true);
+ } catch (IllegalArgumentException e) {
+ Slog.e(TAG, "Could not revoke " + permissionName + " from "
+ + packageName, e);
+ }
+ }
+ }
+ }
+ }
+ });
+ }
+
+ private void removeAllPermissions(
+ @NonNull PackageParser.Package pkg,
+ @NonNull List<String> allPackageNames,
+ @Nullable PermissionCallback permissionCallback,
+ boolean chatty) {
synchronized (mLock) {
int N = pkg.permissions.size();
+ List<BasePermission> bps = new ArrayList<BasePermission>(N);
StringBuilder r = null;
for (int i=0; i<N; i++) {
PackageParser.Permission p = pkg.permissions.get(i);
@@ -581,6 +634,9 @@
bp = mSettings.mPermissionTrees.get(p.info.name);
}
if (bp != null && bp.isPermission(p)) {
+ if ((p.info.getProtection() & PermissionInfo.PROTECTION_DANGEROUS) != 0) {
+ bps.add(bp);
+ }
bp.setPermission(null);
if (DEBUG_REMOVE && chatty) {
if (r == null) {
@@ -599,6 +655,7 @@
}
}
}
+ revokeAllPermissions(bps, allPackageNames, permissionCallback);
if (r != null) {
if (DEBUG_REMOVE) Log.d(TAG, " Permissions: " + r);
}
@@ -1511,9 +1568,10 @@
}
}
-
- private void revokeRuntimePermission(String permName, String packageName,
- boolean overridePolicy, int callingUid, int userId, PermissionCallback callback) {
+
+ private void revokeRuntimePermission(String permName, String packageName, BasePermission bp,
+ boolean overridePolicy, int callingUid, int userId, PermissionCallback callback,
+ boolean permissionRemoved) {
if (!mUserManagerInt.exists(userId)) {
Log.e(TAG, "No such user:" + userId);
return;
@@ -1538,7 +1596,7 @@
if (mPackageManagerInt.filterAppAccess(pkg, Binder.getCallingUid(), userId)) {
throw new IllegalArgumentException("Unknown package: " + packageName);
}
- final BasePermission bp = mSettings.getPermissionLocked(permName);
+
if (bp == null) {
throw new IllegalArgumentException("Unknown permission: " + permName);
}
@@ -2094,8 +2152,10 @@
PermissionManagerService.this.addAllPermissionGroups(pkg, chatty);
}
@Override
- public void removeAllPermissions(Package pkg, boolean chatty) {
- PermissionManagerService.this.removeAllPermissions(pkg, chatty);
+ public void removeAllPermissions(Package pkg, List<String> allPackageNames,
+ PermissionCallback permissionCallback, boolean chatty) {
+ PermissionManagerService.this.removeAllPermissions(
+ pkg, allPackageNames, permissionCallback, chatty);
}
@Override
public boolean addDynamicPermission(PermissionInfo info, boolean async, int callingUid,
@@ -2131,7 +2191,8 @@
boolean overridePolicy, int callingUid, int userId,
PermissionCallback callback) {
PermissionManagerService.this.revokeRuntimePermission(permName, packageName,
- overridePolicy, callingUid, userId, callback);
+ mSettings.getPermission(permName), overridePolicy, callingUid, userId,
+ callback, false);
}
@Override
public void updatePermissions(String packageName, Package pkg, boolean replaceGrant,
diff --git a/services/core/java/com/android/server/policy/BarController.java b/services/core/java/com/android/server/policy/BarController.java
index eca6f9f..14c985c 100644
--- a/services/core/java/com/android/server/policy/BarController.java
+++ b/services/core/java/com/android/server/policy/BarController.java
@@ -196,7 +196,7 @@
}
protected boolean skipAnimation() {
- return false;
+ return !mWin.isDrawnLw();
}
private int computeStateLw(boolean wasVis, boolean wasAnim, WindowState win, boolean change) {
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 71e342e..b6222bb 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -76,6 +76,7 @@
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_STATUS_BAR_EXPANDED;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SYSTEM_ERROR;
import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_CROSSFADE;
import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_JUMPCUT;
@@ -124,7 +125,6 @@
import static android.view.WindowManager.TAKE_SCREENSHOT_SELECTED_REGION;
import static android.view.WindowManagerGlobal.ADD_OKAY;
import static android.view.WindowManagerGlobal.ADD_PERMISSION_DENIED;
-
import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVERED;
import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVER_ABSENT;
import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_UNCOVERED;
@@ -155,8 +155,6 @@
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
-import android.app.ActivityTaskManagerInternal;
-import android.app.ActivityTaskManagerInternal.SleepToken;
import android.app.ActivityTaskManager;
import android.app.ActivityThread;
import android.app.AppOpsManager;
@@ -275,8 +273,8 @@
import com.android.internal.policy.PhoneWindow;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.ScreenshotHelper;
import com.android.internal.util.ScreenShapeHelper;
+import com.android.internal.util.ScreenshotHelper;
import com.android.internal.widget.PointerLocationView;
import com.android.server.GestureLauncherService;
import com.android.server.LocalServices;
@@ -286,10 +284,14 @@
import com.android.server.policy.keyguard.KeyguardStateMonitor.StateCallback;
import com.android.server.statusbar.StatusBarManagerInternal;
import com.android.server.vr.VrManagerInternal;
+import com.android.server.wm.ActivityTaskManagerInternal;
+import com.android.server.wm.ActivityTaskManagerInternal.SleepToken;
import com.android.server.wm.AppTransition;
import com.android.server.wm.DisplayFrames;
+import com.android.server.wm.WindowFrames;
import com.android.server.wm.WindowManagerInternal;
import com.android.server.wm.WindowManagerInternal.AppTransitionListener;
+import com.android.server.wm.utils.InsetUtils;
import java.io.File;
import java.io.FileReader;
@@ -630,8 +632,10 @@
int mPointerLocationMode = 0; // guarded by mLock
- // The last window we were told about in focusChanged.
+ // The windows we were told about in focusChanged.
WindowState mFocusedWindow;
+ WindowState mLastFocusedWindow;
+
IApplicationToken mFocusedApp;
PointerLocationView mPointerLocationView;
@@ -662,15 +666,7 @@
InputConsumer mInputConsumer = null;
- static final Rect mTmpParentFrame = new Rect();
- static final Rect mTmpDisplayFrame = new Rect();
- static final Rect mTmpOverscanFrame = new Rect();
- static final Rect mTmpContentFrame = new Rect();
- static final Rect mTmpVisibleFrame = new Rect();
- static final Rect mTmpDecorFrame = new Rect();
- static final Rect mTmpStableFrame = new Rect();
- static final Rect mTmpNavigationFrame = new Rect();
- static final Rect mTmpOutsetFrame = new Rect();
+ private final WindowFrames mWindowFrames = new WindowFrames();
private static final Rect mTmpDisplayCutoutSafeExceptMaybeBarsRect = new Rect();
private static final Rect mTmpRect = new Rect();
@@ -3333,6 +3329,9 @@
mNavigationBar = null;
mNavigationBarController.setWindow(null);
}
+ if (mLastFocusedWindow == win) {
+ mLastFocusedWindow = null;
+ }
mScreenDecorWindows.remove(win);
}
@@ -4536,16 +4535,15 @@
@Override
// TODO: Should probably be moved into DisplayFrames.
- public boolean getLayoutHintLw(WindowManager.LayoutParams attrs, Rect taskBounds,
- DisplayFrames displayFrames, Rect outFrame, Rect outContentInsets, Rect outStableInsets,
+ public boolean getLayoutHintLw(LayoutParams attrs, Rect taskBounds,
+ DisplayFrames displayFrames, boolean floatingStack, Rect outFrame,
+ Rect outContentInsets, Rect outStableInsets,
Rect outOutsets, DisplayCutout.ParcelableWrapper outDisplayCutout) {
final int fl = PolicyControl.getWindowFlags(null, attrs);
final int pfl = attrs.privateFlags;
final int requestedSysUiVis = PolicyControl.getSystemUiVisibility(null, attrs);
final int sysUiVis = requestedSysUiVis | getImpliedSysUiFlagsForLayout(attrs);
final int displayRotation = displayFrames.mRotation;
- final int displayWidth = displayFrames.mDisplayWidth;
- final int displayHeight = displayFrames.mDisplayHeight;
final boolean useOutsets = outOutsets != null && shouldUseOutsets(attrs, fl);
if (useOutsets) {
@@ -4569,45 +4567,40 @@
final boolean screenDecor = (pfl & PRIVATE_FLAG_IS_SCREEN_DECOR) != 0;
if (layoutInScreenAndInsetDecor && !screenDecor) {
- int availRight, availBottom;
if (canHideNavigationBar() &&
(sysUiVis & View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) != 0) {
outFrame.set(displayFrames.mUnrestricted);
- availRight = displayFrames.mUnrestricted.right;
- availBottom = displayFrames.mUnrestricted.bottom;
} else {
outFrame.set(displayFrames.mRestricted);
- availRight = displayFrames.mRestricted.right;
- availBottom = displayFrames.mRestricted.bottom;
}
- outStableInsets.set(displayFrames.mStable.left, displayFrames.mStable.top,
- availRight - displayFrames.mStable.right,
- availBottom - displayFrames.mStable.bottom);
- if ((sysUiVis & View.SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0) {
+ final Rect sf;
+ if (floatingStack) {
+ sf = null;
+ } else {
+ sf = displayFrames.mStable;
+ }
+
+ final Rect cf;
+ if (floatingStack) {
+ cf = null;
+ } else if ((sysUiVis & View.SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0) {
if ((fl & FLAG_FULLSCREEN) != 0) {
- outContentInsets.set(displayFrames.mStableFullscreen.left,
- displayFrames.mStableFullscreen.top,
- availRight - displayFrames.mStableFullscreen.right,
- availBottom - displayFrames.mStableFullscreen.bottom);
+ cf = displayFrames.mStableFullscreen;
} else {
- outContentInsets.set(outStableInsets);
+ cf = displayFrames.mStable;
}
} else if ((fl & FLAG_FULLSCREEN) != 0 || (fl & FLAG_LAYOUT_IN_OVERSCAN) != 0) {
- outContentInsets.setEmpty();
+ cf = displayFrames.mOverscan;
} else {
- outContentInsets.set(displayFrames.mCurrent.left, displayFrames.mCurrent.top,
- availRight - displayFrames.mCurrent.right,
- availBottom - displayFrames.mCurrent.bottom);
+ cf = displayFrames.mCurrent;
}
if (taskBounds != null) {
- calculateRelevantTaskInsets(taskBounds, outContentInsets,
- displayWidth, displayHeight);
- calculateRelevantTaskInsets(taskBounds, outStableInsets,
- displayWidth, displayHeight);
outFrame.intersect(taskBounds);
}
+ InsetUtils.insetsBetweenFrames(outFrame, cf, outContentInsets);
+ InsetUtils.insetsBetweenFrames(outFrame, sf, outStableInsets);
outDisplayCutout.set(displayFrames.mDisplayCutout.calculateRelativeTo(outFrame)
.getDisplayCutout());
return mForceShowSystemBars;
@@ -4628,22 +4621,6 @@
}
}
- /**
- * For any given task bounds, the insets relevant for these bounds given the insets relevant
- * for the entire display.
- */
- private void calculateRelevantTaskInsets(Rect taskBounds, Rect inOutInsets, int displayWidth,
- int displayHeight) {
- mTmpRect.set(0, 0, displayWidth, displayHeight);
- mTmpRect.inset(inOutInsets);
- mTmpRect.intersect(taskBounds);
- int leftInset = mTmpRect.left - taskBounds.left;
- int topInset = mTmpRect.top - taskBounds.top;
- int rightInset = taskBounds.right - mTmpRect.right;
- int bottomInset = taskBounds.bottom - mTmpRect.bottom;
- inOutInsets.set(leftInset, topInset, rightInset, bottomInset);
- }
-
private boolean shouldUseOutsets(WindowManager.LayoutParams attrs, int fl) {
return attrs.type == TYPE_WALLPAPER || (fl & (WindowManager.LayoutParams.FLAG_FULLSCREEN
| WindowManager.LayoutParams.FLAG_LAYOUT_IN_OVERSCAN)) != 0;
@@ -4659,17 +4636,8 @@
mDockLayer = 0x10000000;
mStatusBarLayer = -1;
- // start with the current dock rect, which will be (0,0,displayWidth,displayHeight)
- final Rect pf = mTmpParentFrame;
- final Rect df = mTmpDisplayFrame;
- final Rect of = mTmpOverscanFrame;
- final Rect vf = mTmpVisibleFrame;
- final Rect dcf = mTmpDecorFrame;
- vf.set(displayFrames.mDock);
- of.set(displayFrames.mDock);
- df.set(displayFrames.mDock);
- pf.set(displayFrames.mDock);
- dcf.setEmpty(); // Decor frame N/A for system bars.
+ mWindowFrames.setDisplayCutout(displayFrames.mDisplayCutout);
+ mWindowFrames.setParentFrameWasClippedByDisplayCutout(false);
if (displayFrames.mDisplayId == DEFAULT_DISPLAY) {
// For purposes of putting out fake window up to steal focus, we will
@@ -4687,8 +4655,7 @@
navTranslucent &= areTranslucentBarsAllowed();
}
boolean statusBarExpandedNotKeyguard = !isKeyguardShowing && mStatusBar != null
- && mStatusBar.getAttrs().height == MATCH_PARENT
- && mStatusBar.getAttrs().width == MATCH_PARENT;
+ && (mStatusBar.getAttrs().privateFlags & PRIVATE_FLAG_STATUS_BAR_EXPANDED) != 0;
// When the navigation bar isn't visible, we put up a fake input window to catch all
// touch events. This way we can detect when the user presses anywhere to bring back the
@@ -4712,16 +4679,15 @@
// be hidden (because of the screen aspect ratio), then take that into account.
navVisible |= !canHideNavigationBar();
- boolean updateSysUiVisibility = layoutNavigationBar(displayFrames, uiMode, dcf,
- navVisible, navTranslucent, navAllowedHidden, statusBarExpandedNotKeyguard);
+ boolean updateSysUiVisibility = layoutNavigationBar(displayFrames, uiMode, navVisible,
+ navTranslucent, navAllowedHidden, statusBarExpandedNotKeyguard);
if (DEBUG_LAYOUT) Slog.i(TAG, "mDock rect:" + displayFrames.mDock);
- updateSysUiVisibility |= layoutStatusBar(
- displayFrames, pf, df, of, vf, dcf, sysui, isKeyguardShowing);
+ updateSysUiVisibility |= layoutStatusBar(displayFrames, sysui, isKeyguardShowing);
if (updateSysUiVisibility) {
updateSystemUiVisibilityLw();
}
}
- layoutScreenDecorWindows(displayFrames, pf, df, dcf);
+ layoutScreenDecorWindows(displayFrames);
if (displayFrames.mDisplayCutoutSafe.top > displayFrames.mUnrestricted.top) {
// Make sure that the zone we're avoiding for the cutout is at least as tall as the
@@ -4732,11 +4698,18 @@
}
}
- private void layoutScreenDecorWindows(DisplayFrames displayFrames, Rect pf, Rect df, Rect dcf) {
+ private void layoutScreenDecorWindows(DisplayFrames displayFrames) {
if (mScreenDecorWindows.isEmpty()) {
return;
}
+ mTmpRect.setEmpty();
+ mWindowFrames.setFrames(displayFrames.mDock /* parentFrame */,
+ displayFrames.mDock /* displayFrame */, displayFrames.mDock /* overscanFrame */,
+ displayFrames.mDock /* contentFrame */, displayFrames.mDock /* visibleFrame */,
+ mTmpRect /* decorFrame */, displayFrames.mDock /* stableFrame */,
+ displayFrames.mDock /* outsetFrame */);
+
final int displayId = displayFrames.mDisplayId;
final Rect dockFrame = displayFrames.mDock;
final int displayHeight = displayFrames.mDisplayHeight;
@@ -4749,10 +4722,7 @@
continue;
}
- w.computeFrameLw(pf /* parentFrame */, df /* displayFrame */, df /* overlayFrame */,
- df /* contentFrame */, df /* visibleFrame */, dcf /* decorFrame */,
- df /* stableFrame */, df /* outsetFrame */, displayFrames.mDisplayCutout,
- false /* parentFrameWasClippedByDisplayCutout */);
+ w.computeFrameLw(mWindowFrames);
final Rect frame = w.getFrameLw();
if (frame.left <= 0 && frame.top <= 0) {
@@ -4797,25 +4767,24 @@
displayFrames.mRestrictedOverscan.set(dockFrame);
}
- private boolean layoutStatusBar(DisplayFrames displayFrames, Rect pf, Rect df, Rect of, Rect vf,
- Rect dcf, int sysui, boolean isKeyguardShowing) {
+ private boolean layoutStatusBar(DisplayFrames displayFrames, int sysui,
+ boolean isKeyguardShowing) {
// decide where the status bar goes ahead of time
if (mStatusBar == null) {
return false;
}
// apply any navigation bar insets
- of.set(displayFrames.mUnrestricted);
- df.set(displayFrames.mUnrestricted);
- pf.set(displayFrames.mUnrestricted);
- vf.set(displayFrames.mStable);
+ mTmpRect.setEmpty();
+ mWindowFrames.setFrames(displayFrames.mUnrestricted /* parentFrame */,
+ displayFrames.mUnrestricted /* displayFrame */,
+ displayFrames.mStable /* overscanFrame */, displayFrames.mStable /* contentFrame */,
+ displayFrames.mStable /* visibleFrame */, mTmpRect /* decorFrame */,
+ displayFrames.mStable /* stableFrame */, displayFrames.mStable /* outsetFrame */);
mStatusBarLayer = mStatusBar.getSurfaceLayer();
// Let the status bar determine its size.
- mStatusBar.computeFrameLw(pf /* parentFrame */, df /* displayFrame */,
- vf /* overlayFrame */, vf /* contentFrame */, vf /* visibleFrame */,
- dcf /* decorFrame */, vf /* stableFrame */, vf /* outsetFrame */,
- displayFrames.mDisplayCutout, false /* parentFrameWasClippedByDisplayCutout */);
+ mStatusBar.computeFrameLw(mWindowFrames);
// For layout, the status bar is always at the top with our fixed height.
displayFrames.mStable.top = displayFrames.mUnrestricted.top
@@ -4862,12 +4831,14 @@
return mStatusBarController.checkHiddenLw();
}
- private boolean layoutNavigationBar(DisplayFrames displayFrames, int uiMode, Rect dcf,
- boolean navVisible, boolean navTranslucent, boolean navAllowedHidden,
+ private boolean layoutNavigationBar(DisplayFrames displayFrames, int uiMode, boolean navVisible,
+ boolean navTranslucent, boolean navAllowedHidden,
boolean statusBarExpandedNotKeyguard) {
if (mNavigationBar == null) {
return false;
}
+
+ final Rect navigationFrame = mWindowFrames.mParentFrame;
boolean transientNavBarShowing = mNavigationBarController.isTransientShowing();
// Force the navigation bar to its appropriate place and size. We need to do this directly,
// instead of relying on it to bubble up from the nav bar, because this needs to change
@@ -4886,7 +4857,7 @@
// It's a system nav bar or a portrait screen; nav bar goes on bottom.
final int top = cutoutSafeUnrestricted.bottom
- getNavigationBarHeight(rotation, uiMode);
- mTmpNavigationFrame.set(0, top, displayWidth, displayFrames.mUnrestricted.bottom);
+ navigationFrame.set(0, top, displayWidth, displayFrames.mUnrestricted.bottom);
displayFrames.mStable.bottom = displayFrames.mStableFullscreen.bottom = top;
if (transientNavBarShowing) {
mNavigationBarController.setBarShowingLw(true);
@@ -4909,7 +4880,7 @@
// Landscape screen; nav bar goes to the right.
final int left = cutoutSafeUnrestricted.right
- getNavigationBarWidth(rotation, uiMode);
- mTmpNavigationFrame.set(left, 0, displayFrames.mUnrestricted.right, displayHeight);
+ navigationFrame.set(left, 0, displayFrames.mUnrestricted.right, displayHeight);
displayFrames.mStable.right = displayFrames.mStableFullscreen.right = left;
if (transientNavBarShowing) {
mNavigationBarController.setBarShowingLw(true);
@@ -4932,7 +4903,7 @@
// Seascape screen; nav bar goes to the left.
final int right = cutoutSafeUnrestricted.left
+ getNavigationBarWidth(rotation, uiMode);
- mTmpNavigationFrame.set(displayFrames.mUnrestricted.left, 0, right, displayHeight);
+ navigationFrame.set(displayFrames.mUnrestricted.left, 0, right, displayHeight);
displayFrames.mStable.left = displayFrames.mStableFullscreen.left = right;
if (transientNavBarShowing) {
mNavigationBarController.setBarShowingLw(true);
@@ -4960,13 +4931,18 @@
displayFrames.mContent.set(dockFrame);
mStatusBarLayer = mNavigationBar.getSurfaceLayer();
// And compute the final frame.
- mNavigationBar.computeFrameLw(mTmpNavigationFrame, mTmpNavigationFrame,
- mTmpNavigationFrame, displayFrames.mDisplayCutoutSafe, mTmpNavigationFrame, dcf,
- mTmpNavigationFrame, displayFrames.mDisplayCutoutSafe,
- displayFrames.mDisplayCutout, false /* parentFrameWasClippedByDisplayCutout */);
+ mTmpRect.setEmpty();
+ mWindowFrames.setFrames(navigationFrame /* parentFrame */,
+ navigationFrame /* displayFrame */, navigationFrame /* overscanFrame */,
+ displayFrames.mDisplayCutoutSafe /* contentFrame */,
+ navigationFrame /* visibleFrame */, mTmpRect /* decorFrame */,
+ navigationFrame /* stableFrame */,
+ displayFrames.mDisplayCutoutSafe /* outsetFrame */);
+
+ mNavigationBar.computeFrameLw(mWindowFrames);
mNavigationBarController.setContentFrame(mNavigationBar.getContentFrameLw());
- if (DEBUG_LAYOUT) Slog.i(TAG, "mNavigationBar frame: " + mTmpNavigationFrame);
+ if (DEBUG_LAYOUT) Slog.i(TAG, "mNavigationBar frame: " + navigationFrame);
return mNavigationBarController.checkHiddenLw();
}
@@ -5094,15 +5070,17 @@
final int requestedSysUiFl = PolicyControl.getSystemUiVisibility(null, attrs);
final int sysUiFl = requestedSysUiFl | getImpliedSysUiFlagsForLayout(attrs);
- final Rect pf = mTmpParentFrame;
- final Rect df = mTmpDisplayFrame;
- final Rect of = mTmpOverscanFrame;
- final Rect cf = mTmpContentFrame;
- final Rect vf = mTmpVisibleFrame;
- final Rect dcf = mTmpDecorFrame;
- final Rect sf = mTmpStableFrame;
- Rect osf = null;
+ final Rect pf = mWindowFrames.mParentFrame;
+ final Rect df = mWindowFrames.mDisplayFrame;
+ final Rect of = mWindowFrames.mOverscanFrame;
+ final Rect cf = mWindowFrames.mContentFrame;
+ final Rect vf = mWindowFrames.mVisibleFrame;
+ final Rect dcf = mWindowFrames.mDecorFrame;
+ final Rect sf = mWindowFrames.mStableFrame;
dcf.setEmpty();
+ mWindowFrames.mOutsetFrame.setEmpty();
+ mWindowFrames.setParentFrameWasClippedByDisplayCutout(false);
+ mWindowFrames.setDisplayCutout(displayFrames.mDisplayCutout);
final boolean hasNavBar = (isDefaultDisplay && mHasNavigationBar
&& mNavigationBar != null && mNavigationBar.isVisibleLw());
@@ -5422,7 +5400,6 @@
}
}
- boolean parentFrameWasClippedByDisplayCutout = false;
final int cutoutMode = attrs.layoutInDisplayCutoutMode;
final boolean attachedInParent = attached != null && !layoutInScreen;
final boolean requestedHideNavigation =
@@ -5473,7 +5450,7 @@
if (!attachedInParent && !floatingInScreenWindow) {
mTmpRect.set(pf);
pf.intersectUnchecked(displayCutoutSafeExceptMaybeBars);
- parentFrameWasClippedByDisplayCutout |= !mTmpRect.equals(pf);
+ mWindowFrames.setParentFrameWasClippedByDisplayCutout(!mTmpRect.equals(pf));
}
// Make sure that NO_LIMITS windows clipped to the display don't extend under the
// cutout.
@@ -5501,7 +5478,7 @@
// apply the outsets to floating dialogs, because they wouldn't make sense there.
final boolean useOutsets = shouldUseOutsets(attrs, fl);
if (isDefaultDisplay && useOutsets) {
- osf = mTmpOutsetFrame;
+ final Rect osf = mWindowFrames.mOutsetFrame;
osf.set(cf.left, cf.top, cf.right, cf.bottom);
int outset = ScreenShapeHelper.getWindowOutsetBottomPx(mContext.getResources());
if (outset > 0) {
@@ -5529,10 +5506,9 @@
+ " cf=" + cf.toShortString() + " vf=" + vf.toShortString()
+ " dcf=" + dcf.toShortString()
+ " sf=" + sf.toShortString()
- + " osf=" + (osf == null ? "null" : osf.toShortString()));
+ + " osf=" + mWindowFrames.mOutsetFrame.toShortString());
- win.computeFrameLw(pf, df, of, cf, vf, dcf, sf, osf, displayFrames.mDisplayCutout,
- parentFrameWasClippedByDisplayCutout);
+ win.computeFrameLw(mWindowFrames);
// Dock windows carve out the bottom of the screen, so normal windows
// can't appear underneath them.
if (type == TYPE_INPUT_METHOD && win.isVisibleLw()
@@ -5691,7 +5667,7 @@
}
// Take note if a window wants to acquire a sleep token.
- if (win.isVisibleLw() && (attrs.privateFlags & PRIVATE_FLAG_ACQUIRES_SLEEP_TOKEN) != 0
+ if ((attrs.privateFlags & PRIVATE_FLAG_ACQUIRES_SLEEP_TOKEN) != 0
&& win.canAcquireSleepToken()) {
mWindowSleepTokenNeeded = true;
}
@@ -5747,9 +5723,8 @@
mStatusBarController.setShowTransparent(true /* transparent */);
}
- WindowManager.LayoutParams statusBarAttrs = mStatusBar.getAttrs();
- boolean statusBarExpanded = statusBarAttrs.height == MATCH_PARENT
- && statusBarAttrs.width == MATCH_PARENT;
+ boolean statusBarExpanded =
+ (mStatusBar.getAttrs().privateFlags & PRIVATE_FLAG_STATUS_BAR_EXPANDED) != 0;
boolean topAppHidesStatusBar = topAppHidesStatusBar();
if (mForceStatusBar || mForceStatusBarFromKeyguard || mForceStatusBarTransparent
|| statusBarExpanded) {
@@ -5902,7 +5877,8 @@
@Override
public int focusChangedLw(WindowState lastFocus, WindowState newFocus) {
mFocusedWindow = newFocus;
- if ((updateSystemUiVisibilityLw()&SYSTEM_UI_CHANGING_LAYOUT) != 0) {
+ mLastFocusedWindow = lastFocus;
+ if ((updateSystemUiVisibilityLw() & SYSTEM_UI_CHANGING_LAYOUT) != 0) {
// If the navigation bar has been hidden or shown, we need to do another
// layout pass to update that window.
return FINISH_LAYOUT_REDO_LAYOUT;
@@ -8128,10 +8104,19 @@
if (winCandidate == null) {
return 0;
}
+
+ // The immersive mode confirmation should never affect the system bar visibility, otherwise
+ // it will unhide the navigation bar and hide itself.
if (winCandidate.getAttrs().token == mImmersiveModeConfirmation.getWindowToken()) {
- // The immersive mode confirmation should never affect the system bar visibility,
- // otherwise it will unhide the navigation bar and hide itself.
- winCandidate = isStatusBarKeyguard() ? mStatusBar : mTopFullscreenOpaqueWindowState;
+
+ // The immersive mode confirmation took the focus from mLastFocusedWindow which was
+ // controlling the system ui visibility. So if mLastFocusedWindow can still receive
+ // keys, we let it keep controlling the visibility.
+ final boolean lastFocusCanReceiveKeys =
+ (mLastFocusedWindow != null && mLastFocusedWindow.canReceiveKeys());
+ winCandidate = isStatusBarKeyguard() ? mStatusBar
+ : lastFocusCanReceiveKeys ? mLastFocusedWindow
+ : mTopFullscreenOpaqueWindowState;
if (winCandidate == null) {
return 0;
}
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index 9fbe419..45ce36b 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -71,7 +71,6 @@
import android.content.pm.ActivityInfo;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
-import android.graphics.Point;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.IBinder;
@@ -94,6 +93,7 @@
import com.android.internal.policy.IKeyguardDismissCallback;
import com.android.internal.policy.IShortcutService;
import com.android.server.wm.DisplayFrames;
+import com.android.server.wm.WindowFrames;
import com.android.server.wm.utils.WmDisplayCutout;
import java.io.PrintWriter;
@@ -157,6 +157,8 @@
int FINISH_LAYOUT_REDO_WALLPAPER = 0x0004;
/** Need to recompute animations */
int FINISH_LAYOUT_REDO_ANIM = 0x0008;
+ /** Layer for the screen off animation */
+ int COLOR_FADE_LAYER = 0x40000001;
/**
* Register shortcuts for window manager to dispatch.
@@ -198,35 +200,10 @@
* getFrame() if so desired. Must be called with the window manager
* lock held.
*
- * @param parentFrame The frame of the parent container this window
- * is in, used for computing its basic position.
- * @param displayFrame The frame of the overall display in which this
- * window can appear, used for constraining the overall dimensions
- * of the window.
- * @param overlayFrame The frame within the display that is inside
- * of the overlay region.
- * @param contentFrame The frame within the display in which we would
- * like active content to appear. This will cause windows behind to
- * be resized to match the given content frame.
- * @param visibleFrame The frame within the display that the window
- * is actually visible, used for computing its visible insets to be
- * given to windows behind.
- * This can be used as a hint for scrolling (avoiding resizing)
- * the window to make certain that parts of its content
- * are visible.
- * @param decorFrame The decor frame specified by policy specific to this window,
- * to use for proper cropping during animation.
- * @param stableFrame The frame around which stable system decoration is positioned.
- * @param outsetFrame The frame that includes areas that aren't part of the surface but we
- * want to treat them as such.
- * @param displayCutout the display cutout
- * @param parentFrameWasClippedByDisplayCutout true if the parent frame would have been
- * different if there was no display cutout.
+ * @param windowFrames Container for all the window frames that affect how the window is
+ * laid out.
*/
- public void computeFrameLw(Rect parentFrame, Rect displayFrame,
- Rect overlayFrame, Rect contentFrame, Rect visibleFrame, Rect decorFrame,
- Rect stableFrame, @Nullable Rect outsetFrame, WmDisplayCutout displayCutout,
- boolean parentFrameWasClippedByDisplayCutout);
+ public void computeFrameLw(WindowFrames windowFrames);
/**
* Retrieve the current frame of the window that has been assigned by
@@ -492,6 +469,9 @@
*/
boolean canAcquireSleepToken();
+ /** @return true if this window desires key events. */
+ boolean canReceiveKeys();
+
/**
* Writes {@link com.android.server.wm.IdentifierProto} to stream.
*/
@@ -1180,6 +1160,7 @@
* @param taskBounds The bounds of the task this window is on or {@code null} if no task is
* associated with the window.
* @param displayFrames display frames.
+ * @param floatingStack Whether the window's stack is floating.
* @param outFrame The frame of the window.
* @param outContentInsets The areas covered by system windows, expressed as positive insets.
* @param outStableInsets The areas covered by stable system windows irrespective of their
@@ -1190,8 +1171,8 @@
* See {@link #isNavBarForcedShownLw(WindowState)}.
*/
default boolean getLayoutHintLw(WindowManager.LayoutParams attrs, Rect taskBounds,
- DisplayFrames displayFrames, Rect outFrame, Rect outContentInsets,
- Rect outStableInsets, Rect outOutsets,
+ DisplayFrames displayFrames, boolean floatingStack,
+ Rect outFrame, Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
DisplayCutout.ParcelableWrapper outDisplayCutout) {
return false;
}
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java
index dd6d71e..4c88bf4 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java
@@ -72,7 +72,7 @@
@GuardedBy("mLock")
private int mBatteryLevel;
- /** Whether the battery level is considered to be "low" or not.*/
+ /** Whether the battery level is considered to be "low" or not. */
@GuardedBy("mLock")
private boolean mIsBatteryLevelLow;
@@ -84,6 +84,9 @@
@GuardedBy("mLock")
private boolean mSettingBatterySaverEnabledSticky;
+ /** Config flag to track if battery saver's sticky behaviour is disabled. */
+ private final boolean mBatterySaverStickyBehaviourDisabled;
+
/**
* Previously known value of Global.LOW_POWER_MODE_TRIGGER_LEVEL.
* (Currently only used in dumpsys.)
@@ -124,6 +127,9 @@
mLock = lock;
mContext = context;
mBatterySaverController = batterySaverController;
+
+ mBatterySaverStickyBehaviourDisabled = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_batterySaverStickyBehaviourDisabled);
}
private boolean isBatterySaverEnabled() {
@@ -304,7 +310,7 @@
BatterySaverController.REASON_PLUGGED_IN,
"Plugged in");
- } else if (mSettingBatterySaverEnabledSticky) {
+ } else if (mSettingBatterySaverEnabledSticky && !mBatterySaverStickyBehaviourDisabled) {
// Re-enable BS.
enableBatterySaverLocked(/*enable=*/ true, /*manual=*/ true,
BatterySaverController.REASON_STICKY_RESTORE,
@@ -383,8 +389,9 @@
putGlobalSetting(Global.LOW_POWER_MODE, enable ? 1 : 0);
if (manual) {
- mSettingBatterySaverEnabledSticky = enable;
- putGlobalSetting(Global.LOW_POWER_MODE_STICKY, enable ? 1 : 0);
+ mSettingBatterySaverEnabledSticky = !mBatterySaverStickyBehaviourDisabled && enable;
+ putGlobalSetting(Global.LOW_POWER_MODE_STICKY,
+ mSettingBatterySaverEnabledSticky ? 1 : 0);
}
mBatterySaverController.enableBatterySaver(enable, intReason);
@@ -449,6 +456,8 @@
pw.println(mSettingBatterySaverEnabledSticky);
pw.print(" mSettingBatterySaverTriggerThreshold=");
pw.println(mSettingBatterySaverTriggerThreshold);
+ pw.print(" mBatterySaverStickyBehaviourDisabled=");
+ pw.println(mBatterySaverStickyBehaviourDisabled);
}
}
diff --git a/services/core/java/com/android/server/slice/PinnedSliceState.java b/services/core/java/com/android/server/slice/PinnedSliceState.java
index 4e7fb96..e139ab8 100644
--- a/services/core/java/com/android/server/slice/PinnedSliceState.java
+++ b/services/core/java/com/android/server/slice/PinnedSliceState.java
@@ -154,8 +154,8 @@
}
ContentProviderClient getClient() {
- ContentProviderClient client =
- mService.getContext().getContentResolver().acquireContentProviderClient(mUri);
+ ContentProviderClient client = mService.getContext().getContentResolver()
+ .acquireUnstableContentProviderClient(mUri);
if (client == null) return null;
client.setDetectNotResponding(SLICE_TIMEOUT);
return client;
diff --git a/services/core/java/com/android/server/slice/SliceManagerService.java b/services/core/java/com/android/server/slice/SliceManagerService.java
index c3b9841..ded2c15 100644
--- a/services/core/java/com/android/server/slice/SliceManagerService.java
+++ b/services/core/java/com/android/server/slice/SliceManagerService.java
@@ -42,6 +42,7 @@
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
+import android.content.pm.ProviderInfo;
import android.content.pm.ResolveInfo;
import android.net.Uri;
import android.os.Binder;
@@ -197,6 +198,7 @@
public SliceSpec[] getPinnedSpecs(Uri uri, String pkg) throws RemoteException {
verifyCaller(pkg);
enforceAccess(pkg, uri);
+ uri = maybeAddUserId(uri, Binder.getCallingUserHandle().getIdentifier());
return getPinnedSlice(uri).getSpecs();
}
@@ -395,30 +397,11 @@
private String getProviderPkg(Uri uri, int user) {
long ident = Binder.clearCallingIdentity();
try {
- IBinder token = new Binder();
- IActivityManager activityManager = ActivityManager.getService();
- ContentProviderHolder holder = null;
String providerName = getUriWithoutUserId(uri).getAuthority();
- try {
- try {
- holder = activityManager.getContentProviderExternal(
- providerName, getUserIdFromUri(uri, user), token);
- if (holder != null && holder.info != null) {
- return holder.info.packageName;
- } else {
- return null;
- }
- } finally {
- if (holder != null && holder.provider != null) {
- activityManager.removeContentProviderExternal(providerName, token);
- }
- }
- } catch (RemoteException e) {
- // Can't happen.
- throw e.rethrowAsRuntimeException();
- }
+ ProviderInfo provider = mContext.getPackageManager().resolveContentProviderAsUser(
+ providerName, 0, getUserIdFromUri(uri, user));
+ return provider.packageName;
} finally {
- // I know, the double finally seems ugly, but seems safest for the identity.
Binder.restoreCallingIdentity(ident);
}
}
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index 4d65440..c7c24a5 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -57,11 +57,14 @@
import android.os.UserManager;
import android.telephony.ModemActivityInfo;
import android.telephony.TelephonyManager;
+import android.util.ArrayMap;
import android.util.Slog;
import android.util.StatsLog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.net.NetworkStatsFactory;
+import com.android.internal.os.BinderCallsStats;
+import com.android.internal.os.BinderCallsStats.ExportedCallStat;
import com.android.internal.os.KernelCpuSpeedReader;
import com.android.internal.os.KernelUidCpuTimeReader;
import com.android.internal.os.KernelUidCpuClusterTimeReader;
@@ -891,6 +894,26 @@
}
}
+ private void pullBinderCallsStats(int tagId, List<StatsLogEventWrapper> pulledData) {
+ List<ExportedCallStat> callStats = BinderCallsStats.getInstance().getExportedCallStats();
+ long elapsedNanos = SystemClock.elapsedRealtimeNanos();
+ for (ExportedCallStat callStat : callStats) {
+ StatsLogEventWrapper e = new StatsLogEventWrapper(elapsedNanos, tagId, 11 /* fields */);
+ e.writeInt(callStat.uid);
+ e.writeString(callStat.className);
+ e.writeString(callStat.methodName);
+ e.writeLong(callStat.callCount);
+ e.writeLong(callStat.exceptionCount);
+ e.writeLong(callStat.latencyMicros);
+ e.writeLong(callStat.maxLatencyMicros);
+ e.writeLong(callStat.cpuTimeMicros);
+ e.writeLong(callStat.maxCpuTimeMicros);
+ e.writeLong(callStat.maxReplySizeBytes);
+ e.writeLong(callStat.maxRequestSizeBytes);
+ pulledData.add(e);
+ }
+ }
+
/**
* Pulls various data.
*/
@@ -973,6 +996,10 @@
pullProcessMemoryState(tagId, ret);
break;
}
+ case StatsLog.BINDER_CALLS: {
+ pullBinderCallsStats(tagId, ret);
+ break;
+ }
default:
Slog.w(TAG, "No such tagId data as " + tagId);
return null;
diff --git a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
index 2742b0f..005212f 100644
--- a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
+++ b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
@@ -28,6 +28,7 @@
import android.os.IBinder;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.provider.Settings;
import android.service.textclassifier.ITextClassificationCallback;
import android.service.textclassifier.ITextClassifierService;
import android.service.textclassifier.ITextLinksCallback;
@@ -38,16 +39,22 @@
import android.view.textclassifier.SelectionEvent;
import android.view.textclassifier.TextClassification;
import android.view.textclassifier.TextClassificationContext;
+import android.view.textclassifier.TextClassificationManager;
import android.view.textclassifier.TextClassificationSessionId;
+import android.view.textclassifier.TextClassifier;
import android.view.textclassifier.TextLinks;
import android.view.textclassifier.TextSelection;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.DumpUtils;
import com.android.internal.util.FunctionalUtils;
import com.android.internal.util.FunctionalUtils.ThrowingRunnable;
+import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
import com.android.server.SystemService;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.util.ArrayDeque;
import java.util.Queue;
@@ -259,6 +266,14 @@
return mUserStates.get(userId);
}
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
+ if (!DumpUtils.checkDumpPermission(mContext, LOG_TAG, fout)) return;
+ IndentingPrintWriter pw = new IndentingPrintWriter(fout, " ");
+ TextClassificationManager tcm = mContext.getSystemService(TextClassificationManager.class);
+ tcm.dump(pw);
+ }
+
private static final class PendingRequest implements IBinder.DeathRecipient {
@Nullable private final IBinder mBinder;
diff --git a/services/core/java/com/android/server/timedetector/SimpleTimeDetectorStrategy.java b/services/core/java/com/android/server/timedetector/SimpleTimeDetectorStrategy.java
index e5207cb..7bdc8a3 100644
--- a/services/core/java/com/android/server/timedetector/SimpleTimeDetectorStrategy.java
+++ b/services/core/java/com/android/server/timedetector/SimpleTimeDetectorStrategy.java
@@ -20,38 +20,213 @@
import android.annotation.Nullable;
import android.app.AlarmManager;
import android.app.timedetector.TimeSignal;
+import android.content.Intent;
import android.util.Slog;
+import android.util.TimestampedValue;
-import java.io.FileDescriptor;
+import com.android.internal.telephony.TelephonyIntents;
+
import java.io.PrintWriter;
/**
- * A placeholder implementation of TimeDetectorStrategy that passes NITZ suggestions immediately
- * to {@link AlarmManager}.
+ * An implementation of TimeDetectorStrategy that passes only NITZ suggestions to
+ * {@link AlarmManager}. The TimeDetectorService handles thread safety: all calls to
+ * this class can be assumed to be single threaded (though the thread used may vary).
*/
+// @NotThreadSafe
public final class SimpleTimeDetectorStrategy implements TimeDetectorStrategy {
private final static String TAG = "timedetector.SimpleTimeDetectorStrategy";
- private Callback mHelper;
+ /**
+ * CLOCK_PARANOIA: The maximum difference allowed between the expected system clock time and the
+ * actual system clock time before a warning is logged. Used to help identify situations where
+ * there is something other than this class setting the system clock.
+ */
+ private static final long SYSTEM_CLOCK_PARANOIA_THRESHOLD_MILLIS = 2 * 1000;
+
+ // @NonNull after initialize()
+ private Callback mCallback;
+
+ // NITZ state.
+ @Nullable private TimestampedValue<Long> mLastNitzTime;
+
+
+ // Information about the last time signal received: Used when toggling auto-time.
+ @Nullable private TimestampedValue<Long> mLastSystemClockTime;
+ private boolean mLastSystemClockTimeSendNetworkBroadcast;
+
+ // System clock state.
+ @Nullable private TimestampedValue<Long> mLastSystemClockTimeSet;
@Override
public void initialize(@NonNull Callback callback) {
- mHelper = callback;
+ mCallback = callback;
}
@Override
public void suggestTime(@NonNull TimeSignal timeSignal) {
if (!TimeSignal.SOURCE_ID_NITZ.equals(timeSignal.getSourceId())) {
- Slog.w(TAG, "Ignoring signal from unknown source: " + timeSignal);
+ Slog.w(TAG, "Ignoring signal from unsupported source: " + timeSignal);
return;
}
- mHelper.setTime(timeSignal.getUtcTime());
+ // NITZ logic
+
+ TimestampedValue<Long> newNitzUtcTime = timeSignal.getUtcTime();
+ boolean nitzTimeIsValid = validateNewNitzTime(newNitzUtcTime, mLastNitzTime);
+ if (!nitzTimeIsValid) {
+ return;
+ }
+ // Always store the last NITZ value received, regardless of whether we go on to use it to
+ // update the system clock. This is so that we can validate future NITZ signals.
+ mLastNitzTime = newNitzUtcTime;
+
+ // System clock update logic.
+
+ // Historically, Android has sent a telephony broadcast only when setting the time using
+ // NITZ.
+ final boolean sendNetworkBroadcast =
+ TimeSignal.SOURCE_ID_NITZ.equals(timeSignal.getSourceId());
+
+ final TimestampedValue<Long> newUtcTime = newNitzUtcTime;
+ setSystemClockIfRequired(newUtcTime, sendNetworkBroadcast);
+ }
+
+ private static boolean validateNewNitzTime(TimestampedValue<Long> newNitzUtcTime,
+ TimestampedValue<Long> lastNitzTime) {
+
+ if (lastNitzTime != null) {
+ long referenceTimeDifference =
+ TimestampedValue.referenceTimeDifference(newNitzUtcTime, lastNitzTime);
+ if (referenceTimeDifference < 0 || referenceTimeDifference > Integer.MAX_VALUE) {
+ // Out of order or bogus.
+ Slog.w(TAG, "validateNewNitzTime: Bad NITZ signal received."
+ + " referenceTimeDifference=" + referenceTimeDifference
+ + " lastNitzTime=" + lastNitzTime
+ + " newNitzUtcTime=" + newNitzUtcTime);
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private void setSystemClockIfRequired(
+ TimestampedValue<Long> time, boolean sendNetworkBroadcast) {
+
+ // Store the last candidate we've seen in all cases so we can set the system clock
+ // when/if time detection is enabled.
+ mLastSystemClockTime = time;
+ mLastSystemClockTimeSendNetworkBroadcast = sendNetworkBroadcast;
+
+ if (!mCallback.isTimeDetectionEnabled()) {
+ Slog.d(TAG, "setSystemClockIfRequired: Time detection is not enabled. time=" + time);
+ return;
+ }
+
+ mCallback.acquireWakeLock();
+ try {
+ long elapsedRealtimeMillis = mCallback.elapsedRealtimeMillis();
+ long actualTimeMillis = mCallback.systemClockMillis();
+
+ // CLOCK_PARANOIA : Check to see if this class owns the clock or if something else
+ // may be setting the clock.
+ if (mLastSystemClockTimeSet != null) {
+ long expectedTimeMillis = TimeDetectorStrategy.getTimeAt(
+ mLastSystemClockTimeSet, elapsedRealtimeMillis);
+ long absSystemClockDifference = Math.abs(expectedTimeMillis - actualTimeMillis);
+ if (absSystemClockDifference > SYSTEM_CLOCK_PARANOIA_THRESHOLD_MILLIS) {
+ Slog.w(TAG, "System clock has not tracked elapsed real time clock. A clock may"
+ + " be inaccurate or something unexpectedly set the system clock."
+ + " elapsedRealtimeMillis=" + elapsedRealtimeMillis
+ + " expectedTimeMillis=" + expectedTimeMillis
+ + " actualTimeMillis=" + actualTimeMillis);
+ }
+ }
+
+ final String reason = "New time signal";
+ adjustAndSetDeviceSystemClock(
+ time, sendNetworkBroadcast, elapsedRealtimeMillis, actualTimeMillis, reason);
+ } finally {
+ mCallback.releaseWakeLock();
+ }
}
@Override
- public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @Nullable String[] args) {
- // No state to dump.
+ public void handleAutoTimeDetectionToggle(boolean enabled) {
+ // If automatic time detection is enabled we update the system clock instantly if we can.
+ // Conversely, if automatic time detection is disabled we leave the clock as it is.
+ if (enabled) {
+ if (mLastSystemClockTime != null) {
+ // Only send the network broadcast if the last candidate would have caused one.
+ final boolean sendNetworkBroadcast = mLastSystemClockTimeSendNetworkBroadcast;
+
+ mCallback.acquireWakeLock();
+ try {
+ long elapsedRealtimeMillis = mCallback.elapsedRealtimeMillis();
+ long actualTimeMillis = mCallback.systemClockMillis();
+
+ final String reason = "Automatic time detection enabled.";
+ adjustAndSetDeviceSystemClock(mLastSystemClockTime, sendNetworkBroadcast,
+ elapsedRealtimeMillis, actualTimeMillis, reason);
+ } finally {
+ mCallback.releaseWakeLock();
+ }
+ }
+ } else {
+ // CLOCK_PARANOIA: We are losing "control" of the system clock so we cannot predict what
+ // it should be in future.
+ mLastSystemClockTimeSet = null;
+ }
+ }
+
+ @Override
+ public void dump(@NonNull PrintWriter pw, @Nullable String[] args) {
+ pw.println("mLastNitzTime=" + mLastNitzTime);
+ pw.println("mLastSystemClockTimeSet=" + mLastSystemClockTimeSet);
+ pw.println("mLastSystemClockTime=" + mLastSystemClockTime);
+ pw.println("mLastSystemClockTimeSendNetworkBroadcast="
+ + mLastSystemClockTimeSendNetworkBroadcast);
+ }
+
+ private void adjustAndSetDeviceSystemClock(
+ TimestampedValue<Long> newTime, boolean sendNetworkBroadcast,
+ long elapsedRealtimeMillis, long actualSystemClockMillis, String reason) {
+
+ // Adjust for the time that has elapsed since the signal was received.
+ long newSystemClockMillis = TimeDetectorStrategy.getTimeAt(newTime, elapsedRealtimeMillis);
+
+ // Check if the new signal would make sufficient difference to the system clock. If it's
+ // below the threshold then ignore it.
+ long absTimeDifference = Math.abs(newSystemClockMillis - actualSystemClockMillis);
+ long systemClockUpdateThreshold = mCallback.systemClockUpdateThresholdMillis();
+ if (absTimeDifference < systemClockUpdateThreshold) {
+ Slog.d(TAG, "adjustAndSetDeviceSystemClock: Not setting system clock. New time and"
+ + " system clock are close enough."
+ + " elapsedRealtimeMillis=" + elapsedRealtimeMillis
+ + " newTime=" + newTime
+ + " reason=" + reason
+ + " systemClockUpdateThreshold=" + systemClockUpdateThreshold
+ + " absTimeDifference=" + absTimeDifference);
+ return;
+ }
+
+ Slog.d(TAG, "Setting system clock using time=" + newTime
+ + " reason=" + reason
+ + " elapsedRealtimeMillis=" + elapsedRealtimeMillis
+ + " newTimeMillis=" + newSystemClockMillis);
+ mCallback.setSystemClock(newSystemClockMillis);
+
+ // CLOCK_PARANOIA : Record the last time this class set the system clock.
+ mLastSystemClockTimeSet = newTime;
+
+ if (sendNetworkBroadcast) {
+ // Send a broadcast that telephony code used to send after setting the clock.
+ // TODO Remove this broadcast as soon as there are no remaining listeners.
+ Intent intent = new Intent(TelephonyIntents.ACTION_NETWORK_SET_TIME);
+ intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
+ intent.putExtra("time", newSystemClockMillis);
+ mCallback.sendStickyBroadcast(intent);
+ }
}
}
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorService.java b/services/core/java/com/android/server/timedetector/TimeDetectorService.java
index 0ec24d8..9c83000 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorService.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorService.java
@@ -20,24 +20,29 @@
import android.annotation.Nullable;
import android.app.timedetector.ITimeDetectorService;
import android.app.timedetector.TimeSignal;
+import android.content.ContentResolver;
import android.content.Context;
+import android.database.ContentObserver;
import android.os.Binder;
+import android.provider.Settings;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.DumpUtils;
+import com.android.server.FgThread;
import com.android.server.SystemService;
+import com.android.server.timedetector.TimeDetectorStrategy.Callback;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.Objects;
public final class TimeDetectorService extends ITimeDetectorService.Stub {
-
private static final String TAG = "timedetector.TimeDetectorService";
public static class Lifecycle extends SystemService {
- public Lifecycle(Context context) {
+ public Lifecycle(@NonNull Context context) {
super(context);
}
@@ -51,31 +56,65 @@
}
}
- private final Context mContext;
- private final TimeDetectorStrategy mTimeDetectorStrategy;
+ @NonNull private final Context mContext;
+ @NonNull private final Callback mCallback;
- private static TimeDetectorService create(Context context) {
- TimeDetectorStrategy timeDetector = new SimpleTimeDetectorStrategy();
- timeDetector.initialize(new TimeDetectorStrategyCallbackImpl(context));
- return new TimeDetectorService(context, timeDetector);
+ // The lock used when call the strategy to ensure thread safety.
+ @NonNull private final Object mStrategyLock = new Object();
+
+ @GuardedBy("mStrategyLock")
+ @NonNull private final TimeDetectorStrategy mTimeDetectorStrategy;
+
+ private static TimeDetectorService create(@NonNull Context context) {
+ final TimeDetectorStrategy timeDetector = new SimpleTimeDetectorStrategy();
+ final TimeDetectorStrategyCallbackImpl callback =
+ new TimeDetectorStrategyCallbackImpl(context);
+ timeDetector.initialize(callback);
+
+ TimeDetectorService timeDetectorService =
+ new TimeDetectorService(context, callback, timeDetector);
+
+ // Wire up event listening.
+ ContentResolver contentResolver = context.getContentResolver();
+ contentResolver.registerContentObserver(
+ Settings.Global.getUriFor(Settings.Global.AUTO_TIME), true,
+ new ContentObserver(FgThread.getHandler()) {
+ public void onChange(boolean selfChange) {
+ timeDetectorService.handleAutoTimeDetectionToggle();
+ }
+ });
+
+ return timeDetectorService;
}
@VisibleForTesting
- public TimeDetectorService(@NonNull Context context,
+ public TimeDetectorService(@NonNull Context context, @NonNull Callback callback,
@NonNull TimeDetectorStrategy timeDetectorStrategy) {
mContext = Objects.requireNonNull(context);
+ mCallback = Objects.requireNonNull(callback);
mTimeDetectorStrategy = Objects.requireNonNull(timeDetectorStrategy);
}
@Override
public void suggestTime(@NonNull TimeSignal timeSignal) {
enforceSetTimePermission();
+ Objects.requireNonNull(timeSignal);
- long callerIdToken = Binder.clearCallingIdentity();
+ long idToken = Binder.clearCallingIdentity();
try {
- mTimeDetectorStrategy.suggestTime(timeSignal);
+ synchronized (mStrategyLock) {
+ mTimeDetectorStrategy.suggestTime(timeSignal);
+ }
} finally {
- Binder.restoreCallingIdentity(callerIdToken);
+ Binder.restoreCallingIdentity(idToken);
+ }
+ }
+
+ @VisibleForTesting
+ public void handleAutoTimeDetectionToggle() {
+ synchronized (mStrategyLock) {
+ final boolean timeDetectionEnabled = mCallback.isTimeDetectionEnabled();
+ mTimeDetectorStrategy.handleAutoTimeDetectionToggle(timeDetectionEnabled);
}
}
@@ -84,7 +123,9 @@
@Nullable String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
- mTimeDetectorStrategy.dump(fd, pw, args);
+ synchronized (mStrategyLock) {
+ mTimeDetectorStrategy.dump(pw, args);
+ }
}
private void enforceSetTimePermission() {
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java
index 5cb2eed..e050865 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java
@@ -19,26 +19,66 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.timedetector.TimeSignal;
+import android.content.Intent;
import android.util.TimestampedValue;
-import java.io.FileDescriptor;
import java.io.PrintWriter;
/**
* The interface for classes that implement the time detection algorithm used by the
- * TimeDetectorService.
+ * TimeDetectorService. The TimeDetectorService handles thread safety: all calls to implementations
+ * of this interface can be assumed to be single threaded (though the thread used may vary).
*
* @hide
*/
+// @NotThreadSafe
public interface TimeDetectorStrategy {
+ /**
+ * The interface used by the strategy to interact with the surrounding service.
+ */
interface Callback {
- void setTime(TimestampedValue<Long> time);
+
+ /**
+ * The absolute threshold below which the system clock need not be updated. i.e. if setting
+ * the system clock would adjust it by less than this (either backwards or forwards) then it
+ * need not be set.
+ */
+ int systemClockUpdateThresholdMillis();
+
+ /** Returns true if automatic time detection is enabled. */
+ boolean isTimeDetectionEnabled();
+
+ /** Acquire a suitable wake lock. Must be followed by {@link #releaseWakeLock()} */
+ void acquireWakeLock();
+
+ /** Returns the elapsedRealtimeMillis clock value. The WakeLock must be held. */
+ long elapsedRealtimeMillis();
+
+ /** Returns the system clock value. The WakeLock must be held. */
+ long systemClockMillis();
+
+ /** Sets the device system clock. The WakeLock must be held. */
+ void setSystemClock(long newTimeMillis);
+
+ /** Release the wake lock acquired by a call to {@link #acquireWakeLock()}. */
+ void releaseWakeLock();
+
+ /** Send the supplied intent as a stick broadcast. */
+ void sendStickyBroadcast(@NonNull Intent intent);
}
+ /** Initialize the strategy. */
void initialize(@NonNull Callback callback);
+
+ /** Process the suggested time. */
void suggestTime(@NonNull TimeSignal timeSignal);
- void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @Nullable String[] args);
+
+ /** Handle the auto-time setting being toggled on or off. */
+ void handleAutoTimeDetectionToggle(boolean enabled);
+
+ /** Dump debug information. */
+ void dump(@NonNull PrintWriter pw, @Nullable String[] args);
// Utility methods below are to be moved to a better home when one becomes more obvious.
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyCallbackImpl.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyCallbackImpl.java
index 568d73a..77b9e62 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyCallbackImpl.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyCallbackImpl.java
@@ -18,41 +18,108 @@
import android.annotation.NonNull;
import android.app.AlarmManager;
+import android.content.ContentResolver;
import android.content.Context;
+import android.content.Intent;
import android.os.PowerManager;
import android.os.SystemClock;
+import android.os.SystemProperties;
+import android.os.UserHandle;
+import android.provider.Settings;
import android.util.Slog;
-import android.util.TimestampedValue;
+
+import java.util.Objects;
/**
* The real implementation of {@link TimeDetectorStrategy.Callback} used on device.
*/
-public class TimeDetectorStrategyCallbackImpl implements TimeDetectorStrategy.Callback {
+public final class TimeDetectorStrategyCallbackImpl implements TimeDetectorStrategy.Callback {
private final static String TAG = "timedetector.TimeDetectorStrategyCallbackImpl";
- @NonNull private PowerManager.WakeLock mWakeLock;
- @NonNull private AlarmManager mAlarmManager;
+ private static final int SYSTEM_CLOCK_UPDATE_THRESHOLD_MILLIS_DEFAULT = 2 * 1000;
- public TimeDetectorStrategyCallbackImpl(Context context) {
+ /**
+ * If a newly calculated system clock time and the current system clock time differs by this or
+ * more the system clock will actually be updated. Used to prevent the system clock being set
+ * for only minor differences.
+ */
+ private final int mSystemClockUpdateThresholdMillis;
+
+ @NonNull private final Context mContext;
+ @NonNull private final ContentResolver mContentResolver;
+ @NonNull private final PowerManager.WakeLock mWakeLock;
+ @NonNull private final AlarmManager mAlarmManager;
+
+ public TimeDetectorStrategyCallbackImpl(@NonNull Context context) {
+ mContext = Objects.requireNonNull(context);
+ mContentResolver = Objects.requireNonNull(context.getContentResolver());
+
PowerManager powerManager = context.getSystemService(PowerManager.class);
+ mWakeLock = Objects.requireNonNull(
+ powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG));
- mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
+ mAlarmManager = Objects.requireNonNull(context.getSystemService(AlarmManager.class));
- mAlarmManager = context.getSystemService(AlarmManager.class);
+ mSystemClockUpdateThresholdMillis =
+ SystemProperties.getInt("ro.sys.time_detector_update_diff",
+ SYSTEM_CLOCK_UPDATE_THRESHOLD_MILLIS_DEFAULT);
}
@Override
- public void setTime(TimestampedValue<Long> time) {
- mWakeLock.acquire();
+ public int systemClockUpdateThresholdMillis() {
+ return mSystemClockUpdateThresholdMillis;
+ }
+
+ @Override
+ public boolean isTimeDetectionEnabled() {
try {
- long elapsedRealtimeMillis = SystemClock.elapsedRealtime();
- long currentTimeMillis = TimeDetectorStrategy.getTimeAt(time, elapsedRealtimeMillis);
- Slog.d(TAG, "Setting system clock using time=" + time
- + ", elapsedRealtimeMillis=" + elapsedRealtimeMillis);
- mAlarmManager.setTime(currentTimeMillis);
- } finally {
- mWakeLock.release();
+ return Settings.Global.getInt(mContentResolver, Settings.Global.AUTO_TIME) != 0;
+ } catch (Settings.SettingNotFoundException snfe) {
+ return true;
+ }
+ }
+
+ @Override
+ public void acquireWakeLock() {
+ if (mWakeLock.isHeld()) {
+ Slog.wtf(TAG, "WakeLock " + mWakeLock + " already held");
+ }
+ mWakeLock.acquire();
+ }
+
+ @Override
+ public long elapsedRealtimeMillis() {
+ checkWakeLockHeld();
+ return SystemClock.elapsedRealtime();
+ }
+
+ @Override
+ public long systemClockMillis() {
+ checkWakeLockHeld();
+ return System.currentTimeMillis();
+ }
+
+ @Override
+ public void setSystemClock(long newTimeMillis) {
+ checkWakeLockHeld();
+ mAlarmManager.setTime(newTimeMillis);
+ }
+
+ @Override
+ public void releaseWakeLock() {
+ checkWakeLockHeld();
+ mWakeLock.release();
+ }
+
+ @Override
+ public void sendStickyBroadcast(@NonNull Intent intent) {
+ mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
+ }
+
+ private void checkWakeLockHeld() {
+ if (!mWakeLock.isHeld()) {
+ Slog.wtf(TAG, "WakeLock " + mWakeLock + " not held");
}
}
}
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
new file mode 100644
index 0000000..5f71b0b
--- /dev/null
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
@@ -0,0 +1,67 @@
+/*
+ * 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.timezonedetector;
+
+import com.android.internal.util.DumpUtils;
+import com.android.server.SystemService;
+import android.app.timezonedetector.ITimeZoneDetectorService;
+import android.content.Context;
+import android.util.Slog;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub {
+ private static final String TAG = "timezonedetector.TimeZoneDetectorService";
+
+ public static class Lifecycle extends SystemService {
+
+ public Lifecycle(Context context) {
+ super(context);
+ }
+
+ @Override
+ public void onStart() {
+ TimeZoneDetectorService service = TimeZoneDetectorService.create(getContext());
+ // Publish the binder service so it can be accessed from other (appropriately
+ // permissioned) processes.
+ publishBinderService(Context.TIME_ZONE_DETECTOR_SERVICE, service);
+ }
+ }
+
+ private final Context mContext;
+
+ private static TimeZoneDetectorService create(Context context) {
+ return new TimeZoneDetectorService(context);
+ }
+
+ public TimeZoneDetectorService(Context context) {
+ mContext = context;
+ }
+
+ @Override
+ public void stubbedCall() {
+ // Empty call for initial tests.
+ Slog.d(TAG, "stubbedCall() called");
+ // TODO: Remove when there are real methods.
+ }
+
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
+ // TODO: Implement when there is state.
+ }
+}
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index 4413666..8a135b8 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -20,6 +20,7 @@
import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.admin.DevicePolicyManager;
+import android.hardware.biometrics.BiometricSourceType;
import android.app.trust.ITrustListener;
import android.app.trust.ITrustManager;
import android.content.BroadcastReceiver;
@@ -130,8 +131,8 @@
private final SparseBooleanArray mTrustUsuallyManagedForUser = new SparseBooleanArray();
// set to true only if user can skip bouncer
- @GuardedBy("mUsersUnlockedByFingerprint")
- private final SparseBooleanArray mUsersUnlockedByFingerprint = new SparseBooleanArray();
+ @GuardedBy("mUsersUnlockedByBiometric")
+ private final SparseBooleanArray mUsersUnlockedByBiometric = new SparseBooleanArray();
private final StrongAuthTracker mStrongAuthTracker;
@@ -440,11 +441,11 @@
boolean secure = mLockPatternUtils.isSecure(id);
boolean trusted = aggregateIsTrusted(id);
boolean showingKeyguard = true;
- boolean fingerprintAuthenticated = false;
+ boolean biometricAuthenticated = false;
if (mCurrentUser == id) {
- synchronized(mUsersUnlockedByFingerprint) {
- fingerprintAuthenticated = mUsersUnlockedByFingerprint.get(id, false);
+ synchronized(mUsersUnlockedByBiometric) {
+ biometricAuthenticated = mUsersUnlockedByBiometric.get(id, false);
}
try {
showingKeyguard = wm.isKeyguardLocked();
@@ -452,7 +453,7 @@
}
}
boolean deviceLocked = secure && showingKeyguard && !trusted &&
- !fingerprintAuthenticated;
+ !biometricAuthenticated;
setDeviceLockedForUser(id, deviceLocked);
}
}
@@ -1021,20 +1022,20 @@
}
@Override
- public void unlockedByFingerprintForUser(int userId) {
+ public void unlockedByBiometricForUser(int userId, BiometricSourceType biometricSource) {
enforceReportPermission();
- synchronized(mUsersUnlockedByFingerprint) {
- mUsersUnlockedByFingerprint.put(userId, true);
+ synchronized(mUsersUnlockedByBiometric) {
+ mUsersUnlockedByBiometric.put(userId, true);
}
mHandler.obtainMessage(MSG_REFRESH_DEVICE_LOCKED_FOR_USER, userId,
0 /* arg2 */).sendToTarget();
}
@Override
- public void clearAllFingerprints() {
+ public void clearAllBiometricRecognized(BiometricSourceType biometricSource) {
enforceReportPermission();
- synchronized(mUsersUnlockedByFingerprint) {
- mUsersUnlockedByFingerprint.clear();
+ synchronized(mUsersUnlockedByBiometric) {
+ mUsersUnlockedByBiometric.clear();
}
mHandler.obtainMessage(MSG_REFRESH_DEVICE_LOCKED_FOR_USER, UserHandle.USER_ALL,
0 /* arg2 */).sendToTarget();
@@ -1188,8 +1189,8 @@
synchronized (mTrustUsuallyManagedForUser) {
mTrustUsuallyManagedForUser.delete(userId);
}
- synchronized (mUsersUnlockedByFingerprint) {
- mUsersUnlockedByFingerprint.delete(userId);
+ synchronized (mUsersUnlockedByBiometric) {
+ mUsersUnlockedByBiometric.delete(userId);
}
refreshAgentList(userId);
refreshDeviceLockedForUser(userId);
diff --git a/services/core/java/com/android/server/vr/Vr2dDisplay.java b/services/core/java/com/android/server/vr/Vr2dDisplay.java
index 9183b08..a16dbb7 100644
--- a/services/core/java/com/android/server/vr/Vr2dDisplay.java
+++ b/services/core/java/com/android/server/vr/Vr2dDisplay.java
@@ -3,9 +3,7 @@
import static android.view.Display.INVALID_DISPLAY;
import android.app.ActivityManagerInternal;
-import android.app.ActivityTaskManagerInternal;
import android.app.Vr2dDisplayProperties;
-import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -14,19 +12,15 @@
import android.hardware.display.DisplayManager;
import android.hardware.display.VirtualDisplay;
import android.media.ImageReader;
-import android.os.Build;
import android.os.Handler;
-import android.os.IBinder;
-import android.os.Message;
import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.SystemProperties;
import android.service.vr.IPersistentVrStateCallbacks;
import android.service.vr.IVrManager;
import android.util.Log;
import android.view.Surface;
import com.android.server.LocalServices;
+import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.WindowManagerInternal;
/**
diff --git a/services/core/java/com/android/server/vr/VrManagerService.java b/services/core/java/com/android/server/vr/VrManagerService.java
index 50bf57f..5c45afc 100644
--- a/services/core/java/com/android/server/vr/VrManagerService.java
+++ b/services/core/java/com/android/server/vr/VrManagerService.java
@@ -18,15 +18,13 @@
import static android.view.Display.INVALID_DISPLAY;
import android.Manifest;
-import android.app.ActivityManagerInternal;
-import android.app.ActivityTaskManagerInternal;
-import android.app.ActivityTaskManagerInternal.ScreenObserver;
+import android.annotation.NonNull;
import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
import android.app.AppOpsManager;
import android.app.INotificationManager;
-import android.app.Vr2dDisplayProperties;
import android.app.NotificationManager;
-import android.annotation.NonNull;
+import android.app.Vr2dDisplayProperties;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentResolver;
@@ -60,30 +58,30 @@
import android.util.ArraySet;
import android.util.Slog;
import android.util.SparseArray;
-
-import com.android.server.FgThread;
-import com.android.server.wm.WindowManagerInternal;
import android.view.inputmethod.InputMethodManagerInternal;
import com.android.internal.R;
import com.android.internal.util.DumpUtils;
+import com.android.server.FgThread;
import com.android.server.LocalServices;
import com.android.server.SystemConfig;
import com.android.server.SystemService;
-import com.android.server.utils.ManagedApplicationService.PendingEvent;
-import com.android.server.utils.ManagedApplicationService.LogEvent;
-import com.android.server.utils.ManagedApplicationService.LogFormattable;
-import com.android.server.vr.EnabledComponentsObserver.EnabledComponentChangeListener;
import com.android.server.utils.ManagedApplicationService;
import com.android.server.utils.ManagedApplicationService.BinderChecker;
+import com.android.server.utils.ManagedApplicationService.LogEvent;
+import com.android.server.utils.ManagedApplicationService.LogFormattable;
+import com.android.server.utils.ManagedApplicationService.PendingEvent;
+import com.android.server.vr.EnabledComponentsObserver.EnabledComponentChangeListener;
+import com.android.server.wm.ActivityTaskManagerInternal;
+import com.android.server.wm.ActivityTaskManagerInternal.ScreenObserver;
+import com.android.server.wm.WindowManagerInternal;
import java.io.FileDescriptor;
import java.io.PrintWriter;
-import java.lang.StringBuilder;
import java.text.SimpleDateFormat;
-import java.util.Arrays;
import java.util.ArrayDeque;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.List;
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 547ab0e..9d68c63 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -51,7 +51,6 @@
import android.content.pm.ServiceInfo;
import android.content.pm.UserInfo;
import android.content.res.Resources;
-import android.database.ContentObserver;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.BitmapRegionDecoder;
@@ -76,7 +75,6 @@
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
-import android.provider.Settings;
import android.service.wallpaper.IWallpaperConnection;
import android.service.wallpaper.IWallpaperEngine;
import android.service.wallpaper.IWallpaperService;
@@ -338,102 +336,6 @@
}
}
- /**
- * Observes changes of theme settings. It will check whether to call
- * notifyWallpaperColorsChanged by the current theme and updated theme.
- * The light theme and dark theme are controlled by the hint values in Wallpaper colors,
- * threrfore, if light theme mode is chosen, HINT_SUPPORTS_DARK_THEME in hint will be
- * removed and then notify listeners.
- */
- private class ThemeSettingsObserver extends ContentObserver {
-
- public ThemeSettingsObserver(Handler handler) {
- super(handler);
- }
-
- public void startObserving(Context context) {
- context.getContentResolver().registerContentObserver(
- Settings.Secure.getUriFor(Settings.Secure.THEME_MODE),
- false,
- this);
- }
-
- public void stopObserving(Context context) {
- context.getContentResolver().unregisterContentObserver(this);
- }
-
- @Override
- public void onChange(boolean selfChange) {
- onThemeSettingsChanged();
- }
- }
-
- /**
- * Check whether to call notifyWallpaperColorsChanged. Assumed that the theme mode
- * was wallpaper theme mode and dark wallpaper was set, therefoe, the theme was dark.
- * Then theme mode changing to dark theme mode, however, theme should not update since
- * theme was dark already.
- */
- private boolean needUpdateLocked(WallpaperColors colors, int themeMode) {
- if (colors == null) {
- return false;
- }
-
- if (themeMode == mThemeMode) {
- return false;
- }
-
- boolean result = true;
- boolean supportDarkTheme =
- (colors.getColorHints() & WallpaperColors.HINT_SUPPORTS_DARK_THEME) != 0;
- switch (themeMode) {
- case Settings.Secure.THEME_MODE_WALLPAPER:
- if (mThemeMode == Settings.Secure.THEME_MODE_LIGHT) {
- result = supportDarkTheme;
- } else {
- result = !supportDarkTheme;
- }
- break;
- case Settings.Secure.THEME_MODE_LIGHT:
- if (mThemeMode == Settings.Secure.THEME_MODE_WALLPAPER) {
- result = supportDarkTheme;
- }
- break;
- case Settings.Secure.THEME_MODE_DARK:
- if (mThemeMode == Settings.Secure.THEME_MODE_WALLPAPER) {
- result = !supportDarkTheme;
- }
- break;
- default:
- Slog.w(TAG, "unkonwn theme mode " + themeMode);
- return false;
- }
- mThemeMode = themeMode;
- return result;
- }
-
- void onThemeSettingsChanged() {
- WallpaperData wallpaper;
- synchronized (mLock) {
- wallpaper = mWallpaperMap.get(mCurrentUserId);
- int updatedThemeMode = Settings.Secure.getInt(
- mContext.getContentResolver(), Settings.Secure.THEME_MODE,
- Settings.Secure.THEME_MODE_WALLPAPER);
-
- if (DEBUG) {
- Slog.v(TAG, "onThemeSettingsChanged, mode = " + updatedThemeMode);
- }
-
- if (!needUpdateLocked(wallpaper.primaryColors, updatedThemeMode)) {
- return;
- }
- }
-
- if (wallpaper != null) {
- notifyWallpaperColorsChanged(wallpaper, FLAG_SYSTEM);
- }
- }
-
void notifyLockWallpaperChanged() {
final IWallpaperManagerCallback cb = mKeyguardListener;
if (cb != null) {
@@ -511,7 +413,6 @@
}
userAllColorListeners.finishBroadcast();
}
- wallpaperColors = getThemeColorsLocked(wallpaperColors);
}
final int count = colorListeners.size();
@@ -580,40 +481,6 @@
}
/**
- * We can easily change theme by modified colors hint. This function will check
- * current theme mode and return the WallpaperColors fit current theme mode.
- * If color need modified, it will return a copied WallpaperColors which
- * its ColorsHint is modified to fit current theme mode.
- *
- * @param colors a wallpaper primary colors representation
- */
- private WallpaperColors getThemeColorsLocked(WallpaperColors colors) {
- if (colors == null) {
- Slog.w(TAG, "Cannot get theme colors because WallpaperColors is null.");
- return null;
- }
-
- int colorHints = colors.getColorHints();
- boolean supportDarkTheme = (colorHints & WallpaperColors.HINT_SUPPORTS_DARK_THEME) != 0;
- if (mThemeMode == Settings.Secure.THEME_MODE_WALLPAPER ||
- (mThemeMode == Settings.Secure.THEME_MODE_LIGHT && !supportDarkTheme) ||
- (mThemeMode == Settings.Secure.THEME_MODE_DARK && supportDarkTheme)) {
- return colors;
- }
-
- WallpaperColors themeColors = new WallpaperColors(colors.getPrimaryColor(),
- colors.getSecondaryColor(), colors.getTertiaryColor());
-
- if (mThemeMode == Settings.Secure.THEME_MODE_LIGHT) {
- colorHints &= ~WallpaperColors.HINT_SUPPORTS_DARK_THEME;
- } else if (mThemeMode == Settings.Secure.THEME_MODE_DARK) {
- colorHints |= WallpaperColors.HINT_SUPPORTS_DARK_THEME;
- }
- themeColors.setColorHints(colorHints);
- return themeColors;
- }
-
- /**
* Once a new wallpaper has been written via setWallpaper(...), it needs to be cropped
* for display.
*/
@@ -809,7 +676,6 @@
final SparseArray<Boolean> mUserRestorecon = new SparseArray<Boolean>();
int mCurrentUserId = UserHandle.USER_NULL;
boolean mInAmbientMode;
- int mThemeMode;
static class WallpaperData {
@@ -868,7 +734,6 @@
long lastDiedTime;
boolean wallpaperUpdating;
WallpaperObserver wallpaperObserver;
- ThemeSettingsObserver themeSettingsObserver;
/**
* List of callbacks registered they should each be notified when the wallpaper is changed.
@@ -1414,10 +1279,6 @@
wallpaper.wallpaperObserver.stopWatching();
wallpaper.wallpaperObserver = null;
}
- if (wallpaper.themeSettingsObserver != null) {
- wallpaper.themeSettingsObserver.stopObserving(mContext);
- wallpaper.themeSettingsObserver = null;
- }
}
}
@@ -1501,13 +1362,6 @@
systemWallpaper.wallpaperObserver = new WallpaperObserver(systemWallpaper);
systemWallpaper.wallpaperObserver.startWatching();
}
- if (systemWallpaper.themeSettingsObserver == null) {
- systemWallpaper.themeSettingsObserver = new ThemeSettingsObserver(null);
- systemWallpaper.themeSettingsObserver.startObserving(mContext);
- }
- mThemeMode = Settings.Secure.getInt(
- mContext.getContentResolver(), Settings.Secure.THEME_MODE,
- Settings.Secure.THEME_MODE_WALLPAPER);
switchWallpaper(systemWallpaper, reply);
}
@@ -1981,7 +1835,7 @@
}
synchronized (mLock) {
- return getThemeColorsLocked(wallpaperData.primaryColors);
+ return wallpaperData.primaryColors;
}
}
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index a6ec3cf..f0898c0 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -549,7 +549,8 @@
touchableRegion.getBounds(touchableFrame);
RectF windowFrame = mTempRectF;
windowFrame.set(touchableFrame);
- windowFrame.offset(-windowState.mFrame.left, -windowState.mFrame.top);
+ windowFrame.offset(-windowState.getFrameLw().left,
+ -windowState.getFrameLw().top);
matrix.mapRect(windowFrame);
Region windowBounds = mTempRegion2;
windowBounds.set((int) windowFrame.left, (int) windowFrame.top,
@@ -1222,7 +1223,7 @@
// Move to origin as all transforms are captured by the matrix.
RectF windowFrame = mTempRectF;
windowFrame.set(touchableFrame);
- windowFrame.offset(-windowState.mFrame.left, -windowState.mFrame.top);
+ windowFrame.offset(-windowState.getFrameLw().left, -windowState.getFrameLw().top);
// Map the frame to get what appears on the screen.
Matrix matrix = mTempMatrix;
diff --git a/core/java/android/app/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
similarity index 82%
rename from core/java/android/app/ActivityTaskManagerInternal.java
rename to services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index 170cb52..bfecd9d 100644
--- a/core/java/android/app/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -14,10 +14,13 @@
* limitations under the License
*/
-package android.app;
+package com.android.server.wm;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.AppProtoEnums;
+import android.app.IActivityManager;
+import android.app.IApplicationThread;
import android.content.ComponentName;
import android.content.Intent;
import android.os.Bundle;
@@ -28,6 +31,7 @@
import android.view.RemoteAnimationAdapter;
import com.android.internal.app.IVoiceInteractor;
+import com.android.server.am.WindowProcessController;
import java.util.List;
@@ -210,11 +214,6 @@
*/
public abstract void setFocusedActivity(IBinder token);
- /**
- * Returns {@code true} if {@code uid} is running an activity from {@code packageName}.
- */
- public abstract boolean hasRunningActivity(int uid, @Nullable String packageName);
-
public abstract void registerScreenObserver(ScreenObserver observer);
/**
@@ -243,4 +242,36 @@
*/
public abstract void notifyActiveVoiceInteractionServiceChanged(ComponentName component);
+ /**
+ * Set a uid that is allowed to bypass stopped app switches, launching an app
+ * whenever it wants.
+ *
+ * @param type Type of the caller -- unique string the caller supplies to identify itself
+ * and disambiguate with other calles.
+ * @param uid The uid of the app to be allowed, or -1 to clear the uid for this type.
+ * @param userId The user it is allowed for.
+ */
+ public abstract void setAllowAppSwitches(@NonNull String type, int uid, int userId);
+
+ /**
+ * Called when a user has been deleted. This can happen during normal device usage
+ * or just at startup, when partially removed users are purged. Any state persisted by the
+ * ActivityManager should be purged now.
+ *
+ * @param userId The user being cleaned up.
+ */
+ public abstract void onUserStopped(int userId);
+ public abstract boolean isGetTasksAllowed(String caller, int callingPid, int callingUid);
+
+ public abstract void onProcessAdded(WindowProcessController proc);
+ public abstract void onProcessRemoved(String name, int uid);
+ public abstract void onCleanUpApplicationRecord(WindowProcessController proc);
+ public abstract int getTopProcessState();
+
+ public abstract boolean isSleeping();
+ public abstract boolean isShuttingDown();
+ public abstract boolean shuttingDown(boolean booted, int timeout);
+ public abstract void enableScreenAfterBoot(boolean booted);
+ public abstract boolean showStrictModeViolationDialog();
+ public abstract void showSystemReadyErrorDialogsIfNeeded();
}
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index fa6079c..d9ddf9f 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -1523,7 +1523,7 @@
if (mLetterbox == null) {
mLetterbox = new Letterbox(() -> makeChildSurface(null));
}
- mLetterbox.layout(getParent().getBounds(), w.mFrame);
+ mLetterbox.layout(getParent().getBounds(), w.getFrameLw());
} else if (mLetterbox != null) {
mLetterbox.hide();
}
@@ -1808,7 +1808,7 @@
// won't exactly match the final freeform window frame (e.g. when overlapping with
// the status bar). In that case we need to use the final frame.
if (freeform) {
- frame.set(win.mFrame);
+ frame.set(win.getFrameLw());
} else if (win.isLetterboxedAppWindow()) {
frame.set(getTask().getBounds());
} else if (win.isDockedResizing()) {
@@ -1816,7 +1816,7 @@
// animation target (which will be different than the task bounds)
frame.set(getTask().getParent().getBounds());
} else {
- frame.set(win.mContainingFrame);
+ frame.set(win.getContainingFrame());
}
surfaceInsets = win.getAttrs().surfaceInsets;
// XXX(b/72757033): These are insets relative to the window frame, but we're really
@@ -2022,7 +2022,7 @@
if (win == null) {
return;
}
- final Rect frame = win.mFrame;
+ final Rect frame = win.getFrameLw();
final int thumbnailDrawableRes = getTask().mUserId == mService.mCurrentUserId
? R.drawable.ic_account_circle
: R.drawable.ic_corp_badge;
@@ -2034,7 +2034,8 @@
}
mThumbnail = new AppWindowThumbnail(getPendingTransaction(), this, thumbnail);
final Animation animation =
- mService.mAppTransition.createCrossProfileAppsThumbnailAnimationLocked(win.mFrame);
+ mService.mAppTransition.createCrossProfileAppsThumbnailAnimationLocked(
+ win.getFrameLw());
mThumbnail.startAnimation(getPendingTransaction(), animation, new Point(frame.left,
frame.top));
}
diff --git a/services/core/java/com/android/server/wm/BlackFrame.java b/services/core/java/com/android/server/wm/BlackFrame.java
index 1977e12..fff1fa4 100644
--- a/services/core/java/com/android/server/wm/BlackFrame.java
+++ b/services/core/java/com/android/server/wm/BlackFrame.java
@@ -56,6 +56,7 @@
.setParent(null) // TODO: Work-around for b/69259549
.build();
+ transaction.setLayerStack(surface, dc.getDisplayId());
transaction.setAlpha(surface, 1);
transaction.setLayer(surface, layer);
transaction.show(surface);
diff --git a/services/core/java/com/android/server/wm/BoundsAnimationController.java b/services/core/java/com/android/server/wm/BoundsAnimationController.java
index 0a49c13..a693071 100644
--- a/services/core/java/com/android/server/wm/BoundsAnimationController.java
+++ b/services/core/java/com/android/server/wm/BoundsAnimationController.java
@@ -31,11 +31,13 @@
import android.os.Debug;
import android.util.ArrayMap;
import android.util.Slog;
+import android.view.Choreographer;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -112,6 +114,7 @@
private final Interpolator mFastOutSlowInInterpolator;
private boolean mFinishAnimationAfterTransition = false;
private final AnimationHandler mAnimationHandler;
+ private Choreographer mChoreographer;
private static final int WAIT_FOR_DRAW_TIMEOUT_MS = 3000;
@@ -123,6 +126,12 @@
mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
com.android.internal.R.interpolator.fast_out_slow_in);
mAnimationHandler = animationHandler;
+ if (animationHandler != null) {
+ // If an animation handler is provided, then ensure that it runs on the sf vsync tick
+ handler.runWithScissors(() -> mChoreographer = Choreographer.getSfInstance(),
+ 0 /* timeout */);
+ animationHandler.setProvider(new SfVsyncFrameCallbackProvider(mChoreographer));
+ }
}
@VisibleForTesting
@@ -196,12 +205,6 @@
@Override
public void onAnimationStart(Animator animation) {
- if (!mTarget.isAttached()) {
- // No point of trying to animate something that isn't attached to the hierarchy
- // anymore.
- cancel();
- }
-
if (DEBUG) Slog.d(TAG, "onAnimationStart: mTarget=" + mTarget
+ " mPrevSchedulePipModeChangedState=" + mPrevSchedulePipModeChangedState
+ " mSchedulePipModeChangedState=" + mSchedulePipModeChangedState);
@@ -216,13 +219,15 @@
// Ensure that we have prepared the target for animation before we trigger any size
// changes, so it can swap surfaces in to appropriate modes, or do as it wishes
// otherwise.
+ boolean continueAnimation;
if (mPrevSchedulePipModeChangedState == NO_PIP_MODE_CHANGED_CALLBACKS) {
- mTarget.onAnimationStart(mSchedulePipModeChangedState ==
+ continueAnimation = mTarget.onAnimationStart(mSchedulePipModeChangedState ==
SCHEDULE_PIP_MODE_CHANGED_ON_START, false /* forceUpdate */);
// When starting an animation from fullscreen, pause here and wait for the
// windows-drawn signal before we start the rest of the transition down into PiP.
- if (mMoveFromFullscreen && mTarget.shouldDeferStartOnMoveToFullscreen()) {
+ if (continueAnimation && mMoveFromFullscreen
+ && mTarget.shouldDeferStartOnMoveToFullscreen()) {
pause();
}
} else if (mPrevSchedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_END &&
@@ -231,8 +236,19 @@
// client will not currently receive any picture-in-picture mode change callbacks.
// However, we still need to report to them that they are leaving PiP, so this will
// force an update via a mode changed callback.
- mTarget.onAnimationStart(true /* schedulePipModeChangedCallback */,
- true /* forceUpdate */);
+ continueAnimation = mTarget.onAnimationStart(
+ true /* schedulePipModeChangedCallback */, true /* forceUpdate */);
+ } else {
+ // The animation is already running, but we should check that the TaskStack is still
+ // valid before continuing with the animation
+ continueAnimation = mTarget.isAttached();
+ }
+
+ if (!continueAnimation) {
+ // No point of trying to animate something that isn't attached to the hierarchy
+ // anymore.
+ cancel();
+ return;
}
// Immediately update the task bounds if they have to become larger, but preserve
@@ -354,6 +370,9 @@
if (DEBUG) Slog.d(TAG, "cancel: mTarget=" + mTarget);
mSkipAnimationEnd = true;
super.cancel();
+
+ // Reset the thread priority of the animation thread if the bounds animation is canceled
+ updateBooster();
}
/**
diff --git a/services/core/java/com/android/server/wm/BoundsAnimationTarget.java b/services/core/java/com/android/server/wm/BoundsAnimationTarget.java
index d66b42f..5cb80de 100644
--- a/services/core/java/com/android/server/wm/BoundsAnimationTarget.java
+++ b/services/core/java/com/android/server/wm/BoundsAnimationTarget.java
@@ -30,8 +30,9 @@
*
* @param schedulePipModeChangedCallback whether or not to schedule the PiP mode changed
* callbacks
+ * @return whether to continue the animation
*/
- void onAnimationStart(boolean schedulePipModeChangedCallback, boolean forceUpdate);
+ boolean onAnimationStart(boolean schedulePipModeChangedCallback, boolean forceUpdate);
/**
* @return Whether the animation should be paused waiting for the windows to draw before
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 0cbf8a7..fa51f80 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -303,6 +303,7 @@
// Accessed directly by all users.
private boolean mLayoutNeeded;
int pendingLayoutChanges;
+ int mDeferredRotationPauseCount;
// TODO(multi-display): remove some of the usages.
boolean isDefaultDisplay;
/**
@@ -578,9 +579,9 @@
w.mAppToken.layoutLetterbox(w);
}
- if (DEBUG_LAYOUT) Slog.v(TAG, " LAYOUT: mFrame=" + w.mFrame
- + " mContainingFrame=" + w.mContainingFrame
- + " mDisplayFrame=" + w.mDisplayFrame);
+ if (DEBUG_LAYOUT) Slog.v(TAG, " LAYOUT: mFrame=" + w.getFrameLw()
+ + " mContainingFrame=" + w.getContainingFrame()
+ + " mDisplayFrame=" + w.getDisplayFrameLw());
}
}
};
@@ -606,9 +607,9 @@
w.prelayout();
mService.mPolicy.layoutWindowLw(w, w.getParentWindow(), mDisplayFrames);
w.mLayoutSeq = mLayoutSeq;
- if (DEBUG_LAYOUT) Slog.v(TAG, " LAYOUT: mFrame=" + w.mFrame
- + " mContainingFrame=" + w.mContainingFrame
- + " mDisplayFrame=" + w.mDisplayFrame);
+ if (DEBUG_LAYOUT) Slog.v(TAG, " LAYOUT: mFrame=" + w.getFrameLw()
+ + " mContainingFrame=" + w.getContainingFrame()
+ + " mDisplayFrame=" + w.getDisplayFrameLw());
}
} else if (w.mAttrs.type == TYPE_DREAM) {
// Don't layout windows behind a dream, so that if it does stuff like hide the
@@ -942,6 +943,36 @@
}
/**
+ * Temporarily pauses rotation changes until resumed.
+ *
+ * This can be used to prevent rotation changes from occurring while the user is
+ * performing certain operations, such as drag and drop.
+ *
+ * This call nests and must be matched by an equal number of calls to
+ * {@link #resumeRotationLocked}.
+ */
+ void pauseRotationLocked() {
+ mDeferredRotationPauseCount++;
+ }
+
+ /**
+ * Resumes normal rotation changes after being paused.
+ */
+ void resumeRotationLocked() {
+ if (mDeferredRotationPauseCount <= 0) {
+ return;
+ }
+
+ mDeferredRotationPauseCount--;
+ if (mDeferredRotationPauseCount == 0) {
+ final boolean changed = updateRotationUnchecked();
+ if (changed) {
+ mService.mH.obtainMessage(SEND_NEW_CONFIGURATION, mDisplayId).sendToTarget();
+ }
+ }
+ }
+
+ /**
* Update rotation of the display.
*
* @return {@code true} if the rotation has been changed. In this case YOU MUST CALL
@@ -964,7 +995,7 @@
boolean updateRotationUnchecked(boolean forceUpdate) {
ScreenRotationAnimation screenRotationAnimation;
if (!forceUpdate) {
- if (mService.mDeferredRotationPauseCount > 0) {
+ if (mDeferredRotationPauseCount > 0) {
// Rotation updates have been paused temporarily. Defer the update until
// updates have been resumed.
if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Deferring rotation, rotation is paused.");
@@ -1065,9 +1096,8 @@
}
mService.mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_ACTIVE;
- mService.mH.removeMessages(WindowManagerService.H.WINDOW_FREEZE_TIMEOUT);
- mService.mH.sendEmptyMessageDelayed(WindowManagerService.H.WINDOW_FREEZE_TIMEOUT,
- WINDOW_FREEZE_TIMEOUT_DURATION);
+ mService.mH.sendNewMessageDelayed(WindowManagerService.H.WINDOW_FREEZE_TIMEOUT,
+ this, WINDOW_FREEZE_TIMEOUT_DURATION);
setLayoutNeeded();
final int[] anim = new int[2];
@@ -1106,6 +1136,11 @@
}
}
+ forAllWindows(w -> {
+ w.forceSeamlesslyRotateIfAllowed(oldRotation, rotation);
+ }, true /* traverseTopToBottom */);
+
+ // TODO(b/111504081): Consolidate seamless rotation logic.
if (rotateSeamlessly) {
seamlesslyRotate(getPendingTransaction(), oldRotation, rotation);
}
@@ -1124,9 +1159,8 @@
}, true /* traverseTopToBottom */);
if (rotateSeamlessly) {
- mService.mH.removeMessages(WindowManagerService.H.SEAMLESS_ROTATION_TIMEOUT);
- mService.mH.sendEmptyMessageDelayed(WindowManagerService.H.SEAMLESS_ROTATION_TIMEOUT,
- SEAMLESS_ROTATION_TIMEOUT_DURATION);
+ mService.mH.sendNewMessageDelayed(WindowManagerService.H.SEAMLESS_ROTATION_TIMEOUT,
+ this, SEAMLESS_ROTATION_TIMEOUT_DURATION);
}
for (int i = mService.mRotationWatchers.size() - 1; i >= 0; i--) {
@@ -1249,11 +1283,21 @@
cutout, mInitialDisplayWidth, mInitialDisplayHeight);
}
final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270);
- final Path bounds = cutout.getBounds().getBoundaryPath();
+ final List<Rect> bounds = WmDisplayCutout.computeSafeInsets(
+ cutout, mInitialDisplayWidth, mInitialDisplayHeight)
+ .getDisplayCutout().getBoundingRects();
transformPhysicalToLogicalCoordinates(rotation, mInitialDisplayWidth, mInitialDisplayHeight,
mTmpMatrix);
- bounds.transform(mTmpMatrix);
- return WmDisplayCutout.computeSafeInsets(DisplayCutout.fromBounds(bounds),
+ final Region region = Region.obtain();
+ for (int i = 0; i < bounds.size(); i++) {
+ final Rect rect = bounds.get(i);
+ final RectF rectF = new RectF(bounds.get(i));
+ mTmpMatrix.mapRect(rectF);
+ rectF.round(rect);
+ region.op(rect, Op.UNION);
+ }
+
+ return WmDisplayCutout.computeSafeInsets(DisplayCutout.fromBounds(region),
rotated ? mInitialDisplayHeight : mInitialDisplayWidth,
rotated ? mInitialDisplayWidth : mInitialDisplayHeight);
}
@@ -2272,6 +2316,8 @@
pw.println();
pw.print(prefix); pw.print("mLayoutSeq="); pw.println(mLayoutSeq);
+ pw.print(prefix);
+ pw.print("mDeferredRotationPauseCount="); pw.println(mDeferredRotationPauseCount);
pw.println();
pw.println(prefix + "Application tokens in top down Z order:");
@@ -2507,11 +2553,12 @@
if (DEBUG_INPUT_METHOD && updateImeTarget) Slog.v(TAG_WM,
"Proposed new IME target: " + target);
- // Now, a special case -- if the last target's window is in the process of exiting, and the
- // new target is home, keep on the last target to avoid flicker. Home is a special case
- // since its above other stacks in the ordering list, but layed out below the others.
- if (curTarget != null && curTarget.isDisplayedLw() && curTarget.isClosing()
- && (target == null || target.isActivityTypeHome())) {
+ // Now, a special case -- if the last target's window is in the process of exiting, but
+ // not removed, and the new target is home, keep on the last target to avoid flicker.
+ // Home is a special case since its above other stacks in the ordering list, but layed
+ // out below the others.
+ if (curTarget != null && !curTarget.mRemoved && curTarget.isDisplayedLw()
+ && curTarget.isClosing() && (target == null || target.isActivityTypeHome())) {
if (DEBUG_INPUT_METHOD) Slog.v(TAG_WM, "New target is home while current target is "
+ "closing, not changing");
return curTarget;
@@ -2865,7 +2912,7 @@
mWallpaperController.adjustWallpaperWindows(this);
}
- if (isDefaultDisplay && (pendingLayoutChanges & FINISH_LAYOUT_REDO_CONFIG) != 0) {
+ if ((pendingLayoutChanges & FINISH_LAYOUT_REDO_CONFIG) != 0) {
if (DEBUG_LAYOUT) Slog.v(TAG, "Computing new config from layout");
if (mService.updateOrientationFromAppTokensLocked(mDisplayId)) {
setLayoutNeeded();
@@ -3427,50 +3474,53 @@
* @return The proper position for the stack.
*/
private int findPositionForStack(int requestedPosition, TaskStack stack, boolean adding) {
- final int topChildPosition = mChildren.size() - 1;
- boolean toTop = requestedPosition == POSITION_TOP;
- toTop |= adding ? requestedPosition >= topChildPosition + 1
- : requestedPosition >= topChildPosition;
-
if (stack.inPinnedWindowingMode()) {
- // Stack in pinned windowing mode is z-ordered on-top of all other stacks so okay to
- // just return the candidate position.
- return requestedPosition;
+ return POSITION_TOP;
}
- // We might call mChildren.get() with targetPosition below, but targetPosition might be
- // POSITION_TOP (INTEGER_MAX). We need to adjust the value to the actual index in the
- // array.
- int targetPosition = toTop ? topChildPosition : requestedPosition;
- // Note that the index we should return varies depending on the value of adding.
- // When we're adding a new stack the index is the current target position.
- // When we're positioning an existing stack the index is the position below the target
- // stack, because WindowContainer#positionAt() first removes element and then adds
- // it to specified place.
- if (toTop && adding) {
+ final int topChildPosition = mChildren.size() - 1;
+ int belowAlwaysOnTopPosition = POSITION_BOTTOM;
+ for (int i = topChildPosition; i >= 0; --i) {
+ if (getStacks().get(i) != stack && !getStacks().get(i).isAlwaysOnTop()) {
+ belowAlwaysOnTopPosition = i;
+ break;
+ }
+ }
+
+ // The max possible position we can insert the stack at.
+ int maxPosition = POSITION_TOP;
+ // The min possible position we can insert the stack at.
+ int minPosition = POSITION_BOTTOM;
+
+ if (stack.isAlwaysOnTop()) {
+ if (hasPinnedStack()) {
+ // Always-on-top stacks go below the pinned stack.
+ maxPosition = getStacks().indexOf(mPinnedStack) - 1;
+ }
+ // Always-on-top stacks need to be above all other stacks.
+ minPosition = belowAlwaysOnTopPosition !=
+ POSITION_BOTTOM ? belowAlwaysOnTopPosition : topChildPosition;
+ } else {
+ // Other stacks need to be below the always-on-top stacks.
+ maxPosition = belowAlwaysOnTopPosition !=
+ POSITION_BOTTOM ? belowAlwaysOnTopPosition : topChildPosition;
+ }
+
+ int targetPosition = requestedPosition;
+ targetPosition = Math.min(targetPosition, maxPosition);
+ targetPosition = Math.max(targetPosition, minPosition);
+
+ int prevPosition = getStacks().indexOf(stack);
+ // The positions we calculated above (maxPosition, minPosition) do not take into
+ // consideration the following edge cases.
+ // 1) We need to adjust the position depending on the value "adding".
+ // 2) When we are moving a stack to another position, we also need to adjust the
+ // position depending on whether the stack is moving to a higher or lower position.
+ if ((targetPosition != requestedPosition) &&
+ (adding || targetPosition < prevPosition)) {
targetPosition++;
}
- // Note we might have multiple always on top windows.
- while (targetPosition >= 0) {
- int adjustedTargetStackId = adding ? targetPosition - 1 : targetPosition;
- if (adjustedTargetStackId < 0 || adjustedTargetStackId > topChildPosition) {
- break;
- }
- TaskStack targetStack = mChildren.get(adjustedTargetStackId);
- if (!targetStack.isAlwaysOnTop()) {
- // We reached a stack that isn't always-on-top.
- break;
- }
- if (stack.isAlwaysOnTop() && !targetStack.inPinnedWindowingMode()) {
- // Always on-top non-pinned windowing mode stacks can go anywhere below pinned
- // stack.
- break;
- }
- // We go one level down, looking for the place on which the new stack can be put.
- targetPosition--;
- }
-
return targetPosition;
}
@@ -3722,6 +3772,19 @@
}
@Override
+ SurfaceControl.Builder makeChildSurface(WindowContainer child) {
+ final SurfaceControl.Builder builder = super.makeChildSurface(child);
+ if (child instanceof WindowToken && ((WindowToken) child).mRoundedCornerOverlay) {
+ // To draw above the ColorFade layer during the screen off transition, the
+ // rounded corner overlays need to be at the root of the surface hierarchy.
+ // TODO: move the ColorLayer into the display overlay layer such that this is not
+ // necessary anymore.
+ builder.setParent(null);
+ }
+ return builder;
+ }
+
+ @Override
void assignChildLayers(SurfaceControl.Transaction t) {
assignChildLayers(t, null /* imeContainer */);
}
@@ -3737,6 +3800,10 @@
wt.assignRelativeLayer(t, mTaskStackContainers.getSplitScreenDividerAnchor(), 1);
continue;
}
+ if (wt.mRoundedCornerOverlay) {
+ wt.assignLayer(t, WindowManagerPolicy.COLOR_FADE_LAYER + 1);
+ continue;
+ }
wt.assignLayer(t, j);
wt.assignChildLayers(t);
diff --git a/services/core/java/com/android/server/wm/DragDropController.java b/services/core/java/com/android/server/wm/DragDropController.java
index fc370d9..f42e979 100644
--- a/services/core/java/com/android/server/wm/DragDropController.java
+++ b/services/core/java/com/android/server/wm/DragDropController.java
@@ -151,6 +151,7 @@
mDragState.mUid = callerUid;
mDragState.mOriginalAlpha = alpha;
mDragState.mToken = dragToken;
+ mDragState.mDisplayContent = displayContent;
final Display display = displayContent.getDisplay();
if (!mCallback.get().registerInputChannel(
@@ -160,7 +161,6 @@
return null;
}
- mDragState.mDisplayContent = displayContent;
mDragState.mData = data;
mDragState.broadcastDragStartedLocked(touchX, touchY);
mDragState.overridePointerIconLocked(touchSource);
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index 1ac9b88..d4046e9 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -255,7 +255,7 @@
if (DEBUG_ORIENTATION) {
Slog.d(TAG_WM, "Pausing rotation during drag");
}
- mService.pauseRotationLocked();
+ mDisplayContent.pauseRotationLocked();
}
void tearDown() {
@@ -274,7 +274,7 @@
if (DEBUG_ORIENTATION) {
Slog.d(TAG_WM, "Resuming rotation after drag");
}
- mService.resumeRotationLocked();
+ mDisplayContent.resumeRotationLocked();
}
}
diff --git a/services/core/java/com/android/server/wm/ForcedSeamlessRotator.java b/services/core/java/com/android/server/wm/ForcedSeamlessRotator.java
new file mode 100644
index 0000000..546edaa
--- /dev/null
+++ b/services/core/java/com/android/server/wm/ForcedSeamlessRotator.java
@@ -0,0 +1,78 @@
+/*
+ * 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.wm;
+
+import static android.view.Surface.ROTATION_270;
+import static android.view.Surface.ROTATION_90;
+
+import android.graphics.Matrix;
+import android.view.DisplayInfo;
+
+import com.android.server.wm.utils.CoordinateTransforms;
+
+/**
+ * Helper class for forced seamless rotation.
+ *
+ * Works by transforming the window token back into the old display rotation.
+ *
+ * Uses deferTransactionUntil instead of latching on the buffer size to allow for seamless 180
+ * degree rotations.
+ * TODO(b/111504081): Consolidate seamless rotation logic.
+ */
+public class ForcedSeamlessRotator {
+
+ private final Matrix mTransform = new Matrix();
+ private final float[] mFloat9 = new float[9];
+
+ public ForcedSeamlessRotator(int oldRotation, int newRotation, DisplayInfo info) {
+ final boolean flipped = info.rotation == ROTATION_90 || info.rotation == ROTATION_270;
+ final int h = flipped ? info.logicalWidth : info.logicalHeight;
+ final int w = flipped ? info.logicalHeight : info.logicalWidth;
+
+ final Matrix tmp = new Matrix();
+ CoordinateTransforms.transformLogicalToPhysicalCoordinates(oldRotation, w, h, mTransform);
+ CoordinateTransforms.transformPhysicalToLogicalCoordinates(newRotation, w, h, tmp);
+ mTransform.postConcat(tmp);
+ }
+
+ /**
+ * Applies a transform to the window token's surface that undoes the effect of the global
+ * display rotation.
+ */
+ public void unrotate(WindowToken token) {
+ token.getPendingTransaction().setMatrix(token.getSurfaceControl(), mTransform, mFloat9);
+ }
+
+ /**
+ * Removes the transform to the window token's surface that undoes the effect of the global
+ * display rotation.
+ *
+ * Removing the transform and the result of the WindowState's layout are both tied to the
+ * WindowState's next frame, such that they apply at the same time the client draws the
+ * window in the new orientation.
+ */
+ public void finish(WindowToken token, WindowState win) {
+ mTransform.reset();
+ token.getPendingTransaction().setMatrix(token.mSurfaceControl, mTransform, mFloat9);
+ token.getPendingTransaction().deferTransactionUntil(token.mSurfaceControl,
+ win.mWinAnimator.mSurfaceController.mSurfaceControl.getHandle(),
+ win.getFrameNumber());
+ win.getPendingTransaction().deferTransactionUntil(win.mSurfaceControl,
+ win.mWinAnimator.mSurfaceController.mSurfaceControl.getHandle(),
+ win.getFrameNumber());
+ }
+}
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index 281e0a8..8ab4651 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -40,6 +40,7 @@
import android.os.Looper;
import android.os.Process;
import android.os.RemoteException;
+import android.os.Trace;
import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.Log;
@@ -325,7 +326,7 @@
inputWindowHandle.ownerUid = child.mSession.mUid;
inputWindowHandle.inputFeatures = child.mAttrs.inputFeatures;
- final Rect frame = child.mFrame;
+ final Rect frame = child.getFrameLw();
inputWindowHandle.frameLeft = frame.left;
inputWindowHandle.frameTop = frame.top;
inputWindowHandle.frameRight = frame.right;
@@ -620,6 +621,8 @@
private void updateInputWindows(boolean inDrag) {
+ Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "updateInputWindows");
+
// TODO: multi-display
navInputConsumer = getInputConsumer(INPUT_CONSUMER_NAVIGATION, DEFAULT_DISPLAY);
pipInputConsumer = getInputConsumer(INPUT_CONSUMER_PIP, DEFAULT_DISPLAY);
@@ -645,6 +648,8 @@
mService.mInputManager.setInputWindows(mInputWindowHandles, mFocusedInputWindowHandle);
clearInputWindowHandlesLw();
+
+ Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
}
@Override
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index b610695..6f5fea9 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -16,13 +16,13 @@
package com.android.server.wm;
-import static android.app.ActivityTaskManagerInternal.APP_TRANSITION_RECENTS_ANIM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.view.RemoteAnimationTarget.MODE_CLOSING;
import static android.view.WindowManager.INPUT_CONSUMER_RECENTS_ANIMATION;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
+import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_RECENTS_ANIM;
import static com.android.server.wm.AnimationAdapterProto.REMOTE;
import static com.android.server.wm.RemoteAnimationAdapterWrapperProto.TARGET;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_RECENTS_ANIMATIONS;
@@ -48,11 +48,14 @@
import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
import android.view.inputmethod.InputMethodManagerInternal;
+
+import com.google.android.collect.Sets;
+
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.LocalServices;
import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
import com.android.server.wm.utils.InsetUtils;
-import com.google.android.collect.Sets;
+
import java.io.PrintWriter;
import java.util.ArrayList;
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index a709c55..2be4001 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -746,22 +746,7 @@
if (mUpdateRotation) {
if (DEBUG_ORIENTATION) Slog.d(TAG, "Performing post-rotate rotation");
- // TODO(multi-display): Update rotation for different displays separately.
- final int displayId = defaultDisplay.getDisplayId();
- if (defaultDisplay.updateRotationUnchecked()) {
- mService.mH.obtainMessage(SEND_NEW_CONFIGURATION, displayId).sendToTarget();
- } else {
- mUpdateRotation = false;
- }
- // Update rotation of VR virtual display separately. Currently this is the only kind of
- // secondary display that can be rotated because of the single-display limitations in
- // PhoneWindowManager.
- final DisplayContent vrDisplay = mService.mVr2dDisplayId != INVALID_DISPLAY
- ? getDisplayContent(mService.mVr2dDisplayId) : null;
- if (vrDisplay != null && vrDisplay.updateRotationUnchecked()) {
- mService.mH.obtainMessage(SEND_NEW_CONFIGURATION, mService.mVr2dDisplayId)
- .sendToTarget();
- }
+ mUpdateRotation = updateRotationUnchecked();
}
if (mService.mWaitingForDrawnCallback != null ||
@@ -958,6 +943,19 @@
return displayHasContent;
}
+ boolean updateRotationUnchecked() {
+ boolean changed = false;
+ for (int i = mChildren.size() - 1; i >= 0; i--) {
+ final DisplayContent displayContent = mChildren.get(i);
+ if (displayContent.updateRotationUnchecked()) {
+ changed = true;
+ mService.mH.obtainMessage(SEND_NEW_CONFIGURATION, displayContent.getDisplayId())
+ .sendToTarget();
+ }
+ }
+ return changed;
+ }
+
boolean copyAnimToLayoutParams() {
boolean doRequest = false;
diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
index fa8a5c6..2f189a6 100644
--- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
+++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
@@ -268,24 +268,18 @@
.setSecure(isSecure)
.build();
- // capture a screenshot into the surface we just created
- // TODO(multidisplay): we should use the proper display
- final int displayId = SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN;
- final IBinder displayHandle = SurfaceControl.getBuiltInDisplay(displayId);
- // This null check below is to guard a race condition where WMS didn't have a chance to
- // respond to display disconnection before handling rotation , that surfaceflinger may
- // return a null handle here because it doesn't think that display is valid anymore.
- if (displayHandle != null) {
- Surface sur = new Surface();
- sur.copyFrom(mSurfaceControl);
- SurfaceControl.screenshot(displayHandle, sur);
+ // Capture a screenshot into the surface we just created.
+ final int displayId = display.getDisplayId();
+ final Surface surface = new Surface();
+ surface.copyFrom(mSurfaceControl);
+ if (mService.mDisplayManagerInternal.screenshot(displayId, surface)) {
t.setLayer(mSurfaceControl, SCREEN_FREEZE_LAYER_SCREENSHOT);
t.setAlpha(mSurfaceControl, 0);
t.show(mSurfaceControl);
- sur.destroy();
} else {
- Slog.w(TAG, "Built-in display " + displayId + " is null.");
+ Slog.w(TAG, "Unable to take screenshot of display " + displayId);
}
+ surface.destroy();
} catch (OutOfResourcesException e) {
Slog.w(TAG, "Unable to allocate freeze surface", e);
}
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 4003d5a..26ddf2c 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -189,15 +189,6 @@
}
@Override
- public int add(IWindow window, int seq, WindowManager.LayoutParams attrs,
- int viewVisibility, Rect outContentInsets, Rect outStableInsets,
- InputChannel outInputChannel) {
- return addToDisplay(window, seq, attrs, viewVisibility, Display.DEFAULT_DISPLAY,
- new Rect() /* outFrame */, outContentInsets, outStableInsets, null /* outOutsets */,
- new DisplayCutout.ParcelableWrapper() /* cutout */, outInputChannel);
- }
-
- @Override
public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
int viewVisibility, int displayId, Rect outFrame, Rect outContentInsets,
Rect outStableInsets, Rect outOutsets,
@@ -207,13 +198,6 @@
}
@Override
- public int addWithoutInputChannel(IWindow window, int seq, WindowManager.LayoutParams attrs,
- int viewVisibility, Rect outContentInsets, Rect outStableInsets) {
- return addToDisplayWithoutInputChannel(window, seq, attrs, viewVisibility,
- Display.DEFAULT_DISPLAY, outContentInsets, outStableInsets);
- }
-
- @Override
public int addToDisplayWithoutInputChannel(IWindow window, int seq, WindowManager.LayoutParams attrs,
int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets) {
return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java b/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
index 2bfff26..cb50460 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
@@ -26,6 +26,8 @@
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.annotation.Nullable;
+import android.hardware.power.V1_0.PowerHint;
+import android.os.PowerManagerInternal;
import android.util.ArrayMap;
import android.view.Choreographer;
import android.view.SurfaceControl;
@@ -57,6 +59,7 @@
private final AnimationHandler mAnimationHandler;
private final Transaction mFrameTransaction;
private final AnimatorFactory mAnimatorFactory;
+ private final PowerManagerInternal mPowerManagerInternal;
private boolean mApplyScheduled;
@GuardedBy("mLock")
@@ -70,13 +73,15 @@
@GuardedBy("mLock")
private boolean mAnimationStartDeferred;
- SurfaceAnimationRunner() {
- this(null /* callbackProvider */, null /* animatorFactory */, new Transaction());
+ SurfaceAnimationRunner(PowerManagerInternal powerManagerInternal) {
+ this(null /* callbackProvider */, null /* animatorFactory */, new Transaction(),
+ powerManagerInternal);
}
@VisibleForTesting
SurfaceAnimationRunner(@Nullable AnimationFrameCallbackProvider callbackProvider,
- AnimatorFactory animatorFactory, Transaction frameTransaction) {
+ AnimatorFactory animatorFactory, Transaction frameTransaction,
+ PowerManagerInternal powerManagerInternal) {
SurfaceAnimationThread.getHandler().runWithScissors(() -> mChoreographer = getSfInstance(),
0 /* timeout */);
mFrameTransaction = frameTransaction;
@@ -87,6 +92,7 @@
mAnimatorFactory = animatorFactory != null
? animatorFactory
: SfValueAnimator::new;
+ mPowerManagerInternal = powerManagerInternal;
}
/**
@@ -231,6 +237,7 @@
synchronized (mLock) {
startPendingAnimationsLocked();
}
+ mPowerManagerInternal.powerHint(PowerHint.INTERACTION, 0);
}
private void scheduleApplyTransaction() {
diff --git a/services/core/java/com/android/server/wm/TEST_MAPPING b/services/core/java/com/android/server/wm/TEST_MAPPING
new file mode 100644
index 0000000..e885afa
--- /dev/null
+++ b/services/core/java/com/android/server/wm/TEST_MAPPING
@@ -0,0 +1,42 @@
+{
+ "presubmit": [
+ {
+ "name": "CtsWindowManagerDeviceTestCases",
+ "options": [
+ {
+ "include-annotation": "android.platform.test.annotations.Presubmit"
+ },
+ {
+ "exclude-annotation": "android.support.test.filters.FlakyTest"
+ }
+ ]
+ },
+ {
+ "name": "FrameworksServicesTests",
+ "options": [
+ {
+ "include-filter": "com.android.server.wm."
+ },
+ {
+ "include-annotation": "android.platform.test.annotations.Presubmit"
+ },
+ {
+ "exclude-annotation": "android.support.test.filters.FlakyTest"
+ }
+ ]
+ }
+ ],
+ "postsubmit": [
+ {
+ "name": "CtsWindowManagerDeviceTestCases"
+ },
+ {
+ "name": "FrameworksServicesTests",
+ "options": [
+ {
+ "include-filter": "com.android.server.wm."
+ }
+ ]
+ }
+ ]
+}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 2da77a1..71f34c9 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -409,7 +409,7 @@
* @param out Rect containing the max visible bounds.
* @return true if the task has some visible app windows; false otherwise.
*/
- boolean getMaxVisibleBounds(Rect out) {
+ private boolean getMaxVisibleBounds(Rect out) {
boolean foundTop = false;
for (int i = mChildren.size() - 1; i >= 0; i--) {
final AppWindowToken token = mChildren.get(i);
@@ -422,22 +422,11 @@
continue;
}
if (!foundTop) {
- out.set(win.mVisibleFrame);
foundTop = true;
- continue;
+ out.setEmpty();
}
- if (win.mVisibleFrame.left < out.left) {
- out.left = win.mVisibleFrame.left;
- }
- if (win.mVisibleFrame.top < out.top) {
- out.top = win.mVisibleFrame.top;
- }
- if (win.mVisibleFrame.right > out.right) {
- out.right = win.mVisibleFrame.right;
- }
- if (win.mVisibleFrame.bottom > out.bottom) {
- out.bottom = win.mVisibleFrame.bottom;
- }
+
+ win.getMaxVisibleBounds(out);
}
return foundTop;
}
diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java
index 30f46a0..bd7e61c 100644
--- a/services/core/java/com/android/server/wm/TaskPositioner.java
+++ b/services/core/java/com/android/server/wm/TaskPositioner.java
@@ -97,7 +97,7 @@
private final WindowManagerService mService;
private final IActivityTaskManager mActivityManager;
private WindowPositionerEventReceiver mInputEventReceiver;
- private Display mDisplay;
+ private DisplayContent mDisplayContent;
private final DisplayMetrics mDisplayMetrics = new DisplayMetrics();
private Rect mTmpRect = new Rect();
private int mSideMargin;
@@ -250,8 +250,8 @@
return;
}
- mDisplay = display;
- mDisplay.getMetrics(mDisplayMetrics);
+ mDisplayContent = displayContent;
+ display.getMetrics(mDisplayMetrics);
final InputChannel[] channels = InputChannel.openInputChannelPair(TAG);
mServerChannel = channels[0];
mClientChannel = channels[1];
@@ -267,7 +267,7 @@
WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
mDragWindowHandle = new InputWindowHandle(mDragApplicationHandle, null, null,
- mDisplay.getDisplayId());
+ display.getDisplayId());
mDragWindowHandle.name = TAG;
mDragWindowHandle.inputChannel = mServerChannel;
mDragWindowHandle.layer = mService.getDragLayerLocked();
@@ -292,7 +292,7 @@
mDragWindowHandle.frameLeft = 0;
mDragWindowHandle.frameTop = 0;
final Point p = new Point();
- mDisplay.getRealSize(p);
+ display.getRealSize(p);
mDragWindowHandle.frameRight = p.x;
mDragWindowHandle.frameBottom = p.y;
@@ -300,12 +300,12 @@
if (DEBUG_ORIENTATION) {
Slog.d(TAG, "Pausing rotation during re-position");
}
- mService.pauseRotationLocked();
+ mDisplayContent.pauseRotationLocked();
mSideMargin = dipToPixel(SIDE_MARGIN_DIP, mDisplayMetrics);
mMinVisibleWidth = dipToPixel(MINIMUM_VISIBLE_WIDTH_IN_DP, mDisplayMetrics);
mMinVisibleHeight = dipToPixel(MINIMUM_VISIBLE_HEIGHT_IN_DP, mDisplayMetrics);
- mDisplay.getRealSize(mMaxVisibleSize);
+ display.getRealSize(mMaxVisibleSize);
mDragEnded = false;
}
@@ -331,14 +331,14 @@
mDragWindowHandle = null;
mDragApplicationHandle = null;
- mDisplay = null;
mDragEnded = true;
// Resume rotations after a drag.
if (DEBUG_ORIENTATION) {
Slog.d(TAG, "Resuming rotation after re-position");
}
- mService.resumeRotationLocked();
+ mDisplayContent.resumeRotationLocked();
+ mDisplayContent = null;
}
void startDrag(WindowState win, boolean resize, boolean preserveOrientation, float startX,
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotPersister.java b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
index a642e6a..21e807e 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
@@ -39,6 +39,7 @@
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayDeque;
+import java.util.Arrays;
/**
* Persists {@link TaskSnapshot}s to disk.
@@ -399,8 +400,8 @@
@VisibleForTesting
RemoveObsoleteFilesQueueItem(ArraySet<Integer> persistentTaskIds,
int[] runningUserIds) {
- mPersistentTaskIds = persistentTaskIds;
- mRunningUserIds = runningUserIds;
+ mPersistentTaskIds = new ArraySet<>(persistentTaskIds);
+ mRunningUserIds = Arrays.copyOf(runningUserIds, runningUserIds.length);
}
@Override
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 76a060e..9075b6c 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -726,18 +726,33 @@
@Override
public void onConfigurationChanged(Configuration newParentConfig) {
final int prevWindowingMode = getWindowingMode();
+ final boolean prevIsAlwaysOnTop = isAlwaysOnTop();
super.onConfigurationChanged(newParentConfig);
// Only need to update surface size here since the super method will handle updating
// surface position.
updateSurfaceSize(getPendingTransaction());
final int windowingMode = getWindowingMode();
+ final boolean isAlwaysOnTop = isAlwaysOnTop();
- if (mDisplayContent == null || prevWindowingMode == windowingMode) {
+ if (mDisplayContent == null) {
return;
}
- mDisplayContent.onStackWindowingModeChanged(this);
- updateBoundsForWindowModeChange();
+
+ if (prevWindowingMode != windowingMode) {
+ mDisplayContent.onStackWindowingModeChanged(this);
+ updateBoundsForWindowModeChange();
+ }
+
+ if (prevIsAlwaysOnTop != isAlwaysOnTop) {
+ // positionStackAt(POSITION_TOP, this) must be called even when always on top gets
+ // turned off because we need to make sure that the stack is moved from among always on
+ // top windows to below other always on top windows. Since the position the stack should
+ // be inserted into is calculated properly in
+ // {@link DisplayContent#findPositionForStack()} in both cases, we can just request that
+ // the stack is put at top here.
+ mDisplayContent.positionStackAt(POSITION_TOP, this);
+ }
}
private void updateSurfaceBounds() {
@@ -1630,9 +1645,14 @@
}
@Override // AnimatesBounds
- public void onAnimationStart(boolean schedulePipModeChangedCallback, boolean forceUpdate) {
+ public boolean onAnimationStart(boolean schedulePipModeChangedCallback, boolean forceUpdate) {
// Hold the lock since this is called from the BoundsAnimator running on the UiThread
synchronized (mService.mWindowMap) {
+ if (!isAttached()) {
+ // Don't run the animation if the stack is already detached
+ return false;
+ }
+
mBoundsAnimatingRequested = false;
mBoundsAnimating = true;
mCancelCurrentBoundsAnimation = false;
@@ -1662,6 +1682,7 @@
controller.updatePictureInPictureModeForPinnedStackAnimation(null, forceUpdate);
}
}
+ return true;
}
@Override // AnimatesBounds
@@ -1705,41 +1726,47 @@
@Override
public boolean isAttached() {
- return mDisplayContent != null;
+ synchronized (mService.mWindowMap) {
+ return mDisplayContent != null;
+ }
}
/**
* Called immediately prior to resizing the tasks at the end of the pinned stack animation.
*/
public void onPipAnimationEndResize() {
- mBoundsAnimating = false;
- for (int i = 0; i < mChildren.size(); i++) {
- final Task t = mChildren.get(i);
- t.clearPreserveNonFloatingState();
+ synchronized (mService.mWindowMap) {
+ mBoundsAnimating = false;
+ for (int i = 0; i < mChildren.size(); i++) {
+ final Task t = mChildren.get(i);
+ t.clearPreserveNonFloatingState();
+ }
+ mService.requestTraversal();
}
- mService.requestTraversal();
}
@Override
public boolean shouldDeferStartOnMoveToFullscreen() {
- // Workaround for the recents animation -- normally we need to wait for the new activity to
- // show before starting the PiP animation, but because we start and show the home activity
- // early for the recents animation prior to the PiP animation starting, there is no
- // subsequent all-drawn signal. In this case, we can skip the pause when the home stack is
- // already visible and drawn.
- final TaskStack homeStack = mDisplayContent.getHomeStack();
- if (homeStack == null) {
- return true;
+ synchronized (mService.mWindowMap) {
+ // Workaround for the recents animation -- normally we need to wait for the new activity
+ // to show before starting the PiP animation, but because we start and show the home
+ // activity early for the recents animation prior to the PiP animation starting, there
+ // is no subsequent all-drawn signal. In this case, we can skip the pause when the home
+ // stack is already visible and drawn.
+ final TaskStack homeStack = mDisplayContent.getHomeStack();
+ if (homeStack == null) {
+ return true;
+ }
+ final Task homeTask = homeStack.getTopChild();
+ if (homeTask == null) {
+ return true;
+ }
+ final AppWindowToken homeApp = homeTask.getTopVisibleAppToken();
+ if (!homeTask.isVisible() || homeApp == null) {
+ return true;
+ }
+ return !homeApp.allDrawn;
}
- final Task homeTask = homeStack.getTopChild();
- if (homeTask == null) {
- return true;
- }
- final AppWindowToken homeApp = homeTask.getTopVisibleAppToken();
- if (!homeTask.isVisible() || homeApp == null) {
- return true;
- }
- return !homeApp.allDrawn;
}
/**
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index c63da77..3d349ce 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -295,7 +295,7 @@
float defaultWallpaperX = wallpaperWin.isRtl() ? 1f : 0f;
float wpx = mLastWallpaperX >= 0 ? mLastWallpaperX : defaultWallpaperX;
float wpxs = mLastWallpaperXStep >= 0 ? mLastWallpaperXStep : -1.0f;
- int availw = wallpaperWin.mFrame.right - wallpaperWin.mFrame.left - dw;
+ int availw = wallpaperWin.getFrameLw().right - wallpaperWin.getFrameLw().left - dw;
int offset = availw > 0 ? -(int)(availw * wpx + .5f) : 0;
if (mLastWallpaperDisplayOffsetX != Integer.MIN_VALUE) {
offset += mLastWallpaperDisplayOffsetX;
@@ -310,7 +310,7 @@
float wpy = mLastWallpaperY >= 0 ? mLastWallpaperY : 0.5f;
float wpys = mLastWallpaperYStep >= 0 ? mLastWallpaperYStep : -1.0f;
- int availh = wallpaperWin.mFrame.bottom - wallpaperWin.mFrame.top - dh;
+ int availh = wallpaperWin.getFrameLw().bottom - wallpaperWin.getFrameLw().top - dh;
offset = availh > 0 ? -(int)(availh * wpy + .5f) : 0;
if (mLastWallpaperDisplayOffsetY != Integer.MIN_VALUE) {
offset += mLastWallpaperDisplayOffsetY;
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 8fe7063..fee0fcb 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -246,6 +246,19 @@
+ " is already a child of container=" + child.getParent().getName()
+ " can't add to container=" + getName());
}
+
+ if ((index < 0 && index != POSITION_BOTTOM)
+ || (index > mChildren.size() && index != POSITION_TOP)) {
+ throw new IllegalArgumentException("addChild: invalid position=" + index
+ + ", children number=" + mChildren.size());
+ }
+
+ if (index == POSITION_TOP) {
+ index = mChildren.size();
+ } else if (index == POSITION_BOTTOM) {
+ index = 0;
+ }
+
mChildren.add(index, child);
onChildAdded(child);
diff --git a/services/core/java/com/android/server/wm/WindowFrames.java b/services/core/java/com/android/server/wm/WindowFrames.java
new file mode 100644
index 0000000..5f41df7
--- /dev/null
+++ b/services/core/java/com/android/server/wm/WindowFrames.java
@@ -0,0 +1,225 @@
+/*
+ * 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.wm;
+
+import static com.android.server.wm.WindowFramesProto.CONTAINING_FRAME;
+import static com.android.server.wm.WindowFramesProto.CONTENT_FRAME;
+import static com.android.server.wm.WindowFramesProto.CUTOUT;
+import static com.android.server.wm.WindowFramesProto.DECOR_FRAME;
+import static com.android.server.wm.WindowFramesProto.DISPLAY_FRAME;
+import static com.android.server.wm.WindowFramesProto.FRAME;
+import static com.android.server.wm.WindowFramesProto.OUTSET_FRAME;
+import static com.android.server.wm.WindowFramesProto.OVERSCAN_FRAME;
+import static com.android.server.wm.WindowFramesProto.PARENT_FRAME;
+import static com.android.server.wm.WindowFramesProto.VISIBLE_FRAME;
+
+import android.annotation.NonNull;
+import android.graphics.Rect;
+import android.util.proto.ProtoOutputStream;
+
+import java.io.PrintWriter;
+import android.view.DisplayCutout;
+
+import com.android.server.wm.utils.WmDisplayCutout;
+
+/**
+ * Container class for all the window frames that affect how windows are laid out.
+ *
+ * TODO(b/111611553): Investigate which frames are still needed and which are duplicates
+ */
+public class WindowFrames {
+
+ /**
+ * In most cases, this is the area of the entire screen.
+ *
+ * TODO(b/111611553): The name is unclear and most likely should be swapped with
+ * {@link #mDisplayFrame}
+ * TODO(b/111611553): In some cases, it also includes top insets, like for IME. Determine
+ * whether this is still necessary to do.
+ */
+ public final Rect mParentFrame = new Rect();
+
+ /**
+ * The entire screen area of the {@link TaskStack} this window is in. Usually equal to the
+ * screen area of the device.
+ *
+ * TODO(b/111611553): The name is unclear and most likely should be swapped with
+ * {@link #mParentFrame}
+ */
+ public final Rect mDisplayFrame = new Rect();
+
+ /**
+ * The region of the display frame that the display type supports displaying content on. This
+ * is mostly a special case for TV where some displays don’t have the entire display usable.
+ * {@link android.view.WindowManager.LayoutParams#FLAG_LAYOUT_IN_OVERSCAN} flag can be used to
+ * allow window display contents to extend into the overscan region.
+ */
+ public final Rect mOverscanFrame = new Rect();
+
+ /**
+ * Legacy stuff. Generally equal to the content frame expect when the IME for older apps
+ * displays hint text.
+ */
+ public final Rect mVisibleFrame = new Rect();
+
+ /**
+ * The area not occupied by the status and navigation bars. So, if both status and navigation
+ * bars are visible, the decor frame is equal to the stable frame.
+ */
+ public final Rect mDecorFrame = new Rect();
+
+ /**
+ * Equal to the decor frame if the IME (e.g. keyboard) is not present. Equal to the decor frame
+ * minus the area occupied by the IME if the IME is present.
+ */
+ public final Rect mContentFrame = new Rect();
+
+ /**
+ * The display frame minus the stable insets. This value is always constant regardless of if
+ * the status bar or navigation bar is visible.
+ */
+ public final Rect mStableFrame = new Rect();
+
+ /**
+ * Frame that includes dead area outside of the surface but where we want to pretend that it's
+ * possible to draw.
+ */
+ final public Rect mOutsetFrame = new Rect();
+
+ /**
+ * Similar to {@link #mDisplayFrame}
+ *
+ * TODO: Why is this different than mDisplayFrame
+ */
+ final Rect mContainingFrame = new Rect();
+
+ /**
+ * "Real" frame that the application sees, in display coordinate space.
+ */
+ final Rect mFrame = new Rect();
+
+ /**
+ * The last real frame that was reported to the client.
+ */
+ final Rect mLastFrame = new Rect();
+
+ /**
+ * Whether the parent frame would have been different if there was no display cutout.
+ */
+ private boolean mParentFrameWasClippedByDisplayCutout;
+
+ /**
+ * Part of the display that has been cut away. See {@link DisplayCutout}.
+ */
+ WmDisplayCutout mDisplayCutout = WmDisplayCutout.NO_CUTOUT;
+
+ /**
+ * The last cutout that has been reported to the client.
+ */
+ WmDisplayCutout mLastDisplayCutout = WmDisplayCutout.NO_CUTOUT;
+
+ public WindowFrames() {
+ }
+
+ public WindowFrames(Rect parentFrame, Rect displayFrame, Rect overscanFrame, Rect contentFrame,
+ Rect visibleFrame, Rect decorFrame, Rect stableFrame, Rect outsetFrame) {
+ setFrames(parentFrame, displayFrame, overscanFrame, contentFrame, visibleFrame, decorFrame,
+ stableFrame, outsetFrame);
+ }
+
+ public void setFrames(Rect parentFrame, Rect displayFrame, Rect overscanFrame,
+ Rect contentFrame, Rect visibleFrame, Rect decorFrame, Rect stableFrame,
+ Rect outsetFrame) {
+ mParentFrame.set(parentFrame);
+ mDisplayFrame.set(displayFrame);
+ mOverscanFrame.set(overscanFrame);
+ mContentFrame.set(contentFrame);
+ mVisibleFrame.set(visibleFrame);
+ mDecorFrame.set(decorFrame);
+ mStableFrame.set(stableFrame);
+ mOutsetFrame.set(outsetFrame);
+ }
+
+ public void setParentFrameWasClippedByDisplayCutout(
+ boolean parentFrameWasClippedByDisplayCutout) {
+ mParentFrameWasClippedByDisplayCutout = parentFrameWasClippedByDisplayCutout;
+ }
+
+ boolean parentFrameWasClippedByDisplayCutout() {
+ return mParentFrameWasClippedByDisplayCutout;
+ }
+
+ public void setDisplayCutout(WmDisplayCutout displayCutout) {
+ mDisplayCutout = displayCutout;
+ }
+
+ /**
+ * @return true if the width or height has changed since last reported to the client.
+ */
+ boolean didFrameSizeChange() {
+ return (mLastFrame.width() != mFrame.width()) || (mLastFrame.height() != mFrame.height());
+ }
+
+ /**
+ * @return true if the display cutout has changed since last reported to the client.
+ */
+ boolean didDisplayCutoutChange() {
+ return !mLastDisplayCutout.equals(mDisplayCutout);
+ }
+
+ public void writeToProto(@NonNull ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ mParentFrame.writeToProto(proto, PARENT_FRAME);
+ mContentFrame.writeToProto(proto, CONTENT_FRAME);
+ mDisplayFrame.writeToProto(proto, DISPLAY_FRAME);
+ mOverscanFrame.writeToProto(proto, OVERSCAN_FRAME);
+ mVisibleFrame.writeToProto(proto, VISIBLE_FRAME);
+ mDecorFrame.writeToProto(proto, DECOR_FRAME);
+ mOutsetFrame.writeToProto(proto, OUTSET_FRAME);
+ mContainingFrame.writeToProto(proto, CONTAINING_FRAME);
+ mFrame.writeToProto(proto, FRAME);
+ mDisplayCutout.getDisplayCutout().writeToProto(proto, CUTOUT);
+ proto.end(token);
+ }
+
+ public void dump(PrintWriter pw, String prefix) {
+ pw.print(prefix); pw.print("Frames: containing=");
+ mContainingFrame.printShortString(pw);
+ pw.print(" parent="); mParentFrame.printShortString(pw);
+ pw.println();
+ pw.print(prefix); pw.print(" display=");
+ mDisplayFrame.printShortString(pw);
+ pw.print(" overscan="); mOverscanFrame.printShortString(pw);
+ pw.println();
+ pw.print(prefix); pw.print(" content=");
+ mContentFrame.printShortString(pw);
+ pw.print(" visible="); mVisibleFrame.printShortString(pw);
+ pw.println();
+ pw.print(prefix); pw.print(" decor=");
+ mDecorFrame.printShortString(pw);
+ pw.println();
+ pw.print(prefix); pw.print(" outset=");
+ mOutsetFrame.printShortString(pw);
+ pw.println();
+ pw.print(prefix); pw.print("mFrame="); mFrame.printShortString(pw);
+ pw.print(" last="); mLastFrame.printShortString(pw);
+ pw.println();
+ pw.print(prefix); pw.print(" cutout=" + mDisplayCutout.getDisplayCutout());
+ pw.print(" last=" + mLastDisplayCutout.getDisplayCutout());
+ pw.println();
+ }
+}
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 6686b80..0c65518 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -368,14 +368,25 @@
* to be synchronized with state in WindowManagerService.
* @param dismissImeOnBackKeyPressed {@code true} if the software keyboard is shown and the back
* key is expected to dismiss the software keyboard.
- * @param targetWindowToken token to identify the target window that the IME is associated with.
- * {@code null} when application, system, or the IME itself decided to
- * change its window visibility before being associated with any target
- * window.
*/
public abstract void updateInputMethodWindowStatus(@NonNull IBinder imeToken,
- boolean imeWindowVisible, boolean dismissImeOnBackKeyPressed,
- @Nullable IBinder targetWindowToken);
+ boolean imeWindowVisible, boolean dismissImeOnBackKeyPressed);
+
+ /**
+ * Notifies WindowManagerService that the current IME window status is being changed.
+ *
+ * <p>Only {@link com.android.server.InputMethodManagerService} is the expected and tested
+ * caller of this method.</p>
+ *
+ * @param imeToken token to track the active input method. Corresponding IME windows can be
+ * identified by checking {@link android.view.WindowManager.LayoutParams#token}.
+ * Note that there is no guarantee that the corresponding window is already
+ * created
+ * @param imeTargetWindowToken token to identify the target window that the IME is associated
+ * with
+ */
+ public abstract void updateInputMethodTargetWindow(@NonNull IBinder imeToken,
+ @NonNull IBinder imeTargetWindowToken);
/**
* Returns true when the hardware keyboard is available.
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 2c7ab7e..06a1968 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -117,7 +117,6 @@
import android.app.ActivityManager.TaskSnapshot;
import android.app.ActivityManagerInternal;
import android.app.ActivityTaskManager;
-import android.app.ActivityTaskManagerInternal;
import android.app.ActivityThread;
import android.app.AppOpsManager;
import android.app.IActivityManager;
@@ -584,7 +583,6 @@
}
ArrayList<RotationWatcher> mRotationWatchers = new ArrayList<>();
- int mDeferredRotationPauseCount;
final WallpaperVisibilityListeners mWallpaperVisibilityListeners =
new WallpaperVisibilityListeners();
@@ -1012,7 +1010,6 @@
mAppTransition.registerListenerLocked(mActivityManagerAppTransitionNotifier);
final AnimationHandler animationHandler = new AnimationHandler();
- animationHandler.setProvider(new SfVsyncFrameCallbackProvider());
mBoundsAnimationController = new BoundsAnimationController(context, mAppTransition,
AnimationThread.getHandler(), animationHandler);
@@ -1070,7 +1067,7 @@
PowerManager.SCREEN_BRIGHT_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE, TAG_WM);
mHoldingScreenWakeLock.setReferenceCounted(false);
- mSurfaceAnimationRunner = new SurfaceAnimationRunner();
+ mSurfaceAnimationRunner = new SurfaceAnimationRunner(mPowerManagerInternal);
mAllowTheaterModeWakeFromLayout = context.getResources().getBoolean(
com.android.internal.R.bool.config_allowTheaterModeWakeFromWindowLayout);
@@ -1470,14 +1467,18 @@
displayFrames.onDisplayInfoUpdated(displayInfo,
displayContent.calculateDisplayCutoutForRotation(displayInfo.rotation));
final Rect taskBounds;
+ final boolean floatingStack;
if (atoken != null && atoken.getTask() != null) {
taskBounds = mTmpRect;
atoken.getTask().getBounds(mTmpRect);
+ floatingStack = atoken.getTask().isFloating();
} else {
taskBounds = null;
+ floatingStack = false;
}
- if (mPolicy.getLayoutHintLw(win.mAttrs, taskBounds, displayFrames, outFrame,
- outContentInsets, outStableInsets, outOutsets, outDisplayCutout)) {
+ if (mPolicy.getLayoutHintLw(win.mAttrs, taskBounds, displayFrames, floatingStack,
+ outFrame, outContentInsets, outStableInsets, outOutsets,
+ outDisplayCutout)) {
res |= WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_NAV_BAR;
}
@@ -1594,7 +1595,7 @@
}
// We use the visible frame, because we want the animation to morph the window from what
// was visible to the user to the final destination of the new window.
- Rect frame = replacedWindow.mVisibleFrame;
+ Rect frame = replacedWindow.getVisibleFrameLw();
// 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);
@@ -1830,7 +1831,7 @@
outDisplayFrame.setEmpty();
return;
}
- outDisplayFrame.set(win.mDisplayFrame);
+ outDisplayFrame.set(win.getDisplayFrameLw());
}
}
@@ -1892,6 +1893,13 @@
}
win.setFrameNumber(frameNumber);
+
+ // TODO(b/111504081): Consolidate seamless rotation logic.
+ if (win.mPendingForcedSeamlessRotate != null && !mWaitingForConfig) {
+ win.mPendingForcedSeamlessRotate.finish(win.mToken, win);
+ win.mPendingForcedSeamlessRotate = null;
+ }
+
int attrChanges = 0;
int flagChanges = 0;
if (attrs != null) {
@@ -2165,9 +2173,9 @@
win.mLastRelayoutContentInsets.set(win.mContentInsets);
outVisibleInsets.set(win.mVisibleInsets);
outStableInsets.set(win.mStableInsets);
- outCutout.set(win.mDisplayCutout.getDisplayCutout());
+ outCutout.set(win.getWmDisplayCutout().getDisplayCutout());
outOutsets.set(win.mOutsets);
- outBackdropFrame.set(win.getBackdropFrame(win.mFrame));
+ outBackdropFrame.set(win.getBackdropFrame(win.getFrameLw()));
if (localLOGV) Slog.v(
TAG_WM, "Relayout given client " + client.asBinder()
+ ", requestedWidth=" + requestedWidth
@@ -2181,8 +2189,6 @@
result |= mInTouchMode ? WindowManagerGlobal.RELAYOUT_RES_IN_TOUCH_MODE : 0;
- mInputMonitor.updateInputWindowsLw(true /*force*/);
-
if (DEBUG_LAYOUT) {
Slog.v(TAG_WM, "Relayout complete " + win + ": outFrame=" + outFrame.toShortString());
}
@@ -3823,37 +3829,6 @@
updateRotationUnchecked(alwaysSendConfiguration, forceRelayout);
}
- /**
- * Temporarily pauses rotation changes until resumed.
- *
- * This can be used to prevent rotation changes from occurring while the user is
- * performing certain operations, such as drag and drop.
- *
- * This call nests and must be matched by an equal number of calls to
- * {@link #resumeRotationLocked}.
- */
- void pauseRotationLocked() {
- mDeferredRotationPauseCount += 1;
- }
-
- /**
- * Resumes normal rotation changes after being paused.
- */
- void resumeRotationLocked() {
- if (mDeferredRotationPauseCount > 0) {
- mDeferredRotationPauseCount -= 1;
- if (mDeferredRotationPauseCount == 0) {
- // TODO(multi-display): Update rotation for different displays separately.
- final DisplayContent displayContent = getDefaultDisplayContentLocked();
- final boolean changed = displayContent.updateRotationUnchecked();
- if (changed) {
- mH.obtainMessage(H.SEND_NEW_CONFIGURATION, displayContent.getDisplayId())
- .sendToTarget();
- }
- }
- }
- }
-
private void updateRotationUnchecked(boolean alwaysSendConfiguration, boolean forceRelayout) {
if(DEBUG_ORIENTATION) Slog.v(TAG_WM, "updateRotationUnchecked:"
+ " alwaysSendConfiguration=" + alwaysSendConfiguration
@@ -3907,6 +3882,15 @@
@Override
public int watchRotation(IRotationWatcher watcher, int displayId) {
+ final DisplayContent displayContent;
+ synchronized (mWindowMap) {
+ displayContent = mRoot.getDisplayContent(displayId);
+ }
+ if (displayContent == null) {
+ throw new IllegalArgumentException("Trying to register rotation event "
+ + "for invalid display: " + displayId);
+ }
+
final IBinder watcherBinder = watcher.asBinder();
IBinder.DeathRecipient dr = new IBinder.DeathRecipient() {
@Override
@@ -3934,7 +3918,7 @@
// Client died, no cleanup needed.
}
- return getDefaultDisplayRotation();
+ return displayContent.getRotation();
}
}
@@ -4719,9 +4703,9 @@
} break;
case WINDOW_FREEZE_TIMEOUT: {
- // TODO(multidisplay): Can non-default displays rotate?
+ final DisplayContent displayContent = (DisplayContent) msg.obj;
synchronized (mWindowMap) {
- getDefaultDisplayContentLocked().onWindowFreezeTimeout();
+ displayContent.onWindowFreezeTimeout();
}
break;
}
@@ -5034,11 +5018,9 @@
}
break;
case SEAMLESS_ROTATION_TIMEOUT: {
- // Rotation only supported on primary display.
- // TODO(multi-display)
- synchronized(mWindowMap) {
- final DisplayContent dc = getDefaultDisplayContentLocked();
- dc.onSeamlessRotationTimeout();
+ final DisplayContent displayContent = (DisplayContent) msg.obj;
+ synchronized (mWindowMap) {
+ displayContent.onSeamlessRotationTimeout();
}
}
break;
@@ -5078,6 +5060,12 @@
Slog.v(TAG_WM, "handleMessage: exit");
}
}
+
+ /** Remove the previous messages with the same 'what' and 'obj' then send the new one. */
+ void sendNewMessageDelayed(int what, Object obj, long delayMillis) {
+ removeMessages(what, obj);
+ sendMessageDelayed(obtainMessage(what, obj), delayMillis);
+ }
}
void destroyPreservedSurfaceLocked() {
@@ -5541,8 +5529,7 @@
mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_ACTIVE;
// XXX should probably keep timeout from
// when we first froze the display.
- mH.removeMessages(H.WINDOW_FREEZE_TIMEOUT);
- mH.sendEmptyMessageDelayed(H.WINDOW_FREEZE_TIMEOUT,
+ mH.sendNewMessageDelayed(H.WINDOW_FREEZE_TIMEOUT, w.getDisplayContent(),
WINDOW_FREEZE_TIMEOUT_DURATION);
}
}
@@ -5667,9 +5654,9 @@
boolean imWindowChanged = false;
if (mInputMethodWindow != null) {
final WindowState prevTarget = mInputMethodTarget;
+
final WindowState newTarget =
displayContent.computeImeTarget(true /* updateImeTarget*/);
-
imWindowChanged = prevTarget != newTarget;
if (mode != UPDATE_FOCUS_WILL_ASSIGN_LAYERS
@@ -5789,8 +5776,7 @@
}
mLatencyTracker.onActionStart(ACTION_ROTATE_SCREEN);
- // TODO(multidisplay): rotation on non-default displays
- if (CUSTOM_SCREEN_ROTATION && displayContent.isDefaultDisplay) {
+ if (CUSTOM_SCREEN_ROTATION) {
mExitAnimId = exitAnim;
mEnterAnimId = enterAnim;
ScreenRotationAnimation screenRotationAnimation =
@@ -6475,7 +6461,6 @@
pw.print(defaultDisplayContent.getLastWindowForcedOrientation());
pw.print(" mLastOrientation=");
pw.println(defaultDisplayContent.getLastOrientation());
- pw.print(" mDeferredRotationPauseCount="); pw.println(mDeferredRotationPauseCount);
pw.print(" Animation settings: disabled="); pw.print(mAnimationsDisabled);
pw.print(" window="); pw.print(mWindowAnimationScaleSetting);
pw.print(" transition="); pw.print(mTransitionAnimationScaleSetting);
@@ -7274,7 +7259,7 @@
synchronized (mWindowMap) {
WindowState windowState = mWindowMap.get(token);
if (windowState != null) {
- outBounds.set(windowState.mFrame);
+ outBounds.set(windowState.getFrameLw());
} else {
outBounds.setEmpty();
}
@@ -7364,16 +7349,18 @@
@Override
public void updateInputMethodWindowStatus(@NonNull IBinder imeToken,
- boolean imeWindowVisible, boolean dismissImeOnBackKeyPressed,
- @Nullable IBinder targetWindowToken) {
+ boolean imeWindowVisible, boolean dismissImeOnBackKeyPressed) {
+ mPolicy.setDismissImeOnBackKeyPressed(dismissImeOnBackKeyPressed);
+ }
+
+ @Override
+ public void updateInputMethodTargetWindow(@NonNull IBinder imeToken,
+ @NonNull IBinder imeTargetWindowToken) {
// TODO (b/34628091): Use this method to address the window animation issue.
if (DEBUG_INPUT_METHOD) {
- Slog.w(TAG_WM, "updateInputMethodWindowStatus: imeToken=" + imeToken
- + " dismissImeOnBackKeyPressed=" + dismissImeOnBackKeyPressed
- + " imeWindowVisible=" + imeWindowVisible
- + " targetWindowToken=" + targetWindowToken);
+ Slog.w(TAG_WM, "updateInputMethodTargetWindow: imeToken=" + imeToken
+ + " imeTargetWindowToken=" + imeTargetWindowToken);
}
- mPolicy.setDismissImeOnBackKeyPressed(dismissImeOnBackKeyPressed);
}
@Override
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index c8c4b58..06ffd5e 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -119,15 +119,9 @@
import static com.android.server.wm.WindowStateProto.ANIMATOR;
import static com.android.server.wm.WindowStateProto.ATTRIBUTES;
import static com.android.server.wm.WindowStateProto.CHILD_WINDOWS;
-import static com.android.server.wm.WindowStateProto.CONTAINING_FRAME;
-import static com.android.server.wm.WindowStateProto.CONTENT_FRAME;
import static com.android.server.wm.WindowStateProto.CONTENT_INSETS;
-import static com.android.server.wm.WindowStateProto.CUTOUT;
-import static com.android.server.wm.WindowStateProto.DECOR_FRAME;
import static com.android.server.wm.WindowStateProto.DESTROYING;
-import static com.android.server.wm.WindowStateProto.DISPLAY_FRAME;
import static com.android.server.wm.WindowStateProto.DISPLAY_ID;
-import static com.android.server.wm.WindowStateProto.FRAME;
import static com.android.server.wm.WindowStateProto.GIVEN_CONTENT_INSETS;
import static com.android.server.wm.WindowStateProto.HAS_SURFACE;
import static com.android.server.wm.WindowStateProto.IDENTIFIER;
@@ -135,10 +129,7 @@
import static com.android.server.wm.WindowStateProto.IS_READY_FOR_DISPLAY;
import static com.android.server.wm.WindowStateProto.IS_VISIBLE;
import static com.android.server.wm.WindowStateProto.OUTSETS;
-import static com.android.server.wm.WindowStateProto.OUTSET_FRAME;
-import static com.android.server.wm.WindowStateProto.OVERSCAN_FRAME;
import static com.android.server.wm.WindowStateProto.OVERSCAN_INSETS;
-import static com.android.server.wm.WindowStateProto.PARENT_FRAME;
import static com.android.server.wm.WindowStateProto.REMOVED;
import static com.android.server.wm.WindowStateProto.REMOVE_ON_EXIT;
import static com.android.server.wm.WindowStateProto.REQUESTED_HEIGHT;
@@ -149,9 +140,9 @@
import static com.android.server.wm.WindowStateProto.SURFACE_POSITION;
import static com.android.server.wm.WindowStateProto.SYSTEM_UI_VISIBILITY;
import static com.android.server.wm.WindowStateProto.VIEW_VISIBILITY;
-import static com.android.server.wm.WindowStateProto.VISIBLE_FRAME;
import static com.android.server.wm.WindowStateProto.VISIBLE_INSETS;
import static com.android.server.wm.WindowStateProto.WINDOW_CONTAINER;
+import static com.android.server.wm.WindowStateProto.WINDOW_FRAMES;
import static com.android.server.wm.utils.CoordinateTransforms.transformRect;
import static com.android.server.wm.utils.CoordinateTransforms.transformToRotation;
@@ -282,6 +273,14 @@
private boolean mDragResizing;
private boolean mDragResizingChangeReported = true;
private int mResizeMode;
+ /**
+ * Special mode that is intended only for the rounded corner overlay: during rotation
+ * transition, we un-rotate the window token such that the window appears as it did before the
+ * rotation.
+ * TODO(b/111504081): Consolidate seamless rotation logic.
+ */
+ final boolean mForceSeamlesslyRotate;
+ ForcedSeamlessRotator mPendingForcedSeamlessRotate;
private RemoteCallbackList<IWindowFocusObserver> mFocusCallbacks;
@@ -358,9 +357,6 @@
private final Rect mLastOutsets = new Rect();
private boolean mOutsetsChanged = false;
- /** Part of the display that has been cut away. See {@link DisplayCutout}. */
- WmDisplayCutout mDisplayCutout = WmDisplayCutout.NO_CUTOUT;
- private WmDisplayCutout mLastDisplayCutout = WmDisplayCutout.NO_CUTOUT;
private boolean mDisplayCutoutChanged;
/**
@@ -401,50 +397,12 @@
float mLastHScale=1, mLastVScale=1;
final Matrix mTmpMatrix = new Matrix();
- // "Real" frame that the application sees, in display coordinate space.
- final Rect mFrame = new Rect();
- final Rect mLastFrame = new Rect();
private boolean mFrameSizeChanged = false;
// Frame that is scaled to the application's coordinate space when in
// screen size compatibility mode.
final Rect mCompatFrame = new Rect();
- final Rect mContainingFrame = new Rect();
-
- final Rect mParentFrame = new Rect();
-
- /** Whether the parent frame would have been different if there was no display cutout. */
- private boolean mParentFrameWasClippedByDisplayCutout;
-
- // The entire screen area of the {@link TaskStack} this window is in. Usually equal to the
- // screen area of the device.
- final Rect mDisplayFrame = new Rect();
-
- // The region of the display frame that the display type supports displaying content on. This
- // is mostly a special case for TV where some displays don’t have the entire display usable.
- // {@link WindowManager.LayoutParams#FLAG_LAYOUT_IN_OVERSCAN} flag can be used to allow
- // window display contents to extend into the overscan region.
- private final Rect mOverscanFrame = new Rect();
-
- // The display frame minus the stable insets. This value is always constant regardless of if
- // the status bar or navigation bar is visible.
- private final Rect mStableFrame = new Rect();
-
- // The area not occupied by the status and navigation bars. So, if both status and navigation
- // bars are visible, the decor frame is equal to the stable frame.
- final Rect mDecorFrame = new Rect();
-
- // Equal to the decor frame if the IME (e.g. keyboard) is not present. Equal to the decor frame
- // minus the area occupied by the IME if the IME is present.
- private final Rect mContentFrame = new Rect();
-
- // Legacy stuff. Generally equal to the content frame expect when the IME for older apps
- // displays hint text.
- final Rect mVisibleFrame = new Rect();
-
- // Frame that includes dead area outside of the surface but where we want to pretend that it's
- // possible to draw.
- private final Rect mOutsetFrame = new Rect();
+ private final WindowFrames mWindowFrames = new WindowFrames();
/**
* Usually empty. Set to the task's tempInsetFrame. See
@@ -671,6 +629,14 @@
private static final float DEFAULT_DIM_AMOUNT_DEAD_WINDOW = 0.5f;
+ void forceSeamlesslyRotateIfAllowed(int oldRotation, int rotation) {
+ if (mForceSeamlesslyRotate) {
+ mPendingForcedSeamlessRotate = new ForcedSeamlessRotator(
+ oldRotation, rotation, getDisplayInfo());
+ mPendingForcedSeamlessRotate.unrotate(this.mToken);
+ }
+ }
+
interface PowerManagerWrapper {
void wakeUp(long time, String reason);
@@ -717,6 +683,7 @@
mSeq = seq;
mEnforceSizeCompat = (mAttrs.privateFlags & PRIVATE_FLAG_COMPATIBLE_WINDOW) != 0;
mPowerManagerWrapper = powerManagerWrapper;
+ mForceSeamlesslyRotate = token.mRoundedCornerOverlay;
if (localLOGV) Slog.v(
TAG, "Window " + this + " client=" + c.asBinder()
+ " token=" + token + " (" + mAttrs.token + ")" + " params=" + a);
@@ -850,10 +817,7 @@
}
@Override
- public void computeFrameLw(Rect parentFrame, Rect displayFrame, Rect overscanFrame,
- Rect contentFrame, Rect visibleFrame, Rect decorFrame, Rect stableFrame,
- Rect outsetFrame, WmDisplayCutout displayCutout,
- boolean parentFrameWasClippedByDisplayCutout) {
+ public void computeFrameLw(WindowFrames windowFrames) {
if (mWillReplaceWindow && (mAnimatingExit || !mReplacingRemoveRequested)) {
// This window is being replaced and either already got information that it's being
// removed or we are still waiting for some information. Because of this we don't
@@ -862,7 +826,8 @@
return;
}
mHaveFrame = true;
- mParentFrameWasClippedByDisplayCutout = parentFrameWasClippedByDisplayCutout;
+ mWindowFrames.setParentFrameWasClippedByDisplayCutout(
+ windowFrames.parentFrameWasClippedByDisplayCutout());
final Task task = getTask();
final boolean inFullscreenContainer = inFullscreenContainer();
@@ -892,36 +857,39 @@
final int layoutYDiff;
if (inFullscreenContainer || layoutInParentFrame()) {
// We use the parent frame as the containing frame for fullscreen and child windows
- mContainingFrame.set(parentFrame);
- mDisplayFrame.set(displayFrame);
- layoutDisplayFrame = displayFrame;
- layoutContainingFrame = parentFrame;
+ mWindowFrames.mContainingFrame.set(windowFrames.mParentFrame);
+ mWindowFrames.mDisplayFrame.set(windowFrames.mDisplayFrame);
+ layoutDisplayFrame = windowFrames.mDisplayFrame;
+ layoutContainingFrame = windowFrames.mParentFrame;
layoutXDiff = 0;
layoutYDiff = 0;
} else {
- getBounds(mContainingFrame);
+ getBounds(mWindowFrames.mContainingFrame);
if (mAppToken != null && !mAppToken.mFrozenBounds.isEmpty()) {
// If the bounds are frozen, we still want to translate the window freely and only
// freeze the size.
Rect frozen = mAppToken.mFrozenBounds.peek();
- mContainingFrame.right = mContainingFrame.left + frozen.width();
- mContainingFrame.bottom = mContainingFrame.top + frozen.height();
+ mWindowFrames.mContainingFrame.right =
+ mWindowFrames.mContainingFrame.left + frozen.width();
+ mWindowFrames.mContainingFrame.bottom =
+ mWindowFrames.mContainingFrame.top + frozen.height();
}
final WindowState imeWin = mService.mInputMethodWindow;
// IME is up and obscuring this window. Adjust the window position so it is visible.
if (imeWin != null && imeWin.isVisibleNow() && isInputMethodTarget()) {
- if (inFreeformWindowingMode()
- && mContainingFrame.bottom > contentFrame.bottom) {
+ if (inFreeformWindowingMode() && mWindowFrames.mContainingFrame.bottom
+ > windowFrames.mContentFrame.bottom) {
// In freeform we want to move the top up directly.
// TODO: Investigate why this is contentFrame not parentFrame.
- mContainingFrame.top -= mContainingFrame.bottom - contentFrame.bottom;
- } else if (!inPinnedWindowingMode()
- && mContainingFrame.bottom > parentFrame.bottom) {
+ mWindowFrames.mContainingFrame.top -= mWindowFrames.mContainingFrame.bottom
+ - windowFrames.mContentFrame.bottom;
+ } else if (!inPinnedWindowingMode() && mWindowFrames.mContainingFrame.bottom
+ > windowFrames.mParentFrame.bottom) {
// But in docked we want to behave like fullscreen and behave as if the task
// were given smaller bounds for the purposes of layout. Skip adjustments for
// the pinned stack, they are handled separately in the PinnedStackController.
- mContainingFrame.bottom = parentFrame.bottom;
+ mWindowFrames.mContainingFrame.bottom = windowFrames.mParentFrame.bottom;
}
}
@@ -929,8 +897,8 @@
// In floating modes (e.g. freeform, pinned) we have only to set the rectangle
// if it wasn't set already. No need to intersect it with the (visible)
// "content frame" since it is allowed to be outside the visible desktop.
- if (mContainingFrame.isEmpty()) {
- mContainingFrame.set(contentFrame);
+ if (mWindowFrames.mContainingFrame.isEmpty()) {
+ mWindowFrames.mContainingFrame.set(windowFrames.mContentFrame);
}
}
@@ -940,31 +908,39 @@
// PIP edge case: When going from pinned to fullscreen, we apply a
// tempInsetFrame for the full task - but we're still at the start of the animation.
// To prevent a jump if there's a letterbox, restrict to the parent frame.
- mInsetFrame.intersectUnchecked(parentFrame);
- mContainingFrame.intersectUnchecked(parentFrame);
+ mInsetFrame.intersectUnchecked(windowFrames.mParentFrame);
+ mWindowFrames.mContainingFrame.intersectUnchecked(windowFrames.mParentFrame);
}
- mDisplayFrame.set(mContainingFrame);
- layoutXDiff = !mInsetFrame.isEmpty() ? mInsetFrame.left - mContainingFrame.left : 0;
- layoutYDiff = !mInsetFrame.isEmpty() ? mInsetFrame.top - mContainingFrame.top : 0;
- layoutContainingFrame = !mInsetFrame.isEmpty() ? mInsetFrame : mContainingFrame;
+ mWindowFrames.mDisplayFrame.set(mWindowFrames.mContainingFrame);
+ layoutXDiff =
+ !mInsetFrame.isEmpty() ? mInsetFrame.left - mWindowFrames.mContainingFrame.left
+ : 0;
+ layoutYDiff =
+ !mInsetFrame.isEmpty() ? mInsetFrame.top - mWindowFrames.mContainingFrame.top
+ : 0;
+ layoutContainingFrame =
+ !mInsetFrame.isEmpty() ? mInsetFrame : mWindowFrames.mContainingFrame;
mTmpRect.set(0, 0, dc.getDisplayInfo().logicalWidth, dc.getDisplayInfo().logicalHeight);
- subtractInsets(mDisplayFrame, layoutContainingFrame, displayFrame, mTmpRect);
+ subtractInsets(mWindowFrames.mDisplayFrame, layoutContainingFrame,
+ windowFrames.mDisplayFrame, mTmpRect);
if (!layoutInParentFrame()) {
- subtractInsets(mContainingFrame, layoutContainingFrame, parentFrame, mTmpRect);
- subtractInsets(mInsetFrame, layoutContainingFrame, parentFrame, mTmpRect);
+ subtractInsets(mWindowFrames.mContainingFrame, layoutContainingFrame,
+ windowFrames.mParentFrame, mTmpRect);
+ subtractInsets(mInsetFrame, layoutContainingFrame, windowFrames.mParentFrame,
+ mTmpRect);
}
- layoutDisplayFrame = displayFrame;
+ layoutDisplayFrame = windowFrames.mDisplayFrame;
layoutDisplayFrame.intersect(layoutContainingFrame);
}
- final int pw = mContainingFrame.width();
- final int ph = mContainingFrame.height();
+ final int pw = mWindowFrames.mContainingFrame.width();
+ final int ph = mWindowFrames.mContainingFrame.height();
- if (!mParentFrame.equals(parentFrame)) {
+ if (!mWindowFrames.mParentFrame.equals(windowFrames.mParentFrame)) {
//Slog.i(TAG_WM, "Window " + this + " content frame from " + mParentFrame
// + " to " + parentFrame);
- mParentFrame.set(parentFrame);
+ mWindowFrames.mParentFrame.set(windowFrames.mParentFrame);
mContentChanged = true;
}
if (mRequestedWidth != mLastRequestedWidth || mRequestedHeight != mLastRequestedHeight) {
@@ -973,146 +949,161 @@
mContentChanged = true;
}
- mOverscanFrame.set(overscanFrame);
- mContentFrame.set(contentFrame);
- mVisibleFrame.set(visibleFrame);
- mDecorFrame.set(decorFrame);
- mStableFrame.set(stableFrame);
- final boolean hasOutsets = outsetFrame != null;
+ mWindowFrames.mOverscanFrame.set(windowFrames.mOverscanFrame);
+ mWindowFrames.mContentFrame.set(windowFrames.mContentFrame);
+ mWindowFrames.mVisibleFrame.set(windowFrames.mVisibleFrame);
+ mWindowFrames.mDecorFrame.set(windowFrames.mDecorFrame);
+ mWindowFrames.mStableFrame.set(windowFrames.mStableFrame);
+ final boolean hasOutsets = !windowFrames.mOutsetFrame.isEmpty();
if (hasOutsets) {
- mOutsetFrame.set(outsetFrame);
+ mWindowFrames.mOutsetFrame.set(windowFrames.mOutsetFrame);
}
- final int fw = mFrame.width();
- final int fh = mFrame.height();
+ final int fw = mWindowFrames.mFrame.width();
+ final int fh = mWindowFrames.mFrame.height();
applyGravityAndUpdateFrame(layoutContainingFrame, layoutDisplayFrame);
// Calculate the outsets before the content frame gets shrinked to the window frame.
if (hasOutsets) {
- mOutsets.set(Math.max(mContentFrame.left - mOutsetFrame.left, 0),
- Math.max(mContentFrame.top - mOutsetFrame.top, 0),
- Math.max(mOutsetFrame.right - mContentFrame.right, 0),
- Math.max(mOutsetFrame.bottom - mContentFrame.bottom, 0));
+ mOutsets.set(
+ Math.max(mWindowFrames.mContentFrame.left - mWindowFrames.mOutsetFrame.left, 0),
+ Math.max(mWindowFrames.mContentFrame.top - mWindowFrames.mOutsetFrame.top, 0),
+ Math.max(mWindowFrames.mOutsetFrame.right - mWindowFrames.mContentFrame.right,
+ 0),
+ Math.max(mWindowFrames.mOutsetFrame.bottom - mWindowFrames.mContentFrame.bottom,
+ 0));
} else {
mOutsets.set(0, 0, 0, 0);
}
// Make sure the content and visible frames are inside of the
// final window frame.
- if (windowsAreFloating && !mFrame.isEmpty()) {
+ if (windowsAreFloating && !mWindowFrames.mFrame.isEmpty()) {
// For pinned workspace the frame isn't limited in any particular
// way since SystemUI controls the bounds. For freeform however
// we want to keep things inside the content frame.
- final Rect limitFrame = task.inPinnedWindowingMode() ? mFrame : mContentFrame;
+ final Rect limitFrame = task.inPinnedWindowingMode() ? mWindowFrames.mFrame
+ : mWindowFrames.mContentFrame;
// Keep the frame out of the blocked system area, limit it in size to the content area
// and make sure that there is always a minimum visible so that the user can drag it
// into a usable area..
- final int height = Math.min(mFrame.height(), limitFrame.height());
- final int width = Math.min(limitFrame.width(), mFrame.width());
+ final int height = Math.min(mWindowFrames.mFrame.height(), limitFrame.height());
+ final int width = Math.min(limitFrame.width(), mWindowFrames.mFrame.width());
final DisplayMetrics displayMetrics = getDisplayContent().getDisplayMetrics();
final int minVisibleHeight = Math.min(height, WindowManagerService.dipToPixel(
MINIMUM_VISIBLE_HEIGHT_IN_DP, displayMetrics));
final int minVisibleWidth = Math.min(width, WindowManagerService.dipToPixel(
MINIMUM_VISIBLE_WIDTH_IN_DP, displayMetrics));
final int top = Math.max(limitFrame.top,
- Math.min(mFrame.top, limitFrame.bottom - minVisibleHeight));
+ Math.min( mWindowFrames.mFrame.top, limitFrame.bottom - minVisibleHeight));
final int left = Math.max(limitFrame.left + minVisibleWidth - width,
- Math.min(mFrame.left, limitFrame.right - minVisibleWidth));
- mFrame.set(left, top, left + width, top + height);
- mContentFrame.set(mFrame);
- mVisibleFrame.set(mContentFrame);
- mStableFrame.set(mContentFrame);
+ Math.min( mWindowFrames.mFrame.left, limitFrame.right - minVisibleWidth));
+ mWindowFrames.mFrame.set(left, top, left + width, top + height);
+ mWindowFrames.mContentFrame.set( mWindowFrames.mFrame);
+ mWindowFrames.mVisibleFrame.set(mWindowFrames.mContentFrame);
+ mWindowFrames.mStableFrame.set(mWindowFrames.mContentFrame);
} else if (mAttrs.type == TYPE_DOCK_DIVIDER) {
- dc.getDockedDividerController().positionDockedStackedDivider(mFrame);
- mContentFrame.set(mFrame);
- if (!mFrame.equals(mLastFrame)) {
+ dc.getDockedDividerController().positionDockedStackedDivider(mWindowFrames.mFrame);
+ mWindowFrames.mContentFrame.set(mWindowFrames.mFrame);
+ if (!mWindowFrames.mFrame.equals(mWindowFrames.mLastFrame)) {
mMovedByResize = true;
}
} else {
- mContentFrame.set(Math.max(mContentFrame.left, mFrame.left),
- Math.max(mContentFrame.top, mFrame.top),
- Math.min(mContentFrame.right, mFrame.right),
- Math.min(mContentFrame.bottom, mFrame.bottom));
+ mWindowFrames.mContentFrame.set(
+ Math.max(mWindowFrames.mContentFrame.left, mWindowFrames.mFrame.left),
+ Math.max(mWindowFrames.mContentFrame.top, mWindowFrames.mFrame.top),
+ Math.min(mWindowFrames.mContentFrame.right, mWindowFrames.mFrame.right),
+ Math.min(mWindowFrames.mContentFrame.bottom, mWindowFrames.mFrame.bottom));
- mVisibleFrame.set(Math.max(mVisibleFrame.left, mFrame.left),
- Math.max(mVisibleFrame.top, mFrame.top),
- Math.min(mVisibleFrame.right, mFrame.right),
- Math.min(mVisibleFrame.bottom, mFrame.bottom));
+ mWindowFrames.mVisibleFrame.set(
+ Math.max(mWindowFrames.mVisibleFrame.left, mWindowFrames.mFrame.left),
+ Math.max(mWindowFrames.mVisibleFrame.top, mWindowFrames.mFrame.top),
+ Math.min(mWindowFrames.mVisibleFrame.right, mWindowFrames.mFrame.right),
+ Math.min(mWindowFrames.mVisibleFrame.bottom, mWindowFrames.mFrame.bottom));
- mStableFrame.set(Math.max(mStableFrame.left, mFrame.left),
- Math.max(mStableFrame.top, mFrame.top),
- Math.min(mStableFrame.right, mFrame.right),
- Math.min(mStableFrame.bottom, mFrame.bottom));
+ mWindowFrames.mStableFrame.set(
+ Math.max(mWindowFrames.mStableFrame.left, mWindowFrames.mFrame.left),
+ Math.max(mWindowFrames.mStableFrame.top, mWindowFrames.mFrame.top),
+ Math.min(mWindowFrames.mStableFrame.right, mWindowFrames.mFrame.right),
+ Math.min(mWindowFrames.mStableFrame.bottom, mWindowFrames.mFrame.bottom));
}
if (inFullscreenContainer && !windowsAreFloating) {
// Windows that are not fullscreen can be positioned outside of the display frame,
// but that is not a reason to provide them with overscan insets.
- mOverscanInsets.set(Math.max(mOverscanFrame.left - layoutContainingFrame.left, 0),
- Math.max(mOverscanFrame.top - layoutContainingFrame.top, 0),
- Math.max(layoutContainingFrame.right - mOverscanFrame.right, 0),
- Math.max(layoutContainingFrame.bottom - mOverscanFrame.bottom, 0));
+ mOverscanInsets.set(
+ Math.max(mWindowFrames.mOverscanFrame.left - layoutContainingFrame.left, 0),
+ Math.max(mWindowFrames.mOverscanFrame.top - layoutContainingFrame.top, 0),
+ Math.max(layoutContainingFrame.right - mWindowFrames.mOverscanFrame.right, 0),
+ Math.max(layoutContainingFrame.bottom - mWindowFrames.mOverscanFrame.bottom,
+ 0));
}
if (mAttrs.type == TYPE_DOCK_DIVIDER) {
// For the docked divider, we calculate the stable insets like a full-screen window
// so it can use it to calculate the snap positions.
- final WmDisplayCutout c = displayCutout.calculateRelativeTo(mDisplayFrame);
- mTmpRect.set(mDisplayFrame);
+ final WmDisplayCutout c = windowFrames.mDisplayCutout.calculateRelativeTo(
+ mWindowFrames.mDisplayFrame);
+ mTmpRect.set(mWindowFrames.mDisplayFrame);
mTmpRect.inset(c.getDisplayCutout().getSafeInsets());
- mTmpRect.intersectUnchecked(mStableFrame);
+ mTmpRect.intersectUnchecked(mWindowFrames.mStableFrame);
- mStableInsets.set(Math.max(mTmpRect.left - mDisplayFrame.left, 0),
- Math.max(mTmpRect.top - mDisplayFrame.top, 0),
- Math.max(mDisplayFrame.right - mTmpRect.right, 0),
- Math.max(mDisplayFrame.bottom - mTmpRect.bottom, 0));
+ mStableInsets.set(Math.max(mTmpRect.left - mWindowFrames.mDisplayFrame.left, 0),
+ Math.max(mTmpRect.top - mWindowFrames.mDisplayFrame.top, 0),
+ Math.max(mWindowFrames.mDisplayFrame.right - mTmpRect.right, 0),
+ Math.max(mWindowFrames.mDisplayFrame.bottom - mTmpRect.bottom, 0));
// The divider doesn't care about insets in any case, so set it to empty so we don't
// trigger a relayout when moving it.
mContentInsets.setEmpty();
mVisibleInsets.setEmpty();
- displayCutout = WmDisplayCutout.NO_CUTOUT;
+ windowFrames.setDisplayCutout(WmDisplayCutout.NO_CUTOUT);
} else {
getDisplayContent().getBounds(mTmpRect);
// Override right and/or bottom insets in case if the frame doesn't fit the screen in
// non-fullscreen mode.
boolean overrideRightInset = !windowsAreFloating && !inFullscreenContainer
- && mFrame.right > mTmpRect.right;
+ && mWindowFrames.mFrame.right > mTmpRect.right;
boolean overrideBottomInset = !windowsAreFloating && !inFullscreenContainer
- && mFrame.bottom > mTmpRect.bottom;
- mContentInsets.set(mContentFrame.left - mFrame.left,
- mContentFrame.top - mFrame.top,
- overrideRightInset ? mTmpRect.right - mContentFrame.right
- : mFrame.right - mContentFrame.right,
- overrideBottomInset ? mTmpRect.bottom - mContentFrame.bottom
- : mFrame.bottom - mContentFrame.bottom);
+ && mWindowFrames.mFrame.bottom > mTmpRect.bottom;
+ mContentInsets.set(mWindowFrames.mContentFrame.left - mWindowFrames.mFrame.left,
+ mWindowFrames.mContentFrame.top - mWindowFrames.mFrame.top,
+ overrideRightInset ? mTmpRect.right - mWindowFrames.mContentFrame.right
+ : mWindowFrames.mFrame.right - mWindowFrames.mContentFrame.right,
+ overrideBottomInset ? mTmpRect.bottom - mWindowFrames.mContentFrame.bottom
+ : mWindowFrames.mFrame.bottom - mWindowFrames.mContentFrame.bottom);
- mVisibleInsets.set(mVisibleFrame.left - mFrame.left,
- mVisibleFrame.top - mFrame.top,
- overrideRightInset ? mTmpRect.right - mVisibleFrame.right
- : mFrame.right - mVisibleFrame.right,
- overrideBottomInset ? mTmpRect.bottom - mVisibleFrame.bottom
- : mFrame.bottom - mVisibleFrame.bottom);
+ mVisibleInsets.set(mWindowFrames.mVisibleFrame.left - mWindowFrames.mFrame.left,
+ mWindowFrames.mVisibleFrame.top - mWindowFrames.mFrame.top,
+ overrideRightInset ? mTmpRect.right - mWindowFrames.mVisibleFrame.right
+ : mWindowFrames.mFrame.right - mWindowFrames.mVisibleFrame.right,
+ overrideBottomInset ? mTmpRect.bottom - mWindowFrames.mVisibleFrame.bottom
+ : mWindowFrames.mFrame.bottom - mWindowFrames.mVisibleFrame.bottom);
- mStableInsets.set(Math.max(mStableFrame.left - mFrame.left, 0),
- Math.max(mStableFrame.top - mFrame.top, 0),
- overrideRightInset ? Math.max(mTmpRect.right - mStableFrame.right, 0)
- : Math.max(mFrame.right - mStableFrame.right, 0),
- overrideBottomInset ? Math.max(mTmpRect.bottom - mStableFrame.bottom, 0)
- : Math.max(mFrame.bottom - mStableFrame.bottom, 0));
+ mStableInsets.set(
+ Math.max(mWindowFrames.mStableFrame.left - mWindowFrames.mFrame.left, 0),
+ Math.max(mWindowFrames.mStableFrame.top - mWindowFrames.mFrame.top, 0),
+ overrideRightInset ? Math.max(mTmpRect.right - mWindowFrames.mStableFrame.right,
+ 0) : Math.max(
+ mWindowFrames.mFrame.right - mWindowFrames.mStableFrame.right, 0),
+ overrideBottomInset ? Math.max(
+ mTmpRect.bottom - mWindowFrames.mStableFrame.bottom, 0) : Math.max(
+ mWindowFrames.mFrame.bottom - mWindowFrames.mStableFrame.bottom, 0));
}
- mDisplayCutout = displayCutout.calculateRelativeTo(mFrame);
+
+ mWindowFrames.setDisplayCutout(
+ windowFrames.mDisplayCutout.calculateRelativeTo(windowFrames.mFrame));
// Offset the actual frame by the amount layout frame is off.
- mFrame.offset(-layoutXDiff, -layoutYDiff);
+ mWindowFrames.mFrame.offset(-layoutXDiff, -layoutYDiff);
mCompatFrame.offset(-layoutXDiff, -layoutYDiff);
- mContentFrame.offset(-layoutXDiff, -layoutYDiff);
- mVisibleFrame.offset(-layoutXDiff, -layoutYDiff);
- mStableFrame.offset(-layoutXDiff, -layoutYDiff);
+ mWindowFrames.mContentFrame.offset(-layoutXDiff, -layoutYDiff);
+ mWindowFrames.mVisibleFrame.offset(-layoutXDiff, -layoutYDiff);
+ mWindowFrames.mStableFrame.offset(-layoutXDiff, -layoutYDiff);
- mCompatFrame.set(mFrame);
+ mCompatFrame.set(mWindowFrames.mFrame);
if (mEnforceSizeCompat) {
// If there is a size compatibility scale being applied to the
// window, we need to apply this to its insets so that they are
@@ -1128,12 +1119,13 @@
mCompatFrame.scale(mInvGlobalScale);
}
- if (mIsWallpaper && (fw != mFrame.width() || fh != mFrame.height())) {
+ if (mIsWallpaper && (fw != mWindowFrames.mFrame.width()
+ || fh != mWindowFrames.mFrame.height())) {
final DisplayContent displayContent = getDisplayContent();
if (displayContent != null) {
final DisplayInfo displayInfo = displayContent.getDisplayInfo();
- getDisplayContent().mWallpaperController.updateWallpaperOffset(
- this, displayInfo.logicalWidth, displayInfo.logicalHeight, false);
+ getDisplayContent().mWallpaperController.updateWallpaperOffset(this,
+ displayInfo.logicalWidth, displayInfo.logicalHeight, false);
}
}
@@ -1141,7 +1133,7 @@
"Resolving (mRequestedWidth="
+ mRequestedWidth + ", mRequestedheight="
+ mRequestedHeight + ") to" + " (pw=" + pw + ", ph=" + ph
- + "): frame=" + mFrame.toShortString()
+ + "): frame=" + mWindowFrames.mFrame.toShortString()
+ " ci=" + mContentInsets.toShortString()
+ " vi=" + mVisibleInsets.toShortString()
+ " si=" + mStableInsets.toShortString()
@@ -1162,31 +1154,47 @@
@Override
public Rect getFrameLw() {
- return mFrame;
+ return mWindowFrames.mFrame;
}
@Override
public Rect getDisplayFrameLw() {
- return mDisplayFrame;
+ return mWindowFrames.mDisplayFrame;
}
@Override
public Rect getOverscanFrameLw() {
- return mOverscanFrame;
+ return mWindowFrames.mOverscanFrame;
}
@Override
public Rect getContentFrameLw() {
- return mContentFrame;
+ return mWindowFrames.mContentFrame;
}
@Override
public Rect getVisibleFrameLw() {
- return mVisibleFrame;
+ return mWindowFrames.mVisibleFrame;
}
Rect getStableFrameLw() {
- return mStableFrame;
+ return mWindowFrames.mStableFrame;
+ }
+
+ Rect getDecorFrame() {
+ return mWindowFrames.mDecorFrame;
+ }
+
+ Rect getParentFrame() {
+ return mWindowFrames.mParentFrame;
+ }
+
+ Rect getContainingFrame() {
+ return mWindowFrames.mContainingFrame;
+ }
+
+ WmDisplayCutout getWmDisplayCutout() {
+ return mWindowFrames.mDisplayCutout;
}
@Override
@@ -1245,9 +1253,8 @@
mVisibleInsetsChanged |= !mLastVisibleInsets.equals(mVisibleInsets);
mStableInsetsChanged |= !mLastStableInsets.equals(mStableInsets);
mOutsetsChanged |= !mLastOutsets.equals(mOutsets);
- mFrameSizeChanged |= (mLastFrame.width() != mFrame.width()) ||
- (mLastFrame.height() != mFrame.height());
- mDisplayCutoutChanged |= !mLastDisplayCutout.equals(mDisplayCutout);
+ mFrameSizeChanged |= mWindowFrames.didFrameSizeChange();
+ mDisplayCutoutChanged |= mWindowFrames.didDisplayCutoutChange();
return mOverscanInsetsChanged || mContentInsetsChanged || mVisibleInsetsChanged
|| mOutsetsChanged || mFrameSizeChanged || mDisplayCutoutChanged;
}
@@ -1282,12 +1289,12 @@
&& !isDragResizingChangeReported();
if (localLOGV) Slog.v(TAG_WM, "Resizing " + this + ": configChanged=" + configChanged
- + " dragResizingChanged=" + dragResizingChanged + " last=" + mLastFrame
- + " frame=" + mFrame);
+ + " dragResizingChanged=" + dragResizingChanged
+ + " last=" + mWindowFrames.mLastFrame + " frame=" + mWindowFrames.mFrame);
// We update mLastFrame always rather than in the conditional with the last inset
// variables, because mFrameSizeChanged only tracks the width and height changing.
- mLastFrame.set(mFrame);
+ mWindowFrames.mLastFrame.set(mWindowFrames.mFrame);
if (mContentInsetsChanged
|| mVisibleInsetsChanged
@@ -1442,13 +1449,13 @@
}
}
- bounds.set(mVisibleFrame);
+ bounds.set(mWindowFrames.mVisibleFrame);
if (intersectWithStackBounds) {
bounds.intersect(mTmpRect);
}
if (bounds.isEmpty()) {
- bounds.set(mFrame);
+ bounds.set(mWindowFrames.mFrame);
if (intersectWithStackBounds) {
bounds.intersect(mTmpRect);
}
@@ -1805,8 +1812,8 @@
// Frame has moved, containing content frame has also moved, and we're not currently
// animating... let's do something.
- final int left = mFrame.left;
- final int top = mFrame.top;
+ final int left = mWindowFrames.mFrame.left;
+ final int top = mWindowFrames.mFrame.top;
final Task task = getTask();
final boolean adjustedForMinimizedDockOrIme = task != null
&& (task.mStack.isAdjustedForMinimizedDockedStack()
@@ -1840,7 +1847,8 @@
private boolean hasMoved() {
return mHasSurface && (mContentChanged || mMovedByResize)
&& !mAnimatingExit
- && (mFrame.top != mLastFrame.top || mFrame.left != mLastFrame.left)
+ && (mWindowFrames.mFrame.top != mWindowFrames.mLastFrame.top
+ || mWindowFrames.mFrame.left != mWindowFrames.mLastFrame.left)
&& (!mIsChildWindow || !getParentWindow().hasMoved());
}
@@ -1854,8 +1862,9 @@
boolean fillsDisplay() {
final DisplayInfo displayInfo = getDisplayInfo();
- return mFrame.left <= 0 && mFrame.top <= 0
- && mFrame.right >= displayInfo.appWidth && mFrame.bottom >= displayInfo.appHeight;
+ return mWindowFrames.mFrame.left <= 0 && mWindowFrames.mFrame.top <= 0
+ && mWindowFrames.mFrame.right >= displayInfo.appWidth
+ && mWindowFrames.mFrame.bottom >= displayInfo.appHeight;
}
/** Returns true if last applied config was not yet requested by client. */
@@ -2493,8 +2502,8 @@
return getWindowConfiguration().keepVisibleDeadAppWindowOnScreen();
}
- /** @return true if this window desires key events. */
- boolean canReceiveKeys() {
+ @Override
+ public boolean canReceiveKeys() {
return isVisibleOrAdding()
&& (mViewVisibility == View.VISIBLE) && !mRemoveOnExit
&& ((mAttrs.flags & WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) == 0)
@@ -2889,10 +2898,10 @@
// All window frames that are fullscreen extend above status bar, but some don't extend
// below navigation bar. Thus, check for display frame for top/left and stable frame for
// bottom right.
- if (win.mFrame.left <= win.mDisplayFrame.left
- && win.mFrame.top <= win.mDisplayFrame.top
- && win.mFrame.right >= win.mStableFrame.right
- && win.mFrame.bottom >= win.mStableFrame.bottom) {
+ if (win.getFrameLw().left <= win.getDisplayFrameLw().left
+ && win.getFrameLw().top <= win.getDisplayFrameLw().top
+ && win.getFrameLw().right >= win.getStableFrameLw().right
+ && win.getFrameLw().bottom >= win.getStableFrameLw().bottom) {
// Is a fullscreen window, like the clock alarm. Show to everyone.
return false;
}
@@ -2909,7 +2918,7 @@
}
void getTouchableRegion(Region outRegion) {
- final Rect frame = mFrame;
+ final Rect frame = mWindowFrames.mFrame;
switch (mTouchableInsets) {
default:
case TOUCHABLE_INSETS_FRAME:
@@ -2994,7 +3003,7 @@
if (DEBUG_ORIENTATION && mWinAnimator.mDrawState == DRAW_PENDING)
Slog.i(TAG, "Resizing " + this + " WITH DRAW PENDING");
- final Rect frame = mFrame;
+ final Rect frame = mWindowFrames.mFrame;
final Rect overscanInsets = mLastOverscanInsets;
final Rect contentInsets = mLastContentInsets;
final Rect visibleInsets = mLastVisibleInsets;
@@ -3003,7 +3012,7 @@
final boolean reportDraw = mWinAnimator.mDrawState == DRAW_PENDING;
final boolean reportOrientation = mReportOrientationChanged;
final int displayId = getDisplayId();
- final DisplayCutout displayCutout = mDisplayCutout.getDisplayCutout();
+ final DisplayCutout displayCutout = getWmDisplayCutout().getDisplayCutout();
if (mAttrs.type != WindowManager.LayoutParams.TYPE_APPLICATION_STARTING
&& mClient instanceof IWindow.Stub) {
// To prevent deadlock simulate one-way call if win.mClient is a local object.
@@ -3136,7 +3145,7 @@
// Only windows with an AppWindowToken are letterboxed.
return false;
}
- if (!mParentFrameWasClippedByDisplayCutout) {
+ if (!mWindowFrames.parentFrameWasClippedByDisplayCutout()) {
// Cutout didn't make a difference, no letterbox
return false;
}
@@ -3159,7 +3168,7 @@
*/
private boolean frameCoversEntireAppTokenBounds() {
mTmpRect.set(mAppToken.getBounds());
- mTmpRect.intersectUnchecked(mFrame);
+ mTmpRect.intersectUnchecked(mWindowFrames.mFrame);
return mAppToken.getBounds().equals(mTmpRect);
}
@@ -3261,10 +3270,7 @@
proto.write(STACK_ID, getStackId());
mAttrs.writeToProto(proto, ATTRIBUTES);
mGivenContentInsets.writeToProto(proto, GIVEN_CONTENT_INSETS);
- mFrame.writeToProto(proto, FRAME);
- mContainingFrame.writeToProto(proto, CONTAINING_FRAME);
- mParentFrame.writeToProto(proto, PARENT_FRAME);
- mContentFrame.writeToProto(proto, CONTENT_FRAME);
+ mWindowFrames.writeToProto(proto, WINDOW_FRAMES);
mContentInsets.writeToProto(proto, CONTENT_INSETS);
mAttrs.surfaceInsets.writeToProto(proto, SURFACE_INSETS);
mSurfacePosition.writeToProto(proto, SURFACE_POSITION);
@@ -3279,16 +3285,10 @@
proto.write(SYSTEM_UI_VISIBILITY, mSystemUiVisibility);
proto.write(HAS_SURFACE, mHasSurface);
proto.write(IS_READY_FOR_DISPLAY, isReadyForDisplay());
- mDisplayFrame.writeToProto(proto, DISPLAY_FRAME);
- mOverscanFrame.writeToProto(proto, OVERSCAN_FRAME);
- mVisibleFrame.writeToProto(proto, VISIBLE_FRAME);
- mDecorFrame.writeToProto(proto, DECOR_FRAME);
- mOutsetFrame.writeToProto(proto, OUTSET_FRAME);
mOverscanInsets.writeToProto(proto, OVERSCAN_INSETS);
mVisibleInsets.writeToProto(proto, VISIBLE_INSETS);
mStableInsets.writeToProto(proto, STABLE_INSETS);
mOutsets.writeToProto(proto, OUTSETS);
- mDisplayCutout.getDisplayCutout().writeToProto(proto, CUTOUT);
proto.write(REMOVE_ON_EXIT, mRemoveOnExit);
proto.write(DESTROYING, mDestroying);
proto.write(REMOVED, mRemoved);
@@ -3405,30 +3405,12 @@
pw.print(prefix); pw.print("mHasSurface="); pw.print(mHasSurface);
pw.print(" isReadyForDisplay()="); pw.print(isReadyForDisplay());
pw.print(" mWindowRemovalAllowed="); pw.println(mWindowRemovalAllowed);
- if (dumpAll) {
- pw.print(prefix); pw.print("mFrame="); mFrame.printShortString(pw);
- pw.print(" last="); mLastFrame.printShortString(pw);
- pw.println();
- }
if (mEnforceSizeCompat) {
pw.print(prefix); pw.print("mCompatFrame="); mCompatFrame.printShortString(pw);
pw.println();
}
if (dumpAll) {
- pw.print(prefix); pw.print("Frames: containing=");
- mContainingFrame.printShortString(pw);
- pw.print(" parent="); mParentFrame.printShortString(pw);
- pw.println();
- pw.print(prefix); pw.print(" display="); mDisplayFrame.printShortString(pw);
- pw.print(" overscan="); mOverscanFrame.printShortString(pw);
- pw.println();
- pw.print(prefix); pw.print(" content="); mContentFrame.printShortString(pw);
- pw.print(" visible="); mVisibleFrame.printShortString(pw);
- pw.println();
- pw.print(prefix); pw.print(" decor="); mDecorFrame.printShortString(pw);
- pw.println();
- pw.print(prefix); pw.print(" outset="); mOutsetFrame.printShortString(pw);
- pw.println();
+ mWindowFrames.dump(pw, prefix);
pw.print(prefix); pw.print("Cur insets: overscan=");
mOverscanInsets.printShortString(pw);
pw.print(" content="); mContentInsets.printShortString(pw);
@@ -3436,8 +3418,6 @@
pw.print(" stable="); mStableInsets.printShortString(pw);
pw.print(" surface="); mAttrs.surfaceInsets.printShortString(pw);
pw.print(" outsets="); mOutsets.printShortString(pw);
- pw.print(" cutout=" + mDisplayCutout.getDisplayCutout());
- pw.println();
pw.print(prefix); pw.print("Lst insets: overscan=");
mLastOverscanInsets.printShortString(pw);
pw.print(" content="); mLastContentInsets.printShortString(pw);
@@ -3445,7 +3425,6 @@
pw.print(" stable="); mLastStableInsets.printShortString(pw);
pw.print(" physical="); mLastOutsets.printShortString(pw);
pw.print(" outset="); mLastOutsets.printShortString(pw);
- pw.print(" cutout=" + mLastDisplayCutout);
pw.println();
}
super.dump(pw, prefix, dumpAll);
@@ -3609,16 +3588,16 @@
// Set mFrame
Gravity.apply(mAttrs.gravity, w, h, containingFrame,
(int) (x + mAttrs.horizontalMargin * pw),
- (int) (y + mAttrs.verticalMargin * ph), mFrame);
+ (int) (y + mAttrs.verticalMargin * ph), mWindowFrames.mFrame);
// Now make sure the window fits in the overall display frame.
if (fitToDisplay) {
- Gravity.applyDisplay(mAttrs.gravity, displayFrame, mFrame);
+ Gravity.applyDisplay(mAttrs.gravity, displayFrame, mWindowFrames.mFrame);
}
// We need to make sure we update the CompatFrame as it is used for
// cropping decisions, etc, on systems where we lack a decor layer.
- mCompatFrame.set(mFrame);
+ mCompatFrame.set(mWindowFrames.mFrame);
if (mEnforceSizeCompat) {
// See comparable block in computeFrameLw.
mCompatFrame.scale(mInvGlobalScale);
@@ -3731,7 +3710,7 @@
}
float translateToWindowX(float x) {
- float winX = x - mFrame.left;
+ float winX = x - mWindowFrames.mFrame.left;
if (mEnforceSizeCompat) {
winX *= mGlobalScale;
}
@@ -3739,7 +3718,7 @@
}
float translateToWindowY(float y) {
- float winY = y - mFrame.top;
+ float winY = y - mWindowFrames.mFrame.top;
if (mEnforceSizeCompat) {
winY *= mGlobalScale;
}
@@ -4303,7 +4282,7 @@
// The decor frame is used to specify the region not covered by the system
// decorations (nav bar, status bar). In case this is empty, for example with
// FLAG_TRANSLUCENT_NAVIGATION, we don't need to do any cropping.
- if (mDecorFrame.isEmpty()) {
+ if (mWindowFrames.mDecorFrame.isEmpty()) {
return true;
}
@@ -4348,12 +4327,12 @@
* by system decorations.
*/
private void calculateSystemDecorRect(Rect systemDecorRect) {
- final Rect decorRect = mDecorFrame;
- final int width = mFrame.width();
- final int height = mFrame.height();
+ final Rect decorRect = mWindowFrames.mDecorFrame;
+ final int width = mWindowFrames.mFrame.width();
+ final int height = mWindowFrames.mFrame.height();
- final int left = mFrame.left;
- final int top = mFrame.top;
+ final int left = mWindowFrames.mFrame.left;
+ final int top = mWindowFrames.mFrame.top;
// Initialize the decor rect to the entire frame.
if (isDockedResizing()) {
@@ -4499,12 +4478,12 @@
mLastVisibleInsets.set(mVisibleInsets);
mLastStableInsets.set(mStableInsets);
mLastOutsets.set(mOutsets);
- mLastDisplayCutout = mDisplayCutout;
+ mWindowFrames.mLastDisplayCutout = mWindowFrames.mDisplayCutout;
}
void startAnimation(Animation anim) {
final DisplayInfo displayInfo = getDisplayContent().getDisplayInfo();
- anim.initialize(mFrame.width(), mFrame.height(),
+ anim.initialize(mWindowFrames.mFrame.width(), mWindowFrames.mFrame.height(),
displayInfo.appWidth, displayInfo.appHeight);
anim.restrictDuration(MAX_ANIMATION_DURATION);
anim.scaleCurrentDuration(mService.getWindowAnimationScaleLocked());
@@ -4519,7 +4498,8 @@
if (DEBUG_ANIM) Slog.v(TAG, "Setting move animation on " + this);
final Point oldPosition = new Point();
final Point newPosition = new Point();
- transformFrameToSurfacePosition(mLastFrame.left, mLastFrame.top, oldPosition);
+ transformFrameToSurfacePosition(mWindowFrames.mLastFrame.left, mWindowFrames.mLastFrame.top,
+ oldPosition);
transformFrameToSurfacePosition(left, top, newPosition);
final AnimationAdapter adapter = new LocalAnimationAdapter(
new MoveAnimationSpec(oldPosition.x, oldPosition.y, newPosition.x, newPosition.y),
@@ -4554,8 +4534,8 @@
final WindowContainer parent = getParent();
if (isChildWindow()) {
final WindowState parentWindow = getParentWindow();
- x += parentWindow.mFrame.left - parentWindow.mAttrs.surfaceInsets.left;
- y += parentWindow.mFrame.top - parentWindow.mAttrs.surfaceInsets.top;
+ x += parentWindow.mWindowFrames.mFrame.left - parentWindow.mAttrs.surfaceInsets.left;
+ y += parentWindow.mWindowFrames.mFrame.top - parentWindow.mAttrs.surfaceInsets.top;
} else if (parent != null) {
final Rect parentBounds = parent.getBounds();
x += parentBounds.left;
@@ -4708,9 +4688,13 @@
return;
}
- transformFrameToSurfacePosition(mFrame.left, mFrame.top, mSurfacePosition);
+ transformFrameToSurfacePosition(mWindowFrames.mFrame.left, mWindowFrames.mFrame.top,
+ mSurfacePosition);
- if (!mSurfaceAnimator.hasLeash() && !mLastSurfacePosition.equals(mSurfacePosition)) {
+ // Freeze position while we're unrotated, so the surface remains at the position it was
+ // prior to the rotation.
+ if (!mSurfaceAnimator.hasLeash() && mPendingForcedSeamlessRotate == null &&
+ !mLastSurfacePosition.equals(mSurfacePosition)) {
t.setPosition(mSurfaceControl, mSurfacePosition.x, mSurfacePosition.y);
mLastSurfacePosition.set(mSurfacePosition.x, mSurfacePosition.y);
if (surfaceInsetsChanging() && mWinAnimator.hasSurface()) {
@@ -4734,8 +4718,8 @@
// Since the parent was outset by its surface insets, we need to undo the outsetting
// with insetting by the same amount.
final WindowState parent = getParentWindow();
- outPoint.offset(-parent.mFrame.left + parent.mAttrs.surfaceInsets.left,
- -parent.mFrame.top + parent.mAttrs.surfaceInsets.top);
+ outPoint.offset(-parent.mWindowFrames.mFrame.left + parent.mAttrs.surfaceInsets.left,
+ -parent.mWindowFrames.mFrame.top + parent.mAttrs.surfaceInsets.top);
} else if (parentWindowContainer != null) {
final Rect parentBounds = parentWindowContainer.getBounds();
outPoint.offset(-parentBounds.left, -parentBounds.top);
@@ -4865,7 +4849,9 @@
@Override
void seamlesslyRotate(Transaction t, int oldRotation, int newRotation) {
- if (!isVisibleNow() || mIsWallpaper) {
+ // Invisible windows, the wallpaper, and force seamlessly rotated windows do not participate
+ // in the regular seamless rotation animation.
+ if (!isVisibleNow() || mIsWallpaper || mForceSeamlesslyRotate) {
return;
}
final Matrix transform = mTmpMatrix;
@@ -4876,7 +4862,7 @@
// we recompute the coordinates of mFrame in the new orientation, so the surface can be
// properly placed.
transformToRotation(oldRotation, newRotation, getDisplayInfo(), transform);
- transformRect(transform, mFrame, null /* tmpRectF */);
+ transformRect(transform, mWindowFrames.mFrame, null /* tmpRectF */);
updateSurfacePosition(t);
mWinAnimator.seamlesslyRotate(t, oldRotation, newRotation);
@@ -4886,6 +4872,26 @@
super.seamlesslyRotate(t, oldRotation, newRotation);
}
+ public void getMaxVisibleBounds(Rect out) {
+ if (out.isEmpty()) {
+ out.set(mWindowFrames.mVisibleFrame);
+ return;
+ }
+
+ if (mWindowFrames.mVisibleFrame.left < out.left) {
+ out.left = mWindowFrames.mVisibleFrame.left;
+ }
+ if (mWindowFrames.mVisibleFrame.top < out.top) {
+ out.top = mWindowFrames.mVisibleFrame.top;
+ }
+ if (mWindowFrames.mVisibleFrame.right > out.right) {
+ out.right = mWindowFrames.mVisibleFrame.right;
+ }
+ if (mWindowFrames.mVisibleFrame.bottom > out.bottom) {
+ out.bottom = mWindowFrames.mVisibleFrame.bottom;
+ }
+ }
+
private final class MoveAnimationSpec implements AnimationSpec {
private final long mDuration;
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 14e0e13..7966d5b 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -540,13 +540,13 @@
}
if (WindowManagerService.localLOGV) Slog.v(TAG, "Got surface: " + mSurfaceController
- + ", set left=" + w.mFrame.left + " top=" + w.mFrame.top
+ + ", set left=" + w.getFrameLw().left + " top=" + w.getFrameLw().top
+ ", animLayer=" + mAnimLayer);
if (SHOW_LIGHT_TRANSACTIONS) {
Slog.i(TAG, ">>> OPEN TRANSACTION createSurfaceLocked");
WindowManagerService.logSurface(w, "CREATE pos=("
- + w.mFrame.left + "," + w.mFrame.top + ") ("
+ + w.getFrameLw().left + "," + w.getFrameLw().top + ") ("
+ width + "x" + height + "), layer=" + mAnimLayer + " HIDE", false);
}
@@ -686,12 +686,16 @@
final int displayId = mWin.getDisplayId();
final ScreenRotationAnimation screenRotationAnimation =
mAnimator.getScreenRotationAnimationLocked(displayId);
- final boolean screenAnimation =
- screenRotationAnimation != null && screenRotationAnimation.isAnimating();
+ // TODO(b/111504081): Consolidate seamless rotation logic.
+ final boolean windowParticipatesInScreenRotationAnimation =
+ !mWin.mForceSeamlesslyRotate;
+ final boolean screenAnimation = screenRotationAnimation != null
+ && screenRotationAnimation.isAnimating()
+ && windowParticipatesInScreenRotationAnimation;
if (screenAnimation) {
// cache often used attributes locally
- final Rect frame = mWin.mFrame;
+ final Rect frame = mWin.getFrameLw();
final float tmpFloats[] = mService.mTmpFloats;
final Matrix tmpMatrix = mWin.mTmpMatrix;
@@ -799,6 +803,13 @@
return false;
}
+ // During forced seamless rotation, the surface bounds get updated with the crop in the
+ // new rotation, which is not compatible with showing the surface in the old rotation.
+ // To work around that we disable cropping for such windows, as it is not necessary anyways.
+ if (w.mForceSeamlesslyRotate) {
+ return false;
+ }
+
// If we're animating, the wallpaper should only
// be updated at the end of the animation.
if (w.mAttrs.type == TYPE_WALLPAPER) {
@@ -811,7 +822,7 @@
w.calculatePolicyCrop(mSystemDecorRect);
if (DEBUG_WINDOW_CROP) Slog.d(TAG, "Applying decor to crop win=" + w + " mDecorFrame="
- + w.mDecorFrame + " mSystemDecorRect=" + mSystemDecorRect);
+ + w.getDecorFrame() + " mSystemDecorRect=" + mSystemDecorRect);
final Task task = w.getTask();
final boolean fullscreen = w.fillsDisplay() || (task != null && task.isFullscreen());
@@ -931,9 +942,9 @@
// Make sure that what we're animating to and from is actually the right size in case
// the window cannot take up the full screen.
- mTmpStackBounds.intersectUnchecked(w.mParentFrame);
- mTmpSourceBounds.intersectUnchecked(w.mParentFrame);
- mTmpAnimatingBounds.intersectUnchecked(w.mParentFrame);
+ mTmpStackBounds.intersectUnchecked(w.getParentFrame());
+ mTmpSourceBounds.intersectUnchecked(w.getParentFrame());
+ mTmpAnimatingBounds.intersectUnchecked(w.getParentFrame());
if (!mTmpSourceBounds.isEmpty()) {
// Get the final target stack bounds, if we are not animating, this is just the
@@ -1498,14 +1509,16 @@
}
}
+ // TODO(b/111504081): Consolidate seamless rotation logic.
+ @Deprecated
void seamlesslyRotate(SurfaceControl.Transaction t, int oldRotation, int newRotation) {
final WindowState w = mWin;
// We rotated the screen, but have not received a new buffer with the correct size yet. In
// the mean time, we rotate the buffer we have to the new orientation.
final Matrix transform = mService.mTmpTransform;
- transformToRotation(oldRotation, newRotation, w.mFrame.width(), w.mFrame.height(),
- transform);
+ transformToRotation(oldRotation, newRotation, w.getFrameLw().width(),
+ w.getFrameLw().height(), transform);
transform.getValues(mService.mTmpFloats);
float DsDx = mService.mTmpFloats[Matrix.MSCALE_X];
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index 1a8753d..aced8e4 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -16,18 +16,10 @@
package com.android.server.wm;
-import static android.app.ActivityTaskManagerInternal.APP_TRANSITION_SNAPSHOT;
-import static android.app.ActivityTaskManagerInternal.APP_TRANSITION_SPLASH_SCREEN;
-import static android.app.ActivityTaskManagerInternal.APP_TRANSITION_WINDOWS_DRAWN;
-
-import static android.view.WindowManager.TRANSIT_CRASHING_ACTIVITY_CLOSE;
-import static android.view.WindowManager.TRANSIT_DOCK_TASK_FROM_RECENTS;
-import static android.view.WindowManager.TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE;
-import static android.view.WindowManager.TRANSIT_TRANSLUCENT_ACTIVITY_OPEN;
-import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG;
-import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
import static android.view.WindowManager.TRANSIT_ACTIVITY_CLOSE;
import static android.view.WindowManager.TRANSIT_ACTIVITY_OPEN;
+import static android.view.WindowManager.TRANSIT_CRASHING_ACTIVITY_CLOSE;
+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_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER;
@@ -39,10 +31,17 @@
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_TRANSLUCENT_ACTIVITY_CLOSE;
+import static android.view.WindowManager.TRANSIT_TRANSLUCENT_ACTIVITY_OPEN;
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.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG;
+import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
+import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_SNAPSHOT;
+import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_SPLASH_SCREEN;
+import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_WINDOWS_DRAWN;
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;
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index b97460a..e411c0a 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -270,12 +270,6 @@
dc.reParentWindowToken(this);
mDisplayContent = dc;
- // The rounded corner overlay should not be rotated. We ensure that by moving it outside
- // the windowing layer.
- if (mRoundedCornerOverlay) {
- mDisplayContent.reparentToOverlay(mPendingTransaction, mSurfaceControl);
- }
-
// TODO(b/36740756): One day this should perhaps be hooked
// up with goodToGo, so we don't move a window
// to another display before the window behind
diff --git a/services/core/java/com/android/server/wm/utils/InsetUtils.java b/services/core/java/com/android/server/wm/utils/InsetUtils.java
index b4a998a..c5b103f 100644
--- a/services/core/java/com/android/server/wm/utils/InsetUtils.java
+++ b/services/core/java/com/android/server/wm/utils/InsetUtils.java
@@ -16,8 +16,11 @@
package com.android.server.wm.utils;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.graphics.Rect;
+
/**
* Utility methods to handle insets represented as rects.
*/
@@ -35,4 +38,30 @@
inOutInsets.right += insetsToAdd.right;
inOutInsets.bottom += insetsToAdd.bottom;
}
+
+ /**
+ * Calculates the insets from the {@code outerFrame} to the {@code innerFrame}.
+ *
+ * Note that if a side of the outer frame is not actually on the outside, the inset for that
+ * side will be clamped to zero.
+ *
+ * @param outerFrame the reference frame from which the insets are calculated
+ * @param innerFrame the inset frame, to which the insets are calculated,
+ * or null to clear the insets.
+ * @param outInsets is set to the result of the inset calculation.
+ */
+ public static void insetsBetweenFrames(@NonNull Rect outerFrame, @Nullable Rect innerFrame,
+ @NonNull Rect outInsets) {
+ if (innerFrame == null) {
+ outInsets.setEmpty();
+ return;
+ }
+ final int w = outerFrame.width();
+ final int h = outerFrame.height();
+ outInsets.set(
+ Math.min(w, Math.max(0, innerFrame.left - outerFrame.left)),
+ Math.min(h, Math.max(0, innerFrame.top - outerFrame.top)),
+ Math.min(w, Math.max(0, outerFrame.right - innerFrame.right)),
+ Math.min(h, Math.max(0, outerFrame.bottom - innerFrame.bottom)));
+ }
}
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 89efe12..9e1191d 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -2,6 +2,8 @@
name: "libservices.core",
defaults: ["libservices.core-libs"],
+ cpp_std: "c++17",
+
cflags: [
"-Wall",
"-Werror",
diff --git a/services/core/jni/com_android_server_AlarmManagerService.cpp b/services/core/jni/com_android_server_AlarmManagerService.cpp
index 921eed9..e79612f 100644
--- a/services/core/jni/com_android_server_AlarmManagerService.cpp
+++ b/services/core/jni/com_android_server_AlarmManagerService.cpp
@@ -194,7 +194,9 @@
uint32_t alarm_idx = events[i].data.u32;
uint64_t unused;
ssize_t err = read(fds[alarm_idx], &unused, sizeof(unused));
- if (err < 0) {
+ // Worth evaluating even if read fails with EAGAIN, since epoll_wait
+ // returned. (see b/78560047#comment34)
+ if (err < 0 && errno != EAGAIN) {
if (alarm_idx == ANDROID_ALARM_TYPE_COUNT && errno == ECANCELED) {
result |= ANDROID_ALARM_TIME_CHANGE_MASK;
} else {
@@ -348,7 +350,7 @@
}
for (size_t i = 0; i < fds.size(); i++) {
- fds[i] = timerfd_create(android_alarm_to_clockid[i], 0);
+ fds[i] = timerfd_create(android_alarm_to_clockid[i], TFD_NONBLOCK);
if (fds[i] < 0) {
log_timerfd_create_error(android_alarm_to_clockid[i]);
close(epollfd);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
index 1c9782f..84de6b4 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
@@ -152,6 +152,11 @@
}
@Override
+ public long forceNetworkLogs() {
+ return 0;
+ }
+
+ @Override
public long forceSecurityLogs() {
return 0;
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 414cf47..ed4d4db 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -5465,7 +5465,14 @@
if (generationResult != KeyChain.KEY_GEN_SUCCESS) {
Log.e(LOG_TAG, String.format(
"KeyChain failed to generate a keypair, error %d.", generationResult));
- return false;
+ switch (generationResult) {
+ case KeyChain.KEY_GEN_STRONGBOX_UNAVAILABLE:
+ throw new ServiceSpecificException(
+ DevicePolicyManager.KEY_GEN_STRONGBOX_UNAVAILABLE,
+ String.format("KeyChain error: %d", generationResult));
+ default:
+ return false;
+ }
}
// Set a grant for the caller here so that when the client calls
@@ -6259,7 +6266,7 @@
}
@Override
- public void reportFailedFingerprintAttempt(int userHandle) {
+ public void reportFailedBiometricAttempt(int userHandle) {
enforceFullCrossUsersPermission(userHandle);
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.BIND_DEVICE_ADMIN, null);
@@ -6270,7 +6277,7 @@
}
@Override
- public void reportSuccessfulFingerprintAttempt(int userHandle) {
+ public void reportSuccessfulBiometricAttempt(int userHandle) {
enforceFullCrossUsersPermission(userHandle);
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.BIND_DEVICE_ADMIN, null);
@@ -10694,7 +10701,6 @@
return false;
}
}
-
return false;
}
@@ -12282,6 +12288,20 @@
}
}
+ @Override
+ public long forceNetworkLogs() {
+ enforceShell("forceNetworkLogs");
+ synchronized (getLockObject()) {
+ if (!isNetworkLoggingEnabledInternalLocked()) {
+ throw new IllegalStateException("logging is not available");
+ }
+ if (mNetworkLogger != null) {
+ return mNetworkLogger.forceBatchFinalization();
+ }
+ return 0;
+ }
+ }
+
/** Pauses security and network logging if there are unaffiliated users on the device */
private void maybePauseDeviceWideLoggingLocked() {
if (!areAllUsersAffiliatedWithDeviceLocked()) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLogger.java b/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLogger.java
index 0967652..4514492 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLogger.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLogger.java
@@ -187,4 +187,8 @@
List<NetworkEvent> retrieveLogs(long batchToken) {
return mNetworkLoggingHandler.retrieveFullLogBatch(batchToken);
}
+
+ long forceBatchFinalization() {
+ return mNetworkLoggingHandler.forceBatchFinalization();
+ }
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLoggingHandler.java b/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLoggingHandler.java
index f91f959..0a7070f 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLoggingHandler.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLoggingHandler.java
@@ -16,6 +16,8 @@
package com.android.server.devicepolicy;
+import static java.util.concurrent.TimeUnit.NANOSECONDS;
+
import android.app.AlarmManager;
import android.app.AlarmManager.OnAlarmListener;
import android.app.admin.DeviceAdminReceiver;
@@ -33,6 +35,7 @@
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.TimeUnit;
/**
* A Handler class for managing network logging on a background thread.
@@ -60,6 +63,12 @@
/** Delay after which older batches get discarded after a retrieval. */
private static final long RETRIEVED_BATCH_DISCARD_DELAY_MS = 5 * 60 * 1000; // 5m
+ /** Throttle batch finalization to 10 seconds.*/
+ private static final long FORCE_FETCH_THROTTLE_NS = TimeUnit.SECONDS.toNanos(10);
+ /** Timestamp of the last call to finalise a batch. Used for throttling forced finalization.*/
+ @GuardedBy("this")
+ private long mLastFinalizationNanos = -1;
+
/** Do not call into mDpm with locks held */
private final DevicePolicyManagerService mDpm;
private final AlarmManager mAlarmManager;
@@ -155,6 +164,26 @@
+ "ms from now.");
}
+ /**
+ * Forces batch finalisation. Throttled to 10 seconds per batch finalisation.
+ * @return the number of milliseconds to wait until batch finalisation can be forced.
+ */
+ long forceBatchFinalization() {
+ Bundle notificationExtras;
+ synchronized (this) {
+ final long toWaitNanos =
+ mLastFinalizationNanos + FORCE_FETCH_THROTTLE_NS - System.nanoTime();
+ if (toWaitNanos > 0) {
+ return NANOSECONDS.toMillis(toWaitNanos) + 1; // Round up.
+ }
+ notificationExtras = finalizeBatchAndBuildDeviceOwnerMessageLocked();
+ }
+ if (notificationExtras != null) {
+ notifyDeviceOwner(notificationExtras);
+ }
+ return 0;
+ }
+
synchronized void pause() {
Slog.d(TAG, "Paused network logging");
mPaused = true;
@@ -192,6 +221,7 @@
@GuardedBy("this")
/** @returns extras if a message should be sent to the device owner */
private Bundle finalizeBatchAndBuildDeviceOwnerMessageLocked() {
+ mLastFinalizationNanos = System.nanoTime();
Bundle notificationExtras = null;
if (mNetworkEvents.size() > 0) {
// Assign ids to the events.
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 2811f71..85de581 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -75,7 +75,8 @@
import com.android.server.display.DisplayManagerService;
import com.android.server.dreams.DreamManagerService;
import com.android.server.emergency.EmergencyAffordanceService;
-import com.android.server.fingerprint.FingerprintService;
+import com.android.server.biometrics.face.FaceService;
+import com.android.server.biometrics.fingerprint.FingerprintService;
import com.android.server.hdmi.HdmiControlService;
import com.android.server.input.InputManagerService;
import com.android.server.job.JobSchedulerService;
@@ -232,6 +233,10 @@
"com.android.internal.car.CarServiceHelperService";
private static final String TIME_DETECTOR_SERVICE_CLASS =
"com.android.server.timedetector.TimeDetectorService$Lifecycle";
+ private static final String TIME_ZONE_DETECTOR_SERVICE_CLASS =
+ "com.android.server.timezonedetector.TimeZoneDetectorService$Lifecycle";
+ private static final String ACCESSIBILITY_MANAGER_SERVICE_CLASS =
+ "com.android.server.accessibility.AccessibilityManagerService$Lifecycle";
private static final String PERSISTENT_DATA_BLOCK_PROP = "ro.frp.pst";
@@ -562,11 +567,10 @@
// TODO: Might need to move after migration to WM.
ActivityTaskManagerService atm = mSystemServiceManager.startService(
ActivityTaskManagerService.Lifecycle.class).getService();
- mActivityManagerService = mSystemServiceManager.startService(
- ActivityManagerService.Lifecycle.class).getService();
+ mActivityManagerService = ActivityManagerService.Lifecycle.startService(
+ mSystemServiceManager, atm);
mActivityManagerService.setSystemServiceManager(mSystemServiceManager);
mActivityManagerService.setInstaller(installer);
- mActivityManagerService.setActivityTaskManager(atm);
traceEnd();
// Power manager needs to be started early because other services need it.
@@ -720,7 +724,7 @@
// Tracks cpu time spent in binder calls
traceBeginAndSlog("StartBinderCallsStatsService");
- BinderCallsStatsService.start();
+ mSystemServiceManager.startService(BinderCallsStatsService.LifeCycle.class);
traceEnd();
}
@@ -955,8 +959,7 @@
traceBeginAndSlog("StartAccessibilityManagerService");
try {
- ServiceManager.addService(Context.ACCESSIBILITY_SERVICE,
- new AccessibilityManagerService(context));
+ mSystemServiceManager.startService(ACCESSIBILITY_MANAGER_SERVICE_CLASS);
} catch (Throwable e) {
reportWtf("starting Accessibility Manager", e);
}
@@ -1243,13 +1246,24 @@
}
traceEnd();
- traceBeginAndSlog("StartTimeDetectorService");
- try {
- mSystemServiceManager.startService(TIME_DETECTOR_SERVICE_CLASS);
- } catch (Throwable e) {
- reportWtf("starting StartTimeDetectorService service", e);
+ final boolean useNewTimeServices = true;
+ if (useNewTimeServices) {
+ traceBeginAndSlog("StartTimeDetectorService");
+ try {
+ mSystemServiceManager.startService(TIME_DETECTOR_SERVICE_CLASS);
+ } catch (Throwable e) {
+ reportWtf("starting StartTimeDetectorService service", e);
+ }
+ traceEnd();
+
+ traceBeginAndSlog("StartTimeZoneDetectorService");
+ try {
+ mSystemServiceManager.startService(TIME_ZONE_DETECTOR_SERVICE_CLASS);
+ } catch (Throwable e) {
+ reportWtf("starting StartTimeZoneDetectorService service", e);
+ }
+ traceEnd();
}
- traceEnd();
if (!isWatch) {
traceBeginAndSlog("StartSearchManagerService");
@@ -1416,7 +1430,12 @@
if (!isWatch) {
traceBeginAndSlog("StartNetworkTimeUpdateService");
try {
- networkTimeUpdater = new NetworkTimeUpdateService(context);
+ if (useNewTimeServices) {
+ networkTimeUpdater = new NewNetworkTimeUpdateService(context);
+ } else {
+ networkTimeUpdater = new OldNetworkTimeUpdateService(context);
+ }
+ Slog.d(TAG, "Using networkTimeUpdater class=" + networkTimeUpdater.getClass());
ServiceManager.addService("network_time_update_service", networkTimeUpdater);
} catch (Throwable e) {
reportWtf("starting NetworkTimeUpdate service", e);
@@ -1513,6 +1532,12 @@
}
traceEnd();
+ if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_FACE)) {
+ traceBeginAndSlog("StartFaceSensor");
+ mSystemServiceManager.startService(FaceService.class);
+ traceEnd();
+ }
+
if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
traceBeginAndSlog("StartFingerprintSensor");
mSystemServiceManager.startService(FingerprintService.class);
diff --git a/services/net/java/android/net/netlink/NetlinkSocket.java b/services/net/java/android/net/netlink/NetlinkSocket.java
index 5af3c29..cfcba3a 100644
--- a/services/net/java/android/net/netlink/NetlinkSocket.java
+++ b/services/net/java/android/net/netlink/NetlinkSocket.java
@@ -59,10 +59,9 @@
final String errPrefix = "Error in NetlinkSocket.sendOneShotKernelMessage";
final long IO_TIMEOUT = 300L;
- FileDescriptor fd;
+ final FileDescriptor fd = forProto(nlProto);
try {
- fd = forProto(nlProto);
connectToKernel(fd);
sendMessage(fd, msg, 0, msg.length, IO_TIMEOUT);
final ByteBuffer bytes = recvMessage(fd, DEFAULT_RECV_BUFSIZE, IO_TIMEOUT);
@@ -96,9 +95,9 @@
} catch (SocketException e) {
Log.e(TAG, errPrefix, e);
throw new ErrnoException(errPrefix, EIO, e);
+ } finally {
+ IoUtils.closeQuietly(fd);
}
-
- IoUtils.closeQuietly(fd);
}
public static FileDescriptor forProto(int nlProto) throws ErrnoException {
diff --git a/services/print/java/com/android/server/print/UserState.java b/services/print/java/com/android/server/print/UserState.java
index 4fbc14c..e8266a5 100644
--- a/services/print/java/com/android/server/print/UserState.java
+++ b/services/print/java/com/android/server/print/UserState.java
@@ -243,7 +243,7 @@
intent.setData(Uri.fromParts("printjob", printJob.getId().flattenToString(), null));
intent.putExtra(PrintManager.EXTRA_PRINT_DOCUMENT_ADAPTER, adapter.asBinder());
intent.putExtra(PrintManager.EXTRA_PRINT_JOB, printJob);
- intent.putExtra(DocumentsContract.EXTRA_PACKAGE_NAME, packageName);
+ intent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName);
IntentSender intentSender = PendingIntent.getActivityAsUser(
mContext, 0, intent, PendingIntent.FLAG_ONE_SHOT
diff --git a/services/robotests/src/android/app/backup/BackupUtilsTest.java b/services/robotests/src/android/app/backup/BackupUtilsTest.java
new file mode 100644
index 0000000..04a2a14
--- /dev/null
+++ b/services/robotests/src/android/app/backup/BackupUtilsTest.java
@@ -0,0 +1,195 @@
+/*
+ * 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.app.backup;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.backup.FullBackup.BackupScheme.PathWithRequiredFlags;
+import android.content.Context;
+import android.platform.test.annotations.Presubmit;
+
+import com.android.server.testing.FrameworkRobolectricTestRunner;
+import com.android.server.testing.SystemLoaderPackages;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.annotation.internal.DoNotInstrument;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+@RunWith(FrameworkRobolectricTestRunner.class)
+@Config(manifest = Config.NONE, sdk = 26)
+@SystemLoaderPackages({"android.app.backup"})
+@Presubmit
+@DoNotInstrument
+public class BackupUtilsTest {
+ private Context mContext;
+
+ @Before
+ public void setUp() throws Exception {
+ mContext = RuntimeEnvironment.application;
+ }
+
+ @Test
+ public void testIsFileSpecifiedInPathList_whenFileAndPathListHasIt() throws Exception {
+ boolean isSpecified =
+ BackupUtils.isFileSpecifiedInPathList(file("a/b.txt"), paths(file("a/b.txt")));
+
+ assertThat(isSpecified).isTrue();
+ }
+
+ @Test
+ public void testIsFileSpecifiedInPathList_whenFileAndPathListHasItsDirectory()
+ throws Exception {
+ boolean isSpecified =
+ BackupUtils.isFileSpecifiedInPathList(file("a/b.txt"), paths(directory("a")));
+
+ assertThat(isSpecified).isTrue();
+ }
+
+ @Test
+ public void testIsFileSpecifiedInPathList_whenFileAndPathListHasOtherFile() throws Exception {
+ boolean isSpecified =
+ BackupUtils.isFileSpecifiedInPathList(file("a/b.txt"), paths(file("a/c.txt")));
+
+ assertThat(isSpecified).isFalse();
+ }
+
+ @Test
+ public void testIsFileSpecifiedInPathList_whenFileAndPathListEmpty() throws Exception {
+ boolean isSpecified = BackupUtils.isFileSpecifiedInPathList(file("a/b.txt"), paths());
+
+ assertThat(isSpecified).isFalse();
+ }
+
+ @Test
+ public void testIsFileSpecifiedInPathList_whenDirectoryAndPathListHasIt() throws Exception {
+ boolean isSpecified =
+ BackupUtils.isFileSpecifiedInPathList(directory("a"), paths(directory("a")));
+
+ assertThat(isSpecified).isTrue();
+ }
+
+ @Test
+ public void testIsFileSpecifiedInPathList_whenDirectoryAndPathListEmpty() throws Exception {
+ boolean isSpecified = BackupUtils.isFileSpecifiedInPathList(directory("a"), paths());
+
+ assertThat(isSpecified).isFalse();
+ }
+
+ @Test
+ public void testIsFileSpecifiedInPathList_whenDirectoryAndPathListHasParent() throws Exception {
+ boolean isSpecified =
+ BackupUtils.isFileSpecifiedInPathList(directory("a/b"), paths(directory("a")));
+
+ assertThat(isSpecified).isFalse();
+ }
+
+ @Test
+ public void testIsFileSpecifiedInPathList_whenFileAndPathListDoesntContainDirectory()
+ throws Exception {
+ boolean isSpecified =
+ BackupUtils.isFileSpecifiedInPathList(file("a/b.txt"), paths(directory("c")));
+
+ assertThat(isSpecified).isFalse();
+ }
+
+ @Test
+ public void testIsFileSpecifiedInPathList_whenFileAndPathListHasDirectoryWhoseNameIsPrefix()
+ throws Exception {
+ boolean isSpecified =
+ BackupUtils.isFileSpecifiedInPathList(file("a/b.txt"), paths(directory("a/b")));
+
+ assertThat(isSpecified).isFalse();
+ }
+
+ @Test
+ public void testIsFileSpecifiedInPathList_whenFileAndPathListHasDirectoryWhoseNameIsPrefix2()
+ throws Exception {
+ boolean isSpecified =
+ BackupUtils.isFileSpecifiedInPathList(
+ file("name/subname.txt"), paths(directory("nam")));
+
+ assertThat(isSpecified).isFalse();
+ }
+
+ @Test
+ public void
+ testIsFileSpecifiedInPathList_whenFileAndPathListContainsFirstNotRelatedAndSecondContainingDirectory()
+ throws Exception {
+ boolean isSpecified =
+ BackupUtils.isFileSpecifiedInPathList(
+ file("a/b.txt"), paths(directory("b"), directory("a")));
+
+ assertThat(isSpecified).isTrue();
+ }
+
+ @Test
+ public void
+ testIsFileSpecifiedInPathList_whenDirectoryAndPathListContainsFirstNotRelatedAndSecondSameDirectory()
+ throws Exception {
+ boolean isSpecified =
+ BackupUtils.isFileSpecifiedInPathList(
+ directory("a/b"), paths(directory("b"), directory("a/b")));
+
+ assertThat(isSpecified).isTrue();
+ }
+
+ @Test
+ public void
+ testIsFileSpecifiedInPathList_whenFileAndPathListContainsFirstNotRelatedFileAndSecondSameFile()
+ throws Exception {
+ boolean isSpecified =
+ BackupUtils.isFileSpecifiedInPathList(
+ file("a/b.txt"), paths(directory("b"), file("a/b.txt")));
+
+ assertThat(isSpecified).isTrue();
+ }
+
+ private File file(String path) throws IOException {
+ File file = new File(mContext.getDataDir(), path);
+ File parent = file.getParentFile();
+ parent.mkdirs();
+ file.createNewFile();
+ if (!file.isFile()) {
+ throw new IOException("Couldn't create file");
+ }
+ return file;
+ }
+
+ private File directory(String path) throws IOException {
+ File directory = new File(mContext.getDataDir(), path);
+ directory.mkdirs();
+ if (!directory.isDirectory()) {
+ throw new IOException("Couldn't create directory");
+ }
+ return directory;
+ }
+
+ private Collection<PathWithRequiredFlags> paths(File... files) {
+ return Stream.of(files)
+ .map(file -> new PathWithRequiredFlags(file.getPath(), 0))
+ .collect(Collectors.toList());
+ }
+}
diff --git a/services/tests/servicestests/Android.mk b/services/tests/servicestests/Android.mk
index a85f21c..8f4e8e4 100644
--- a/services/tests/servicestests/Android.mk
+++ b/services/tests/servicestests/Android.mk
@@ -41,6 +41,7 @@
LOCAL_JAVA_LIBRARIES := \
android.hidl.manager-V1.0-java \
+ android.hardware.tv.cec-V1.0-java \
android.test.mock \
android.test.base android.test.runner \
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityGestureDetectorTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityGestureDetectorTest.java
index 14abb8a1..41b834a 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityGestureDetectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityGestureDetectorTest.java
@@ -195,7 +195,7 @@
point.x, point.y, 0);
// Send event.
- mDetector.onMotionEvent(event, policyFlags);
+ mDetector.onMotionEvent(event, event, policyFlags);
eventTimeMs += PATH_STEP_MILLISEC;
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
index e5c6c6e..412e844 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
@@ -141,4 +141,15 @@
when(mMockUserState.getBindingServicesLocked())
.thenReturn(new HashSet<>(Arrays.asList(componentName)));
}
+
+ @Test
+ public void binderDied_keysGetFlushed() {
+ IBinder mockBinder = mock(IBinder.class);
+ setServiceBinding(COMPONENT_NAME);
+ mConnection.bindLocked();
+ mConnection.onServiceConnected(COMPONENT_NAME, mockBinder);
+ mConnection.binderDied();
+ assertTrue(mConnection.getServiceInfo().crashed);
+ verify(mMockKeyEventDispatcher).flush(mConnection);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityLaunchParamsModifierTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityLaunchParamsModifierTests.java
index f741c70..583a9dd 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityLaunchParamsModifierTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityLaunchParamsModifierTests.java
@@ -50,7 +50,7 @@
@RunWith(AndroidJUnit4.class)
public class ActivityLaunchParamsModifierTests extends ActivityTestsBase {
private ActivityLaunchParamsModifier mModifier;
- private ActivityManagerService mService;
+ private ActivityTaskManagerService mService;
private ActivityStack mStack;
private TaskRecord mTask;
private ActivityRecord mActivity;
@@ -62,7 +62,7 @@
@Override
public void setUp() throws Exception {
super.setUp();
- mService = createActivityManagerService();
+ mService = createActivityTaskManagerService();
mModifier = new ActivityLaunchParamsModifier(mService.mStackSupervisor);
mCurrent = new LaunchParams();
mResult = new LaunchParams();
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
index c70d1e1..01f5384 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
@@ -258,7 +258,7 @@
uidRec.hasInternetPermission = true;
mAms.mActiveUids.put(uid, uidRec);
- final ProcessRecord appRec = new ProcessRecord(null, mBatteryStatsImpl,
+ final ProcessRecord appRec = new ProcessRecord(mAms, mBatteryStatsImpl,
new ApplicationInfo(), TAG, uid);
appRec.thread = Mockito.mock(IApplicationThread.class);
mAms.mLruProcesses.add(appRec);
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityOptionsTest.java b/services/tests/servicestests/src/com/android/server/am/ActivityOptionsTest.java
new file mode 100644
index 0000000..28b37c5
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityOptionsTest.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.am;
+
+import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_ROTATE;
+import static org.junit.Assert.assertEquals;
+
+import android.app.ActivityOptions;
+import android.os.Bundle;
+import android.os.Debug;
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.FlakyTest;
+import android.support.test.filters.MediumTest;
+import android.support.test.runner.AndroidJUnit4;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+
+/**
+ * atest FrameworksServicesTests:ActivityOptionsTest
+ */
+@MediumTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class ActivityOptionsTest {
+
+ @Test
+ public void testMerge_NoClobber() {
+ // Construct some options with set values
+ ActivityOptions opts = ActivityOptions.makeBasic();
+ opts.setLaunchDisplayId(Integer.MAX_VALUE);
+ opts.setLaunchActivityType(ACTIVITY_TYPE_STANDARD);
+ opts.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ opts.setAvoidMoveToFront();
+ opts.setLaunchTaskId(Integer.MAX_VALUE);
+ opts.setLockTaskEnabled(true);
+ opts.setRotationAnimationHint(ROTATION_ANIMATION_ROTATE);
+ opts.setTaskOverlay(true, true);
+ opts.setSplitScreenCreateMode(SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT);
+ Bundle optsBundle = opts.toBundle();
+
+ // Try and merge the constructed options with a new set of options
+ optsBundle.putAll(ActivityOptions.makeBasic().toBundle());
+
+ // Ensure the set values are not clobbered
+ ActivityOptions restoredOpts = ActivityOptions.fromBundle(optsBundle);
+ assertEquals(Integer.MAX_VALUE, restoredOpts.getLaunchDisplayId());
+ assertEquals(ACTIVITY_TYPE_STANDARD, restoredOpts.getLaunchActivityType());
+ assertEquals(WINDOWING_MODE_FULLSCREEN, restoredOpts.getLaunchWindowingMode());
+ assertEquals(true, restoredOpts.getAvoidMoveToFront());
+ assertEquals(Integer.MAX_VALUE, restoredOpts.getLaunchTaskId());
+ assertEquals(true, restoredOpts.getLockTaskMode());
+ assertEquals(ROTATION_ANIMATION_ROTATE, restoredOpts.getRotationAnimationHint());
+ assertEquals(true, restoredOpts.getTaskOverlay());
+ assertEquals(true, restoredOpts.canTaskOverlayResume());
+ assertEquals(SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT,
+ restoredOpts.getSplitScreenCreateMode());
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
index 5ee1c40..6cfa317 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
@@ -43,6 +43,7 @@
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -73,7 +74,7 @@
@Presubmit
@RunWith(AndroidJUnit4.class)
public class ActivityRecordTests extends ActivityTestsBase {
- private ActivityManagerService mService;
+ private ActivityTaskManagerService mService;
private TestActivityStack mStack;
private TaskRecord mTask;
private ActivityRecord mActivity;
@@ -83,7 +84,7 @@
public void setUp() throws Exception {
super.setUp();
- mService = createActivityManagerService();
+ mService = createActivityTaskManagerService();
mStack = mService.mStackSupervisor.getDefaultDisplay().createStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
mTask = new TaskBuilder(mService.mStackSupervisor).setStack(mStack).build();
@@ -126,7 +127,7 @@
pauseFound.value = true;
}
return null;
- }).when(mActivity.app.thread).scheduleTransaction(any());
+ }).when(mActivity.app.getThread()).scheduleTransaction(any());
mActivity.setState(STOPPED, "testPausingWhenVisibleFromStopped");
@@ -136,7 +137,8 @@
assertFalse(pauseFound.value);
// Clear focused stack
- mActivity.mStackSupervisor.mFocusedStack = null;
+ final ActivityDisplay display = mActivity.mStackSupervisor.getDefaultDisplay();
+ when(display.getFocusedStack()).thenReturn(null);
// In the unfocused stack, the activity should move to paused.
mActivity.makeVisibleIfNeeded(null /* starting */, true /* reportToClient */);
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
index 0674d85..3c4fe18 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
@@ -68,7 +68,7 @@
@Presubmit
@RunWith(AndroidJUnit4.class)
public class ActivityStackSupervisorTests extends ActivityTestsBase {
- private ActivityManagerService mService;
+ private ActivityTaskManagerService mService;
private ActivityStackSupervisor mSupervisor;
private ActivityStack mFullscreenStack;
@@ -77,19 +77,19 @@
public void setUp() throws Exception {
super.setUp();
- mService = createActivityManagerService();
+ mService = createActivityTaskManagerService();
mSupervisor = mService.mStackSupervisor;
mFullscreenStack = mService.mStackSupervisor.getDefaultDisplay().createStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
}
/**
- * This test ensures that we do not try to restore a task based off an invalid task id. The
- * stack supervisor is a test version so there will be no tasks present. We should expect
- * {@code null} to be returned in this case.
+ * This test ensures that we do not try to restore a task based off an invalid task id. We
+ * should expect {@code null} to be returned in this case.
*/
@Test
public void testRestoringInvalidTask() throws Exception {
+ ((TestActivityDisplay) mSupervisor.getDefaultDisplay()).removeAllTasks();
TaskRecord task = mSupervisor.anyTaskForIdLocked(0 /*taskId*/,
MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE, null, false /* onTop */);
assertNull(task);
@@ -109,7 +109,7 @@
.setStack(mFullscreenStack).build();
final TaskRecord secondTask = secondActivity.getTask();
- mSupervisor.setFocusStackUnchecked("testReplacingTaskInPinnedStack", mFullscreenStack);
+ mFullscreenStack.moveToFront("testReplacingTaskInPinnedStack");
// Ensure full screen stack has both tasks.
ensureStackPlacement(mFullscreenStack, firstTask, secondTask);
@@ -239,7 +239,7 @@
doReturn(displaySleeping).when(display).isSleeping();
doReturn(keyguardShowing).when(keyguard).isKeyguardOrAodShowing(anyInt());
- mSupervisor.mFocusedStack = isFocusedStack ? stack : null;
+ doReturn(isFocusedStack ? stack : null).when(display).getFocusedStack();
mSupervisor.applySleepTokensLocked(true);
verify(stack, times(expectWakeFromSleep ? 1 : 0)).awakeFromSleepingLocked();
verify(stack, times(expectResumeTopActivity ? 1 : 0)).resumeTopActivityUncheckedLocked(
@@ -253,12 +253,11 @@
doAnswer((InvocationOnMock invocationOnMock) -> {
final SparseIntArray displayIds = invocationOnMock.<SparseIntArray>getArgument(0);
- displayIds.put(0, unknownDisplayId);
+ displayIds.put(0, 0);
+ displayIds.put(1, unknownDisplayId);
return null;
}).when(mSupervisor.mWindowManager).getDisplaysInFocusOrder(any());
- mSupervisor.mFocusedStack = mock(ActivityStack.class);
-
// Supervisor should skip over the non-existent display.
assertEquals(null, mSupervisor.topRunningActivityLocked());
}
@@ -278,7 +277,7 @@
assertEquals(originalStackCount + 1, defaultDisplay.getChildCount());
// Let's pretend that the app has crashed.
- firstActivity.app.thread = null;
+ firstActivity.app.setThread(null);
mService.mStackSupervisor.finishTopCrashedActivitiesLocked(firstActivity.app, "test");
// Verify that the stack was removed.
@@ -330,8 +329,9 @@
@Test
public void testTopRunningActivity() throws Exception {
// Create stack to hold focus
- final ActivityStack emptyStack = mService.mStackSupervisor.getDefaultDisplay()
- .createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ final ActivityDisplay display = mService.mStackSupervisor.getDefaultDisplay();
+ final ActivityStack emptyStack = display.createStack(WINDOWING_MODE_FULLSCREEN,
+ ACTIVITY_TYPE_STANDARD, true /* onTop */);
final KeyguardController keyguard = mSupervisor.getKeyguardController();
final ActivityStack stack = mService.mStackSupervisor.getDefaultDisplay().createStack(
@@ -339,11 +339,9 @@
final ActivityRecord activity = new ActivityBuilder(mService).setCreateTask(true)
.setStack(stack).build();
- mSupervisor.mFocusedStack = emptyStack;
-
doAnswer((InvocationOnMock invocationOnMock) -> {
final SparseIntArray displayIds = invocationOnMock.<SparseIntArray>getArgument(0);
- displayIds.put(0, mSupervisor.getDefaultDisplay().mDisplayId);
+ displayIds.put(0, display.mDisplayId);
return null;
}).when(mSupervisor.mWindowManager).getDisplaysInFocusOrder(any());
@@ -359,7 +357,8 @@
true /* considerKeyguardState */));
// Change focus to stack with activity.
- mSupervisor.mFocusedStack = stack;
+ stack.moveToFront("focusChangeToTestStack");
+ assertEquals(stack, display.getFocusedStack());
assertEquals(activity, mService.mStackSupervisor.topRunningActivityLocked());
assertEquals(null, mService.mStackSupervisor.topRunningActivityLocked(
true /* considerKeyguardState */));
@@ -377,10 +376,12 @@
true /* considerKeyguardState */));
// Change focus back to empty stack
- mSupervisor.mFocusedStack = emptyStack;
- // Ensure the show when locked activity is returned when not the focused stack
- assertEquals(showWhenLockedActivity, mService.mStackSupervisor.topRunningActivityLocked());
- assertEquals(showWhenLockedActivity, mService.mStackSupervisor.topRunningActivityLocked(
+ emptyStack.moveToFront("focusChangeToEmptyStack");
+ assertEquals(emptyStack, display.getFocusedStack());
+ // Looking for running activity only in top and focused stack, so nothing should be returned
+ // from empty stack.
+ assertEquals(null, mService.mStackSupervisor.topRunningActivityLocked());
+ assertEquals(null, mService.mStackSupervisor.topRunningActivityLocked(
true /* considerKeyguardState */));
}
@@ -407,4 +408,27 @@
// Assert that the primary stack is returned.
assertEquals(primaryStack, result);
}
+
+ /**
+ * Verify split-screen primary stack & task can resized by
+ * {@link android.app.IActivityTaskManager#resizeDockedStack} as expect.
+ */
+ @Test
+ public void testResizeDockedStackForSplitScreenPrimary() throws Exception {
+ final Rect TASK_SIZE = new Rect(0, 0, 600, 600);
+ final Rect STACK_SIZE = new Rect(0, 0, 300, 300);
+
+ // Create primary split-screen stack with a task.
+ final ActivityStack primaryStack = mService.mStackSupervisor.getDefaultDisplay()
+ .createStack(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD,
+ true /* onTop */);
+ final TaskRecord task = new TaskBuilder(mSupervisor).setStack(primaryStack).build();
+
+ // Resize dock stack.
+ mService.resizeDockedStack(STACK_SIZE, TASK_SIZE, null, null, null);
+
+ // Verify dock stack & its task bounds if is equal as resized result.
+ assertEquals(primaryStack.getBounds(), STACK_SIZE);
+ assertEquals(task.getBounds(), TASK_SIZE);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
index 01425ed..d51c99b 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
@@ -19,6 +19,7 @@
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_FREEFORM;
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;
@@ -61,7 +62,7 @@
@Presubmit
@RunWith(AndroidJUnit4.class)
public class ActivityStackTests extends ActivityTestsBase {
- private ActivityManagerService mService;
+ private ActivityTaskManagerService mService;
private ActivityStackSupervisor mSupervisor;
private ActivityDisplay mDefaultDisplay;
private ActivityStack mStack;
@@ -72,7 +73,7 @@
public void setUp() throws Exception {
super.setUp();
- mService = createActivityManagerService();
+ mService = createActivityTaskManagerService();
mSupervisor = mService.mStackSupervisor;
mDefaultDisplay = mService.mStackSupervisor.getDefaultDisplay();
mStack = mDefaultDisplay.createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
@@ -166,7 +167,7 @@
public void testStopActivityWhenActivityDestroyed() throws Exception {
final ActivityRecord r = new ActivityBuilder(mService).setTask(mTask).build();
r.info.flags |= ActivityInfo.FLAG_NO_HISTORY;
- mSupervisor.setFocusStackUnchecked("testStopActivityWithDestroy", mStack);
+ mStack.moveToFront("testStopActivityWithDestroy");
mStack.stopActivityLocked(r);
// Mostly testing to make sure there is a crash in the call part, so if we get here we are
// good-to-go!
@@ -473,6 +474,51 @@
}
@Test
+ public void testSetAlwaysOnTop() {
+ final TestActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
+ final ActivityStack pinnedStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+ WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ assertTrue(mDefaultDisplay.getStackAbove(homeStack) == pinnedStack);
+
+ final TestActivityStack alwaysOnTopStack = createStackForShouldBeVisibleTest(
+ mDefaultDisplay, WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD,
+ true /* onTop */);
+ alwaysOnTopStack.setAlwaysOnTop(true);
+ assertTrue(alwaysOnTopStack.isAlwaysOnTop());
+ // Ensure (non-pinned) always on top stack is put below pinned stack.
+ assertTrue(mDefaultDisplay.getStackAbove(alwaysOnTopStack) == pinnedStack);
+
+ final TestActivityStack nonAlwaysOnTopStack = createStackForShouldBeVisibleTest(
+ mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
+ true /* onTop */);
+ // Ensure non always on top stack is put below always on top stacks.
+ assertTrue(mDefaultDisplay.getStackAbove(nonAlwaysOnTopStack) == alwaysOnTopStack);
+
+ final TestActivityStack alwaysOnTopStack2 = createStackForShouldBeVisibleTest(
+ mDefaultDisplay, WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD,
+ true /* onTop */);
+ alwaysOnTopStack2.setAlwaysOnTop(true);
+ assertTrue(alwaysOnTopStack2.isAlwaysOnTop());
+ // Ensure newly created always on top stack is placed above other all always on top stacks.
+ assertTrue(mDefaultDisplay.getStackAbove(alwaysOnTopStack2) == pinnedStack);
+
+ alwaysOnTopStack2.setAlwaysOnTop(false);
+ // Ensure, when always on top is turned off for a stack, the stack is put just below all
+ // other always on top stacks.
+ assertTrue(mDefaultDisplay.getStackAbove(alwaysOnTopStack2) == alwaysOnTopStack);
+ alwaysOnTopStack2.setAlwaysOnTop(true);
+
+ // Ensure always on top state changes properly when windowing mode changes.
+ alwaysOnTopStack2.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ assertFalse(alwaysOnTopStack2.isAlwaysOnTop());
+ assertTrue(mDefaultDisplay.getStackAbove(alwaysOnTopStack2) == alwaysOnTopStack);
+ alwaysOnTopStack2.setWindowingMode(WINDOWING_MODE_FREEFORM);
+ assertTrue(alwaysOnTopStack2.isAlwaysOnTop());
+ assertTrue(mDefaultDisplay.getStackAbove(alwaysOnTopStack2) == pinnedStack);
+ }
+
+ @Test
public void testSplitScreenMoveToFront() throws Exception {
final TestActivityStack splitScreenPrimary = createStackForShouldBeVisibleTest(
mDefaultDisplay, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD,
@@ -500,9 +546,20 @@
private <T extends ActivityStack> T createStackForShouldBeVisibleTest(
ActivityDisplay display, int windowingMode, int activityType, boolean onTop) {
- final T stack = display.createStack(windowingMode, activityType, onTop);
- final ActivityRecord r = new ActivityBuilder(mService).setUid(0).setStack(stack)
- .setCreateTask(true).build();
+ final T stack;
+ if (activityType == ACTIVITY_TYPE_HOME) {
+ // Home stack and activity are created in ActivityTestsBase#setupActivityManagerService
+ stack = mDefaultDisplay.getStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME);
+ if (onTop) {
+ mDefaultDisplay.positionChildAtTop(stack);
+ } else {
+ mDefaultDisplay.positionChildAtBottom(stack);
+ }
+ } else {
+ stack = display.createStack(windowingMode, activityType, onTop);
+ final ActivityRecord r = new ActivityBuilder(mService).setUid(0).setStack(stack)
+ .setCreateTask(true).build();
+ }
return stack;
}
@@ -608,14 +665,13 @@
private void verifyShouldSleepActivities(boolean focusedStack,
boolean keyguardGoingAway, boolean displaySleeping, boolean expected) {
- mSupervisor.mFocusedStack = focusedStack ? mStack : null;
-
final ActivityDisplay display = mock(ActivityDisplay.class);
final KeyguardController keyguardController = mSupervisor.getKeyguardController();
doReturn(display).when(mSupervisor).getActivityDisplay(anyInt());
doReturn(keyguardGoingAway).when(keyguardController).isKeyguardGoingAway();
doReturn(displaySleeping).when(display).isSleeping();
+ doReturn(focusedStack ? mStack : null).when(mSupervisor).getTopDisplayFocusedStack();
assertEquals(expected, mStack.shouldSleepActivities());
}
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStartControllerTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStartControllerTests.java
index a86372a..f5ae46cd 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStartControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStartControllerTests.java
@@ -19,7 +19,9 @@
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import android.app.IApplicationThread;
import android.content.Intent;
+import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -49,7 +51,7 @@
@Presubmit
@RunWith(AndroidJUnit4.class)
public class ActivityStartControllerTests extends ActivityTestsBase {
- private ActivityManagerService mService;
+ private ActivityTaskManagerService mService;
private ActivityStartController mController;
private Factory mFactory;
private ActivityStarter mStarter;
@@ -57,11 +59,11 @@
@Override
public void setUp() throws Exception {
super.setUp();
- mService = createActivityManagerService();
+ mService = createActivityTaskManagerService();
mFactory = mock(Factory.class);
- mController = new ActivityStartController(mService.mActivityTaskManager, mService.mStackSupervisor, mFactory);
- mStarter = spy(new ActivityStarter(mController, mService.mActivityTaskManager, mService.mStackSupervisor,
- mock(ActivityStartInterceptor.class)));
+ mController = new ActivityStartController(mService, mService.mStackSupervisor, mFactory);
+ mStarter = spy(new ActivityStarter(mController, mService,
+ mService.mStackSupervisor, mock(ActivityStartInterceptor.class)));
doReturn(mStarter).when(mFactory).obtain();
}
@@ -77,11 +79,14 @@
final int startFlags = random.nextInt();
final ActivityStack stack = mService.mStackSupervisor.getDefaultDisplay().createStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
- final ProcessRecord process= new ProcessRecord(null, null,
- mService.mContext.getApplicationInfo(), "name", 12345);
+ final WindowProcessController wpc = new WindowProcessController(mService,
+ mService.mContext.getApplicationInfo(), "name", 12345,
+ UserHandle.getUserId(12345), mock(Object.class),
+ mock(WindowProcessListener.class));
+ wpc.setThread(mock(IApplicationThread.class));
mController.addPendingActivityLaunch(
- new PendingActivityLaunch(activity, source, startFlags, stack, process));
+ new PendingActivityLaunch(activity, source, startFlags, stack, wpc));
final boolean resume = random.nextBoolean();
mController.doPendingActivityLaunches(resume);
@@ -96,7 +101,7 @@
@Test
public void testRecycling() throws Exception {
final Intent intent = new Intent();
- final ActivityStarter optionStarter = new ActivityStarter(mController, mService.mActivityTaskManager,
+ final ActivityStarter optionStarter = new ActivityStarter(mController, mService,
mService.mStackSupervisor, mock(ActivityStartInterceptor.class));
optionStarter
.setIntent(intent)
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStartInterceptorTest.java b/services/tests/servicestests/src/com/android/server/am/ActivityStartInterceptorTest.java
index 7f55824..420987d 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStartInterceptorTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStartInterceptorTest.java
@@ -27,6 +27,7 @@
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.when;
+import android.app.ActivityManagerInternal;
import android.app.KeyguardManager;
import android.app.admin.DevicePolicyManagerInternal;
import android.content.Context;
@@ -94,11 +95,11 @@
@Mock
private UserManager mUserManager;
@Mock
- private UserController mUserController;
- @Mock
private KeyguardManager mKeyguardManager;
@Mock
private PackageManagerService mPackageManager;
+ @Mock
+ private ActivityManagerInternal mAmInternal;
private ActivityStartInterceptor mInterceptor;
private ActivityInfo mAInfo = new ActivityInfo();
@@ -107,11 +108,15 @@
public void setUp() {
MockitoAnnotations.initMocks(this);
mService.mAm = mAm;
- mInterceptor = new ActivityStartInterceptor(mService, mSupervisor, mContext,
- mUserController);
+ mService.mAmInternal = mAmInternal;
+ mInterceptor = new ActivityStartInterceptor(mService, mSupervisor, mContext);
mInterceptor.setStates(TEST_USER_ID, TEST_REAL_CALLING_PID, TEST_REAL_CALLING_UID,
TEST_START_FLAGS, TEST_CALLING_PACKAGE);
+ // Mock ActivityManagerInternal
+ LocalServices.removeServiceForTest(ActivityManagerInternal.class);
+ LocalServices.addService(ActivityManagerInternal.class, mAmInternal);
+
// Mock DevicePolicyManagerInternal
LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class);
LocalServices.addService(DevicePolicyManagerInternal.class,
@@ -193,7 +198,7 @@
@Test
public void testWorkChallenge() {
// GIVEN that the user the activity is starting as is currently locked
- when(mUserController.shouldConfirmCredentials(TEST_USER_ID)).thenReturn(true);
+ when(mAmInternal.shouldConfirmCredentials(TEST_USER_ID)).thenReturn(true);
// THEN calling intercept returns true
mInterceptor.intercept(null, null, mAInfo, null, null, 0, 0, null);
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java
index 10d255e..19a3e4a 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java
@@ -55,6 +55,7 @@
import static android.content.Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED;
import static com.android.server.am.ActivityManagerService.ANIMATE;
+import static com.android.server.am.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
@@ -77,6 +78,7 @@
import com.android.server.am.TaskRecord.TaskRecordFactory;
import java.util.ArrayList;
+import java.util.List;
/**
* Tests for the {@link ActivityStarter} class.
@@ -88,7 +90,7 @@
@Presubmit
@RunWith(AndroidJUnit4.class)
public class ActivityStarterTests extends ActivityTestsBase {
- private ActivityManagerService mService;
+ private ActivityTaskManagerService mService;
private ActivityStarter mStarter;
private ActivityStartController mController;
@@ -107,9 +109,9 @@
@Override
public void setUp() throws Exception {
super.setUp();
- mService = createActivityManagerService();
+ mService = createActivityTaskManagerService();
mController = mock(ActivityStartController.class);
- mStarter = new ActivityStarter(mController, mService.mActivityTaskManager, mService.mStackSupervisor,
+ mStarter = new ActivityStarter(mController, mService, mService.mStackSupervisor,
mock(ActivityStartInterceptor.class));
}
@@ -134,7 +136,7 @@
assertTrue(task2.getStack() instanceof PinnedActivityStack);
mStarter.updateBounds(task2, bounds);
- verify(mService.mActivityTaskManager, times(1)).resizeStack(eq(task2.getStack().mStackId),
+ verify(mService, times(1)).resizeStack(eq(task2.getStack().mStackId),
eq(bounds), anyBoolean(), anyBoolean(), anyBoolean(), anyInt());
// In the case of no animation, the stack and task bounds should be set immediately.
@@ -189,20 +191,21 @@
*/
private void verifyStartActivityPreconditions(int preconditions, int launchFlags,
int expectedResult) {
- final ActivityManagerService service = createActivityManagerService();
+ final ActivityTaskManagerService service = mService;
final IPackageManager packageManager = mock(IPackageManager.class);
final ActivityStartController controller = mock(ActivityStartController.class);
- final ActivityStarter starter = new ActivityStarter(controller, service.mActivityTaskManager,
+ final ActivityStarter starter = new ActivityStarter(controller, service,
service.mStackSupervisor, mock(ActivityStartInterceptor.class));
+ prepareStarter(launchFlags);
final IApplicationThread caller = mock(IApplicationThread.class);
// If no caller app, return {@code null} {@link ProcessRecord}.
final ProcessRecord record = containsConditions(preconditions, PRECONDITION_NO_CALLER_APP)
- ? null : new ProcessRecord(null, mock(BatteryStatsImpl.class),
+ ? null : new ProcessRecord(service.mAm, mock(BatteryStatsImpl.class),
mock(ApplicationInfo.class), null, 0);
- doReturn(record).when(service).getRecordForAppLocked(anyObject());
+ doReturn(record).when(service.mAm).getRecordForAppLocked(anyObject());
final Intent intent = new Intent();
intent.setFlags(launchFlags);
@@ -236,8 +239,8 @@
}
if (containsConditions(preconditions, PRECONDITION_DISALLOW_APP_SWITCHING)) {
- doReturn(false).when(service).checkAppSwitchAllowedLocked(anyInt(), anyInt(), anyInt(),
- anyInt(), any());
+ doReturn(false).when(service).checkAppSwitchAllowedLocked(
+ anyInt(), anyInt(), anyInt(), anyInt(), any());
}
if (containsConditions(preconditions,PRECONDITION_CANNOT_START_ANY_ACTIVITY)) {
@@ -282,7 +285,7 @@
// Ensure that {@link ActivityOptions} are aborted with unsuccessful result.
if (expectedResult != START_SUCCESS) {
- final ActivityStarter optionStarter = new ActivityStarter(mController, mService.mActivityTaskManager,
+ final ActivityStarter optionStarter = new ActivityStarter(mController, mService,
mService.mStackSupervisor, mock(ActivityStartInterceptor.class));
final ActivityOptions options = spy(ActivityOptions.makeBasic());
@@ -311,9 +314,6 @@
.setCreateStack(false)
.build();
- // supervisor needs a focused stack.
- mService.mStackSupervisor.mFocusedStack = stack;
-
// use factory that only returns spy task.
final TaskRecordFactory factory = mock(TaskRecordFactory.class);
TaskRecord.setTaskRecordFactory(factory);
@@ -336,7 +336,7 @@
info.applicationInfo = new ApplicationInfo();
info.applicationInfo.packageName = ActivityBuilder.getDefaultComponent().getPackageName();
- return new ActivityStarter(mController, mService.mActivityTaskManager,
+ return new ActivityStarter(mController, mService,
mService.mStackSupervisor, mock(ActivityStartInterceptor.class))
.setIntent(intent)
.setActivityInfo(info);
@@ -403,8 +403,8 @@
reusableActivity.getStack().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
// Set focus back to primary.
- mService.mStackSupervisor.setFocusStackUnchecked("testSplitScreenDeliverToTop",
- focusActivity.getStack());
+ final ActivityStack focusStack = focusActivity.getStack();
+ focusStack.moveToFront("testSplitScreenDeliverToTop");
doReturn(reusableActivity).when(mService.mStackSupervisor).findTaskLocked(any(), anyInt());
@@ -452,11 +452,12 @@
@Test
public void testTaskModeViolation() {
final ActivityDisplay display = mService.mStackSupervisor.getDefaultDisplay();
+ ((TestActivityDisplay) display).removeAllTasks();
assertNoTasks(display);
final ActivityStarter starter = prepareStarter(0);
- final LockTaskController lockTaskController = mService.mActivityTaskManager.getLockTaskController();
+ final LockTaskController lockTaskController = mService.getLockTaskController();
doReturn(true).when(lockTaskController).isLockTaskModeViolation(any());
final int result = starter.setReason("testTaskModeViolation").execute();
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
index 06ac3b0..677fd52 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
@@ -16,11 +16,16 @@
package com.android.server.am;
+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.view.Display.DEFAULT_DISPLAY;
+
+import static com.android.server.am.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
+import static com.android.server.am.ActivityStackSupervisor.ON_TOP;
+
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
@@ -29,11 +34,14 @@
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+import android.app.ActivityManagerInternal;
import android.app.ActivityOptions;
import com.android.server.wm.DisplayWindowController;
import org.junit.Rule;
+import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import android.app.IApplicationThread;
@@ -48,9 +56,11 @@
import android.hardware.display.DisplayManager;
import android.os.HandlerThread;
import android.os.Looper;
+import android.os.UserHandle;
import android.service.voice.IVoiceInteractionSession;
import android.support.test.InstrumentationRegistry;
import android.testing.DexmakerShareClassLoaderRule;
+import android.util.SparseIntArray;
import com.android.internal.app.IVoiceInteractor;
@@ -66,6 +76,8 @@
import org.junit.Before;
import org.mockito.MockitoAnnotations;
+import java.util.List;
+
/**
* A base class to handle common operations in activity related unit tests.
@@ -101,31 +113,45 @@
mHandlerThread.quitSafely();
}
- protected ActivityManagerService createActivityManagerService() {
- final ActivityManagerService service =
- setupActivityManagerService(new TestActivityManagerService(mContext));
- AttributeCache.init(mContext);
- return service;
+ protected ActivityTaskManagerService createActivityTaskManagerService() {
+ final TestActivityTaskManagerService atm =
+ spy(new TestActivityTaskManagerService(mContext));
+ setupActivityManagerService(atm);
+ return atm;
}
- protected ActivityManagerService setupActivityManagerService(
- ActivityManagerService service, ActivityTaskManagerService atm) {
- service = spy(service);
- // Makes sure activity task is created with the spy object.
- atm = spy(atm);
- service.setActivityTaskManager(atm);
+ ActivityManagerService createActivityManagerService() {
+ final TestActivityTaskManagerService atm =
+ spy(new TestActivityTaskManagerService(mContext));
+ return setupActivityManagerService(atm);
+ }
+
+ ActivityManagerService setupActivityManagerService(TestActivityTaskManagerService atm) {
+ AttributeCache.init(mContext);
+ final ActivityManagerService am = spy(new TestActivityManagerService(mContext, atm));
+ setupActivityManagerService(am, atm);
+ return am;
+ }
+
+ void setupActivityManagerService(ActivityManagerService am, ActivityTaskManagerService atm) {
+ atm.setActivityManagerService(am);
+ atm.mAmInternal = am.new LocalService();
+ am.mAtmInternal = atm.new LocalService();
// Makes sure the supervisor is using with the spy object.
atm.mStackSupervisor.setService(atm);
- doReturn(mock(IPackageManager.class)).when(service).getPackageManager();
- doNothing().when(service).grantEphemeralAccessLocked(anyInt(), any(), anyInt(), anyInt());
- service.mWindowManager = prepareMockWindowManager();
- atm.setWindowManager(service.mWindowManager);
- return service;
- }
+ doReturn(mock(IPackageManager.class)).when(am).getPackageManager();
+ doNothing().when(am).grantEphemeralAccessLocked(anyInt(), any(), anyInt(), anyInt());
+ am.mWindowManager = prepareMockWindowManager();
+ atm.setWindowManager(am.mWindowManager);
- protected ActivityManagerService setupActivityManagerService(ActivityManagerService service) {
- return setupActivityManagerService(
- service, new TestActivityTaskManagerService(service.mContext));
+ // Put a home stack on the default display, so that we'll always have something focusable.
+ final TestActivityStackSupervisor supervisor =
+ (TestActivityStackSupervisor) atm.mStackSupervisor;
+ supervisor.mHomeStack = supervisor.mDisplay.createStack(WINDOWING_MODE_FULLSCREEN,
+ ACTIVITY_TYPE_HOME, ON_TOP);
+ final TaskRecord task = new TaskBuilder(atm.mStackSupervisor)
+ .setStack(supervisor.mHomeStack).build();
+ new ActivityBuilder(atm).setTask(task).build();
}
/**
@@ -137,7 +163,7 @@
- private final ActivityManagerService mService;
+ private final ActivityTaskManagerService mService;
private ComponentName mComponent;
private TaskRecord mTaskRecord;
@@ -146,7 +172,7 @@
private ActivityStack mStack;
private int mActivityFlags;
- ActivityBuilder(ActivityManagerService service) {
+ ActivityBuilder(ActivityTaskManagerService service) {
mService = service;
}
@@ -206,8 +232,7 @@
aInfo.applicationInfo.uid = mUid;
aInfo.flags |= mActivityFlags;
- final ActivityRecord activity = new ActivityRecord(mService.mActivityTaskManager,
- null /* caller */,
+ final ActivityRecord activity = new ActivityRecord(mService, null /* caller */,
0 /* launchedFromPid */, 0, null, intent, null,
aInfo /*aInfo*/, new Configuration(), null /* resultTo */, null /* resultWho */,
0 /* reqCode */, false /*componentSpecified*/, false /* rootVoiceInteraction */,
@@ -218,10 +243,12 @@
mTaskRecord.addActivityToTop(activity);
}
- activity.setProcess(new ProcessRecord(null, null,
- mService.mContext.getApplicationInfo(), "name", 12345));
- activity.app.thread = mock(IApplicationThread.class);
-
+ final WindowProcessController wpc = new WindowProcessController(mService,
+ mService.mContext.getApplicationInfo(), "name", 12345,
+ UserHandle.getUserId(12345), mock(Object.class),
+ mock(WindowProcessListener.class));
+ wpc.setThread(mock(IApplicationThread.class));
+ activity.setProcess(wpc);
return activity;
}
}
@@ -238,7 +265,8 @@
private ComponentName mComponent;
private String mPackage;
private int mFlags = 0;
- private int mTaskId = 0;
+ // Task id 0 is reserved in ARC for the home app.
+ private int mTaskId = 1;
private int mUserId = 0;
private IVoiceInteractionSession mVoiceSession;
private boolean mCreateStack = true;
@@ -317,7 +345,7 @@
task.userId = mUserId;
if (mStack != null) {
- mSupervisor.setFocusStackUnchecked("test", mStack);
+ mStack.moveToFront("test");
mStack.addTask(task, true, "creating test task");
task.setStack(mStack);
task.setWindowContainerController();
@@ -351,6 +379,16 @@
TestActivityTaskManagerService(Context context) {
super(context);
+ mSupportsMultiWindow = true;
+ mSupportsMultiDisplay = true;
+ mSupportsSplitScreenMultiWindow = true;
+ mSupportsFreeformWindowManagement = true;
+ mSupportsPictureInPicture = true;
+ }
+
+ @Override
+ int handleIncomingUser(int callingPid, int callingUid, int userId, String name) {
+ return userId;
}
@Override
@@ -398,13 +436,8 @@
*/
protected static class TestActivityManagerService extends ActivityManagerService {
- TestActivityManagerService(Context context) {
- super(context);
- mSupportsMultiWindow = true;
- mSupportsMultiDisplay = true;
- mSupportsSplitScreenMultiWindow = true;
- mSupportsFreeformWindowManagement = true;
- mSupportsPictureInPicture = true;
+ TestActivityManagerService(Context context, TestActivityTaskManagerService atm) {
+ super(context, atm);
}
@Override
@@ -449,13 +482,6 @@
ActivityDisplay getDefaultDisplay() {
return mDisplay;
}
-
- // Just return the current front task. This is called internally so we cannot use spy to mock this out.
- @Override
- ActivityStack getNextFocusableStackLocked(ActivityStack currentFocus,
- boolean ignoreCurrent) {
- return mFocusedStack;
- }
}
protected static class TestActivityDisplay extends ActivityDisplay {
@@ -492,6 +518,15 @@
protected DisplayWindowController createWindowContainerController() {
return mock(DisplayWindowController.class);
}
+
+ void removeAllTasks() {
+ for (int i = 0; i < getChildCount(); i++) {
+ final ActivityStack stack = getChildAt(i);
+ for (TaskRecord task : (List<TaskRecord>) stack.getAllTasks()) {
+ stack.removeTask(task, "removeAllTasks", REMOVE_TASK_MODE_DESTROYING);
+ }
+ }
+ }
}
private static WindowManagerService prepareMockWindowManager() {
@@ -505,6 +540,12 @@
return null;
}).when(service).inSurfaceTransaction(any());
+ doAnswer((InvocationOnMock invocationOnMock) -> {
+ final SparseIntArray displayIds = invocationOnMock.<SparseIntArray>getArgument(0);
+ displayIds.put(0, 0);
+ return null;
+ }).when(service).getDisplaysInFocusOrder(any());
+
return service;
}
diff --git a/services/tests/servicestests/src/com/android/server/am/AssistDataRequesterTest.java b/services/tests/servicestests/src/com/android/server/am/AssistDataRequesterTest.java
index 0a436b9..f74d116 100644
--- a/services/tests/servicestests/src/com/android/server/am/AssistDataRequesterTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/AssistDataRequesterTest.java
@@ -113,7 +113,7 @@
mHandler = new Handler(Looper.getMainLooper());
mCallbacksLock = new Object();
mCallbacks = new Callbacks();
- mDataRequester = new AssistDataRequester(mContext, mAm, mWm, mAppOpsManager, mCallbacks,
+ mDataRequester = new AssistDataRequester(mContext, mWm, mAppOpsManager, mCallbacks,
mCallbacksLock, OP_ASSIST_STRUCTURE, OP_ASSIST_SCREENSHOT);
// Gate the continuation of the assist data callbacks until we are ready within the tests
diff --git a/services/tests/servicestests/src/com/android/server/am/LaunchParamsControllerTests.java b/services/tests/servicestests/src/com/android/server/am/LaunchParamsControllerTests.java
index 93e0b5a..fbe552d 100644
--- a/services/tests/servicestests/src/com/android/server/am/LaunchParamsControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/LaunchParamsControllerTests.java
@@ -57,14 +57,14 @@
@Presubmit
@RunWith(AndroidJUnit4.class)
public class LaunchParamsControllerTests extends ActivityTestsBase {
- private ActivityManagerService mService;
+ private ActivityTaskManagerService mService;
private LaunchParamsController mController;
@Before
@Override
public void setUp() throws Exception {
super.setUp();
- mService = createActivityManagerService();
+ mService = createActivityTaskManagerService();
mController = new LaunchParamsController(mService);
}
@@ -200,9 +200,9 @@
mController.registerModifier(positioner);
- doNothing().when(mService.mActivityTaskManager).moveStackToDisplay(anyInt(), anyInt());
+ doNothing().when(mService).moveStackToDisplay(anyInt(), anyInt());
mController.layoutTask(task, null /* windowLayout */);
- verify(mService.mActivityTaskManager, times(1)).moveStackToDisplay(eq(task.getStackId()),
+ verify(mService, times(1)).moveStackToDisplay(eq(task.getStackId()),
eq(params.mPreferredDisplayId));
}
diff --git a/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java b/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
index cd70677..37de795 100644
--- a/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
@@ -94,7 +94,7 @@
private static int INVALID_STACK_ID = 999;
private Context mContext = InstrumentationRegistry.getContext();
- private ActivityManagerService mService;
+ private TestActivityTaskManagerService mService;
private ActivityDisplay mDisplay;
private ActivityDisplay mOtherDisplay;
private ActivityStack mStack;
@@ -108,48 +108,18 @@
private CallbacksRecorder mCallbacksRecorder;
- class TestUserController extends UserController {
- TestUserController(ActivityManagerService service) {
- super(service);
- }
-
- @Override
- int[] getCurrentProfileIds() {
- return new int[] { TEST_USER_0_ID, TEST_QUIET_USER_ID };
- }
-
- @Override
- Set<Integer> getProfileIds(int userId) {
- Set<Integer> profileIds = new HashSet<>();
- profileIds.add(TEST_USER_0_ID);
- profileIds.add(TEST_QUIET_USER_ID);
- return profileIds;
- }
-
- @Override
- UserInfo getUserInfo(int userId) {
- switch (userId) {
- case TEST_USER_0_ID:
- case TEST_USER_1_ID:
- return DEFAULT_USER_INFO;
- case TEST_QUIET_USER_ID:
- return QUIET_USER_INFO;
- }
- return null;
- }
- }
-
@Before
@Override
public void setUp() throws Exception {
super.setUp();
mTaskPersister = new TestTaskPersister(mContext.getFilesDir());
- mService = setupActivityManagerService(new MyTestActivityManagerService(mContext),
- new MyTestActivityTaskManagerService(mContext));
- mRecentTasks = (TestRecentTasks) mService.mActivityTaskManager.getRecentTasks();
+ mService = spy(new MyTestActivityTaskManagerService(mContext));
+ final ActivityManagerService am = spy(new MyTestActivityManagerService(mContext, mService));
+ setupActivityManagerService(am, mService);
+ mRecentTasks = (TestRecentTasks) mService.getRecentTasks();
mRecentTasks.loadParametersFromResources(mContext.getResources());
- mHomeStack = mService.mStackSupervisor.getDefaultDisplay().createStack(
+ mHomeStack = mService.mStackSupervisor.getDefaultDisplay().getOrCreateStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
mStack = mService.mStackSupervisor.getDefaultDisplay().createStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
@@ -674,9 +644,8 @@
@Test
public void testNotRecentsComponent_denyApiAccess() throws Exception {
- doReturn(PackageManager.PERMISSION_DENIED).when(mService).checkPermission(anyString(),
- anyInt(), anyInt());
-
+ doReturn(PackageManager.PERMISSION_DENIED).when(mService)
+ .checkGetTasksPermission(anyString(), anyInt(), anyInt());
// Expect the following methods to fail due to recents component not being set
mRecentTasks.setIsCallerRecentsOverride(TestRecentTasks.DENY_THROW_SECURITY_EXCEPTION);
testRecentTasksApis(false /* expectNoSecurityException */);
@@ -687,8 +656,8 @@
@Test
public void testRecentsComponent_allowApiAccessWithoutPermissions() throws Exception {
- doReturn(PackageManager.PERMISSION_DENIED).when(mService).checkPermission(anyString(),
- anyInt(), anyInt());
+ doReturn(PackageManager.PERMISSION_DENIED).when(mService)
+ .checkGetTasksPermission(anyString(), anyInt(), anyInt());
// Set the recents component and ensure that the following calls do not fail
mRecentTasks.setIsCallerRecentsOverride(TestRecentTasks.GRANT);
@@ -697,58 +666,52 @@
}
private void testRecentTasksApis(boolean expectCallable) {
- assertSecurityException(expectCallable, () -> mService.mActivityTaskManager.removeStack(INVALID_STACK_ID));
+ assertSecurityException(expectCallable, () -> mService.removeStack(INVALID_STACK_ID));
assertSecurityException(expectCallable,
- () -> mService.mActivityTaskManager.removeStacksInWindowingModes(
- new int[] {WINDOWING_MODE_UNDEFINED}));
+ () -> mService.removeStacksInWindowingModes(new int[] {WINDOWING_MODE_UNDEFINED}));
assertSecurityException(expectCallable,
- () -> mService.mActivityTaskManager.removeStacksWithActivityTypes(
- new int[] {ACTIVITY_TYPE_UNDEFINED}));
+ () -> mService.removeStacksWithActivityTypes(new int[] {ACTIVITY_TYPE_UNDEFINED}));
assertSecurityException(expectCallable, () -> mService.removeTask(0));
assertSecurityException(expectCallable,
- () -> mService.mActivityTaskManager.setTaskWindowingMode(
- 0, WINDOWING_MODE_UNDEFINED, true));
+ () -> mService.setTaskWindowingMode(0, WINDOWING_MODE_UNDEFINED, true));
assertSecurityException(expectCallable,
() -> mService.moveTaskToStack(0, INVALID_STACK_ID, true));
assertSecurityException(expectCallable,
- () -> mService.mActivityTaskManager.setTaskWindowingModeSplitScreenPrimary(0,
+ () -> mService.setTaskWindowingModeSplitScreenPrimary(0,
SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT, true, true, new Rect(), true));
- assertSecurityException(expectCallable, () -> mService.mActivityTaskManager.dismissSplitScreenMode(true));
- assertSecurityException(expectCallable, () -> mService.mActivityTaskManager.dismissPip(true, 0));
+ assertSecurityException(expectCallable, () -> mService.dismissSplitScreenMode(true));
+ assertSecurityException(expectCallable, () -> mService.dismissPip(true, 0));
assertSecurityException(expectCallable,
- () -> mService.mActivityTaskManager.moveTopActivityToPinnedStack(INVALID_STACK_ID, new Rect()));
+ () -> mService.moveTopActivityToPinnedStack(INVALID_STACK_ID, new Rect()));
assertSecurityException(expectCallable,
() -> mService.resizeStack(INVALID_STACK_ID, new Rect(), true, true, true, 0));
assertSecurityException(expectCallable,
- () -> mService.mActivityTaskManager.resizeDockedStack(new Rect(), new Rect(), new Rect(), new Rect(),
+ () -> mService.resizeDockedStack(new Rect(), new Rect(), new Rect(), new Rect(),
new Rect()));
assertSecurityException(expectCallable,
- () -> mService.mActivityTaskManager.resizePinnedStack(new Rect(), new Rect()));
- assertSecurityException(expectCallable, () -> mService.mActivityTaskManager.getAllStackInfos());
+ () -> mService.resizePinnedStack(new Rect(), new Rect()));
+ assertSecurityException(expectCallable, () -> mService.getAllStackInfos());
assertSecurityException(expectCallable,
- () -> mService.mActivityTaskManager.getStackInfo(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_UNDEFINED));
+ () -> mService.getStackInfo(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_UNDEFINED));
assertSecurityException(expectCallable, () -> {
try {
- mService.mActivityTaskManager.getFocusedStackInfo();
+ mService.getFocusedStackInfo();
} catch (RemoteException e) {
// Ignore
}
});
assertSecurityException(expectCallable,
- () -> mService.mActivityTaskManager.moveTasksToFullscreenStack(INVALID_STACK_ID, true));
+ () -> mService.moveTasksToFullscreenStack(INVALID_STACK_ID, true));
assertSecurityException(expectCallable,
- () -> mService.mActivityTaskManager.startActivityFromRecents(0, new Bundle()));
- assertSecurityException(expectCallable,
- () -> mService.mActivityTaskManager.getTaskSnapshot(0, true));
- assertSecurityException(expectCallable,
- () -> mService.mActivityTaskManager.registerTaskStackListener(null));
- assertSecurityException(expectCallable,
- () -> mService.mActivityTaskManager.unregisterTaskStackListener(null));
- assertSecurityException(expectCallable, () -> mService.mActivityTaskManager.getTaskDescription(0));
- assertSecurityException(expectCallable, () -> mService.mActivityTaskManager.cancelTaskWindowTransition(0));
- assertSecurityException(expectCallable, () -> mService.mActivityTaskManager.startRecentsActivity(null, null,
+ () -> mService.startActivityFromRecents(0, new Bundle()));
+ assertSecurityException(expectCallable,() -> mService.getTaskSnapshot(0, true));
+ assertSecurityException(expectCallable,() -> mService.registerTaskStackListener(null));
+ assertSecurityException(expectCallable,() -> mService.unregisterTaskStackListener(null));
+ assertSecurityException(expectCallable, () -> mService.getTaskDescription(0));
+ assertSecurityException(expectCallable, () -> mService.cancelTaskWindowTransition(0));
+ assertSecurityException(expectCallable, () -> mService.startRecentsActivity(null, null,
null));
- assertSecurityException(expectCallable, () -> mService.mActivityTaskManager.cancelRecentsAnimation(true));
+ assertSecurityException(expectCallable, () -> mService.cancelRecentsAnimation(true));
assertSecurityException(expectCallable, () -> mService.stopAppSwitches());
assertSecurityException(expectCallable, () -> mService.resumeAppSwitches());
}
@@ -835,7 +798,7 @@
@Override
protected RecentTasks createRecentTasks() {
- return new TestRecentTasks(this, mTaskPersister, new TestUserController(mAm));
+ return new TestRecentTasks(this, mTaskPersister);
}
@Override
@@ -846,8 +809,8 @@
}
private class MyTestActivityManagerService extends TestActivityManagerService {
- MyTestActivityManagerService(Context context) {
- super(context);
+ MyTestActivityManagerService(Context context, TestActivityTaskManagerService atm) {
+ super(context, atm);
}
@Override
@@ -960,9 +923,33 @@
boolean lastAllowed;
- TestRecentTasks(ActivityTaskManagerService service, TaskPersister taskPersister,
- UserController userController) {
- super(service, taskPersister, userController);
+ TestRecentTasks(ActivityTaskManagerService service, TaskPersister taskPersister) {
+ super(service, taskPersister);
+ }
+
+ @Override
+ Set<Integer> getProfileIds(int userId) {
+ Set<Integer> profileIds = new HashSet<>();
+ profileIds.add(TEST_USER_0_ID);
+ profileIds.add(TEST_QUIET_USER_ID);
+ return profileIds;
+ }
+
+ @Override
+ UserInfo getUserInfo(int userId) {
+ switch (userId) {
+ case TEST_USER_0_ID:
+ case TEST_USER_1_ID:
+ return DEFAULT_USER_INFO;
+ case TEST_QUIET_USER_ID:
+ return QUIET_USER_INFO;
+ }
+ return null;
+ }
+
+ @Override
+ int[] getCurrentProfileIds() {
+ return new int[] { TEST_USER_0_ID, TEST_QUIET_USER_ID };
}
@Override
diff --git a/services/tests/servicestests/src/com/android/server/am/RecentsAnimationTest.java b/services/tests/servicestests/src/com/android/server/am/RecentsAnimationTest.java
index 91a02e4..b642d26 100644
--- a/services/tests/servicestests/src/com/android/server/am/RecentsAnimationTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/RecentsAnimationTest.java
@@ -16,18 +16,16 @@
package com.android.server.am;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
-import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -51,10 +49,9 @@
@Presubmit
@RunWith(AndroidJUnit4.class)
public class RecentsAnimationTest extends ActivityTestsBase {
- private static final int TEST_CALLING_PID = 3;
private Context mContext = InstrumentationRegistry.getContext();
- private ActivityManagerService mService;
+ private TestActivityTaskManagerService mService;
private ComponentName mRecentsComponent;
@Before
@@ -63,9 +60,8 @@
super.setUp();
mRecentsComponent = new ComponentName(mContext.getPackageName(), "RecentsActivity");
- mService = setupActivityManagerService(new TestActivityManagerService(mContext),
- new MyTestActivityTaskManagerService(mContext));
- AttributeCache.init(mContext);
+ mService = spy(new MyTestActivityTaskManagerService(mContext));
+ setupActivityManagerService(mService);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/am/RunningTasksTest.java b/services/tests/servicestests/src/com/android/server/am/RunningTasksTest.java
index c6ce7e1..944f20f 100644
--- a/services/tests/servicestests/src/com/android/server/am/RunningTasksTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/RunningTasksTest.java
@@ -50,7 +50,7 @@
public class RunningTasksTest extends ActivityTestsBase {
private Context mContext = InstrumentationRegistry.getContext();
- private ActivityManagerService mService;
+ private ActivityTaskManagerService mService;
private RunningTasks mRunningTasks;
@@ -59,7 +59,7 @@
public void setUp() throws Exception {
super.setUp();
- mService = createActivityManagerService();
+ mService = createActivityTaskManagerService();
mRunningTasks = new RunningTasks();
}
diff --git a/services/tests/servicestests/src/com/android/server/am/TaskLaunchParamsModifierTests.java b/services/tests/servicestests/src/com/android/server/am/TaskLaunchParamsModifierTests.java
index 3d323f0..f71a6e7 100644
--- a/services/tests/servicestests/src/com/android/server/am/TaskLaunchParamsModifierTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/TaskLaunchParamsModifierTests.java
@@ -57,7 +57,7 @@
private final static Rect STACK_BOUNDS = new Rect(0, 0, STACK_WIDTH, STACK_HEIGHT);
- private ActivityManagerService mService;
+ private ActivityTaskManagerService mService;
private ActivityStack mStack;
private TaskRecord mTask;
@@ -71,7 +71,7 @@
public void setUp() throws Exception {
super.setUp();
- mService = createActivityManagerService();
+ mService = createActivityTaskManagerService();
mStack = mService.mStackSupervisor.getDefaultDisplay().createStack(
WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD, true /* onTop */);
mStack.requestResize(STACK_BOUNDS);
diff --git a/services/tests/servicestests/src/com/android/server/am/TaskPersisterTest.java b/services/tests/servicestests/src/com/android/server/am/TaskPersisterTest.java
index 9e6055d..8d54bc2 100644
--- a/services/tests/servicestests/src/com/android/server/am/TaskPersisterTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/TaskPersisterTest.java
@@ -44,7 +44,11 @@
super.setUp();
mUserManager = UserManager.get(getContext());
mTaskPersister = new TaskPersister(getContext().getFilesDir());
- testUserId = createUser(TEST_USER_NAME, 0);
+ // In ARC, the maximum number of supported users is one, which is different from the ones of
+ // most phones (more than 4). This prevents TaskPersisterTest from creating another user for
+ // test. However, since guest users can be added as much as possible, we create guest user
+ // in the test.
+ testUserId = createUser(TEST_USER_NAME, UserInfo.FLAG_GUEST);
}
@Override
diff --git a/services/tests/servicestests/src/com/android/server/am/TaskRecordTests.java b/services/tests/servicestests/src/com/android/server/am/TaskRecordTests.java
index 72851d0..b8680bf 100644
--- a/services/tests/servicestests/src/com/android/server/am/TaskRecordTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/TaskRecordTests.java
@@ -156,9 +156,9 @@
private TaskRecord createTaskRecord(int taskId) {
return new TaskRecord(mService.mActivityTaskManager, taskId, new Intent(), null, null, null,
- null, null, false,
- false, false, 0, 10050, null, new ArrayList<>(), 0, false, null, 0, 0, 0, 0, 0,
- null, 0, false, false, false, 0, 0);
+ ActivityBuilder.getDefaultComponent(), null, false, false, false, 0, 10050, null,
+ new ArrayList<>(), 0, false, null, 0, 0, 0, 0, 0, null, 0, false, false, false, 0, 0
+ );
}
private static class TestTaskRecordFactory extends TaskRecordFactory {
diff --git a/services/tests/servicestests/src/com/android/server/am/TaskStackChangedListenerTest.java b/services/tests/servicestests/src/com/android/server/am/TaskStackChangedListenerTest.java
index 8e87a5f..5cd410e 100644
--- a/services/tests/servicestests/src/com/android/server/am/TaskStackChangedListenerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/TaskStackChangedListenerTest.java
@@ -181,6 +181,7 @@
final ActivityTaskChangeCallbacks activity =
(ActivityTaskChangeCallbacks) startTestActivity(ActivityTaskChangeCallbacks.class);
+ activity.setDetachedFromWindowLatch(onDetachedFromWindowLatch);
final int id = activity.getTaskId();
// Test for onTaskCreated.
@@ -207,6 +208,7 @@
assertEquals(1, taskRemovedLatch.getCount());
waitForCallback(taskRemovedLatch);
assertEquals(id, params[0]);
+ waitForCallback(onDetachedFromWindowLatch);
assertTrue(activity.onDetachedFromWindowCalled);
}
@@ -288,11 +290,17 @@
}
public static class ActivityTaskChangeCallbacks extends Activity {
- public boolean onDetachedFromWindowCalled = false;;
+ boolean onDetachedFromWindowCalled = false;
+ CountDownLatch onDetachedFromWindowCountDownLatch;
@Override
public void onDetachedFromWindow() {
onDetachedFromWindowCalled = true;
+ onDetachedFromWindowCountDownLatch.countDown();
+ }
+
+ void setDetachedFromWindowLatch(CountDownLatch countDownLatch) {
+ onDetachedFromWindowCountDownLatch = countDownLatch;
}
}
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/DetectTvSystemAudioModeSupportActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/DetectTvSystemAudioModeSupportActionTest.java
new file mode 100644
index 0000000..d437ca1
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/hdmi/DetectTvSystemAudioModeSupportActionTest.java
@@ -0,0 +1,140 @@
+/*
+ * 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.hdmi;
+
+import static org.junit.Assert.assertEquals;
+
+import android.annotation.Nullable;
+import android.hardware.hdmi.HdmiDeviceInfo;
+import android.hardware.tv.cec.V1_0.SendMessageResult;
+import android.os.Looper;
+import android.os.test.TestLooper;
+import android.support.test.filters.SmallTest;
+
+import com.android.server.hdmi.HdmiCecLocalDeviceAudioSystem.TvSystemAudioModeSupportedCallback;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * Tests for {@link DetectTvSystemAudioModeSupportAction} class.
+ */
+@SmallTest
+@RunWith(JUnit4.class)
+public class DetectTvSystemAudioModeSupportActionTest {
+
+ private HdmiDeviceInfo mDeviceInfoForTests;
+ private HdmiCecLocalDeviceAudioSystem mHdmiCecLocalDeviceAudioSystem;
+ private DetectTvSystemAudioModeSupportAction mAction;
+
+ private TestLooper mTestLooper = new TestLooper();
+ private boolean mSendCecCommandSuccess;
+ private boolean mShouldDispatchFeatureAbort;
+ private Boolean mSupported;
+
+ @Before
+ public void SetUp() {
+ mDeviceInfoForTests = new HdmiDeviceInfo(1001, 1234);
+ HdmiControlService hdmiControlService = new HdmiControlService(null) {
+
+ @Override
+ void sendCecCommand(HdmiCecMessage command,
+ @Nullable SendMessageCallback callback) {
+ switch (command.getOpcode()) {
+ case Constants.MESSAGE_SET_SYSTEM_AUDIO_MODE:
+ if (callback != null) {
+ callback.onSendCompleted(mSendCecCommandSuccess
+ ? SendMessageResult.SUCCESS : SendMessageResult.NACK);
+ }
+ if (mShouldDispatchFeatureAbort) {
+ mHdmiCecLocalDeviceAudioSystem.dispatchMessage(
+ HdmiCecMessageBuilder.buildFeatureAbortCommand(
+ Constants.ADDR_TV,
+ mHdmiCecLocalDeviceAudioSystem.mAddress,
+ Constants.MESSAGE_SET_SYSTEM_AUDIO_MODE,
+ Constants.ABORT_UNRECOGNIZED_OPCODE));
+ }
+ break;
+ default:
+ throw new IllegalArgumentException("Unexpected message");
+ }
+ }
+
+ @Override
+ boolean isPowerStandby() {
+ return false;
+ }
+
+ @Override
+ boolean isAddressAllocated() {
+ return true;
+ }
+
+ @Override
+ Looper getServiceLooper() {
+ return mTestLooper.getLooper();
+ }
+ };
+ mHdmiCecLocalDeviceAudioSystem =
+ new HdmiCecLocalDeviceAudioSystem(hdmiControlService) {
+ @Override
+ HdmiDeviceInfo getDeviceInfo() {
+ return mDeviceInfoForTests;
+ }
+ };
+ mHdmiCecLocalDeviceAudioSystem.init();
+ Looper looper = mTestLooper.getLooper();
+ hdmiControlService.setIoLooper(looper);
+
+ mAction = new DetectTvSystemAudioModeSupportAction(
+ mHdmiCecLocalDeviceAudioSystem,
+ new TvSystemAudioModeSupportedCallback() {
+ public void onResult(boolean supported) {
+ mSupported = Boolean.valueOf(supported);
+ }
+ });
+ mSupported = null;
+ }
+
+ @Test
+ public void testSendCecCommandNotSucceed() {
+ mSendCecCommandSuccess = false;
+ mShouldDispatchFeatureAbort = false;
+ mHdmiCecLocalDeviceAudioSystem.addAndStartAction(mAction);
+ mTestLooper.dispatchAll();
+ assertEquals(Boolean.FALSE, mSupported);
+ }
+
+ @Test
+ public void testFeatureAbort() {
+ mSendCecCommandSuccess = true;
+ mShouldDispatchFeatureAbort = true;
+ mHdmiCecLocalDeviceAudioSystem.addAndStartAction(mAction);
+ mTestLooper.dispatchAll();
+ assertEquals(Boolean.FALSE, mSupported);
+ }
+
+ @Test
+ public void testSupported() {
+ mSendCecCommandSuccess = true;
+ mShouldDispatchFeatureAbort = false;
+ mHdmiCecLocalDeviceAudioSystem.addAndStartAction(mAction);
+ mTestLooper.moveTimeForward(2000);
+ mTestLooper.dispatchAll();
+ assertEquals(Boolean.TRUE, mSupported);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java b/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java
new file mode 100644
index 0000000..715f8a6
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java
@@ -0,0 +1,113 @@
+/*
+ * 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.hdmi;
+
+import android.hardware.hdmi.HdmiPortInfo;
+import android.hardware.tv.cec.V1_0.SendMessageResult;
+import android.os.MessageQueue;
+import com.android.server.hdmi.HdmiCecController.NativeWrapper;
+
+/** Fake {@link NativeWrapper} useful for testing. */
+final class FakeNativeWrapper implements NativeWrapper {
+ private final int[] mPollAddressResponse =
+ new int[] {
+ SendMessageResult.NACK,
+ SendMessageResult.NACK,
+ SendMessageResult.NACK,
+ SendMessageResult.NACK,
+ SendMessageResult.NACK,
+ SendMessageResult.NACK,
+ SendMessageResult.NACK,
+ SendMessageResult.NACK,
+ SendMessageResult.NACK,
+ SendMessageResult.NACK,
+ SendMessageResult.NACK,
+ SendMessageResult.NACK,
+ SendMessageResult.NACK,
+ SendMessageResult.NACK,
+ SendMessageResult.NACK,
+ };
+
+ private HdmiCecMessage mResultMessage;
+
+ @Override
+ public long nativeInit(HdmiCecController handler, MessageQueue messageQueue) {
+ return 1L;
+ }
+
+ @Override
+ public int nativeSendCecCommand(
+ long controllerPtr, int srcAddress, int dstAddress, byte[] body) {
+ if (body.length == 0) {
+ return mPollAddressResponse[dstAddress];
+ } else {
+ mResultMessage = HdmiCecMessageBuilder.of(srcAddress, dstAddress, body);
+ }
+ return SendMessageResult.SUCCESS;
+ }
+
+ @Override
+ public int nativeAddLogicalAddress(long controllerPtr, int logicalAddress) {
+ return 0;
+ }
+
+ @Override
+ public void nativeClearLogicalAddress(long controllerPtr) {}
+
+ @Override
+ public int nativeGetPhysicalAddress(long controllerPtr) {
+ return 0;
+ }
+
+ @Override
+ public int nativeGetVersion(long controllerPtr) {
+ return 0;
+ }
+
+ @Override
+ public int nativeGetVendorId(long controllerPtr) {
+ return 0;
+ }
+
+ @Override
+ public HdmiPortInfo[] nativeGetPortInfos(long controllerPtr) {
+ HdmiPortInfo[] hdmiPortInfo = new HdmiPortInfo[1];
+ hdmiPortInfo[0] = new HdmiPortInfo(1, 1, 0x1000, true, true, true);
+ return hdmiPortInfo;
+ }
+
+ @Override
+ public void nativeSetOption(long controllerPtr, int flag, boolean enabled) {}
+
+ @Override
+ public void nativeSetLanguage(long controllerPtr, String language) {}
+
+ @Override
+ public void nativeEnableAudioReturnChannel(long controllerPtr, int port, boolean flag) {}
+
+ @Override
+ public boolean nativeIsConnected(long controllerPtr, int port) {
+ return false;
+ }
+
+ public HdmiCecMessage getResultMessage() {
+ return mResultMessage;
+ }
+
+ public void setPollAddressResponse(int logicalAddress, int response) {
+ mPollAddressResponse[logicalAddress] = response;
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java
index 1089f69..84e1b7e 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java
@@ -17,6 +17,7 @@
import android.content.Context;
import android.hardware.hdmi.HdmiPortInfo;
+import android.hardware.tv.cec.V1_0.SendMessageResult;
import android.os.Looper;
import android.os.MessageQueue;
import android.os.test.TestLooper;
@@ -48,69 +49,7 @@
@RunWith(JUnit4.class)
public class HdmiCecControllerTest {
- private static final class NativeWrapperImpl implements NativeWrapper {
-
- @Override
- public long nativeInit(HdmiCecController handler, MessageQueue messageQueue) {
- return 1L;
- }
-
- @Override
- public int nativeSendCecCommand(long controllerPtr, int srcAddress, int dstAddress,
- byte[] body) {
- return mOccupied[srcAddress] ? 0 : 1;
- }
-
- @Override
- public int nativeAddLogicalAddress(long controllerPtr, int logicalAddress) {
- return 0;
- }
-
- @Override
- public void nativeClearLogicalAddress(long controllerPtr) {
-
- }
-
- @Override
- public int nativeGetPhysicalAddress(long controllerPtr) {
- return 0;
- }
-
- @Override
- public int nativeGetVersion(long controllerPtr) {
- return 0;
- }
-
- @Override
- public int nativeGetVendorId(long controllerPtr) {
- return 0;
- }
-
- @Override
- public HdmiPortInfo[] nativeGetPortInfos(long controllerPtr) {
- return new HdmiPortInfo[0];
- }
-
- @Override
- public void nativeSetOption(long controllerPtr, int flag, boolean enabled) {
-
- }
-
- @Override
- public void nativeSetLanguage(long controllerPtr, String language) {
-
- }
-
- @Override
- public void nativeEnableAudioReturnChannel(long controllerPtr, int port, boolean flag) {
-
- }
-
- @Override
- public boolean nativeIsConnected(long controllerPtr, int port) {
- return false;
- }
- }
+ private FakeNativeWrapper mNativeWrapper;
private class MyHdmiControlService extends HdmiControlService {
@@ -129,10 +68,8 @@
}
}
- private static final String TAG = HdmiCecControllerTest.class.getSimpleName();
private HdmiControlService mHdmiControlService;
private HdmiCecController mHdmiCecController;
- private static boolean[] mOccupied = new boolean[15];
private int mLogicalAddress = 16;
private AllocateAddressCallback mCallback = new AllocateAddressCallback() {
@Override
@@ -148,8 +85,9 @@
mMyLooper = mTestLooper.getLooper();
mMyLooper = mTestLooper.getLooper();
mHdmiControlService = new MyHdmiControlService(null);
+ mNativeWrapper = new FakeNativeWrapper();
mHdmiCecController = HdmiCecController.createWithNativeWrapper(
- mHdmiControlService, new NativeWrapperImpl());
+ mHdmiControlService, mNativeWrapper);
}
/**
@@ -157,8 +95,6 @@
*/
@Test
public void testAllocatLogicalAddress_TvDevicePreferredNotOcupied() {
- mOccupied[ADDR_TV] = false;
- mOccupied[ADDR_SPECIFIC_USE] = false;
mHdmiCecController.allocateLogicalAddress(DEVICE_TV, ADDR_TV, mCallback);
mTestLooper.dispatchAll();
assertEquals(ADDR_TV, mLogicalAddress);
@@ -166,8 +102,7 @@
@Test
public void testAllocatLogicalAddress_TvDeviceNonPreferredNotOcupied() {
- mOccupied[ADDR_TV] = false;
- mOccupied[ADDR_SPECIFIC_USE] = false;
+
mHdmiCecController.allocateLogicalAddress(DEVICE_TV, ADDR_UNREGISTERED, mCallback);
mTestLooper.dispatchAll();
assertEquals(ADDR_TV, mLogicalAddress);
@@ -175,8 +110,7 @@
@Test
public void testAllocatLogicalAddress_TvDeviceNonPreferredFirstOcupied() {
- mOccupied[ADDR_TV] = true;
- mOccupied[ADDR_SPECIFIC_USE] = false;
+ mNativeWrapper.setPollAddressResponse(ADDR_TV, SendMessageResult.SUCCESS);
mHdmiCecController.allocateLogicalAddress(DEVICE_TV, ADDR_UNREGISTERED, mCallback);
mTestLooper.dispatchAll();
assertEquals(ADDR_SPECIFIC_USE, mLogicalAddress);
@@ -184,8 +118,8 @@
@Test
public void testAllocatLogicalAddress_TvDeviceNonPreferredAllOcupied() {
- mOccupied[ADDR_TV] = true;
- mOccupied[ADDR_SPECIFIC_USE] = true;
+ mNativeWrapper.setPollAddressResponse(ADDR_TV, SendMessageResult.SUCCESS);
+ mNativeWrapper.setPollAddressResponse(ADDR_SPECIFIC_USE, SendMessageResult.SUCCESS);
mHdmiCecController.allocateLogicalAddress(DEVICE_TV, ADDR_UNREGISTERED, mCallback);
mTestLooper.dispatchAll();
assertEquals(ADDR_UNREGISTERED, mLogicalAddress);
@@ -193,7 +127,6 @@
@Test
public void testAllocatLogicalAddress_AudioSystemNonPreferredNotOcupied() {
- mOccupied[ADDR_AUDIO_SYSTEM] = false;
mHdmiCecController.allocateLogicalAddress(
DEVICE_AUDIO_SYSTEM, ADDR_UNREGISTERED, mCallback);
mTestLooper.dispatchAll();
@@ -202,7 +135,7 @@
@Test
public void testAllocatLogicalAddress_AudioSystemNonPreferredAllOcupied() {
- mOccupied[ADDR_AUDIO_SYSTEM] = true;
+ mNativeWrapper.setPollAddressResponse(ADDR_AUDIO_SYSTEM, SendMessageResult.SUCCESS);
mHdmiCecController.allocateLogicalAddress(
DEVICE_AUDIO_SYSTEM, ADDR_UNREGISTERED, mCallback);
mTestLooper.dispatchAll();
@@ -211,9 +144,6 @@
@Test
public void testAllocatLogicalAddress_PlaybackPreferredNotOccupied() {
- mOccupied[ADDR_PLAYBACK_1] = false;
- mOccupied[ADDR_PLAYBACK_2] = false;
- mOccupied[ADDR_PLAYBACK_3] = false;
mHdmiCecController.allocateLogicalAddress(DEVICE_PLAYBACK, ADDR_PLAYBACK_1, mCallback);
mTestLooper.dispatchAll();
assertEquals(ADDR_PLAYBACK_1, mLogicalAddress);
@@ -221,9 +151,7 @@
@Test
public void testAllocatLogicalAddress_PlaybackPreferredOcuppied() {
- mOccupied[ADDR_PLAYBACK_1] = true;
- mOccupied[ADDR_PLAYBACK_2] = false;
- mOccupied[ADDR_PLAYBACK_3] = false;
+ mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_1, SendMessageResult.SUCCESS);
mHdmiCecController.allocateLogicalAddress(
DEVICE_PLAYBACK, ADDR_PLAYBACK_1, mCallback);
mTestLooper.dispatchAll();
@@ -232,9 +160,6 @@
@Test
public void testAllocatLogicalAddress_PlaybackNoPreferredNotOcuppied() {
- mOccupied[ADDR_PLAYBACK_1] = false;
- mOccupied[ADDR_PLAYBACK_2] = false;
- mOccupied[ADDR_PLAYBACK_3] = false;
mHdmiCecController.allocateLogicalAddress(DEVICE_PLAYBACK, ADDR_UNREGISTERED, mCallback);
mTestLooper.dispatchAll();
assertEquals(ADDR_PLAYBACK_1, mLogicalAddress);
@@ -242,9 +167,7 @@
@Test
public void testAllocatLogicalAddress_PlaybackNoPreferredFirstOcuppied() {
- mOccupied[ADDR_PLAYBACK_1] = true;
- mOccupied[ADDR_PLAYBACK_2] = false;
- mOccupied[ADDR_PLAYBACK_3] = false;
+ mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_1, SendMessageResult.SUCCESS);
mHdmiCecController.allocateLogicalAddress(DEVICE_PLAYBACK, ADDR_UNREGISTERED, mCallback);
mTestLooper.dispatchAll();
assertEquals(ADDR_PLAYBACK_2, mLogicalAddress);
@@ -252,9 +175,8 @@
@Test
public void testAllocatLogicalAddress_PlaybackNonPreferredFirstTwoOcuppied() {
- mOccupied[ADDR_PLAYBACK_1] = true;
- mOccupied[ADDR_PLAYBACK_2] = true;
- mOccupied[ADDR_PLAYBACK_3] = false;
+ mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_1, SendMessageResult.SUCCESS);
+ mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_2, SendMessageResult.SUCCESS);
mHdmiCecController.allocateLogicalAddress(DEVICE_PLAYBACK, ADDR_UNREGISTERED, mCallback);
mTestLooper.dispatchAll();
assertEquals(ADDR_PLAYBACK_3, mLogicalAddress);
@@ -262,9 +184,9 @@
@Test
public void testAllocatLogicalAddress_PlaybackNonPreferredAllOcupied() {
- mOccupied[ADDR_PLAYBACK_1] = true;
- mOccupied[ADDR_PLAYBACK_2] = true;
- mOccupied[ADDR_PLAYBACK_3] = true;
+ mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_1, SendMessageResult.SUCCESS);
+ mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_2, SendMessageResult.SUCCESS);
+ mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_3, SendMessageResult.SUCCESS);
mHdmiCecController.allocateLogicalAddress(DEVICE_PLAYBACK, ADDR_UNREGISTERED, mCallback);
mTestLooper.dispatchAll();
assertEquals(ADDR_UNREGISTERED, mLogicalAddress);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
new file mode 100644
index 0000000..b392935
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
@@ -0,0 +1,299 @@
+/*
+ * 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.hdmi;
+
+import static com.android.server.hdmi.Constants.ADDR_AUDIO_SYSTEM;
+import static com.android.server.hdmi.Constants.ADDR_BROADCAST;
+import static com.android.server.hdmi.Constants.ADDR_TV;
+import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC;
+import static com.android.server.hdmi.HdmiControlService.STANDBY_SCREEN_OFF;
+import static com.google.common.truth.Truth.assertThat;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+import static junit.framework.Assert.assertEquals;
+
+import android.media.AudioManager;
+import android.os.Looper;
+import android.os.test.TestLooper;
+import android.support.test.filters.SmallTest;
+
+import java.util.ArrayList;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@SmallTest
+@RunWith(JUnit4.class)
+/**
+ * Tests for {@link HdmiCecLocalDeviceAudioSystem} class.
+ */
+public class HdmiCecLocalDeviceAudioSystemTest {
+
+ private static final String TAG = "HdmiCecLocalDeviceAudioSystemTest";
+ private HdmiControlService mHdmiControlService;
+ private HdmiCecController mHdmiCecController;
+ private HdmiCecLocalDeviceAudioSystem mHdmiCecLocalDeviceAudioSystem;
+ private FakeNativeWrapper mNativeWrapper;
+ private Looper mMyLooper;
+ private TestLooper mTestLooper = new TestLooper();
+ private ArrayList<HdmiCecLocalDevice> mLocalDevices = new ArrayList<>();
+ private int mMusicVolume;
+ private int mMusicMaxVolume;
+ private boolean mMusicMute;
+ private boolean isAwake;
+
+ @Before
+ public void SetUp() {
+ isAwake = false;
+ mHdmiControlService = new HdmiControlService(null) {
+ @Override
+ AudioManager getAudioManager() {
+ return new AudioManager() {
+ @Override
+ public int getStreamVolume(int streamType) {
+ switch (streamType) {
+ case STREAM_MUSIC:
+ return mMusicVolume;
+ default:
+ return 0;
+ }
+ }
+
+ @Override
+ public boolean isStreamMute(int streamType) {
+ switch (streamType) {
+ case STREAM_MUSIC:
+ return mMusicMute;
+ default:
+ return false;
+ }
+ }
+
+ @Override
+ public int getStreamMaxVolume(int streamType) {
+ switch (streamType) {
+ case STREAM_MUSIC:
+ return mMusicMaxVolume;
+ default:
+ return 100;
+ }
+ }
+
+ @Override
+ public void adjustStreamVolume(int streamType, int direction, int flags) {
+ switch (streamType) {
+ case STREAM_MUSIC:
+ if (direction == AudioManager.ADJUST_UNMUTE) {
+ mMusicMute = false;
+ } else if (direction == AudioManager.ADJUST_MUTE) {
+ mMusicMute = true;
+ }
+ default:
+ }
+ }
+ };
+ }
+
+ @Override
+ void wakeUp() {
+ isAwake = true;
+ }
+ };
+ mMyLooper = mTestLooper.getLooper();
+ mHdmiCecLocalDeviceAudioSystem = new HdmiCecLocalDeviceAudioSystem(mHdmiControlService);
+ mHdmiCecLocalDeviceAudioSystem.init();
+ mHdmiControlService.setIoLooper(mMyLooper);
+
+ mNativeWrapper = new FakeNativeWrapper();
+ mHdmiCecController = HdmiCecController.createWithNativeWrapper(
+ mHdmiControlService, mNativeWrapper);
+ mHdmiControlService.setCecController(mHdmiCecController);
+ mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
+ mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
+
+ mLocalDevices.add(mHdmiCecLocalDeviceAudioSystem);
+ mHdmiControlService.initPortInfo();
+ // No TV device interacts with AVR so system audio control won't be turned on here
+ mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
+
+ mTestLooper.dispatchAll();
+ }
+
+ @Test
+ public void handleGiveAudioStatus_volume_10_mute_true() {
+ mMusicVolume = 10;
+ mMusicMute = true;
+ mMusicMaxVolume = 20;
+ int scaledVolume = VolumeControlAction.scaleToCecVolume(10, mMusicMaxVolume);
+ HdmiCecMessage expectMessage = HdmiCecMessageBuilder.buildReportAudioStatus(
+ ADDR_AUDIO_SYSTEM, ADDR_TV, scaledVolume, true);
+ HdmiCecMessage messageGive = HdmiCecMessageBuilder.buildGiveAudioStatus(
+ ADDR_TV, ADDR_AUDIO_SYSTEM);
+
+ assertTrue(mHdmiCecLocalDeviceAudioSystem.handleGiveAudioStatus(messageGive));
+ mTestLooper.dispatchAll();
+ assertEquals(expectMessage, mNativeWrapper.getResultMessage());
+ }
+
+ @Test
+ public void handleGiveSystemAudioModeStatus_originalOff() {
+ HdmiCecMessage expectMessage = HdmiCecMessageBuilder
+ .buildReportSystemAudioMode(ADDR_AUDIO_SYSTEM, ADDR_TV, false);
+ HdmiCecMessage messageGive = HdmiCecMessageBuilder
+ .buildGiveSystemAudioModeStatus(ADDR_TV, ADDR_AUDIO_SYSTEM);
+
+ assertTrue(mHdmiCecLocalDeviceAudioSystem.handleGiveSystemAudioModeStatus(messageGive));
+ mTestLooper.dispatchAll();
+ assertEquals(expectMessage, mNativeWrapper.getResultMessage());
+ }
+
+ @Test
+ public void handleRequestArcInitiate() {
+ // TODO(b/80296911): Add tests when finishing handler impl.
+ HdmiCecMessage expectMessage = HdmiCecMessageBuilder
+ .buildInitiateArc(ADDR_AUDIO_SYSTEM, ADDR_TV);
+ HdmiCecMessage message = HdmiCecMessageBuilder
+ .buildRequestArcInitiation(ADDR_TV, ADDR_AUDIO_SYSTEM);
+
+ assertTrue(mHdmiCecLocalDeviceAudioSystem.handleRequestArcInitiate(message));
+ mTestLooper.dispatchAll();
+ assertEquals(expectMessage, mNativeWrapper.getResultMessage());
+ }
+
+ @Test
+ public void handleRequestArcTermination() {
+ // TODO(b/80297105): Add tests when finishing handler impl.
+ HdmiCecMessage expectMessage = HdmiCecMessageBuilder
+ .buildTerminateArc(ADDR_AUDIO_SYSTEM, ADDR_TV);
+ HdmiCecMessage messageRequestOff = HdmiCecMessageBuilder
+ .buildRequestArcTermination(ADDR_TV, ADDR_AUDIO_SYSTEM);
+
+ assertTrue(mHdmiCecLocalDeviceAudioSystem.handleRequestArcTermination(messageRequestOff));
+ mTestLooper.dispatchAll();
+ assertEquals(expectMessage, mNativeWrapper.getResultMessage());
+ }
+
+ @Test
+ public void handleSetSystemAudioMode_setOn_orignalOff() {
+ mMusicMute = true;
+ HdmiCecMessage messageSet = HdmiCecMessageBuilder
+ .buildSetSystemAudioMode(ADDR_TV, ADDR_AUDIO_SYSTEM, true);
+ HdmiCecMessage messageGive = HdmiCecMessageBuilder
+ .buildGiveSystemAudioModeStatus(ADDR_TV, ADDR_AUDIO_SYSTEM);
+
+ // Check if originally off
+ HdmiCecMessage expectMessage = HdmiCecMessageBuilder
+ .buildReportSystemAudioMode(ADDR_AUDIO_SYSTEM, ADDR_TV, false);
+
+ assertTrue(mHdmiCecLocalDeviceAudioSystem.handleGiveSystemAudioModeStatus(messageGive));
+ mTestLooper.dispatchAll();
+ assertEquals(expectMessage, mNativeWrapper.getResultMessage());
+
+ // Check if correctly turned on
+ expectMessage = HdmiCecMessageBuilder
+ .buildReportSystemAudioMode(ADDR_AUDIO_SYSTEM, ADDR_TV, true);
+
+ assertTrue(mHdmiCecLocalDeviceAudioSystem.handleSetSystemAudioMode(messageSet));
+ mTestLooper.dispatchAll();
+ assertTrue(mHdmiCecLocalDeviceAudioSystem.handleGiveSystemAudioModeStatus(messageGive));
+ mTestLooper.dispatchAll();
+ assertEquals(expectMessage, mNativeWrapper.getResultMessage());
+ assertFalse(mMusicMute);
+ }
+
+ @Test
+ public void handleSystemAudioModeRequest_turnOffByTv() {
+ assertFalse(mMusicMute);
+
+ // Check if feature correctly turned off
+ HdmiCecMessage messageGive = HdmiCecMessageBuilder
+ .buildGiveSystemAudioModeStatus(ADDR_TV, ADDR_AUDIO_SYSTEM);
+ HdmiCecMessage messageRequestOff = HdmiCecMessageBuilder
+ .buildSystemAudioModeRequest(ADDR_TV, ADDR_AUDIO_SYSTEM, 2, false);
+
+ HdmiCecMessage expectMessage = HdmiCecMessageBuilder
+ .buildSetSystemAudioMode(ADDR_AUDIO_SYSTEM, ADDR_BROADCAST, false);
+ assertTrue(mHdmiCecLocalDeviceAudioSystem.handleSystemAudioModeRequest(messageRequestOff));
+ mTestLooper.dispatchAll();
+ assertEquals(expectMessage, mNativeWrapper.getResultMessage());
+
+ expectMessage = HdmiCecMessageBuilder
+ .buildReportSystemAudioMode(ADDR_AUDIO_SYSTEM, ADDR_TV, false);
+ assertTrue(mHdmiCecLocalDeviceAudioSystem.handleGiveSystemAudioModeStatus(messageGive));
+ mTestLooper.dispatchAll();
+ assertEquals(expectMessage, mNativeWrapper.getResultMessage());
+ assertTrue(mMusicMute);
+ }
+
+ @Test
+ public void onStandbyAudioSystem_currentSystemAudioControlOn() {
+ // Set system audio control on first
+ mHdmiCecLocalDeviceAudioSystem.setSystemAudioMode(true);
+
+ // Check if standby correctly turns off the feature
+ mHdmiCecLocalDeviceAudioSystem.onStandby(false, STANDBY_SCREEN_OFF);
+ mTestLooper.dispatchAll();
+ HdmiCecMessage expectMessage = HdmiCecMessageBuilder
+ .buildSetSystemAudioMode(ADDR_AUDIO_SYSTEM, ADDR_BROADCAST, false);
+ assertEquals(expectMessage, mNativeWrapper.getResultMessage());
+ assertTrue(mMusicMute);
+ }
+
+ @Test
+ public void systemAudioControlOnPowerOn_alwaysOn() {
+ mHdmiCecLocalDeviceAudioSystem.removeAction(
+ SystemAudioInitiationActionFromAvr.class);
+ mHdmiCecLocalDeviceAudioSystem.systemAudioControlOnPowerOn(
+ Constants.ALWAYS_SYSTEM_AUDIO_CONTROL_ON_POWER_ON, true);
+
+ assertThat(mHdmiCecLocalDeviceAudioSystem
+ .getActions(SystemAudioInitiationActionFromAvr.class)).isNotEmpty();
+ }
+
+ @Test
+ public void systemAudioControlOnPowerOn_neverOn() {
+ mHdmiCecLocalDeviceAudioSystem.removeAction(
+ SystemAudioInitiationActionFromAvr.class);
+ mHdmiCecLocalDeviceAudioSystem.systemAudioControlOnPowerOn(
+ Constants.NEVER_SYSTEM_AUDIO_CONTROL_ON_POWER_ON, false);
+
+ assertThat(mHdmiCecLocalDeviceAudioSystem
+ .getActions(SystemAudioInitiationActionFromAvr.class)).isEmpty();
+ }
+
+ @Test
+ public void systemAudioControlOnPowerOn_useLastState_off() {
+ mHdmiCecLocalDeviceAudioSystem.removeAction(
+ SystemAudioInitiationActionFromAvr.class);
+ mHdmiCecLocalDeviceAudioSystem.systemAudioControlOnPowerOn(
+ Constants.USE_LAST_STATE_SYSTEM_AUDIO_CONTROL_ON_POWER_ON, false);
+
+ assertThat(mHdmiCecLocalDeviceAudioSystem
+ .getActions(SystemAudioInitiationActionFromAvr.class)).isEmpty();
+ }
+
+ @Test
+ public void systemAudioControlOnPowerOn_useLastState_on() {
+ mHdmiCecLocalDeviceAudioSystem.removeAction(
+ SystemAudioInitiationActionFromAvr.class);
+ mHdmiCecLocalDeviceAudioSystem.systemAudioControlOnPowerOn(
+ Constants.USE_LAST_STATE_SYSTEM_AUDIO_CONTROL_ON_POWER_ON, true);
+
+ assertThat(mHdmiCecLocalDeviceAudioSystem
+ .getActions(SystemAudioInitiationActionFromAvr.class)).isNotEmpty();
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java
index 78cb56b..833ffa6 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java
@@ -16,6 +16,7 @@
package com.android.server.hdmi;
import static android.hardware.hdmi.HdmiDeviceInfo.DEVICE_TV;
+import static com.android.server.hdmi.Constants.ADDR_AUDIO_SYSTEM;
import static com.android.server.hdmi.Constants.ADDR_BROADCAST;
import static com.android.server.hdmi.Constants.ADDR_TV;
import static com.android.server.hdmi.Constants.ADDR_UNREGISTERED;
@@ -25,7 +26,9 @@
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
+import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiPortInfo;
+import android.os.PowerManager;
import android.os.test.TestLooper;
import android.support.test.filters.SmallTest;
import android.os.MessageQueue;
@@ -44,69 +47,6 @@
*/
public class HdmiCecLocalDeviceTest {
- private static final class NativeWrapperImpl implements NativeWrapper {
-
- @Override
- public long nativeInit(HdmiCecController handler, MessageQueue messageQueue) {
- return 1L;
- }
-
- @Override
- public int nativeSendCecCommand(long controllerPtr, int srcAddress, int dstAddress,
- byte[] body) {
- return SendCecCommandFactory(srcAddress, dstAddress, body);
- }
-
- @Override
- public int nativeAddLogicalAddress(long controllerPtr, int logicalAddress) {
- return 0;
- }
-
- @Override
- public void nativeClearLogicalAddress(long controllerPtr) {
-
- }
-
- @Override
- public int nativeGetPhysicalAddress(long controllerPtr) {
- return mPhysicalAddr;
- }
-
- @Override
- public int nativeGetVersion(long controllerPtr) {
- return 0;
- }
-
- @Override
- public int nativeGetVendorId(long controllerPtr) {
- return 0;
- }
-
- @Override
- public HdmiPortInfo[] nativeGetPortInfos(long controllerPtr) {
- return new HdmiPortInfo[0];
- }
-
- @Override
- public void nativeSetOption(long controllerPtr, int flag, boolean enabled) {
-
- }
-
- @Override
- public void nativeSetLanguage(long controllerPtr, String language) {
-
- }
-
- @Override
- public void nativeEnableAudioReturnChannel(long controllerPtr, int port, boolean flag) {
-
- }
-
- @Override
- public boolean nativeIsConnected(long controllerPtr, int port) {
- return false;
- }
- }
private static int SendCecCommandFactory(int srcAddress, int dstAddress, byte[] body) {
switch(body[0] & 0xFF) {
@@ -154,13 +94,32 @@
private int callbackResult;
private HdmiCecMessageValidator mMessageValidator;
private static byte[] param;
+ private boolean mStandbyMessageReceived;
+ private boolean isControlEnabled;
+ private int mPowerStatus;
@Before
public void SetUp() {
- mHdmiControlService = new HdmiControlService(null);
+ mHdmiControlService = new HdmiControlService(null) {
+ @Override
+ boolean isControlEnabled() {
+ return isControlEnabled;
+ }
+
+ @Override
+ boolean isPowerOnOrTransient() {
+ return mPowerStatus == HdmiControlManager.POWER_STATUS_ON
+ || mPowerStatus == HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON;
+ }
+
+ @Override
+ void standby() {
+ mStandbyMessageReceived = true;
+ }
+ };
mHdmiControlService.setIoLooper(mTestLooper.getLooper());
mHdmiCecController = HdmiCecController.createWithNativeWrapper(
- mHdmiControlService, new NativeWrapperImpl());
+ mHdmiControlService, new FakeNativeWrapper());
mHdmiControlService.setCecController(mHdmiCecController);
mHdmiLocalDevice = new MyHdmiCecLocalDevice(
mHdmiControlService, DEVICE_TV);
@@ -218,4 +177,14 @@
mTestLooper.dispatchAll();
assertEquals(0, callbackResult);
}
+
+ @Test
+ public void handleStandby_isPowerOn() {
+ mPowerStatus = HdmiControlManager.POWER_STATUS_ON;
+ isControlEnabled = true;
+ assertFalse(mStandbyMessageReceived);
+ mHdmiLocalDevice.handleStandby(
+ HdmiCecMessageBuilder.buildStandby(ADDR_TV, ADDR_AUDIO_SYSTEM));
+ assertTrue(mStandbyMessageReceived);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageBuilderTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageBuilderTest.java
new file mode 100644
index 0000000..50c6880
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageBuilderTest.java
@@ -0,0 +1,46 @@
+/*
+ * 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.hdmi;
+
+import static com.android.server.hdmi.Constants.ADDR_BROADCAST;
+import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_1;
+import static com.google.common.truth.Truth.assertThat;
+
+import android.hardware.hdmi.HdmiDeviceInfo;
+import android.support.test.filters.SmallTest;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@SmallTest
+@RunWith(JUnit4.class)
+/** Tests for {@link HdmiCecMessageBuilder}.. */
+public class HdmiCecMessageBuilderTest {
+
+ @Test
+ public void buildReportPhysicalAddressCommand() {
+ HdmiCecMessage message =
+ HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
+ ADDR_PLAYBACK_1, 01234, HdmiDeviceInfo.DEVICE_PLAYBACK);
+ assertThat(message)
+ .isEqualTo(
+ new HdmiCecMessage(
+ ADDR_PLAYBACK_1,
+ ADDR_BROADCAST,
+ Constants.MESSAGE_REPORT_PHYSICAL_ADDRESS,
+ new byte[] {012, 034}));
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
new file mode 100644
index 0000000..90ad349
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
@@ -0,0 +1,162 @@
+/*
+ * 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.hdmi;
+
+import static android.hardware.hdmi.HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM;
+import static android.hardware.hdmi.HdmiDeviceInfo.DEVICE_PLAYBACK;
+import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import android.os.Looper;
+import android.os.test.TestLooper;
+import android.support.test.filters.SmallTest;
+import java.util.ArrayList;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * Tests for {@link HdmiControlService} class.
+ */
+@SmallTest
+@RunWith(JUnit4.class)
+public class HdmiControlServiceTest {
+
+ private class HdmiCecLocalDeviceMyDevice extends HdmiCecLocalDevice {
+
+ private boolean mCanGoToStandby;
+ private boolean mIsStandby;
+ private boolean mIsDisabled;
+
+ protected HdmiCecLocalDeviceMyDevice(HdmiControlService service, int deviceType) {
+ super(service, deviceType);
+ }
+
+ @Override
+ protected void onAddressAllocated(int logicalAddress, int reason) {
+
+ }
+
+ @Override
+ protected int getPreferredAddress() {
+ return 0;
+ }
+
+ @Override
+ protected void setPreferredAddress(int addr) {
+
+ }
+
+ @Override
+ protected boolean canGoToStandby() {
+ return mCanGoToStandby;
+ }
+
+ @Override
+ protected void disableDevice(boolean initiatedByCec,
+ final PendingActionClearedCallback originalCallback) {
+ mIsDisabled = true;
+ originalCallback.onCleared(this);
+ }
+
+ @Override
+ protected void onStandby(boolean initiatedByCec, int standbyAction) {
+ mIsStandby = true;
+ }
+
+ protected boolean isStandby() {
+ return mIsStandby;
+ }
+
+ protected boolean isDisabled() {
+ return mIsDisabled;
+ }
+
+ protected void setCanGoToStandby(boolean canGoToStandby) {
+ mCanGoToStandby = canGoToStandby;
+ }
+ }
+
+ private static final String TAG = "HdmiControlServiceTest";
+ private HdmiControlService mHdmiControlService;
+ private HdmiCecController mHdmiCecController;
+ private HdmiCecLocalDeviceMyDevice mMyAudioSystemDevice;
+ private HdmiCecLocalDeviceMyDevice mMyPlaybackDevice;
+ private FakeNativeWrapper mNativeWrapper;
+ private Looper mMyLooper;
+ private TestLooper mTestLooper = new TestLooper();
+ private ArrayList<HdmiCecLocalDevice> mLocalDevices = new ArrayList<>();
+ private boolean mStandbyMessageReceived;
+
+ @Before
+ public void SetUp() {
+ mHdmiControlService = new HdmiControlService(null) {
+ @Override
+ boolean isStandbyMessageReceived() {
+ return mStandbyMessageReceived;
+ }
+ };
+ mMyLooper = mTestLooper.getLooper();
+
+ mMyAudioSystemDevice = new HdmiCecLocalDeviceMyDevice(
+ mHdmiControlService, DEVICE_AUDIO_SYSTEM);
+ mMyPlaybackDevice = new HdmiCecLocalDeviceMyDevice(
+ mHdmiControlService, DEVICE_PLAYBACK);
+ mMyAudioSystemDevice.init();
+ mMyPlaybackDevice.init();
+
+ mHdmiControlService.setIoLooper(mMyLooper);
+
+ mNativeWrapper = new FakeNativeWrapper();
+ mHdmiCecController = HdmiCecController.createWithNativeWrapper(
+ mHdmiControlService, mNativeWrapper);
+ mHdmiControlService.setCecController(mHdmiCecController);
+ mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
+ mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
+
+ mLocalDevices.add(mMyAudioSystemDevice);
+ mLocalDevices.add(mMyPlaybackDevice);
+ mHdmiControlService.initPortInfo();
+ mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
+
+ mTestLooper.dispatchAll();
+ }
+
+ @Test
+ public void onStandby_notByCec_cannotGoToStandby() {
+ mStandbyMessageReceived = false;
+ mMyPlaybackDevice.setCanGoToStandby(false);
+
+ mHdmiControlService.onStandby(HdmiControlService.STANDBY_SCREEN_OFF);
+ assertTrue(mMyPlaybackDevice.isStandby());
+ assertTrue(mMyAudioSystemDevice.isStandby());
+ assertFalse(mMyPlaybackDevice.isDisabled());
+ assertFalse(mMyAudioSystemDevice.isDisabled());
+ }
+
+ @Test
+ public void onStandby_byCec() {
+ mStandbyMessageReceived = true;
+
+ mHdmiControlService.onStandby(HdmiControlService.STANDBY_SCREEN_OFF);
+ assertTrue(mMyPlaybackDevice.isStandby());
+ assertTrue(mMyAudioSystemDevice.isStandby());
+ assertTrue(mMyPlaybackDevice.isDisabled());
+ assertTrue(mMyAudioSystemDevice.isDisabled());
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java
new file mode 100644
index 0000000..5442674
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java
@@ -0,0 +1,256 @@
+/*
+ * 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.hdmi;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.annotation.Nullable;
+import android.hardware.hdmi.HdmiDeviceInfo;
+import android.hardware.tv.cec.V1_0.SendMessageResult;
+import android.media.AudioManager;
+import android.os.Looper;
+import android.os.test.TestLooper;
+import android.support.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * Tests for {@link SystemAudioInitiationActionFromAvr}
+ */
+@SmallTest
+@RunWith(JUnit4.class)
+public class SystemAudioInitiationActionFromAvrTest {
+
+ private HdmiCecLocalDeviceAudioSystem mHdmiCecLocalDeviceAudioSystem;
+ private TestLooper mTestLooper = new TestLooper();
+
+ private boolean mShouldDispatchActiveSource;
+ private boolean mTvSystemAudioModeSupport;
+ private int mTryCountBeforeSucceed;
+ private HdmiDeviceInfo mDeviceInfoForTests;
+
+ private int mMsgRequestActiveSourceCount;
+ private int mMsgSetSystemAudioModeCount;
+ private int mQueryTvSystemAudioModeSupportCount;
+
+ @Before
+ public void SetUp() {
+ mDeviceInfoForTests = new HdmiDeviceInfo(1001, 1234);
+ HdmiControlService hdmiControlService = new HdmiControlService(null) {
+
+ @Override
+ void sendCecCommand(HdmiCecMessage command,
+ @Nullable SendMessageCallback callback) {
+ switch (command.getOpcode()) {
+ case Constants.MESSAGE_REQUEST_ACTIVE_SOURCE:
+ mMsgRequestActiveSourceCount++;
+ if (mTryCountBeforeSucceed >= mMsgRequestActiveSourceCount
+ && callback != null) {
+ callback.onSendCompleted(SendMessageResult.NACK);
+ break;
+ }
+ if (mShouldDispatchActiveSource) {
+ mHdmiCecLocalDeviceAudioSystem.dispatchMessage(
+ HdmiCecMessageBuilder.buildActiveSource(
+ Constants.ADDR_TV, 1002));
+ }
+ break;
+ case Constants.MESSAGE_SET_SYSTEM_AUDIO_MODE:
+ mMsgSetSystemAudioModeCount++;
+ if (mTryCountBeforeSucceed >= mMsgSetSystemAudioModeCount
+ && callback != null) {
+ callback.onSendCompleted(SendMessageResult.NACK);
+ }
+ break;
+ default:
+ throw new IllegalArgumentException("Unexpected message");
+ }
+ }
+
+ @Override
+ AudioManager getAudioManager() {
+ return new AudioManager() {
+
+ @Override
+ public int setHdmiSystemAudioSupported(boolean on) {
+ return 0;
+ }
+
+ @Override
+ public int getStreamVolume(int streamType) {
+ return 0;
+ }
+
+ @Override
+ public boolean isStreamMute(int streamType) {
+ return false;
+ }
+
+ @Override
+ public int getStreamMaxVolume(int streamType) {
+ return 100;
+ }
+
+ @Override
+ public void adjustStreamVolume(int streamType, int direction, int flags) {
+
+ }
+ };
+ }
+
+ @Override
+ boolean isPowerStandby() {
+ return false;
+ }
+
+ @Override
+ boolean isAddressAllocated() {
+ return true;
+ }
+
+ @Override
+ void wakeUp() {
+
+ }
+ };
+ mHdmiCecLocalDeviceAudioSystem =
+ new HdmiCecLocalDeviceAudioSystem(hdmiControlService) {
+ @Override
+ void queryTvSystemAudioModeSupport(
+ TvSystemAudioModeSupportedCallback callback) {
+ mQueryTvSystemAudioModeSupportCount++;
+ if (callback != null) {
+ callback.onResult(mTvSystemAudioModeSupport);
+ }
+ }
+
+ @Override
+ HdmiDeviceInfo getDeviceInfo() {
+ return mDeviceInfoForTests;
+ }
+ };
+ mHdmiCecLocalDeviceAudioSystem.init();
+ Looper looper = mTestLooper.getLooper();
+ hdmiControlService.setIoLooper(looper);
+ }
+
+ @Test
+ public void testNoActiveSourceMessageReceived() {
+ resetTestVariables();
+ mShouldDispatchActiveSource = false;
+
+ assertThat(mHdmiCecLocalDeviceAudioSystem.mActiveSource.physicalAddress)
+ .isEqualTo(Constants.INVALID_PHYSICAL_ADDRESS);
+
+ mHdmiCecLocalDeviceAudioSystem.addAndStartAction(
+ new SystemAudioInitiationActionFromAvr(mHdmiCecLocalDeviceAudioSystem));
+ mTestLooper.dispatchAll();
+
+ assertThat(mMsgRequestActiveSourceCount).isEqualTo(1);
+ assertThat(mMsgSetSystemAudioModeCount).isEqualTo(0);
+ assertThat(mQueryTvSystemAudioModeSupportCount).isEqualTo(0);
+ assertFalse(mHdmiCecLocalDeviceAudioSystem.isSystemAudioActivated());
+
+ assertThat(mHdmiCecLocalDeviceAudioSystem.mActiveSource.physicalAddress)
+ .isEqualTo(Constants.INVALID_PHYSICAL_ADDRESS);
+ }
+
+ @Test
+ public void testTvNotSupport() {
+ resetTestVariables();
+ mShouldDispatchActiveSource = true;
+ mTvSystemAudioModeSupport = false;
+
+ mHdmiCecLocalDeviceAudioSystem.addAndStartAction(
+ new SystemAudioInitiationActionFromAvr(mHdmiCecLocalDeviceAudioSystem));
+ mTestLooper.dispatchAll();
+
+ assertThat(mMsgRequestActiveSourceCount).isEqualTo(1);
+ assertThat(mMsgSetSystemAudioModeCount).isEqualTo(0);
+ assertThat(mQueryTvSystemAudioModeSupportCount).isEqualTo(1);
+ assertFalse(mHdmiCecLocalDeviceAudioSystem.isSystemAudioActivated());
+ }
+
+ @Test
+ public void testTvSupport() {
+ resetTestVariables();
+ mShouldDispatchActiveSource = true;
+ mTvSystemAudioModeSupport = true;
+
+ mHdmiCecLocalDeviceAudioSystem.addAndStartAction(
+ new SystemAudioInitiationActionFromAvr(mHdmiCecLocalDeviceAudioSystem));
+ mTestLooper.dispatchAll();
+
+ assertThat(mMsgRequestActiveSourceCount).isEqualTo(1);
+ assertThat(mMsgSetSystemAudioModeCount).isEqualTo(1);
+ assertThat(mQueryTvSystemAudioModeSupportCount).isEqualTo(1);
+ assertTrue(mHdmiCecLocalDeviceAudioSystem.isSystemAudioActivated());
+
+ assertThat(mHdmiCecLocalDeviceAudioSystem.mActiveSource.physicalAddress).isEqualTo(1002);
+
+ }
+
+ @Test
+ public void testKnownActiveSource() {
+ resetTestVariables();
+ mTvSystemAudioModeSupport = true;
+ mHdmiCecLocalDeviceAudioSystem.mActiveSource.physicalAddress = 1001;
+
+ mHdmiCecLocalDeviceAudioSystem.addAndStartAction(
+ new SystemAudioInitiationActionFromAvr(mHdmiCecLocalDeviceAudioSystem));
+ mTestLooper.dispatchAll();
+
+ assertThat(mMsgRequestActiveSourceCount).isEqualTo(0);
+ assertThat(mMsgSetSystemAudioModeCount).isEqualTo(1);
+ assertThat(mQueryTvSystemAudioModeSupportCount).isEqualTo(1);
+ assertTrue(mHdmiCecLocalDeviceAudioSystem.isSystemAudioActivated());
+ }
+
+ @Test
+ public void testRetry() {
+ resetTestVariables();
+ mTvSystemAudioModeSupport = true;
+ mShouldDispatchActiveSource = true;
+ mTryCountBeforeSucceed = 3;
+ assertThat(mTryCountBeforeSucceed)
+ .isAtMost(SystemAudioInitiationActionFromAvr.MAX_RETRY_COUNT);
+ assertThat(mHdmiCecLocalDeviceAudioSystem.mActiveSource.physicalAddress)
+ .isEqualTo(Constants.INVALID_PHYSICAL_ADDRESS);
+
+ mHdmiCecLocalDeviceAudioSystem.addAndStartAction(
+ new SystemAudioInitiationActionFromAvr(mHdmiCecLocalDeviceAudioSystem));
+ mTestLooper.dispatchAll();
+
+ assertThat(mMsgRequestActiveSourceCount).isEqualTo(4);
+ assertThat(mMsgSetSystemAudioModeCount).isEqualTo(4);
+ assertThat(mQueryTvSystemAudioModeSupportCount).isEqualTo(1);
+ assertTrue(mHdmiCecLocalDeviceAudioSystem.isSystemAudioActivated());
+ }
+
+ private void resetTestVariables() {
+ mMsgRequestActiveSourceCount = 0;
+ mMsgSetSystemAudioModeCount = 0;
+ mQueryTvSystemAudioModeSupportCount = 0;
+ mTryCountBeforeSucceed = 0;
+ mHdmiCecLocalDeviceAudioSystem.mActiveSource.physicalAddress =
+ Constants.INVALID_PHYSICAL_ADDRESS;
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsShellCommandTest.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsShellCommandTest.java
index 424c08c..2214d74 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsShellCommandTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsShellCommandTest.java
@@ -80,7 +80,7 @@
MockitoAnnotations.initMocks(this);
final Context context = InstrumentationRegistry.getTargetContext();
mUserId = ActivityManager.getCurrentUser();
- mCommand = new LockSettingsShellCommand(context, mLockPatternUtils);
+ mCommand = new LockSettingsShellCommand(mLockPatternUtils);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
index 3d64f2a..2de5d87 100644
--- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
@@ -41,7 +41,6 @@
import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
-import android.app.ActivityTaskManagerInternal;
import android.app.IUidObserver;
import android.app.usage.UsageStatsManagerInternal;
import android.content.ActivityNotFoundException;
@@ -93,6 +92,7 @@
import com.android.server.pm.LauncherAppsService.LauncherAppsImpl;
import com.android.server.pm.ShortcutUser.PackageWithUser;
+import com.android.server.wm.ActivityTaskManagerInternal;
import org.junit.Assert;
import org.mockito.ArgumentCaptor;
import org.mockito.invocation.InvocationOnMock;
diff --git a/services/tests/servicestests/src/com/android/server/pm/CrossProfileAppsServiceImplTest.java b/services/tests/servicestests/src/com/android/server/pm/CrossProfileAppsServiceImplTest.java
index 3514e5a..c4c2ad9 100644
--- a/services/tests/servicestests/src/com/android/server/pm/CrossProfileAppsServiceImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/CrossProfileAppsServiceImplTest.java
@@ -14,7 +14,6 @@
import static org.testng.Assert.assertThrows;
import android.app.ActivityManagerInternal;
-import android.app.ActivityTaskManagerInternal;
import android.app.AppOpsManager;
import android.app.IApplicationThread;
import android.content.ComponentName;
@@ -32,6 +31,8 @@
import android.platform.test.annotations.Presubmit;
import android.util.SparseArray;
+import com.android.server.wm.ActivityTaskManagerInternal;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
diff --git a/services/tests/servicestests/src/com/android/server/policy/FakeWindowState.java b/services/tests/servicestests/src/com/android/server/policy/FakeWindowState.java
index 7487d44..b238e43 100644
--- a/services/tests/servicestests/src/com/android/server/policy/FakeWindowState.java
+++ b/services/tests/servicestests/src/com/android/server/policy/FakeWindowState.java
@@ -25,20 +25,12 @@
import android.view.IApplicationToken;
import android.view.WindowManager;
+import com.android.server.wm.WindowFrames;
import com.android.server.wm.utils.WmDisplayCutout;
public class FakeWindowState implements WindowManagerPolicy.WindowState {
- public final Rect parentFrame = new Rect();
- public final Rect displayFrame = new Rect();
- public final Rect overscanFrame = new Rect();
- public final Rect contentFrame = new Rect();
- public final Rect visibleFrame = new Rect();
- public final Rect decorFrame = new Rect();
- public final Rect stableFrame = new Rect();
- public Rect outsetFrame = new Rect();
-
- public WmDisplayCutout displayCutout;
+ private WindowFrames windowFrames;
public WindowManager.LayoutParams attrs;
public int displayId;
@@ -61,44 +53,41 @@
}
@Override
- public void computeFrameLw(Rect parentFrame, Rect displayFrame, Rect overlayFrame,
- Rect contentFrame, Rect visibleFrame, Rect decorFrame, Rect stableFrame,
- @Nullable Rect outsetFrame, WmDisplayCutout displayCutout,
- boolean parentFrameWasClippedByDisplayCutout) {
- this.parentFrame.set(parentFrame);
- this.displayFrame.set(displayFrame);
- this.overscanFrame.set(overlayFrame);
- this.contentFrame.set(contentFrame);
- this.visibleFrame.set(visibleFrame);
- this.decorFrame.set(decorFrame);
- this.stableFrame.set(stableFrame);
- this.outsetFrame = outsetFrame == null ? null : new Rect(outsetFrame);
- this.displayCutout = displayCutout;
+ public void computeFrameLw(WindowFrames windowFrames) {
+ this.windowFrames = windowFrames;
}
@Override
public Rect getFrameLw() {
- return parentFrame;
+ return windowFrames.mParentFrame;
}
@Override
public Rect getDisplayFrameLw() {
- return displayFrame;
+ return windowFrames.mDisplayFrame;
}
@Override
public Rect getOverscanFrameLw() {
- return overscanFrame;
+ return windowFrames.mOverscanFrame;
}
@Override
public Rect getContentFrameLw() {
- return contentFrame;
+ return windowFrames.mContentFrame;
}
@Override
public Rect getVisibleFrameLw() {
- return visibleFrame;
+ return windowFrames.mVisibleFrame;
+ }
+
+ public Rect getStableFrame() {
+ return windowFrames.mStableFrame;
+ }
+
+ public Rect getDecorFrame() {
+ return windowFrames.mDecorFrame;
}
@Override
@@ -255,6 +244,9 @@
}
@Override
+ public boolean canReceiveKeys() { return false; }
+
+ @Override
public void writeIdentifierToProto(ProtoOutputStream proto, long fieldId){
throw new UnsupportedOperationException("not implemented");
}
diff --git a/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerLayoutTest.java b/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerLayoutTest.java
index 97a716f..cb9fab3 100644
--- a/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerLayoutTest.java
+++ b/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerLayoutTest.java
@@ -22,7 +22,6 @@
import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
-import static android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
@@ -76,11 +75,11 @@
mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
- assertInsetByTopBottom(mAppWindow.parentFrame, 0, 0);
- assertInsetByTopBottom(mAppWindow.stableFrame, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mAppWindow.contentFrame, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mAppWindow.decorFrame, 0, 0);
- assertInsetBy(mAppWindow.displayFrame, 0, 0, 0, 0);
+ assertInsetByTopBottom(mAppWindow.getFrameLw(), 0, 0);
+ assertInsetByTopBottom(mAppWindow.getStableFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mAppWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mAppWindow.getDecorFrame(), 0, 0);
+ assertInsetBy(mAppWindow.getDisplayFrameLw(), 0, 0, 0, 0);
}
@Test
@@ -91,11 +90,11 @@
mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
- assertInsetByTopBottom(mAppWindow.parentFrame, 0, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mAppWindow.stableFrame, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mAppWindow.contentFrame, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mAppWindow.decorFrame, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mAppWindow.displayFrame, 0, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mAppWindow.getFrameLw(), 0, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mAppWindow.getStableFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mAppWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mAppWindow.getDecorFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mAppWindow.getDisplayFrameLw(), 0, NAV_BAR_HEIGHT);
}
@Test
@@ -107,11 +106,11 @@
mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
- assertInsetByTopBottom(mAppWindow.parentFrame, 0, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mAppWindow.stableFrame, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mAppWindow.contentFrame, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mAppWindow.decorFrame, 0, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mAppWindow.displayFrame, 0, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mAppWindow.getFrameLw(), 0, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mAppWindow.getStableFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mAppWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mAppWindow.getDecorFrame(), 0, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mAppWindow.getDisplayFrameLw(), 0, NAV_BAR_HEIGHT);
}
@Test
@@ -132,11 +131,11 @@
mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
- assertInsetByTopBottom(mAppWindow.parentFrame, 0, 0);
- assertInsetByTopBottom(mAppWindow.stableFrame, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mAppWindow.contentFrame, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mAppWindow.decorFrame, 0, 0);
- assertInsetByTopBottom(mAppWindow.displayFrame, 0, 0);
+ assertInsetByTopBottom(mAppWindow.getFrameLw(), 0, 0);
+ assertInsetByTopBottom(mAppWindow.getStableFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mAppWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mAppWindow.getDecorFrame(), 0, 0);
+ assertInsetByTopBottom(mAppWindow.getDisplayFrameLw(), 0, 0);
}
@Test
@@ -149,11 +148,11 @@
mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
- assertInsetByTopBottom(mAppWindow.parentFrame, STATUS_BAR_HEIGHT, 0);
- assertInsetByTopBottom(mAppWindow.stableFrame, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mAppWindow.contentFrame, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mAppWindow.decorFrame, 0, 0);
- assertInsetByTopBottom(mAppWindow.displayFrame, STATUS_BAR_HEIGHT, 0);
+ assertInsetByTopBottom(mAppWindow.getFrameLw(), STATUS_BAR_HEIGHT, 0);
+ assertInsetByTopBottom(mAppWindow.getStableFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mAppWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mAppWindow.getDecorFrame(), 0, 0);
+ assertInsetByTopBottom(mAppWindow.getDisplayFrameLw(), STATUS_BAR_HEIGHT, 0);
}
@Test
@@ -166,11 +165,11 @@
mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
- assertInsetByTopBottom(mAppWindow.parentFrame, 0, 0);
- assertInsetByTopBottom(mAppWindow.stableFrame, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mAppWindow.contentFrame, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mAppWindow.decorFrame, 0, 0);
- assertInsetBy(mAppWindow.displayFrame, 0, 0, 0, 0);
+ assertInsetByTopBottom(mAppWindow.getFrameLw(), 0, 0);
+ assertInsetByTopBottom(mAppWindow.getStableFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mAppWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mAppWindow.getDecorFrame(), 0, 0);
+ assertInsetBy(mAppWindow.getDisplayFrameLw(), 0, 0, 0, 0);
}
@Test
@@ -183,11 +182,11 @@
mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
- assertInsetByTopBottom(mAppWindow.parentFrame, STATUS_BAR_HEIGHT, 0);
- assertInsetByTopBottom(mAppWindow.stableFrame, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mAppWindow.contentFrame, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mAppWindow.decorFrame, 0, 0);
- assertInsetByTopBottom(mAppWindow.displayFrame, STATUS_BAR_HEIGHT, 0);
+ assertInsetByTopBottom(mAppWindow.getFrameLw(), STATUS_BAR_HEIGHT, 0);
+ assertInsetByTopBottom(mAppWindow.getStableFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mAppWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mAppWindow.getDecorFrame(), 0, 0);
+ assertInsetByTopBottom(mAppWindow.getDisplayFrameLw(), STATUS_BAR_HEIGHT, 0);
}
@Test
@@ -201,11 +200,11 @@
mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
- assertInsetByTopBottom(mAppWindow.parentFrame, 0, 0);
- assertInsetByTopBottom(mAppWindow.stableFrame, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mAppWindow.contentFrame, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mAppWindow.decorFrame, 0, 0);
- assertInsetByTopBottom(mAppWindow.displayFrame, 0, 0);
+ assertInsetByTopBottom(mAppWindow.getFrameLw(), 0, 0);
+ assertInsetByTopBottom(mAppWindow.getStableFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mAppWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mAppWindow.getDecorFrame(), 0, 0);
+ assertInsetByTopBottom(mAppWindow.getDisplayFrameLw(), 0, 0);
}
@@ -218,12 +217,12 @@
mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
- assertInsetBy(mAppWindow.parentFrame, DISPLAY_CUTOUT_HEIGHT, 0, 0, 0);
- assertInsetBy(mAppWindow.stableFrame, 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
- assertInsetBy(mAppWindow.contentFrame,
+ assertInsetBy(mAppWindow.getFrameLw(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0);
+ assertInsetBy(mAppWindow.getStableFrame(), 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
+ assertInsetBy(mAppWindow.getContentFrameLw(),
DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
- assertInsetBy(mAppWindow.decorFrame, 0, 0, 0, 0);
- assertInsetBy(mAppWindow.displayFrame, DISPLAY_CUTOUT_HEIGHT, 0, 0, 0);
+ assertInsetBy(mAppWindow.getDecorFrame(), 0, 0, 0, 0);
+ assertInsetBy(mAppWindow.getDisplayFrameLw(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0);
}
@Test
@@ -235,12 +234,12 @@
mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
- assertInsetBy(mAppWindow.parentFrame, 0, 0, DISPLAY_CUTOUT_HEIGHT, 0);
- assertInsetBy(mAppWindow.stableFrame, NAV_BAR_HEIGHT, STATUS_BAR_HEIGHT, 0, 0);
- assertInsetBy(mAppWindow.contentFrame,
+ assertInsetBy(mAppWindow.getFrameLw(), 0, 0, DISPLAY_CUTOUT_HEIGHT, 0);
+ assertInsetBy(mAppWindow.getStableFrame(), NAV_BAR_HEIGHT, STATUS_BAR_HEIGHT, 0, 0);
+ assertInsetBy(mAppWindow.getContentFrameLw(),
NAV_BAR_HEIGHT, STATUS_BAR_HEIGHT, DISPLAY_CUTOUT_HEIGHT, 0);
- assertInsetBy(mAppWindow.decorFrame, 0, 0, 0, 0);
- assertInsetBy(mAppWindow.displayFrame, 0, 0, DISPLAY_CUTOUT_HEIGHT, 0);
+ assertInsetBy(mAppWindow.getDecorFrame(), 0, 0, 0, 0);
+ assertInsetBy(mAppWindow.getDisplayFrameLw(), 0, 0, DISPLAY_CUTOUT_HEIGHT, 0);
}
@Test
@@ -254,11 +253,11 @@
mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
- assertInsetBy(mAppWindow.parentFrame, DISPLAY_CUTOUT_HEIGHT, 0, 0, 0);
- assertInsetBy(mAppWindow.stableFrame, 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
- assertInsetBy(mAppWindow.contentFrame,
+ assertInsetBy(mAppWindow.getFrameLw(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0);
+ assertInsetBy(mAppWindow.getStableFrame(), 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
+ assertInsetBy(mAppWindow.getContentFrameLw(),
DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
- assertInsetBy(mAppWindow.decorFrame, 0, 0, 0, 0);
+ assertInsetBy(mAppWindow.getDecorFrame(), 0, 0, 0, 0);
}
@Test
@@ -274,8 +273,8 @@
mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
- assertInsetByTopBottom(mAppWindow.parentFrame, 0, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mAppWindow.displayFrame, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mAppWindow.getFrameLw(), 0, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mAppWindow.getDisplayFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
}
@Test
@@ -290,11 +289,11 @@
mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
- assertInsetBy(mAppWindow.parentFrame, 0, 0, 0, 0);
- assertInsetBy(mAppWindow.stableFrame, 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
- assertInsetBy(mAppWindow.contentFrame,
+ assertInsetBy(mAppWindow.getFrameLw(), 0, 0, 0, 0);
+ assertInsetBy(mAppWindow.getStableFrame(), 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
+ assertInsetBy(mAppWindow.getContentFrameLw(),
DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
- assertInsetBy(mAppWindow.decorFrame, 0, 0, 0, 0);
+ assertInsetBy(mAppWindow.getDecorFrame(), 0, 0, 0, 0);
}
@Test
@@ -309,8 +308,8 @@
final Rect stable = new Rect();
final Rect outsets = new Rect();
final DisplayCutout.ParcelableWrapper cutout = new DisplayCutout.ParcelableWrapper();
- mPolicy.getLayoutHintLw(mAppWindow.attrs, null /* taskBounds */, mFrames, frame, content,
- stable, outsets, cutout);
+ mPolicy.getLayoutHintLw(mAppWindow.attrs, null /* taskBounds */, mFrames,
+ false /* floatingStack */, frame, content, stable, outsets, cutout);
assertThat(frame, equalTo(mFrames.mUnrestricted));
assertThat(content, equalTo(new Rect()));
@@ -331,8 +330,8 @@
final DisplayCutout.ParcelableWrapper outDisplayCutout =
new DisplayCutout.ParcelableWrapper();
- mPolicy.getLayoutHintLw(mAppWindow.attrs, null, mFrames, outFrame, outContentInsets,
- outStableInsets, outOutsets, outDisplayCutout);
+ mPolicy.getLayoutHintLw(mAppWindow.attrs, null, mFrames, false /* floatingStack */,
+ outFrame, outContentInsets, outStableInsets, outOutsets, outDisplayCutout);
assertThat(outFrame, is(mFrames.mUnrestricted));
assertThat(outContentInsets, is(new Rect(0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT)));
@@ -355,8 +354,35 @@
final DisplayCutout.ParcelableWrapper outDisplayCutout =
new DisplayCutout.ParcelableWrapper();
- mPolicy.getLayoutHintLw(mAppWindow.attrs, taskBounds, mFrames, outFrame, outContentInsets,
- outStableInsets, outOutsets, outDisplayCutout);
+ mPolicy.getLayoutHintLw(mAppWindow.attrs, taskBounds, mFrames, false /* floatingStack */,
+ outFrame, outContentInsets, outStableInsets, outOutsets, outDisplayCutout);
+
+ assertThat(outFrame, is(taskBounds));
+ assertThat(outContentInsets, is(new Rect()));
+ assertThat(outStableInsets, is(new Rect()));
+ assertThat(outOutsets, is(new Rect()));
+ assertThat(outDisplayCutout, is(new DisplayCutout.ParcelableWrapper()));
+ }
+
+ @Test
+ public void layoutHint_appWindowInTask_outsideContentFrame() {
+ // Initialize DisplayFrames
+ mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+
+ // Task is in the nav bar area (usually does not happen, but this is similar enough to the
+ // possible overlap with the IME)
+ final Rect taskBounds = new Rect(100, mFrames.mContent.bottom + 1,
+ 200, mFrames.mContent.bottom + 10);
+
+ final Rect outFrame = new Rect();
+ final Rect outContentInsets = new Rect();
+ final Rect outStableInsets = new Rect();
+ final Rect outOutsets = new Rect();
+ final DisplayCutout.ParcelableWrapper outDisplayCutout =
+ new DisplayCutout.ParcelableWrapper();
+
+ mPolicy.getLayoutHintLw(mAppWindow.attrs, taskBounds, mFrames, true /* floatingStack */,
+ outFrame, outContentInsets, outStableInsets, outOutsets, outDisplayCutout);
assertThat(outFrame, is(taskBounds));
assertThat(outContentInsets, is(new Rect()));
diff --git a/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerTestBase.java b/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerTestBase.java
index 2c47a94..1d37802 100644
--- a/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerTestBase.java
+++ b/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerTestBase.java
@@ -37,6 +37,7 @@
import android.graphics.Path;
import android.graphics.PixelFormat;
import android.graphics.Rect;
+import android.graphics.RectF;
import android.os.IBinder;
import android.os.UserHandle;
import android.support.test.InstrumentationRegistry;
@@ -172,15 +173,14 @@
}
private static DisplayCutout displayCutoutForRotation(int rotation) {
- Path p = new Path();
- p.addRect(DISPLAY_WIDTH / 4, 0, DISPLAY_WIDTH * 3 / 4, DISPLAY_CUTOUT_HEIGHT,
- Path.Direction.CCW);
+ RectF rectF = new RectF(DISPLAY_WIDTH / 4, 0, DISPLAY_WIDTH * 3 / 4, DISPLAY_CUTOUT_HEIGHT);
Matrix m = new Matrix();
transformPhysicalToLogicalCoordinates(rotation, DISPLAY_WIDTH, DISPLAY_HEIGHT, m);
- p.transform(m);
+ m.mapRect(rectF);
- return DisplayCutout.fromBounds(p);
+ return DisplayCutout.fromBoundingRect((int) rectF.left, (int) rectF.top,
+ (int) rectF.right, (int) rectF.bottom);
}
static class TestContextWrapper extends ContextWrapper {
diff --git a/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverStateMachineTest.java b/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverStateMachineTest.java
index 5b24725..0764a56 100644
--- a/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverStateMachineTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverStateMachineTest.java
@@ -23,6 +23,7 @@
import static org.mockito.Mockito.when;
import android.content.ContentResolver;
+import android.content.res.Resources;
import android.provider.Settings.Global;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -48,12 +49,18 @@
private BatterySaverController mMockBatterySaverController;
private Device mDevice;
private TestableBatterySaverStateMachine mTarget;
+ private Resources mMockResources;
private class MyMockContext extends MockContext {
@Override
public ContentResolver getContentResolver() {
return mMockContextResolver;
}
+
+ @Override
+ public Resources getResources() {
+ return mMockResources;
+ }
}
private DevicePersistedState mPersistedState;
@@ -157,11 +164,15 @@
mMockContext = new MyMockContext();
mMockContextResolver = mock(ContentResolver.class);
mMockBatterySaverController = mock(BatterySaverController.class);
+ mMockResources = mock(Resources.class);
doAnswer((inv) -> mDevice.batterySaverEnabled = inv.getArgument(0))
.when(mMockBatterySaverController).enableBatterySaver(anyBoolean(), anyInt());
when(mMockBatterySaverController.isEnabled())
.thenAnswer((inv) -> mDevice.batterySaverEnabled);
+ when(mMockResources.getBoolean(
+ com.android.internal.R.bool.config_batterySaverStickyBehaviourDisabled))
+ .thenReturn(false);
mPersistedState = new DevicePersistedState();
initDevice();
@@ -173,7 +184,6 @@
mTarget = new TestableBatterySaverStateMachine();
mDevice.pushBatteryStatus();
- mDevice.pushGlobalSettings();
mTarget.onBootCompleted();
}
@@ -498,8 +508,83 @@
}
@Test
- public void testNoAutoBatterySaver_fromAdb() {
+ public void testAutoBatterySaver_withStickyDisabled() {
+ when(mMockResources.getBoolean(
+ com.android.internal.R.bool.config_batterySaverStickyBehaviourDisabled))
+ .thenReturn(true);
+ initDevice();
+ mDevice.putGlobalSetting(Global.LOW_POWER_MODE_TRIGGER_LEVEL, 50);
+ mTarget.setBatterySaverEnabledManually(true);
+
+ assertEquals(true, mDevice.batterySaverEnabled);
+ assertEquals(100, mPersistedState.batteryLevel);
+ assertEquals(false, mPersistedState.batteryLow);
+
+ mDevice.setBatteryLevel(30);
+
+ assertEquals(true, mDevice.batterySaverEnabled);
+ assertEquals(30, mPersistedState.batteryLevel);
+ assertEquals(true, mPersistedState.batteryLow);
+
+ mDevice.setBatteryLevel(80);
+
+ assertEquals(false, mDevice.batterySaverEnabled); // Not sticky.
+ assertEquals(80, mPersistedState.batteryLevel);
+ assertEquals(false, mPersistedState.batteryLow);
+
+ mDevice.setPowered(true);
+
+ assertEquals(false, mDevice.batterySaverEnabled);
+ assertEquals(80, mPersistedState.batteryLevel);
+ assertEquals(false, mPersistedState.batteryLow);
+
+ mDevice.setBatteryLevel(30);
+
+ assertEquals(false, mDevice.batterySaverEnabled);
+ assertEquals(30, mPersistedState.batteryLevel);
+ assertEquals(true, mPersistedState.batteryLow);
+
+ mDevice.setPowered(false);
+
+ assertEquals(true, mDevice.batterySaverEnabled); // Restores BS.
+ assertEquals(30, mPersistedState.batteryLevel);
+ assertEquals(true, mPersistedState.batteryLow);
+
+ mDevice.setPowered(true);
+ mDevice.setBatteryLevel(90);
+
+ assertEquals(false, mDevice.batterySaverEnabled);
+ assertEquals(90, mPersistedState.batteryLevel);
+ assertEquals(false, mPersistedState.batteryLow);
+
+ initDevice();
+
+ assertEquals(false, mDevice.batterySaverEnabled);
+ assertEquals(90, mPersistedState.batteryLevel);
+ assertEquals(false, mPersistedState.batteryLow);
+
+ mDevice.setPowered(false);
+
+ assertEquals(false, mDevice.batterySaverEnabled);
+ assertEquals(90, mPersistedState.batteryLevel);
+ assertEquals(false, mPersistedState.batteryLow);
+
+ mTarget.setBatterySaverEnabledManually(false);
+
+ assertEquals(false, mDevice.batterySaverEnabled);
+ assertEquals(90, mPersistedState.batteryLevel);
+ assertEquals(false, mPersistedState.batteryLow);
+
+ initDevice();
+
+ assertEquals(false, mDevice.batterySaverEnabled);
+ assertEquals(90, mPersistedState.batteryLevel);
+ assertEquals(false, mPersistedState.batteryLow);
+ }
+
+ @Test
+ public void testNoAutoBatterySaver_fromAdb() {
assertEquals(0, mDevice.getLowPowerModeTriggerLevel());
assertEquals(false, mDevice.batterySaverEnabled);
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/SimpleTimeZoneDetectorStrategyTest.java b/services/tests/servicestests/src/com/android/server/timedetector/SimpleTimeZoneDetectorStrategyTest.java
index e4b3b13..62f1433 100644
--- a/services/tests/servicestests/src/com/android/server/timedetector/SimpleTimeZoneDetectorStrategyTest.java
+++ b/services/tests/servicestests/src/com/android/server/timedetector/SimpleTimeZoneDetectorStrategyTest.java
@@ -16,12 +16,18 @@
package com.android.server.timedetector;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import android.app.timedetector.TimeSignal;
+import android.content.Intent;
+import android.icu.util.Calendar;
+import android.icu.util.GregorianCalendar;
+import android.icu.util.TimeZone;
import android.support.test.runner.AndroidJUnit4;
import android.util.TimestampedValue;
@@ -32,37 +38,476 @@
@RunWith(AndroidJUnit4.class)
public class SimpleTimeZoneDetectorStrategyTest {
- private TimeDetectorStrategy.Callback mMockCallback;
+ private static final Scenario SCENARIO_1 = new Scenario.Builder()
+ .setInitialDeviceSystemClockUtc(1977, 1, 1, 12, 0, 0)
+ .setInitialDeviceRealtimeMillis(123456789L)
+ .setActualTimeUtc(2018, 1, 1, 12, 0, 0)
+ .build();
- private SimpleTimeDetectorStrategy mSimpleTimeZoneDetectorStrategy;
+ private Script mScript;
@Before
public void setUp() {
- mMockCallback = mock(TimeDetectorStrategy.Callback.class);
- mSimpleTimeZoneDetectorStrategy = new SimpleTimeDetectorStrategy();
- mSimpleTimeZoneDetectorStrategy.initialize(mMockCallback);
+ mScript = new Script();
}
@Test
- public void testSuggestTime_nitz() {
- TimestampedValue<Long> utcTime = createUtcTime();
- TimeSignal timeSignal = new TimeSignal(TimeSignal.SOURCE_ID_NITZ, utcTime);
+ public void testSuggestTime_nitz_timeDetectionEnabled() {
+ Scenario scenario = SCENARIO_1;
+ mScript.pokeFakeClocks(scenario)
+ .pokeTimeDetectionEnabled(true);
- mSimpleTimeZoneDetectorStrategy.suggestTime(timeSignal);
+ TimeSignal timeSignal = scenario.createTimeSignalForActual(TimeSignal.SOURCE_ID_NITZ);
+ final int clockIncrement = 1000;
+ long expectSystemClockMillis = scenario.getActualTimeMillis() + clockIncrement;
- verify(mMockCallback).setTime(utcTime);
+ mScript.simulateTimePassing(clockIncrement)
+ .simulateTimeSignalReceived(timeSignal)
+ .verifySystemClockWasSetAndResetCallTracking(expectSystemClockMillis);
+ }
+
+ @Test
+ public void testSuggestTime_systemClockThreshold() {
+ Scenario scenario = SCENARIO_1;
+ final int systemClockUpdateThresholdMillis = 1000;
+ mScript.pokeFakeClocks(scenario)
+ .pokeThresholds(systemClockUpdateThresholdMillis)
+ .pokeTimeDetectionEnabled(true);
+
+ TimeSignal timeSignal1 = scenario.createTimeSignalForActual(TimeSignal.SOURCE_ID_NITZ);
+ TimestampedValue<Long> utcTime1 = timeSignal1.getUtcTime();
+
+ final int clockIncrement = 100;
+ // Increment the the device clocks to simulate the passage of time.
+ mScript.simulateTimePassing(clockIncrement);
+
+ long expectSystemClockMillis1 =
+ TimeDetectorStrategy.getTimeAt(utcTime1, mScript.peekElapsedRealtimeMillis());
+
+ // Send the first time signal. It should be used.
+ mScript.simulateTimeSignalReceived(timeSignal1)
+ .verifySystemClockWasSetAndResetCallTracking(expectSystemClockMillis1);
+
+ // Now send another time signal, but one that is too similar to the last one and should be
+ // ignored.
+ int underThresholdMillis = systemClockUpdateThresholdMillis - 1;
+ TimestampedValue<Long> utcTime2 = new TimestampedValue<>(
+ mScript.peekElapsedRealtimeMillis(),
+ mScript.peekSystemClockMillis() + underThresholdMillis);
+ TimeSignal timeSignal2 = new TimeSignal(TimeSignal.SOURCE_ID_NITZ, utcTime2);
+ mScript.simulateTimePassing(clockIncrement)
+ .simulateTimeSignalReceived(timeSignal2)
+ .verifySystemClockWasNotSetAndResetCallTracking();
+
+ // Now send another time signal, but one that is on the threshold and so should be used.
+ TimestampedValue<Long> utcTime3 = new TimestampedValue<>(
+ mScript.peekElapsedRealtimeMillis(),
+ mScript.peekSystemClockMillis() + systemClockUpdateThresholdMillis);
+
+ TimeSignal timeSignal3 = new TimeSignal(TimeSignal.SOURCE_ID_NITZ, utcTime3);
+ mScript.simulateTimePassing(clockIncrement);
+
+ long expectSystemClockMillis3 =
+ TimeDetectorStrategy.getTimeAt(utcTime3, mScript.peekElapsedRealtimeMillis());
+
+ mScript.simulateTimeSignalReceived(timeSignal3)
+ .verifySystemClockWasSetAndResetCallTracking(expectSystemClockMillis3);
+ }
+
+ @Test
+ public void testSuggestTime_nitz_timeDetectionDisabled() {
+ Scenario scenario = SCENARIO_1;
+ mScript.pokeFakeClocks(scenario)
+ .pokeTimeDetectionEnabled(false);
+
+ TimeSignal timeSignal = scenario.createTimeSignalForActual(TimeSignal.SOURCE_ID_NITZ);
+ mScript.simulateTimeSignalReceived(timeSignal)
+ .verifySystemClockWasNotSetAndResetCallTracking();
+ }
+
+ @Test
+ public void testSuggestTime_nitz_invalidNitzReferenceTimesIgnored() {
+ Scenario scenario = SCENARIO_1;
+ final int systemClockUpdateThreshold = 2000;
+ mScript.pokeFakeClocks(scenario)
+ .pokeThresholds(systemClockUpdateThreshold)
+ .pokeTimeDetectionEnabled(true);
+ TimeSignal timeSignal1 = scenario.createTimeSignalForActual(TimeSignal.SOURCE_ID_NITZ);
+ TimestampedValue<Long> utcTime1 = timeSignal1.getUtcTime();
+
+ // Initialize the strategy / device with a time set from NITZ.
+ mScript.simulateTimePassing(100);
+ long expectedSystemClockMillis1 =
+ TimeDetectorStrategy.getTimeAt(utcTime1, mScript.peekElapsedRealtimeMillis());
+ mScript.simulateTimeSignalReceived(timeSignal1)
+ .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis1);
+
+ // The UTC time increment should be larger than the system clock update threshold so we
+ // know it shouldn't be ignored for other reasons.
+ long validUtcTimeMillis = utcTime1.getValue() + (2 * systemClockUpdateThreshold);
+
+ // Now supply a new signal that has an obviously bogus reference time : older than the last
+ // one.
+ long referenceTimeBeforeLastSignalMillis = utcTime1.getReferenceTimeMillis() - 1;
+ TimestampedValue<Long> utcTime2 = new TimestampedValue<>(
+ referenceTimeBeforeLastSignalMillis, validUtcTimeMillis);
+ TimeSignal timeSignal2 = new TimeSignal(TimeSignal.SOURCE_ID_NITZ, utcTime2);
+ mScript.simulateTimeSignalReceived(timeSignal2)
+ .verifySystemClockWasNotSetAndResetCallTracking();
+
+ // Now supply a new signal that has an obviously bogus reference time : substantially in the
+ // future.
+ long referenceTimeInFutureMillis =
+ utcTime1.getReferenceTimeMillis() + Integer.MAX_VALUE + 1;
+ TimestampedValue<Long> utcTime3 = new TimestampedValue<>(
+ referenceTimeInFutureMillis, validUtcTimeMillis);
+ TimeSignal timeSignal3 = new TimeSignal(TimeSignal.SOURCE_ID_NITZ, utcTime3);
+ mScript.simulateTimeSignalReceived(timeSignal3)
+ .verifySystemClockWasNotSetAndResetCallTracking();
+
+ // Just to prove validUtcTimeMillis is valid.
+ long validReferenceTimeMillis = utcTime1.getReferenceTimeMillis() + 100;
+ TimestampedValue<Long> utcTime4 = new TimestampedValue<>(
+ validReferenceTimeMillis, validUtcTimeMillis);
+ long expectedSystemClockMillis4 =
+ TimeDetectorStrategy.getTimeAt(utcTime4, mScript.peekElapsedRealtimeMillis());
+ TimeSignal timeSignal4 = new TimeSignal(TimeSignal.SOURCE_ID_NITZ, utcTime4);
+ mScript.simulateTimeSignalReceived(timeSignal4)
+ .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis4);
+ }
+
+ @Test
+ public void testSuggestTime_timeDetectionToggled() {
+ Scenario scenario = SCENARIO_1;
+ final int clockIncrementMillis = 100;
+ final int systemClockUpdateThreshold = 2000;
+ mScript.pokeFakeClocks(scenario)
+ .pokeThresholds(systemClockUpdateThreshold)
+ .pokeTimeDetectionEnabled(false);
+
+ TimeSignal timeSignal1 = scenario.createTimeSignalForActual(TimeSignal.SOURCE_ID_NITZ);
+ TimestampedValue<Long> utcTime1 = timeSignal1.getUtcTime();
+
+ // Simulate time passing.
+ mScript.simulateTimePassing(clockIncrementMillis);
+
+ // Simulate the time signal being received. It should not be used because auto time
+ // detection is off but it should be recorded.
+ mScript.simulateTimeSignalReceived(timeSignal1)
+ .verifySystemClockWasNotSetAndResetCallTracking();
+
+ // Simulate more time passing.
+ mScript.simulateTimePassing(clockIncrementMillis);
+
+ long expectedSystemClockMillis1 =
+ TimeDetectorStrategy.getTimeAt(utcTime1, mScript.peekElapsedRealtimeMillis());
+
+ // Turn on auto time detection.
+ mScript.simulateAutoTimeDetectionToggle()
+ .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis1);
+
+ // Turn off auto time detection.
+ mScript.simulateAutoTimeDetectionToggle()
+ .verifySystemClockWasNotSetAndResetCallTracking();
+
+ // Receive another valid time signal.
+ // It should be on the threshold and accounting for the clock increments.
+ TimestampedValue<Long> utcTime2 = new TimestampedValue<>(
+ mScript.peekElapsedRealtimeMillis(),
+ mScript.peekSystemClockMillis() + systemClockUpdateThreshold);
+ TimeSignal timeSignal2 = new TimeSignal(TimeSignal.SOURCE_ID_NITZ, utcTime2);
+
+ // Simulate more time passing.
+ mScript.simulateTimePassing(clockIncrementMillis);
+
+ long expectedSystemClockMillis2 =
+ TimeDetectorStrategy.getTimeAt(utcTime2, mScript.peekElapsedRealtimeMillis());
+
+ // The new time, though valid, should not be set in the system clock because auto time is
+ // disabled.
+ mScript.simulateTimeSignalReceived(timeSignal2)
+ .verifySystemClockWasNotSetAndResetCallTracking();
+
+ // Turn on auto time detection.
+ mScript.simulateAutoTimeDetectionToggle()
+ .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis2);
}
@Test
public void testSuggestTime_unknownSource() {
- TimestampedValue<Long> utcTime = createUtcTime();
- TimeSignal timeSignal = new TimeSignal("unknown", utcTime);
- mSimpleTimeZoneDetectorStrategy.suggestTime(timeSignal);
+ Scenario scenario = SCENARIO_1;
+ mScript.pokeFakeClocks(scenario)
+ .pokeTimeDetectionEnabled(true);
- verify(mMockCallback, never()).setTime(any());
+ TimeSignal timeSignal = scenario.createTimeSignalForActual("unknown");
+ mScript.simulateTimeSignalReceived(timeSignal)
+ .verifySystemClockWasNotSetAndResetCallTracking();
}
- private static TimestampedValue<Long> createUtcTime() {
- return new TimestampedValue<>(321L, 123456L);
+ /**
+ * A fake implementation of TimeDetectorStrategy.Callback. Besides tracking changes and behaving
+ * like the real thing should, it also asserts preconditions.
+ */
+ private static class FakeCallback implements TimeDetectorStrategy.Callback {
+ private boolean mTimeDetectionEnabled;
+ private boolean mWakeLockAcquired;
+ private long mElapsedRealtimeMillis;
+ private long mSystemClockMillis;
+ private int mSystemClockUpdateThresholdMillis = 2000;
+
+ // Tracking operations.
+ private boolean mSystemClockWasSet;
+ private Intent mBroadcastSent;
+
+ @Override
+ public int systemClockUpdateThresholdMillis() {
+ return mSystemClockUpdateThresholdMillis;
+ }
+
+ @Override
+ public boolean isTimeDetectionEnabled() {
+ return mTimeDetectionEnabled;
+ }
+
+ @Override
+ public void acquireWakeLock() {
+ if (mWakeLockAcquired) {
+ fail("Wake lock already acquired");
+ }
+ mWakeLockAcquired = true;
+ }
+
+ @Override
+ public long elapsedRealtimeMillis() {
+ assertWakeLockAcquired();
+ return mElapsedRealtimeMillis;
+ }
+
+ @Override
+ public long systemClockMillis() {
+ assertWakeLockAcquired();
+ return mSystemClockMillis;
+ }
+
+ @Override
+ public void setSystemClock(long newTimeMillis) {
+ assertWakeLockAcquired();
+ mSystemClockWasSet = true;
+ mSystemClockMillis = newTimeMillis;
+ }
+
+ @Override
+ public void releaseWakeLock() {
+ assertWakeLockAcquired();
+ mWakeLockAcquired = false;
+ }
+
+ @Override
+ public void sendStickyBroadcast(Intent intent) {
+ assertNotNull(intent);
+ mBroadcastSent = intent;
+ }
+
+ // Methods below are for managing the fake's behavior.
+
+ public void pokeSystemClockUpdateThreshold(int thresholdMillis) {
+ mSystemClockUpdateThresholdMillis = thresholdMillis;
+ }
+
+ public void pokeElapsedRealtimeMillis(long elapsedRealtimeMillis) {
+ mElapsedRealtimeMillis = elapsedRealtimeMillis;
+ }
+
+ public void pokeSystemClockMillis(long systemClockMillis) {
+ mSystemClockMillis = systemClockMillis;
+ }
+
+ public void pokeTimeDetectionEnabled(boolean enabled) {
+ mTimeDetectionEnabled = enabled;
+ }
+
+ public long peekElapsedRealtimeMillis() {
+ return mElapsedRealtimeMillis;
+ }
+
+ public long peekSystemClockMillis() {
+ return mSystemClockMillis;
+ }
+
+ public void simulateTimePassing(int incrementMillis) {
+ mElapsedRealtimeMillis += incrementMillis;
+ mSystemClockMillis += incrementMillis;
+ }
+
+ public void verifySystemClockNotSet() {
+ assertFalse(mSystemClockWasSet);
+ }
+
+ public void verifySystemClockWasSet(long expectSystemClockMillis) {
+ assertTrue(mSystemClockWasSet);
+ assertEquals(expectSystemClockMillis, mSystemClockMillis);
+ }
+
+ public void verifyIntentWasBroadcast() {
+ assertTrue(mBroadcastSent != null);
+ }
+
+ public void verifyIntentWasNotBroadcast() {
+ assertNull(mBroadcastSent);
+ }
+
+ public void resetCallTracking() {
+ mSystemClockWasSet = false;
+ mBroadcastSent = null;
+ }
+
+ private void assertWakeLockAcquired() {
+ assertTrue("The operation must be performed only after acquiring the wakelock",
+ mWakeLockAcquired);
+ }
+ }
+
+ /**
+ * A fluent helper class for tests.
+ */
+ private class Script {
+
+ private final FakeCallback mFakeCallback;
+ private final SimpleTimeDetectorStrategy mSimpleTimeDetectorStrategy;
+
+ public Script() {
+ mFakeCallback = new FakeCallback();
+ mSimpleTimeDetectorStrategy = new SimpleTimeDetectorStrategy();
+ mSimpleTimeDetectorStrategy.initialize(mFakeCallback);
+
+ }
+
+ Script pokeTimeDetectionEnabled(boolean enabled) {
+ mFakeCallback.pokeTimeDetectionEnabled(enabled);
+ return this;
+ }
+
+ Script pokeFakeClocks(Scenario scenario) {
+ mFakeCallback.pokeElapsedRealtimeMillis(scenario.getInitialRealTimeMillis());
+ mFakeCallback.pokeSystemClockMillis(scenario.getInitialSystemClockMillis());
+ return this;
+ }
+
+ Script pokeThresholds(int systemClockUpdateThreshold) {
+ mFakeCallback.pokeSystemClockUpdateThreshold(systemClockUpdateThreshold);
+ return this;
+ }
+
+ long peekElapsedRealtimeMillis() {
+ return mFakeCallback.peekElapsedRealtimeMillis();
+ }
+
+ long peekSystemClockMillis() {
+ return mFakeCallback.peekSystemClockMillis();
+ }
+
+ Script simulateTimeSignalReceived(TimeSignal timeSignal) {
+ mSimpleTimeDetectorStrategy.suggestTime(timeSignal);
+ return this;
+ }
+
+ Script simulateAutoTimeDetectionToggle() {
+ boolean enabled = !mFakeCallback.isTimeDetectionEnabled();
+ mFakeCallback.pokeTimeDetectionEnabled(enabled);
+ mSimpleTimeDetectorStrategy.handleAutoTimeDetectionToggle(enabled);
+ return this;
+ }
+
+ Script simulateTimePassing(int clockIncrement) {
+ mFakeCallback.simulateTimePassing(clockIncrement);
+ return this;
+ }
+
+ Script verifySystemClockWasNotSetAndResetCallTracking() {
+ mFakeCallback.verifySystemClockNotSet();
+ mFakeCallback.verifyIntentWasNotBroadcast();
+ mFakeCallback.resetCallTracking();
+ return this;
+ }
+
+ Script verifySystemClockWasSetAndResetCallTracking(long expectSystemClockMillis) {
+ mFakeCallback.verifySystemClockWasSet(expectSystemClockMillis);
+ mFakeCallback.verifyIntentWasBroadcast();
+ mFakeCallback.resetCallTracking();
+ return this;
+ }
+ }
+
+ /**
+ * A starting scenario used during tests. Describes a fictional "physical" reality.
+ */
+ private static class Scenario {
+
+ private final long mInitialDeviceSystemClockMillis;
+ private final long mInitialDeviceRealtimeMillis;
+ private final long mActualTimeMillis;
+
+ Scenario(long initialDeviceSystemClock, long elapsedRealtime, long timeMillis) {
+ mInitialDeviceSystemClockMillis = initialDeviceSystemClock;
+ mActualTimeMillis = timeMillis;
+ mInitialDeviceRealtimeMillis = elapsedRealtime;
+ }
+
+ long getInitialRealTimeMillis() {
+ return mInitialDeviceRealtimeMillis;
+ }
+
+ long getInitialSystemClockMillis() {
+ return mInitialDeviceSystemClockMillis;
+ }
+
+ long getActualTimeMillis() {
+ return mActualTimeMillis;
+ }
+
+ TimeSignal createTimeSignalForActual(String sourceId) {
+ TimestampedValue<Long> time = new TimestampedValue<>(
+ mInitialDeviceRealtimeMillis, mActualTimeMillis);
+ return new TimeSignal(sourceId, time);
+ }
+
+ static class Builder {
+
+ private long mInitialDeviceSystemClockMillis;
+ private long mInitialDeviceRealtimeMillis;
+ private long mActualTimeMillis;
+
+ Builder setInitialDeviceSystemClockUtc(int year, int monthInYear, int day,
+ int hourOfDay, int minute, int second) {
+ mInitialDeviceSystemClockMillis = createUtcTime(year, monthInYear, day, hourOfDay,
+ minute, second);
+ return this;
+ }
+
+ Builder setInitialDeviceRealtimeMillis(long realtimeMillis) {
+ mInitialDeviceRealtimeMillis = realtimeMillis;
+ return this;
+ }
+
+ Builder setActualTimeUtc(int year, int monthInYear, int day, int hourOfDay,
+ int minute, int second) {
+ mActualTimeMillis =
+ createUtcTime(year, monthInYear, day, hourOfDay, minute, second);
+ return this;
+ }
+
+ Scenario build() {
+ return new Scenario(mInitialDeviceSystemClockMillis, mInitialDeviceRealtimeMillis,
+ mActualTimeMillis);
+ }
+ }
+ }
+
+ private static long createUtcTime(int year, int monthInYear, int day, int hourOfDay, int minute,
+ int second) {
+ Calendar cal = new GregorianCalendar(TimeZone.getTimeZone("Etc/UTC"));
+ cal.clear();
+ cal.set(year, monthInYear - 1, day, hourOfDay, minute, second);
+ return cal.getTimeInMillis();
}
}
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
index 22dea92..ed74cd7 100644
--- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
@@ -16,6 +16,9 @@
package com.android.server.timedetector;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
@@ -23,36 +26,40 @@
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
import android.app.timedetector.TimeSignal;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.support.test.runner.AndroidJUnit4;
import android.util.TimestampedValue;
-import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import com.android.server.timedetector.TimeDetectorStrategy.Callback;
+
+import java.io.PrintWriter;
+
@RunWith(AndroidJUnit4.class)
public class TimeDetectorServiceTest {
- private TimeDetectorService mTimeDetectorService;
-
private Context mMockContext;
- private TimeDetectorStrategy mMockTimeDetectorStrategy;
+ private StubbedTimeDetectorStrategy mStubbedTimeDetectorStrategy;
+ private Callback mMockCallback;
+
+ private TimeDetectorService mTimeDetectorService;
@Before
public void setUp() {
mMockContext = mock(Context.class);
- mMockTimeDetectorStrategy = mock(TimeDetectorStrategy.class);
- mTimeDetectorService = new TimeDetectorService(mMockContext, mMockTimeDetectorStrategy);
- }
+ mMockCallback = mock(Callback.class);
+ mStubbedTimeDetectorStrategy = new StubbedTimeDetectorStrategy();
- @After
- public void tearDown() {
- verifyNoMoreInteractions(mMockContext, mMockTimeDetectorStrategy);
+ mTimeDetectorService = new TimeDetectorService(
+ mMockContext, mMockCallback,
+ mStubbedTimeDetectorStrategy);
}
@Test(expected=SecurityException.class)
@@ -78,11 +85,86 @@
verify(mMockContext)
.enforceCallingPermission(eq(android.Manifest.permission.SET_TIME), anyString());
- verify(mMockTimeDetectorStrategy).suggestTime(timeSignal);
+ mStubbedTimeDetectorStrategy.verifySuggestTimeCalled(timeSignal);
+ }
+
+ @Test
+ public void testDump() {
+ when(mMockContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP))
+ .thenReturn(PackageManager.PERMISSION_GRANTED);
+
+ mTimeDetectorService.dump(null, null, null);
+
+ verify(mMockContext).checkCallingOrSelfPermission(eq(android.Manifest.permission.DUMP));
+ mStubbedTimeDetectorStrategy.verifyDumpCalled();
+ }
+
+ @Test
+ public void testAutoTimeDetectionToggle() {
+ when(mMockCallback.isTimeDetectionEnabled()).thenReturn(true);
+
+ mTimeDetectorService.handleAutoTimeDetectionToggle();
+
+ mStubbedTimeDetectorStrategy.verifyHandleAutoTimeDetectionToggleCalled(true);
+
+ when(mMockCallback.isTimeDetectionEnabled()).thenReturn(false);
+
+ mTimeDetectorService.handleAutoTimeDetectionToggle();
+
+ mStubbedTimeDetectorStrategy.verifyHandleAutoTimeDetectionToggleCalled(false);
}
private static TimeSignal createNitzTimeSignal() {
TimestampedValue<Long> timeValue = new TimestampedValue<>(100L, 1_000_000L);
return new TimeSignal(TimeSignal.SOURCE_ID_NITZ, timeValue);
}
+
+ private static class StubbedTimeDetectorStrategy implements TimeDetectorStrategy {
+
+ // Call tracking.
+ private TimeSignal mLastSuggestedTime;
+ private Boolean mLastAutoTimeDetectionToggle;
+ private boolean mDumpCalled;
+
+ @Override
+ public void initialize(Callback ignored) {
+ }
+
+ @Override
+ public void suggestTime(TimeSignal timeSignal) {
+ resetCallTracking();
+ mLastSuggestedTime = timeSignal;
+ }
+
+ @Override
+ public void handleAutoTimeDetectionToggle(boolean enabled) {
+ resetCallTracking();
+ mLastAutoTimeDetectionToggle = enabled;
+ }
+
+ @Override
+ public void dump(PrintWriter pw, String[] args) {
+ resetCallTracking();
+ mDumpCalled = true;
+ }
+
+ void resetCallTracking() {
+ mLastSuggestedTime = null;
+ mLastAutoTimeDetectionToggle = null;
+ mDumpCalled = false;
+ }
+
+ void verifySuggestTimeCalled(TimeSignal expectedSignal) {
+ assertEquals(expectedSignal, mLastSuggestedTime);
+ }
+
+ void verifyHandleAutoTimeDetectionToggleCalled(boolean expectedEnable) {
+ assertNotNull(mLastAutoTimeDetectionToggle);
+ assertEquals(expectedEnable, mLastAutoTimeDetectionToggle);
+ }
+
+ void verifyDumpCalled() {
+ assertTrue(mDumpCalled);
+ }
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
new file mode 100644
index 0000000..19d31cf
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
@@ -0,0 +1,45 @@
+/*
+ * 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.timezonedetector;
+
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Unit tests for the {@link TimeZoneDetectorService}.
+ */
+@RunWith(AndroidJUnit4.class)
+public class TimeZoneDetectorServiceTest {
+
+ private TimeZoneDetectorService mTimeZoneDetectorService;
+
+ @Before
+ public void setUp() {
+ final Context context = InstrumentationRegistry.getContext();
+ mTimeZoneDetectorService = new TimeZoneDetectorService(context);
+ }
+
+ @Test
+ public void testStubbedCall() {
+ mTimeZoneDetectorService.stubbedCall();
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java
index ff631e7..08b522c 100644
--- a/services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java
@@ -151,11 +151,13 @@
}
@Override
- public void onAnimationStart(boolean schedulePipModeChangedCallback, boolean forceUpdate) {
+ public boolean onAnimationStart(boolean schedulePipModeChangedCallback,
+ boolean forceUpdate) {
mAwaitingAnimationStart = false;
mAnimationStarted = true;
mSchedulePipModeChangedOnStart = schedulePipModeChangedCallback;
mForcePipModeChangedCallback = forceUpdate;
+ return true;
}
@Override
diff --git a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
index c3b2f87..4e9894b 100644
--- a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
@@ -17,6 +17,8 @@
package com.android.server.wm;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
@@ -35,6 +37,8 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -349,34 +353,36 @@
*/
@Test
public void testMaxUiWidth() throws Exception {
+ // Prevent base display metrics for test from being updated to the value of real display.
+ final DisplayContent displayContent = createDisplayNoUpdateDisplayInfo();
final int baseWidth = 1440;
final int baseHeight = 2560;
final int baseDensity = 300;
- mDisplayContent.updateBaseDisplayMetrics(baseWidth, baseHeight, baseDensity);
+ displayContent.updateBaseDisplayMetrics(baseWidth, baseHeight, baseDensity);
final int maxWidth = 300;
final int resultingHeight = (maxWidth * baseHeight) / baseWidth;
final int resultingDensity = (maxWidth * baseDensity) / baseWidth;
- mDisplayContent.setMaxUiWidth(maxWidth);
- verifySizes(mDisplayContent, maxWidth, resultingHeight, resultingDensity);
+ displayContent.setMaxUiWidth(maxWidth);
+ verifySizes(displayContent, maxWidth, resultingHeight, resultingDensity);
// Assert setting values again does not change;
- mDisplayContent.updateBaseDisplayMetrics(baseWidth, baseHeight, baseDensity);
- verifySizes(mDisplayContent, maxWidth, resultingHeight, resultingDensity);
+ displayContent.updateBaseDisplayMetrics(baseWidth, baseHeight, baseDensity);
+ verifySizes(displayContent, maxWidth, resultingHeight, resultingDensity);
final int smallerWidth = 200;
final int smallerHeight = 400;
final int smallerDensity = 100;
// Specify smaller dimension, verify that it is honored
- mDisplayContent.updateBaseDisplayMetrics(smallerWidth, smallerHeight, smallerDensity);
- verifySizes(mDisplayContent, smallerWidth, smallerHeight, smallerDensity);
+ displayContent.updateBaseDisplayMetrics(smallerWidth, smallerHeight, smallerDensity);
+ verifySizes(displayContent, smallerWidth, smallerHeight, smallerDensity);
// Verify that setting the max width to a greater value than the base width has no effect
- mDisplayContent.setMaxUiWidth(maxWidth);
- verifySizes(mDisplayContent, smallerWidth, smallerHeight, smallerDensity);
+ displayContent.setMaxUiWidth(maxWidth);
+ verifySizes(displayContent, smallerWidth, smallerHeight, smallerDensity);
}
/**
@@ -384,10 +390,14 @@
*/
@Test
public void testAlwaysOnTopStackLocation() {
- final TaskStack alwaysOnTopStack = createTaskStackOnDisplay(mDisplayContent);
+ final TaskStack alwaysOnTopStack = createStackControllerOnStackOnDisplay(
+ WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD, mDisplayContent).mContainer;
+ final Task task = createTaskInStack(alwaysOnTopStack, 0 /* userId */);
alwaysOnTopStack.setAlwaysOnTop(true);
mDisplayContent.positionStackAt(POSITION_TOP, alwaysOnTopStack);
assertTrue(alwaysOnTopStack.isAlwaysOnTop());
+ // Ensure always on top state is synced to the children of the stack.
+ assertTrue(alwaysOnTopStack.getTopChild().isAlwaysOnTop());
assertEquals(alwaysOnTopStack, mDisplayContent.getTopStack());
final TaskStack pinnedStack = createStackControllerOnStackOnDisplay(
@@ -395,7 +405,8 @@
assertEquals(pinnedStack, mDisplayContent.getPinnedStack());
assertEquals(pinnedStack, mDisplayContent.getTopStack());
- final TaskStack anotherAlwaysOnTopStack = createTaskStackOnDisplay(mDisplayContent);
+ final TaskStack anotherAlwaysOnTopStack = createStackControllerOnStackOnDisplay(
+ WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD, mDisplayContent).mContainer;
anotherAlwaysOnTopStack.setAlwaysOnTop(true);
mDisplayContent.positionStackAt(POSITION_TOP, anotherAlwaysOnTopStack);
assertTrue(anotherAlwaysOnTopStack.isAlwaysOnTop());
@@ -404,12 +415,29 @@
// existing alwaysOnTop stack.
assertEquals(anotherAlwaysOnTopStack, mDisplayContent.getStacks().get(topPosition - 1));
- final TaskStack nonAlwaysOnTopStack = createTaskStackOnDisplay(mDisplayContent);
+ final TaskStack nonAlwaysOnTopStack = createStackControllerOnStackOnDisplay(
+ WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD, mDisplayContent).mContainer;
assertEquals(mDisplayContent, nonAlwaysOnTopStack.getDisplayContent());
topPosition = mDisplayContent.getStacks().size() - 1;
// Ensure the non-alwaysOnTop stack is put below the three alwaysOnTop stacks, but above the
// existing other non-alwaysOnTop stacks.
assertEquals(nonAlwaysOnTopStack, mDisplayContent.getStacks().get(topPosition - 3));
+
+ anotherAlwaysOnTopStack.setAlwaysOnTop(false);
+ mDisplayContent.positionStackAt(POSITION_TOP, anotherAlwaysOnTopStack);
+ assertFalse(anotherAlwaysOnTopStack.isAlwaysOnTop());
+ // Ensure, when always on top is turned off for a stack, the stack is put just below all
+ // other always on top stacks.
+ assertEquals(anotherAlwaysOnTopStack, mDisplayContent.getStacks().get(topPosition - 2));
+ anotherAlwaysOnTopStack.setAlwaysOnTop(true);
+
+ // Ensure always on top state changes properly when windowing mode changes.
+ anotherAlwaysOnTopStack.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ assertFalse(anotherAlwaysOnTopStack.isAlwaysOnTop());
+ assertEquals(anotherAlwaysOnTopStack, mDisplayContent.getStacks().get(topPosition - 2));
+ anotherAlwaysOnTopStack.setWindowingMode(WINDOWING_MODE_FREEFORM);
+ assertTrue(anotherAlwaysOnTopStack.isAlwaysOnTop());
+ assertEquals(anotherAlwaysOnTopStack, mDisplayContent.getStacks().get(topPosition - 1));
}
/**
@@ -452,22 +480,42 @@
@Test
public void testDisplayCutout_rot90() throws Exception {
synchronized (sWm.getWindowManagerLock()) {
- final DisplayContent dc = createNewDisplay();
- dc.mInitialDisplayWidth = 200;
- dc.mInitialDisplayHeight = 400;
- Rect r1 = new Rect(80, 0, 120, 10);
+ // Prevent mInitialDisplayCutout from being updated from real display (e.g. null
+ // if the device has no cutout).
+ final DisplayContent dc = createDisplayNoUpdateDisplayInfo();
+ // Rotation may use real display info to compute bound, so here also uses the
+ // same width and height.
+ final int displayWidth = dc.mInitialDisplayWidth;
+ final int displayHeight = dc.mInitialDisplayHeight;
+ final int cutoutWidth = 40;
+ final int cutoutHeight = 10;
+ final int left = (displayWidth - cutoutWidth) / 2;
+ final int top = 0;
+ final int right = (displayWidth + cutoutWidth) / 2;
+ final int bottom = cutoutHeight;
+
+ final Rect r1 = new Rect(left, top, right, bottom);
final DisplayCutout cutout = new WmDisplayCutout(
fromBoundingRect(r1.left, r1.top, r1.right, r1.bottom), null)
- .computeSafeInsets(200, 400).getDisplayCutout();
+ .computeSafeInsets(displayWidth, displayHeight).getDisplayCutout();
dc.mInitialDisplayCutout = cutout;
dc.setRotation(Surface.ROTATION_90);
dc.computeScreenConfiguration(new Configuration()); // recomputes dc.mDisplayInfo.
- final Rect r = new Rect(0, 80, 10, 120);
+ // ----o---------- -------------
+ // | | | | |
+ // | ------o | o---
+ // | | | |
+ // | | -> | |
+ // | | ---o
+ // | | |
+ // | | -------------
+ final Rect r = new Rect(top, left, bottom, right);
assertEquals(new WmDisplayCutout(
fromBoundingRect(r.left, r.top, r.right, r.bottom), null)
- .computeSafeInsets(400, 200).getDisplayCutout(), dc.getDisplayInfo().displayCutout);
+ .computeSafeInsets(displayHeight, displayWidth)
+ .getDisplayCutout(), dc.getDisplayInfo().displayCutout);
}
}
@@ -529,6 +577,16 @@
assertEquals(displayContent.mBaseDisplayDensity, expectedBaseDensity);
}
+ /**
+ * Create DisplayContent that does not update display base/initial values from device to keep
+ * the values set by test.
+ */
+ private DisplayContent createDisplayNoUpdateDisplayInfo() {
+ final DisplayContent displayContent = spy(createNewDisplay());
+ doNothing().when(displayContent).updateDisplayInfo();
+ return displayContent;
+ }
+
private void assertForAllWindowsOrder(List<WindowState> expectedWindowsBottomToTop) {
final LinkedList<WindowState> actualWindows = new LinkedList<>();
diff --git a/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java b/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java
index edac8a5..79e9bb4 100644
--- a/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java
@@ -20,24 +20,23 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.animation.AnimationHandler;
import android.animation.AnimationHandler.AnimationFrameCallbackProvider;
import android.animation.ValueAnimator;
import android.graphics.Matrix;
import android.graphics.Point;
+import android.os.PowerManagerInternal;
import android.platform.test.annotations.Presubmit;
import android.support.test.filters.FlakyTest;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
-import android.util.Log;
import android.view.Choreographer;
import android.view.Choreographer.FrameCallback;
import android.view.SurfaceControl;
@@ -46,7 +45,6 @@
import android.view.animation.TranslateAnimation;
import com.android.server.wm.LocalAnimationAdapter.AnimationSpec;
-import com.android.server.wm.SurfaceAnimationRunner.AnimatorFactory;
import org.junit.Before;
import org.junit.Rule;
@@ -71,6 +69,7 @@
@Mock SurfaceControl mMockSurface;
@Mock Transaction mMockTransaction;
@Mock AnimationSpec mMockAnimationSpec;
+ @Mock PowerManagerInternal mMockPowerManager;
@Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
private SurfaceAnimationRunner mSurfaceAnimationRunner;
@@ -81,7 +80,7 @@
super.setUp();
mFinishCallbackLatch = new CountDownLatch(1);
mSurfaceAnimationRunner = new SurfaceAnimationRunner(null /* callbackProvider */, null,
- mMockTransaction);
+ mMockTransaction, mMockPowerManager);
}
private void finishedCallback() {
@@ -113,7 +112,7 @@
@Test
public void testCancel_notStarted() throws Exception {
mSurfaceAnimationRunner = new SurfaceAnimationRunner(new NoOpFrameCallbackProvider(), null,
- mMockTransaction);
+ mMockTransaction, mMockPowerManager);
mSurfaceAnimationRunner
.startAnimation(createTranslateAnimation(), mMockSurface, mMockTransaction,
this::finishedCallback);
@@ -126,7 +125,7 @@
@Test
public void testCancel_running() throws Exception {
mSurfaceAnimationRunner = new SurfaceAnimationRunner(new NoOpFrameCallbackProvider(), null,
- mMockTransaction);
+ mMockTransaction, mMockPowerManager);
mSurfaceAnimationRunner.startAnimation(createTranslateAnimation(), mMockSurface,
mMockTransaction, this::finishedCallback);
waitUntilNextFrame();
@@ -156,7 +155,7 @@
listener.onAnimationUpdate(animation);
});
}
- }, mMockTransaction);
+ }, mMockTransaction, mMockPowerManager);
when(mMockAnimationSpec.getDuration()).thenReturn(200L);
mSurfaceAnimationRunner.startAnimation(mMockAnimationSpec, mMockSurface, mMockTransaction,
this::finishedCallback);
@@ -184,6 +183,19 @@
assertFinishCallbackCalled();
}
+ @Test
+ public void testPowerHint() throws Exception {
+ mSurfaceAnimationRunner = new SurfaceAnimationRunner(new NoOpFrameCallbackProvider(), null,
+ mMockTransaction, mMockPowerManager);
+ mSurfaceAnimationRunner.startAnimation(createTranslateAnimation(), mMockSurface,
+ mMockTransaction, this::finishedCallback);
+ waitUntilNextFrame();
+
+ // TODO: For some reason we don't have access to PowerHint definition from the tests. For
+ // now let's just verify that we got some kind of hint.
+ verify(mMockPowerManager).powerHint(anyInt(), anyInt());
+ }
+
private void waitUntilNextFrame() throws Exception {
final CountDownLatch latch = new CountDownLatch(1);
mSurfaceAnimationRunner.mChoreographer.postCallback(Choreographer.CALLBACK_COMMIT,
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
index b19373e..21402ce 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
@@ -59,7 +59,7 @@
private void setupSurface(int width, int height, Rect contentInsets, int sysuiVis,
int windowFlags, Rect taskBounds) {
final GraphicBuffer buffer = GraphicBuffer.create(width, height, PixelFormat.RGBA_8888,
- GraphicBuffer.USAGE_SW_READ_NEVER | GraphicBuffer.USAGE_SW_WRITE_NEVER);
+ GraphicBuffer.USAGE_SW_READ_RARELY | GraphicBuffer.USAGE_SW_WRITE_NEVER);
final TaskSnapshot snapshot = new TaskSnapshot(buffer,
ORIENTATION_PORTRAIT, contentInsets, false, 1.0f, true /* isRealSnapshot */,
WINDOWING_MODE_FULLSCREEN, 0 /* systemUiVisibility */, false /* isTranslucent */);
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowConfigurationTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowConfigurationTests.java
index 513c1ec..10d7aad 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowConfigurationTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowConfigurationTests.java
@@ -31,6 +31,7 @@
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOW_CONFIG_ALWAYS_ON_TOP;
import static android.app.WindowConfiguration.WINDOW_CONFIG_APP_BOUNDS;
import static android.app.WindowConfiguration.WINDOW_CONFIG_WINDOWING_MODE;
import static android.content.pm.ActivityInfo.CONFIG_WINDOW_CONFIGURATION;
@@ -81,6 +82,11 @@
assertEquals(WINDOW_CONFIG_APP_BOUNDS | WINDOW_CONFIG_WINDOWING_MODE,
winConfig1.diff(winConfig2, false /* compareUndefined */));
+ winConfig2.setAlwaysOnTop(true);
+ assertEquals(WINDOW_CONFIG_APP_BOUNDS | WINDOW_CONFIG_WINDOWING_MODE
+ | WINDOW_CONFIG_ALWAYS_ON_TOP,
+ winConfig1.diff(winConfig2, false /* compareUndefined */));
+
assertEquals(0, config1.diff(config3));
assertEquals(0, config1.diffPublicOnly(config3));
assertEquals(0, winConfig1.diff(winConfig3, false /* compareUndefined */));
@@ -108,6 +114,12 @@
assertNotEquals(winConfig1.compareTo(winConfig2), 0);
winConfig2.setWindowingMode(winConfig1.getWindowingMode());
+ // Different always on top state
+ winConfig2.setAlwaysOnTop(true);
+ assertNotEquals(config1.compareTo(config2), 0);
+ assertNotEquals(winConfig1.compareTo(winConfig2), 0);
+ winConfig2.setAlwaysOnTop(winConfig1.isAlwaysOnTop());
+
// Different bounds
winConfig2.setAppBounds(0, 2, 3, 4);
assertNotEquals(config1.compareTo(config2), 0);
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java
index 6c7830e..f8b2828 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java
@@ -256,6 +256,33 @@
}
@Test
+ public void testAddChildByIndex() throws Exception {
+ final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
+ final TestWindowContainer root = builder.setLayer(0).build();
+
+ final TestWindowContainer child = root.addChildWindow();
+
+ final TestWindowContainer child2 = builder.setLayer(1).build();
+ final TestWindowContainer child3 = builder.setLayer(2).build();
+ final TestWindowContainer child4 = builder.setLayer(3).build();
+
+ // Test adding at top.
+ root.addChild(child2, POSITION_TOP);
+ assertEquals(child2, root.getChildAt(root.getChildrenCount() - 1));
+
+ // Test adding at bottom.
+ root.addChild(child3, POSITION_BOTTOM);
+ assertEquals(child3, root.getChildAt(0));
+
+ // Test adding in the middle.
+ root.addChild(child4, 1);
+ assertEquals(child3, root.getChildAt(0));
+ assertEquals(child4, root.getChildAt(1));
+ assertEquals(child, root.getChildAt(2));
+ assertEquals(child2, root.getChildAt(3));
+ }
+
+ @Test
public void testPositionChildAt() throws Exception {
final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
final TestWindowContainer root = builder.setLayer(0).build();
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
index 5a56332..4864332 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
@@ -51,6 +51,7 @@
private WindowToken mWindowToken;
private final IWindow mIWindow = new TestIWindow();
+ private final Rect mEmptyRect = new Rect();
class WindowStateWithTask extends WindowState {
final Task mTask;
@@ -160,8 +161,9 @@
// When mFrame extends past cf, the content insets are
// the difference between mFrame and ContentFrame. Visible
// and stable frames work the same way.
- w.computeFrameLw(pf, df, of, cf, vf, dcf, sf, null, WmDisplayCutout.NO_CUTOUT, false);
- assertRect(w.mFrame,0, 0, 1000, 1000);
+ final WindowFrames windowFrames = new WindowFrames(pf, df, of, cf, vf, dcf, sf, mEmptyRect);
+ w.computeFrameLw(windowFrames);
+ assertRect(w.getFrameLw(),0, 0, 1000, 1000);
assertRect(w.mContentInsets, 0, topContentInset, 0, bottomContentInset);
assertRect(w.mVisibleInsets, 0, topVisibleInset, 0, bottomVisibleInset);
assertRect(w.mStableInsets, leftStableInset, 0, rightStableInset, 0);
@@ -169,19 +171,19 @@
assertTrue(cf.equals(w.getContentFrameLw()));
assertTrue(vf.equals(w.getVisibleFrameLw()));
assertTrue(sf.equals(w.getStableFrameLw()));
- // On the other hand mFrame doesn't extend past cf we won't get any insets
+ // On the other hand getFrame() doesn't extend past cf we won't get any insets
w.mAttrs.x = 100;
w.mAttrs.y = 100;
w.mAttrs.width = 100; w.mAttrs.height = 100; //have to clear MATCH_PARENT
w.mRequestedWidth = 100;
w.mRequestedHeight = 100;
- w.computeFrameLw(pf, df, of, cf, vf, dcf, sf, null, WmDisplayCutout.NO_CUTOUT, false);
- assertRect(w.mFrame, 100, 100, 200, 200);
+ w.computeFrameLw(windowFrames);
+ assertRect(w.getFrameLw(), 100, 100, 200, 200);
assertRect(w.mContentInsets, 0, 0, 0, 0);
// In this case the frames are shrunk to the window frame.
- assertTrue(w.mFrame.equals(w.getContentFrameLw()));
- assertTrue(w.mFrame.equals(w.getVisibleFrameLw()));
- assertTrue(w.mFrame.equals(w.getStableFrameLw()));
+ assertTrue(w.getFrameLw().equals(w.getContentFrameLw()));
+ assertTrue(w.getFrameLw().equals(w.getVisibleFrameLw()));
+ assertTrue(w.getFrameLw().equals(w.getStableFrameLw()));
}
@Test
@@ -196,25 +198,26 @@
// Here the window has FILL_PARENT, FILL_PARENT
// so we expect it to fill the entire available frame.
- w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, WmDisplayCutout.NO_CUTOUT, false);
- assertRect(w.mFrame, 0, 0, 1000, 1000);
+ final WindowFrames windowFrames = new WindowFrames(pf, pf, pf, pf, pf, pf, pf, pf);
+ w.computeFrameLw(windowFrames);
+ assertRect(w.getFrameLw(), 0, 0, 1000, 1000);
// It can select various widths and heights within the bounds.
// Strangely the window attribute width is ignored for normal windows
// and we use mRequestedWidth/mRequestedHeight
w.mAttrs.width = 300;
w.mAttrs.height = 300;
- w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, WmDisplayCutout.NO_CUTOUT, false);
+ w.computeFrameLw(windowFrames);
// Explicit width and height without requested width/height
// gets us nothing.
- assertRect(w.mFrame, 0, 0, 0, 0);
+ assertRect(w.getFrameLw(), 0, 0, 0, 0);
w.mRequestedWidth = 300;
w.mRequestedHeight = 300;
- w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, WmDisplayCutout.NO_CUTOUT, false);
+ w.computeFrameLw(windowFrames);
// With requestedWidth/Height we can freely choose our size within the
// parent bounds.
- assertRect(w.mFrame, 0, 0, 300, 300);
+ assertRect(w.getFrameLw(), 0, 0, 300, 300);
// With FLAG_SCALED though, requestedWidth/height is used to control
// the unscaled surface size, and mAttrs.width/height becomes the
@@ -224,23 +227,23 @@
w.mRequestedWidth = -1;
w.mAttrs.width = 100;
w.mAttrs.height = 100;
- w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, WmDisplayCutout.NO_CUTOUT, false);
- assertRect(w.mFrame, 0, 0, 100, 100);
+ w.computeFrameLw(windowFrames);
+ assertRect(w.getFrameLw(), 0, 0, 100, 100);
w.mAttrs.flags = 0;
// But sizes too large will be clipped to the containing frame
w.mRequestedWidth = 1200;
w.mRequestedHeight = 1200;
- w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, WmDisplayCutout.NO_CUTOUT, false);
- assertRect(w.mFrame, 0, 0, 1000, 1000);
+ w.computeFrameLw(windowFrames);
+ assertRect(w.getFrameLw(), 0, 0, 1000, 1000);
// Before they are clipped though windows will be shifted
w.mAttrs.x = 300;
w.mAttrs.y = 300;
w.mRequestedWidth = 1000;
w.mRequestedHeight = 1000;
- w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, WmDisplayCutout.NO_CUTOUT, false);
- assertRect(w.mFrame, 0, 0, 1000, 1000);
+ w.computeFrameLw(windowFrames);
+ assertRect(w.getFrameLw(), 0, 0, 1000, 1000);
// If there is room to move around in the parent frame the window will be shifted according
// to gravity.
@@ -249,17 +252,17 @@
w.mRequestedWidth = 300;
w.mRequestedHeight = 300;
w.mAttrs.gravity = Gravity.RIGHT | Gravity.TOP;
- w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, WmDisplayCutout.NO_CUTOUT, false);
- assertRect(w.mFrame, 700, 0, 1000, 300);
+ w.computeFrameLw(windowFrames);
+ assertRect(w.getFrameLw(), 700, 0, 1000, 300);
w.mAttrs.gravity = Gravity.RIGHT | Gravity.BOTTOM;
- w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, WmDisplayCutout.NO_CUTOUT, false);
- assertRect(w.mFrame, 700, 700, 1000, 1000);
+ w.computeFrameLw(windowFrames);
+ assertRect(w.getFrameLw(), 700, 700, 1000, 1000);
// Window specified x and y are interpreted as offsets in the opposite
// direction of gravity
w.mAttrs.x = 100;
w.mAttrs.y = 100;
- w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, WmDisplayCutout.NO_CUTOUT, false);
- assertRect(w.mFrame, 600, 600, 900, 900);
+ w.computeFrameLw(windowFrames);
+ assertRect(w.getFrameLw(), 600, 600, 900, 900);
}
@Test
@@ -279,10 +282,11 @@
w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP;
final Rect pf = new Rect(0, 0, logicalWidth, logicalHeight);
- w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, null, WmDisplayCutout.NO_CUTOUT, false);
+ final WindowFrames windowFrames = new WindowFrames(pf, pf, pf, pf, pf, pf, pf, mEmptyRect);
+ w.computeFrameLw(windowFrames);
// For non fullscreen tasks the containing frame is based off the
// task bounds not the parent frame.
- assertRect(w.mFrame, taskLeft, taskTop, taskRight, taskBottom);
+ assertRect(w.getFrameLw(), taskLeft, taskTop, taskRight, taskBottom);
assertRect(w.getContentFrameLw(), taskLeft, taskTop, taskRight, taskBottom);
assertRect(w.mContentInsets, 0, 0, 0, 0);
@@ -291,8 +295,10 @@
final int cfRight = logicalWidth / 2;
final int cfBottom = logicalHeight / 2;
final Rect cf = new Rect(0, 0, cfRight, cfBottom);
- w.computeFrameLw(pf, pf, pf, cf, cf, pf, cf, null, WmDisplayCutout.NO_CUTOUT, false);
- assertRect(w.mFrame, taskLeft, taskTop, taskRight, taskBottom);
+
+ windowFrames.setFrames(pf, pf, pf, cf, cf, pf, cf, mEmptyRect);
+ w.computeFrameLw(windowFrames);
+ assertRect(w.getFrameLw(), taskLeft, taskTop, taskRight, taskBottom);
int contentInsetRight = taskRight - cfRight;
int contentInsetBottom = taskBottom - cfBottom;
assertRect(w.mContentInsets, 0, 0, contentInsetRight, contentInsetBottom);
@@ -308,8 +314,10 @@
final int insetRight = insetLeft + (taskRight - taskLeft);
final int insetBottom = insetTop + (taskBottom - taskTop);
task.mInsetBounds.set(insetLeft, insetTop, insetRight, insetBottom);
- w.computeFrameLw(pf, pf, pf, cf, cf, pf, cf, null, WmDisplayCutout.NO_CUTOUT, false);
- assertRect(w.mFrame, taskLeft, taskTop, taskRight, taskBottom);
+
+ windowFrames.setFrames(pf, pf, pf, cf, cf, pf, cf, mEmptyRect);
+ w.computeFrameLw(windowFrames);
+ assertRect(w.getFrameLw(), taskLeft, taskTop, taskRight, taskBottom);
contentInsetRight = insetRight - cfRight;
contentInsetBottom = insetBottom - cfBottom;
assertRect(w.mContentInsets, 0, 0, contentInsetRight, contentInsetBottom);
@@ -340,13 +348,14 @@
final Rect policyCrop = new Rect();
- w.computeFrameLw(pf, df, of, cf, vf, dcf, sf, null, WmDisplayCutout.NO_CUTOUT, false);
+ final WindowFrames windowFrames = new WindowFrames(pf, df, of, cf, vf, dcf, sf, mEmptyRect);
+ w.computeFrameLw(windowFrames);
w.calculatePolicyCrop(policyCrop);
assertRect(policyCrop, 0, cf.top, logicalWidth, cf.bottom);
- dcf.setEmpty();
+ windowFrames.mDecorFrame.setEmpty();
// Likewise with no decor frame we would get no crop
- w.computeFrameLw(pf, df, of, cf, vf, dcf, sf, null, WmDisplayCutout.NO_CUTOUT, false);
+ w.computeFrameLw(windowFrames);
w.calculatePolicyCrop(policyCrop);
assertRect(policyCrop, 0, 0, logicalWidth, logicalHeight);
@@ -354,12 +363,14 @@
// Normally it would be cropped to it's frame but in the case of docked resizing
// we need to account for the fact the windows surface will be made
// fullscreen and thus also make the crop fullscreen.
+
+ windowFrames.setFrames(pf, pf, pf, pf, pf, pf, pf, pf);
w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP;
w.mAttrs.width = logicalWidth / 2;
w.mAttrs.height = logicalHeight / 2;
w.mRequestedWidth = logicalWidth / 2;
w.mRequestedHeight = logicalHeight / 2;
- w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, WmDisplayCutout.NO_CUTOUT, false);
+ w.computeFrameLw(windowFrames);
w.calculatePolicyCrop(policyCrop);
// Normally the crop is shrunk from the decor frame
@@ -394,12 +405,11 @@
w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP;
final Rect pf = new Rect(0, 0, logicalWidth, logicalHeight);
- w.computeFrameLw(pf /* parentFrame */, pf /* displayFrame */, pf /* overscanFrame */,
- pf /* contentFrame */, pf /* visibleFrame */, pf /* decorFrame */,
- pf /* stableFrame */, null /* outsetFrame */, WmDisplayCutout.NO_CUTOUT, false);
+ final WindowFrames windowFrames = new WindowFrames(pf, pf, pf, pf, pf, pf, pf, mEmptyRect);
+ w.computeFrameLw(windowFrames);
// For non fullscreen tasks the containing frame is based off the
// task bounds not the parent frame.
- assertRect(w.mFrame, taskLeft, taskTop, taskRight, taskBottom);
+ assertRect(w.getFrameLw(), taskLeft, taskTop, taskRight, taskBottom);
assertRect(w.getContentFrameLw(), taskLeft, taskTop, taskRight, taskBottom);
assertRect(w.mContentInsets, 0, 0, 0, 0);
@@ -413,10 +423,9 @@
pf.set(0, 0, logicalWidth, logicalHeight);
task.mFullscreenForTest = true;
- w.computeFrameLw(pf /* parentFrame */, pf /* displayFrame */, pf /* overscanFrame */,
- cf /* contentFrame */, cf /* visibleFrame */, pf /* decorFrame */,
- cf /* stableFrame */, null /* outsetFrame */, WmDisplayCutout.NO_CUTOUT, false);
- assertEquals(cf, w.mFrame);
+ windowFrames.setFrames(pf, pf, pf, cf, cf, pf, cf, mEmptyRect);
+ w.computeFrameLw(windowFrames);
+ assertEquals(cf, w.getFrameLw());
assertEquals(cf, w.getContentFrameLw());
assertRect(w.mContentInsets, 0, 0, 0, 0);
}
@@ -433,12 +442,14 @@
final WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
fromBoundingRect(500, 0, 550, 50), pf.width(), pf.height());
- w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, cutout, false);
+ final WindowFrames windowFrames = new WindowFrames(pf, pf, pf, pf, pf, pf, pf, pf);
+ windowFrames.setDisplayCutout(cutout);
+ w.computeFrameLw(windowFrames);
- assertEquals(w.mDisplayCutout.getDisplayCutout().getSafeInsetTop(), 50);
- assertEquals(w.mDisplayCutout.getDisplayCutout().getSafeInsetBottom(), 0);
- assertEquals(w.mDisplayCutout.getDisplayCutout().getSafeInsetLeft(), 0);
- assertEquals(w.mDisplayCutout.getDisplayCutout().getSafeInsetRight(), 0);
+ assertEquals(w.getWmDisplayCutout().getDisplayCutout().getSafeInsetTop(), 50);
+ assertEquals(w.getWmDisplayCutout().getDisplayCutout().getSafeInsetBottom(), 0);
+ assertEquals(w.getWmDisplayCutout().getDisplayCutout().getSafeInsetLeft(), 0);
+ assertEquals(w.getWmDisplayCutout().getDisplayCutout().getSafeInsetRight(), 0);
}
@Test
@@ -455,12 +466,14 @@
final WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
fromBoundingRect(500, 0, 550, 50), pf.width(), pf.height());
- w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, cutout, false);
+ final WindowFrames windowFrames = new WindowFrames(pf, pf, pf, pf, pf, pf, pf, pf);
+ windowFrames.setDisplayCutout(cutout);
+ w.computeFrameLw(windowFrames);
- assertEquals(w.mDisplayCutout.getDisplayCutout().getSafeInsetTop(), 50);
- assertEquals(w.mDisplayCutout.getDisplayCutout().getSafeInsetBottom(), 0);
- assertEquals(w.mDisplayCutout.getDisplayCutout().getSafeInsetLeft(), 0);
- assertEquals(w.mDisplayCutout.getDisplayCutout().getSafeInsetRight(), 0);
+ assertEquals(w.getWmDisplayCutout().getDisplayCutout().getSafeInsetTop(), 50);
+ assertEquals(w.getWmDisplayCutout().getDisplayCutout().getSafeInsetBottom(), 0);
+ assertEquals(w.getWmDisplayCutout().getDisplayCutout().getSafeInsetLeft(), 0);
+ assertEquals(w.getWmDisplayCutout().getDisplayCutout().getSafeInsetRight(), 0);
}
private WindowStateWithTask createWindow(Task task, int width, int height) {
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowManagerServiceRule.java b/services/tests/servicestests/src/com/android/server/wm/WindowManagerServiceRule.java
index 69cfaad..d13c3c9 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowManagerServiceRule.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowManagerServiceRule.java
@@ -29,7 +29,6 @@
import static org.mockito.Mockito.mock;
import android.app.ActivityManagerInternal;
-import android.app.ActivityTaskManagerInternal;
import android.content.Context;
import android.hardware.display.DisplayManagerInternal;
import android.os.PowerManagerInternal;
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
index 9f113ad..0ddba6a 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
@@ -364,15 +364,15 @@
app.mSurfaceControl = mock(SurfaceControl.class);
app.mWinAnimator.mSurfaceController = mock(WindowSurfaceController.class);
try {
- app.mFrame.set(10, 20, 60, 80);
+ app.getFrameLw().set(10, 20, 60, 80);
app.seamlesslyRotate(t, ROTATION_0, ROTATION_90);
assertTrue(app.mSeamlesslyRotated);
assertEquals(new Rect(20, mDisplayInfo.logicalWidth - 60,
- 80, mDisplayInfo.logicalWidth - 10), app.mFrame);
+ 80, mDisplayInfo.logicalWidth - 10), app.getFrameLw());
- verify(t).setPosition(app.mSurfaceControl, app.mFrame.left, app.mFrame.top);
+ verify(t).setPosition(app.mSurfaceControl, app.getFrameLw().left, app.getFrameLw().top);
verify(app.mWinAnimator.mSurfaceController).setPosition(t, 0, 50, false);
verify(app.mWinAnimator.mSurfaceController).setMatrix(t, 0, -1, 1, 0, false);
} finally {
diff --git a/services/tests/servicestests/src/com/android/server/wm/utils/CoordinateTransformsTest.java b/services/tests/servicestests/src/com/android/server/wm/utils/CoordinateTransformsTest.java
index 361522c..f82b012 100644
--- a/services/tests/servicestests/src/com/android/server/wm/utils/CoordinateTransformsTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/utils/CoordinateTransformsTest.java
@@ -99,7 +99,7 @@
checkPoint(0, W).transformsTo(0, 0);
checkPoint(H, 0).transformsTo(W, H);
-}
+ }
@Test
public void transformLogicalToPhysicalCoordinates_rot180() {
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
index 7809999..dbba2b2 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
@@ -19,7 +19,9 @@
import static android.app.Notification.GROUP_ALERT_CHILDREN;
import static android.app.Notification.GROUP_ALERT_SUMMARY;
import static android.app.NotificationManager.IMPORTANCE_HIGH;
+import static android.app.NotificationManager.IMPORTANCE_LOW;
import static android.app.NotificationManager.IMPORTANCE_MIN;
+import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNull;
@@ -149,6 +151,9 @@
mService.setFallbackVibrationPattern(FALLBACK_VIBRATION_PATTERN);
mService.setUsageStats(mUsageStats);
mService.setAccessibilityManager(accessibilityManager);
+ mService.mScreenOn = false;
+ mService.mInCall = false;
+ mService.mNotificationPulseEnabled = true;
}
//
@@ -190,6 +195,11 @@
true /* noisy */, false /* buzzy*/, false /* lights */);
}
+ private NotificationRecord getInsistentBeepyOnceNotification() {
+ return getNotificationRecord(mId, true /* insistent */, true /* once */,
+ true /* noisy */, false /* buzzy*/, false /* lights */);
+ }
+
private NotificationRecord getInsistentBeepyLeanbackNotification() {
return getLeanbackNotificationRecord(mId, true /* insistent */, false /* once */,
true /* noisy */, false /* buzzy*/, false /* lights */);
@@ -216,8 +226,13 @@
}
private NotificationRecord getLightsNotification() {
+ return getNotificationRecord(mId, false /* insistent */, false /* once */,
+ false /* noisy */, false /* buzzy*/, true /* lights */);
+ }
+
+ private NotificationRecord getLightsOnceNotification() {
return getNotificationRecord(mId, false /* insistent */, true /* once */,
- false /* noisy */, true /* buzzy*/, true /* lights */);
+ false /* noisy */, false /* buzzy*/, true /* lights */);
}
private NotificationRecord getCustomLightsNotification() {
@@ -244,6 +259,12 @@
groupKey, groupAlertBehavior, false);
}
+ private NotificationRecord getLightsNotificationRecord(String groupKey,
+ int groupAlertBehavior) {
+ return getNotificationRecord(mId, false, false, false, false, true /*lights*/, true, true,
+ true, groupKey, groupAlertBehavior, false);
+ }
+
private NotificationRecord getNotificationRecord(int id, boolean insistent, boolean once,
boolean noisy, boolean buzzy, boolean lights, boolean defaultVibration,
boolean defaultSound, boolean defaultLights, String groupKey, int groupAlertBehavior,
@@ -369,6 +390,10 @@
verify(mVibrator, never()).cancel();
}
+ private void verifyNeverLights() {
+ verify(mLight, never()).setFlashing(anyInt(), anyInt(), anyInt(), anyInt());
+ }
+
private void verifyLights() {
verify(mLight, times(1)).setFlashing(anyInt(), anyInt(), anyInt(), anyInt());
}
@@ -507,6 +532,24 @@
assertFalse(s.isInterruptive());
}
+ /**
+ * Tests the case where the user re-posts a {@link Notification} with looping sound where
+ * {@link Notification.Builder#setOnlyAlertOnce(true)} has been called. This should silence
+ * the sound associated with the notification.
+ * @throws Exception
+ */
+ @Test
+ public void testNoisyOnceUpdateDoesCancelAudio() throws Exception {
+ NotificationRecord r = getInsistentBeepyNotification();
+ NotificationRecord s = getInsistentBeepyOnceNotification();
+ s.isUpdate = true;
+
+ mService.buzzBeepBlinkLocked(r);
+ mService.buzzBeepBlinkLocked(s);
+
+ verifyStopAudio();
+ }
+
@Test
public void testQuietUpdateDoesNotCancelAudioFromOther() throws Exception {
NotificationRecord r = getBeepyNotification();
@@ -712,7 +755,8 @@
mService.buzzBeepBlinkLocked(summary);
verifyBeepLooped();
- assertTrue(summary.isInterruptive());
+ // summaries are never interruptive for notification counts
+ assertFalse(summary.isInterruptive());
}
@Test
@@ -990,6 +1034,156 @@
verify(mAccessibilityService, times(1)).sendAccessibilityEvent(any(), anyInt());
}
+ @Test
+ public void testLightsScreenOn() {
+ mService.mScreenOn = true;
+ NotificationRecord r = getLightsNotification();
+ mService.buzzBeepBlinkLocked(r);
+ verifyNeverLights();
+ assertFalse(r.isInterruptive());
+ }
+
+ @Test
+ public void testLightsInCall() {
+ mService.mInCall = true;
+ NotificationRecord r = getLightsNotification();
+ mService.buzzBeepBlinkLocked(r);
+ verifyNeverLights();
+ assertFalse(r.isInterruptive());
+ }
+
+ @Test
+ public void testLightsSilentUpdate() {
+ NotificationRecord r = getLightsOnceNotification();
+ mService.buzzBeepBlinkLocked(r);
+ verifyLights();
+ assertTrue(r.isInterruptive());
+
+ r = getLightsOnceNotification();
+ r.isUpdate = true;
+ mService.buzzBeepBlinkLocked(r);
+ // checks that lights happened once, i.e. this new call didn't trigger them again
+ verifyLights();
+ assertFalse(r.isInterruptive());
+ }
+
+ @Test
+ public void testLightsUnimportant() {
+ NotificationRecord r = getLightsNotification();
+ r.setImportance(IMPORTANCE_LOW, "testing");
+ mService.buzzBeepBlinkLocked(r);
+ verifyNeverLights();
+ assertFalse(r.isInterruptive());
+ }
+
+ @Test
+ public void testLightsNoLights() {
+ NotificationRecord r = getQuietNotification();
+ mService.buzzBeepBlinkLocked(r);
+ verifyNeverLights();
+ assertFalse(r.isInterruptive());
+ }
+
+ @Test
+ public void testLightsNoLightOnDevice() {
+ mService.mHasLight = false;
+ NotificationRecord r = getLightsNotification();
+ mService.buzzBeepBlinkLocked(r);
+ verifyNeverLights();
+ assertFalse(r.isInterruptive());
+ }
+
+ @Test
+ public void testLightsLightsOffGlobally() {
+ mService.mNotificationPulseEnabled = false;
+ NotificationRecord r = getLightsNotification();
+ mService.buzzBeepBlinkLocked(r);
+ verifyNeverLights();
+ assertFalse(r.isInterruptive());
+ }
+
+ @Test
+ public void testLightsDndIntercepted() {
+ NotificationRecord r = getLightsNotification();
+ r.setSuppressedVisualEffects(SUPPRESSED_EFFECT_LIGHTS);
+ mService.buzzBeepBlinkLocked(r);
+ verifyNeverLights();
+ assertFalse(r.isInterruptive());
+ }
+
+ @Test
+ public void testGroupAlertSummaryNoLightsChild() {
+ NotificationRecord child = getLightsNotificationRecord("a", GROUP_ALERT_SUMMARY);
+
+ mService.buzzBeepBlinkLocked(child);
+
+ verifyNeverLights();
+ assertFalse(child.isInterruptive());
+ }
+
+ @Test
+ public void testGroupAlertSummaryLightsSummary() {
+ NotificationRecord summary = getLightsNotificationRecord("a", GROUP_ALERT_SUMMARY);
+ summary.getNotification().flags |= Notification.FLAG_GROUP_SUMMARY;
+
+ mService.buzzBeepBlinkLocked(summary);
+
+ verifyLights();
+ // summaries should never count for interruptiveness counts
+ assertFalse(summary.isInterruptive());
+ }
+
+ @Test
+ public void testGroupAlertSummaryLightsNonGroupChild() {
+ NotificationRecord nonGroup = getLightsNotificationRecord(null, GROUP_ALERT_SUMMARY);
+
+ mService.buzzBeepBlinkLocked(nonGroup);
+
+ verifyLights();
+ assertTrue(nonGroup.isInterruptive());
+ }
+
+ @Test
+ public void testGroupAlertChildNoLightsSummary() {
+ NotificationRecord summary = getLightsNotificationRecord("a", GROUP_ALERT_CHILDREN);
+ summary.getNotification().flags |= Notification.FLAG_GROUP_SUMMARY;
+
+ mService.buzzBeepBlinkLocked(summary);
+
+ verifyNeverLights();
+ assertFalse(summary.isInterruptive());
+ }
+
+ @Test
+ public void testGroupAlertChildLightsChild() {
+ NotificationRecord child = getLightsNotificationRecord("a", GROUP_ALERT_CHILDREN);
+
+ mService.buzzBeepBlinkLocked(child);
+
+ verifyLights();
+ assertTrue(child.isInterruptive());
+ }
+
+ @Test
+ public void testGroupAlertChildLightsNonGroupSummary() {
+ NotificationRecord nonGroup = getLightsNotificationRecord(null, GROUP_ALERT_CHILDREN);
+
+ mService.buzzBeepBlinkLocked(nonGroup);
+
+ verifyLights();
+ assertTrue(nonGroup.isInterruptive());
+ }
+
+ @Test
+ public void testGroupAlertAllLightsGroup() {
+ NotificationRecord group = getLightsNotificationRecord("a", GROUP_ALERT_ALL);
+
+ mService.buzzBeepBlinkLocked(group);
+
+ verifyLights();
+ assertTrue(group.isInterruptive());
+ }
+
static class VibrateRepeatMatcher implements ArgumentMatcher<VibrationEffect> {
private final int mRepeatIndex;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java
index 8d4c5b1..84ef0c9 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java
@@ -50,13 +50,14 @@
public class GroupHelperTest extends UiServiceTestCase {
private @Mock GroupHelper.Callback mCallback;
+ private final static int AUTOGROUP_AT_COUNT = 4;
private GroupHelper mGroupHelper;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- mGroupHelper = new GroupHelper(mCallback);
+ mGroupHelper = new GroupHelper(AUTOGROUP_AT_COUNT, mCallback);
}
private StatusBarNotification getSbn(String pkg, int id, String tag,
@@ -79,7 +80,7 @@
@Test
public void testNoGroup_postingUnderLimit() throws Exception {
final String pkg = "package";
- for (int i = 0; i < GroupHelper.AUTOGROUP_AT_COUNT - 1; i++) {
+ for (int i = 0; i < AUTOGROUP_AT_COUNT - 1; i++) {
mGroupHelper.onNotificationPosted(getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM),
false);
}
@@ -94,12 +95,12 @@
public void testNoGroup_multiPackage() throws Exception {
final String pkg = "package";
final String pkg2 = "package2";
- for (int i = 0; i < GroupHelper.AUTOGROUP_AT_COUNT - 1; i++) {
+ for (int i = 0; i < AUTOGROUP_AT_COUNT - 1; i++) {
mGroupHelper.onNotificationPosted(getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM),
false);
}
mGroupHelper.onNotificationPosted(
- getSbn(pkg2, GroupHelper.AUTOGROUP_AT_COUNT, "four", UserHandle.SYSTEM), false);
+ getSbn(pkg2, AUTOGROUP_AT_COUNT, "four", UserHandle.SYSTEM), false);
verify(mCallback, never()).addAutoGroupSummary(
eq(UserHandle.USER_SYSTEM), eq(pkg), anyString());
verify(mCallback, never()).addAutoGroup(anyString());
@@ -110,13 +111,12 @@
@Test
public void testNoGroup_multiUser() throws Exception {
final String pkg = "package";
- for (int i = 0; i < GroupHelper.AUTOGROUP_AT_COUNT - 1; i++) {
+ for (int i = 0; i < AUTOGROUP_AT_COUNT - 1; i++) {
mGroupHelper.onNotificationPosted(getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM),
false);
}
mGroupHelper.onNotificationPosted(
- getSbn(pkg, GroupHelper.AUTOGROUP_AT_COUNT, "four", UserHandle.ALL),
- false);
+ getSbn(pkg, AUTOGROUP_AT_COUNT, "four", UserHandle.ALL), false);
verify(mCallback, never()).addAutoGroupSummary(anyInt(), eq(pkg), anyString());
verify(mCallback, never()).addAutoGroup(anyString());
verify(mCallback, never()).removeAutoGroup(anyString());
@@ -126,13 +126,12 @@
@Test
public void testNoGroup_someAreGrouped() throws Exception {
final String pkg = "package";
- for (int i = 0; i < GroupHelper.AUTOGROUP_AT_COUNT - 1; i++) {
+ for (int i = 0; i < AUTOGROUP_AT_COUNT - 1; i++) {
mGroupHelper.onNotificationPosted(
getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM), false);
}
mGroupHelper.onNotificationPosted(
- getSbn(pkg, GroupHelper.AUTOGROUP_AT_COUNT, "four", UserHandle.SYSTEM, "a"),
- false);
+ getSbn(pkg, AUTOGROUP_AT_COUNT, "four", UserHandle.SYSTEM, "a"), false);
verify(mCallback, never()).addAutoGroupSummary(
eq(UserHandle.USER_SYSTEM), eq(pkg), anyString());
verify(mCallback, never()).addAutoGroup(anyString());
@@ -144,12 +143,12 @@
@Test
public void testPostingOverLimit() throws Exception {
final String pkg = "package";
- for (int i = 0; i < GroupHelper.AUTOGROUP_AT_COUNT; i++) {
+ for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
mGroupHelper.onNotificationPosted(
getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM), false);
}
verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString());
- verify(mCallback, times(GroupHelper.AUTOGROUP_AT_COUNT)).addAutoGroup(anyString());
+ verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString());
verify(mCallback, never()).removeAutoGroup(anyString());
verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString());
}
@@ -158,18 +157,18 @@
public void testDropToZeroRemoveGroup() throws Exception {
final String pkg = "package";
List<StatusBarNotification> posted = new ArrayList<>();
- for (int i = 0; i < GroupHelper.AUTOGROUP_AT_COUNT; i++) {
+ for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
final StatusBarNotification sbn = getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM);
posted.add(sbn);
mGroupHelper.onNotificationPosted(sbn, false);
}
verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString());
- verify(mCallback, times(GroupHelper.AUTOGROUP_AT_COUNT)).addAutoGroup(anyString());
+ verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString());
verify(mCallback, never()).removeAutoGroup(anyString());
verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString());
Mockito.reset(mCallback);
- for (int i = 0; i < GroupHelper.AUTOGROUP_AT_COUNT - 1; i++) {
+ for (int i = 0; i < AUTOGROUP_AT_COUNT - 1; i++) {
mGroupHelper.onNotificationRemoved(posted.remove(0));
}
verify(mCallback, never()).removeAutoGroup(anyString());
@@ -185,28 +184,28 @@
public void testAppStartsGrouping() throws Exception {
final String pkg = "package";
List<StatusBarNotification> posted = new ArrayList<>();
- for (int i = 0; i < GroupHelper.AUTOGROUP_AT_COUNT; i++) {
+ for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
final StatusBarNotification sbn = getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM);
posted.add(sbn);
mGroupHelper.onNotificationPosted(sbn, false);
}
verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString());
- verify(mCallback, times(GroupHelper.AUTOGROUP_AT_COUNT)).addAutoGroup(anyString());
+ verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString());
verify(mCallback, never()).removeAutoGroup(anyString());
verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString());
Mockito.reset(mCallback);
int i = 0;
- for (i = 0; i < GroupHelper.AUTOGROUP_AT_COUNT - 2; i++) {
+ for (i = 0; i < AUTOGROUP_AT_COUNT - 2; i++) {
final StatusBarNotification sbn =
getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM, "app group");
mGroupHelper.onNotificationPosted(sbn, false);
}
- verify(mCallback, times(GroupHelper.AUTOGROUP_AT_COUNT - 2)).removeAutoGroup(anyString());
+ verify(mCallback, times(AUTOGROUP_AT_COUNT - 2)).removeAutoGroup(anyString());
verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString());
Mockito.reset(mCallback);
- for (; i < GroupHelper.AUTOGROUP_AT_COUNT; i++) {
+ for (; i < AUTOGROUP_AT_COUNT; i++) {
final StatusBarNotification sbn =
getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM, "app group");
mGroupHelper.onNotificationPosted(sbn, false);
@@ -220,13 +219,13 @@
throws Exception {
final String pkg = "package";
List<StatusBarNotification> posted = new ArrayList<>();
- for (int i = 0; i < GroupHelper.AUTOGROUP_AT_COUNT; i++) {
+ for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
final StatusBarNotification sbn = getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM);
posted.add(sbn);
mGroupHelper.onNotificationPosted(sbn, false);
}
verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString());
- verify(mCallback, times(GroupHelper.AUTOGROUP_AT_COUNT)).addAutoGroup(anyString());
+ verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString());
verify(mCallback, never()).removeAutoGroup(anyString());
verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString());
Mockito.reset(mCallback);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAdjustmentExtractorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAdjustmentExtractorTest.java
index fd674f0..f17a30d 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAdjustmentExtractorTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAdjustmentExtractorTest.java
@@ -25,6 +25,9 @@
import android.app.Notification;
import android.app.NotificationChannel;
+import android.app.PendingIntent;
+import android.content.Intent;
+import android.graphics.drawable.Icon;
import android.os.Bundle;
import android.os.UserHandle;
import android.service.notification.Adjustment;
@@ -54,6 +57,9 @@
ArrayList<String> people = new ArrayList<>();
people.add("you");
signals.putStringArrayList(Adjustment.KEY_PEOPLE, people);
+ ArrayList<Notification.Action> smartActions = new ArrayList<>();
+ smartActions.add(createAction());
+ signals.putParcelableArrayList(Adjustment.KEY_SMART_ACTIONS, smartActions);
Adjustment adjustment = new Adjustment("pkg", r.getKey(), signals, "", 0);
r.addAdjustment(adjustment);
@@ -66,6 +72,7 @@
assertTrue(r.getGroupKey().contains(GroupHelper.AUTOGROUP_KEY));
assertEquals(people, r.getPeopleOverride());
assertEquals(snoozeCriteria, r.getSnoozeCriteria());
+ assertEquals(smartActions, r.getSmartActions());
}
@Test
@@ -114,4 +121,11 @@
0, n, UserHandle.ALL, null, System.currentTimeMillis());
return new NotificationRecord(getContext(), sbn, channel);
}
+
+ private Notification.Action createAction() {
+ return new Notification.Action.Builder(
+ Icon.createWithResource(getContext(), android.R.drawable.sym_def_app_icon),
+ "action",
+ PendingIntent.getBroadcast(getContext(), 0, new Intent("Action"), 0)).build();
+ }
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelTest.java
index 2241047..ca473c6 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelTest.java
@@ -19,20 +19,26 @@
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNull;
import android.app.NotificationChannel;
+import android.net.Uri;
import android.os.Parcel;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Xml;
import com.android.internal.util.FastXmlSerializer;
import com.android.server.UiServiceTestCase;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlSerializer;
+import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
+import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
@SmallTest
@@ -68,4 +74,23 @@
serializer.setOutput(new BufferedOutputStream(baos), "utf-8");
channel.writeXml(serializer);
}
+
+ @Test
+ public void testBackupEmptySound() throws Exception {
+ NotificationChannel channel = new NotificationChannel("a", "ab", IMPORTANCE_DEFAULT);
+ channel.setSound(Uri.EMPTY, null);
+
+ XmlSerializer serializer = new FastXmlSerializer();
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ serializer.setOutput(new BufferedOutputStream(baos), "utf-8");
+ channel.writeXmlForBackup(serializer, getContext());
+
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(new BufferedInputStream(
+ new ByteArrayInputStream(baos.toByteArray())), null);
+ NotificationChannel restored = new NotificationChannel("a", "ab", IMPORTANCE_DEFAULT);
+ restored.populateFromXmlForRestore(parser, getContext());
+
+ assertNull(restored.getSound());
+ }
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
index ef9ba78..742ad65 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
@@ -31,11 +31,14 @@
import static org.mockito.Mockito.when;
import android.app.INotificationManager;
+import android.app.Notification;
import android.app.NotificationChannel;
+import android.app.PendingIntent;
import android.content.Intent;
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
+import android.os.Parcel;
import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationListenerService.Ranking;
import android.service.notification.NotificationRankingUpdate;
@@ -91,6 +94,7 @@
assertEquals(getShowBadge(i), ranking.canShowBadge());
assertEquals(getUserSentiment(i), ranking.getUserSentiment());
assertEquals(getHidden(i), ranking.isSuspended());
+ assertActionsEqual(getSmartActions(key, i), ranking.getSmartActions());
}
}
@@ -107,6 +111,7 @@
int[] importance = new int[mKeys.length];
Bundle userSentiment = new Bundle();
Bundle mHidden = new Bundle();
+ Bundle smartActions = new Bundle();
for (int i = 0; i < mKeys.length; i++) {
String key = mKeys[i];
@@ -124,11 +129,13 @@
showBadge.putBoolean(key, getShowBadge(i));
userSentiment.putInt(key, getUserSentiment(i));
mHidden.putBoolean(key, getHidden(i));
+ smartActions.putParcelableArrayList(key, getSmartActions(key, i));
}
NotificationRankingUpdate update = new NotificationRankingUpdate(mKeys,
interceptedKeys.toArray(new String[0]), visibilityOverrides,
suppressedVisualEffects, importance, explanation, overrideGroupKeys,
- channels, overridePeople, snoozeCriteria, showBadge, userSentiment, mHidden);
+ channels, overridePeople, snoozeCriteria, showBadge, userSentiment, mHidden,
+ smartActions);
return update;
}
@@ -196,6 +203,29 @@
return snooze;
}
+ private ArrayList<Notification.Action> getSmartActions(String key, int index) {
+ ArrayList<Notification.Action> actions = new ArrayList<>();
+ for (int i = 0; i < index; i++) {
+ PendingIntent intent = PendingIntent.getBroadcast(
+ getContext(),
+ index /*requestCode*/,
+ new Intent("ACTION_" + key),
+ 0 /*flags*/);
+ actions.add(new Notification.Action.Builder(null /*icon*/, key, intent).build());
+ }
+ return actions;
+ }
+
+ private void assertActionsEqual(
+ List<Notification.Action> expecteds, List<Notification.Action> actuals) {
+ assertEquals(expecteds.size(), actuals.size());
+ for (int i = 0; i < expecteds.size(); i++) {
+ Notification.Action expected = expecteds.get(i);
+ Notification.Action actual = actuals.get(i);
+ assertEquals(expected.title, actual.title);
+ }
+ }
+
public static class TestListenerService extends NotificationListenerService {
private final IBinder binder = new LocalBinder();
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 96c948f..e7a8b58 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -158,6 +158,7 @@
private TestableLooper mTestableLooper;
@Mock
private RankingHelper mRankingHelper;
+ @Mock private PreferencesHelper mPreferencesHelper;
AtomicFile mPolicyFile;
File mFile;
@Mock
@@ -600,8 +601,8 @@
@Test
public void testBlockedNotifications_blockedChannelGroup() throws Exception {
when(mPackageManager.isPackageSuspendedForUser(anyString(), anyInt())).thenReturn(false);
- mService.setRankingHelper(mRankingHelper);
- when(mRankingHelper.isGroupBlocked(anyString(), anyInt(), anyString())).thenReturn(true);
+ mService.setPreferencesHelper(mPreferencesHelper);
+ when(mPreferencesHelper.isGroupBlocked(anyString(), anyInt(), anyString())).thenReturn(true);
NotificationChannel channel = new NotificationChannel("id", "name",
NotificationManager.IMPORTANCE_HIGH);
@@ -1222,36 +1223,36 @@
@Test
public void testTvExtenderChannelOverride_onTv() throws Exception {
mService.setIsTelevision(true);
- mService.setRankingHelper(mRankingHelper);
- when(mRankingHelper.getNotificationChannel(
+ mService.setPreferencesHelper(mPreferencesHelper);
+ when(mPreferencesHelper.getNotificationChannel(
anyString(), anyInt(), eq("foo"), anyBoolean())).thenReturn(
new NotificationChannel("foo", "foo", IMPORTANCE_HIGH));
Notification.TvExtender tv = new Notification.TvExtender().setChannelId("foo");
mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag", 0,
generateNotificationRecord(null, tv).getNotification(), 0);
- verify(mRankingHelper, times(1)).getNotificationChannel(
+ verify(mPreferencesHelper, times(1)).getNotificationChannel(
anyString(), anyInt(), eq("foo"), anyBoolean());
}
@Test
public void testTvExtenderChannelOverride_notOnTv() throws Exception {
mService.setIsTelevision(false);
- mService.setRankingHelper(mRankingHelper);
- when(mRankingHelper.getNotificationChannel(
+ mService.setPreferencesHelper(mPreferencesHelper);
+ when(mPreferencesHelper.getNotificationChannel(
anyString(), anyInt(), anyString(), anyBoolean())).thenReturn(
mTestNotificationChannel);
Notification.TvExtender tv = new Notification.TvExtender().setChannelId("foo");
mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag", 0,
generateNotificationRecord(null, tv).getNotification(), 0);
- verify(mRankingHelper, times(1)).getNotificationChannel(
+ verify(mPreferencesHelper, times(1)).getNotificationChannel(
anyString(), anyInt(), eq(mTestNotificationChannel.getId()), anyBoolean());
}
@Test
public void testUpdateAppNotifyCreatorBlock() throws Exception {
- mService.setRankingHelper(mRankingHelper);
+ mService.setPreferencesHelper(mPreferencesHelper);
mBinderService.setNotificationsEnabledForPackage(PKG, 0, false);
ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
@@ -1265,7 +1266,7 @@
@Test
public void testUpdateAppNotifyCreatorUnblock() throws Exception {
- mService.setRankingHelper(mRankingHelper);
+ mService.setPreferencesHelper(mPreferencesHelper);
mBinderService.setNotificationsEnabledForPackage(PKG, 0, true);
ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
@@ -1279,8 +1280,8 @@
@Test
public void testUpdateChannelNotifyCreatorBlock() throws Exception {
- mService.setRankingHelper(mRankingHelper);
- when(mRankingHelper.getNotificationChannel(eq(PKG), anyInt(),
+ mService.setPreferencesHelper(mPreferencesHelper);
+ when(mPreferencesHelper.getNotificationChannel(eq(PKG), anyInt(),
eq(mTestNotificationChannel.getId()), anyBoolean()))
.thenReturn(mTestNotificationChannel);
@@ -1305,8 +1306,8 @@
NotificationChannel existingChannel =
new NotificationChannel(mTestNotificationChannel.getId(),
mTestNotificationChannel.getName(), IMPORTANCE_NONE);
- mService.setRankingHelper(mRankingHelper);
- when(mRankingHelper.getNotificationChannel(eq(PKG), anyInt(),
+ mService.setPreferencesHelper(mPreferencesHelper);
+ when(mPreferencesHelper.getNotificationChannel(eq(PKG), anyInt(),
eq(mTestNotificationChannel.getId()), anyBoolean()))
.thenReturn(existingChannel);
@@ -1327,8 +1328,8 @@
NotificationChannel existingChannel =
new NotificationChannel(mTestNotificationChannel.getId(),
mTestNotificationChannel.getName(), IMPORTANCE_MAX);
- mService.setRankingHelper(mRankingHelper);
- when(mRankingHelper.getNotificationChannel(eq(PKG), anyInt(),
+ mService.setPreferencesHelper(mPreferencesHelper);
+ when(mPreferencesHelper.getNotificationChannel(eq(PKG), anyInt(),
eq(mTestNotificationChannel.getId()), anyBoolean()))
.thenReturn(existingChannel);
@@ -1339,8 +1340,8 @@
@Test
public void testUpdateGroupNotifyCreatorBlock() throws Exception {
NotificationChannelGroup existing = new NotificationChannelGroup("id", "name");
- mService.setRankingHelper(mRankingHelper);
- when(mRankingHelper.getNotificationChannelGroup(eq(existing.getId()), eq(PKG), anyInt()))
+ mService.setPreferencesHelper(mPreferencesHelper);
+ when(mPreferencesHelper.getNotificationChannelGroup(eq(existing.getId()), eq(PKG), anyInt()))
.thenReturn(existing);
NotificationChannelGroup updated = new NotificationChannelGroup("id", "name");
@@ -1362,8 +1363,8 @@
public void testUpdateGroupNotifyCreatorUnblock() throws Exception {
NotificationChannelGroup existing = new NotificationChannelGroup("id", "name");
existing.setBlocked(true);
- mService.setRankingHelper(mRankingHelper);
- when(mRankingHelper.getNotificationChannelGroup(eq(existing.getId()), eq(PKG), anyInt()))
+ mService.setPreferencesHelper(mPreferencesHelper);
+ when(mPreferencesHelper.getNotificationChannelGroup(eq(existing.getId()), eq(PKG), anyInt()))
.thenReturn(existing);
mBinderService.updateNotificationChannelGroupForPackage(
@@ -1382,8 +1383,8 @@
@Test
public void testUpdateGroupNoNotifyCreatorOtherChanges() throws Exception {
NotificationChannelGroup existing = new NotificationChannelGroup("id", "name");
- mService.setRankingHelper(mRankingHelper);
- when(mRankingHelper.getNotificationChannelGroup(eq(existing.getId()), eq(PKG), anyInt()))
+ mService.setPreferencesHelper(mPreferencesHelper);
+ when(mPreferencesHelper.getNotificationChannelGroup(eq(existing.getId()), eq(PKG), anyInt()))
.thenReturn(existing);
mBinderService.updateNotificationChannelGroupForPackage(
@@ -1396,12 +1397,12 @@
List<String> associations = new ArrayList<>();
associations.add("a");
when(mCompanionMgr.getAssociations(PKG, mUid)).thenReturn(associations);
- mService.setRankingHelper(mRankingHelper);
- when(mRankingHelper.getNotificationChannel(eq(PKG), anyInt(),
+ mService.setPreferencesHelper(mPreferencesHelper);
+ when(mPreferencesHelper.getNotificationChannel(eq(PKG), anyInt(),
eq(mTestNotificationChannel.getId()), anyBoolean()))
.thenReturn(mTestNotificationChannel);
NotificationChannel channel2 = new NotificationChannel("a", "b", IMPORTANCE_LOW);
- when(mRankingHelper.getNotificationChannel(eq(PKG), anyInt(),
+ when(mPreferencesHelper.getNotificationChannel(eq(PKG), anyInt(),
eq(channel2.getId()), anyBoolean()))
.thenReturn(channel2);
@@ -1421,7 +1422,7 @@
List<String> associations = new ArrayList<>();
associations.add("a");
when(mCompanionMgr.getAssociations(PKG, mUid)).thenReturn(associations);
- mService.setRankingHelper(mRankingHelper);
+ mService.setPreferencesHelper(mPreferencesHelper);
NotificationChannelGroup group1 = new NotificationChannelGroup("a", "b");
NotificationChannelGroup group2 = new NotificationChannelGroup("n", "m");
@@ -1441,9 +1442,9 @@
List<String> associations = new ArrayList<>();
associations.add("a");
when(mCompanionMgr.getAssociations(PKG, mUid)).thenReturn(associations);
- mService.setRankingHelper(mRankingHelper);
+ mService.setPreferencesHelper(mPreferencesHelper);
mTestNotificationChannel.setLightColor(Color.CYAN);
- when(mRankingHelper.getNotificationChannel(eq(PKG), anyInt(),
+ when(mPreferencesHelper.getNotificationChannel(eq(PKG), anyInt(),
eq(mTestNotificationChannel.getId()), anyBoolean()))
.thenReturn(mTestNotificationChannel);
@@ -1459,8 +1460,8 @@
List<String> associations = new ArrayList<>();
associations.add("a");
when(mCompanionMgr.getAssociations(PKG, mUid)).thenReturn(associations);
- mService.setRankingHelper(mRankingHelper);
- when(mRankingHelper.getNotificationChannel(eq(PKG), anyInt(),
+ mService.setPreferencesHelper(mPreferencesHelper);
+ when(mPreferencesHelper.getNotificationChannel(eq(PKG), anyInt(),
eq(mTestNotificationChannel.getId()), anyBoolean()))
.thenReturn(mTestNotificationChannel);
reset(mListeners);
@@ -1476,8 +1477,8 @@
associations.add("a");
when(mCompanionMgr.getAssociations(PKG, mUid)).thenReturn(associations);
NotificationChannelGroup ncg = new NotificationChannelGroup("a", "b/c");
- mService.setRankingHelper(mRankingHelper);
- when(mRankingHelper.getNotificationChannelGroup(eq(ncg.getId()), eq(PKG), anyInt()))
+ mService.setPreferencesHelper(mPreferencesHelper);
+ when(mPreferencesHelper.getNotificationChannelGroup(eq(ncg.getId()), eq(PKG), anyInt()))
.thenReturn(ncg);
reset(mListeners);
mBinderService.deleteNotificationChannelGroup(PKG, ncg.getId());
@@ -1488,18 +1489,18 @@
@Test
public void testUpdateNotificationChannelFromPrivilegedListener_success() throws Exception {
- mService.setRankingHelper(mRankingHelper);
+ mService.setPreferencesHelper(mPreferencesHelper);
List<String> associations = new ArrayList<>();
associations.add("a");
when(mCompanionMgr.getAssociations(PKG, mUid)).thenReturn(associations);
- when(mRankingHelper.getNotificationChannel(eq(PKG), anyInt(),
+ when(mPreferencesHelper.getNotificationChannel(eq(PKG), anyInt(),
eq(mTestNotificationChannel.getId()), anyBoolean()))
.thenReturn(mTestNotificationChannel);
mBinderService.updateNotificationChannelFromPrivilegedListener(
null, PKG, Process.myUserHandle(), mTestNotificationChannel);
- verify(mRankingHelper, times(1)).updateNotificationChannel(
+ verify(mPreferencesHelper, times(1)).updateNotificationChannel(
anyString(), anyInt(), any(), anyBoolean());
verify(mListeners, never()).notifyNotificationChannelChanged(eq(PKG),
@@ -1509,7 +1510,7 @@
@Test
public void testUpdateNotificationChannelFromPrivilegedListener_noAccess() throws Exception {
- mService.setRankingHelper(mRankingHelper);
+ mService.setPreferencesHelper(mPreferencesHelper);
List<String> associations = new ArrayList<>();
when(mCompanionMgr.getAssociations(PKG, mUid)).thenReturn(associations);
@@ -1521,7 +1522,7 @@
// pass
}
- verify(mRankingHelper, never()).updateNotificationChannel(
+ verify(mPreferencesHelper, never()).updateNotificationChannel(
anyString(), anyInt(), any(), anyBoolean());
verify(mListeners, never()).notifyNotificationChannelChanged(eq(PKG),
@@ -1531,7 +1532,7 @@
@Test
public void testUpdateNotificationChannelFromPrivilegedListener_badUser() throws Exception {
- mService.setRankingHelper(mRankingHelper);
+ mService.setPreferencesHelper(mPreferencesHelper);
List<String> associations = new ArrayList<>();
associations.add("a");
when(mCompanionMgr.getAssociations(PKG, mUid)).thenReturn(associations);
@@ -1548,7 +1549,7 @@
// pass
}
- verify(mRankingHelper, never()).updateNotificationChannel(
+ verify(mPreferencesHelper, never()).updateNotificationChannel(
anyString(), anyInt(), any(), anyBoolean());
verify(mListeners, never()).notifyNotificationChannelChanged(eq(PKG),
@@ -1558,7 +1559,7 @@
@Test
public void testGetNotificationChannelFromPrivilegedListener_success() throws Exception {
- mService.setRankingHelper(mRankingHelper);
+ mService.setPreferencesHelper(mPreferencesHelper);
List<String> associations = new ArrayList<>();
associations.add("a");
when(mCompanionMgr.getAssociations(PKG, mUid)).thenReturn(associations);
@@ -1566,13 +1567,13 @@
mBinderService.getNotificationChannelsFromPrivilegedListener(
null, PKG, Process.myUserHandle());
- verify(mRankingHelper, times(1)).getNotificationChannels(
+ verify(mPreferencesHelper, times(1)).getNotificationChannels(
anyString(), anyInt(), anyBoolean());
}
@Test
public void testGetNotificationChannelFromPrivilegedListener_noAccess() throws Exception {
- mService.setRankingHelper(mRankingHelper);
+ mService.setPreferencesHelper(mPreferencesHelper);
List<String> associations = new ArrayList<>();
when(mCompanionMgr.getAssociations(PKG, mUid)).thenReturn(associations);
@@ -1584,13 +1585,13 @@
// pass
}
- verify(mRankingHelper, never()).getNotificationChannels(
+ verify(mPreferencesHelper, never()).getNotificationChannels(
anyString(), anyInt(), anyBoolean());
}
@Test
public void testGetNotificationChannelFromPrivilegedListener_badUser() throws Exception {
- mService.setRankingHelper(mRankingHelper);
+ mService.setPreferencesHelper(mPreferencesHelper);
List<String> associations = new ArrayList<>();
associations.add("a");
when(mCompanionMgr.getAssociations(PKG, mUid)).thenReturn(associations);
@@ -1606,13 +1607,13 @@
// pass
}
- verify(mRankingHelper, never()).getNotificationChannels(
+ verify(mPreferencesHelper, never()).getNotificationChannels(
anyString(), anyInt(), anyBoolean());
}
@Test
public void testGetNotificationChannelGroupsFromPrivilegedListener_success() throws Exception {
- mService.setRankingHelper(mRankingHelper);
+ mService.setPreferencesHelper(mPreferencesHelper);
List<String> associations = new ArrayList<>();
associations.add("a");
when(mCompanionMgr.getAssociations(PKG, mUid)).thenReturn(associations);
@@ -1620,12 +1621,12 @@
mBinderService.getNotificationChannelGroupsFromPrivilegedListener(
null, PKG, Process.myUserHandle());
- verify(mRankingHelper, times(1)).getNotificationChannelGroups(anyString(), anyInt());
+ verify(mPreferencesHelper, times(1)).getNotificationChannelGroups(anyString(), anyInt());
}
@Test
public void testGetNotificationChannelGroupsFromPrivilegedListener_noAccess() throws Exception {
- mService.setRankingHelper(mRankingHelper);
+ mService.setPreferencesHelper(mPreferencesHelper);
List<String> associations = new ArrayList<>();
when(mCompanionMgr.getAssociations(PKG, mUid)).thenReturn(associations);
@@ -1637,12 +1638,12 @@
// pass
}
- verify(mRankingHelper, never()).getNotificationChannelGroups(anyString(), anyInt());
+ verify(mPreferencesHelper, never()).getNotificationChannelGroups(anyString(), anyInt());
}
@Test
public void testGetNotificationChannelGroupsFromPrivilegedListener_badUser() throws Exception {
- mService.setRankingHelper(mRankingHelper);
+ mService.setPreferencesHelper(mPreferencesHelper);
List<String> associations = new ArrayList<>();
when(mCompanionMgr.getAssociations(PKG, mUid)).thenReturn(associations);
mListener = mock(ManagedServices.ManagedServiceInfo.class);
@@ -1657,7 +1658,7 @@
// pass
}
- verify(mRankingHelper, never()).getNotificationChannelGroups(anyString(), anyInt());
+ verify(mPreferencesHelper, never()).getNotificationChannelGroups(anyString(), anyInt());
}
@Test
@@ -2182,7 +2183,7 @@
@Test
public void testHandleRankingSort_sendsUpdateOnSignalExtractorChange() throws Exception {
- mService.setRankingHelper(mRankingHelper);
+ mService.setPreferencesHelper(mPreferencesHelper);
NotificationManagerService.WorkerHandler handler = mock(
NotificationManagerService.WorkerHandler.class);
mService.setHandler(handler);
@@ -3003,6 +3004,20 @@
}
@Test
+ public void testVisualDifference_summaryNewNotification() {
+ Notification.Builder nb2 = new Notification.Builder(mContext, "")
+ .setGroup("bananas")
+ .setFlag(Notification.FLAG_GROUP_SUMMARY, true)
+ .setContentText("bar");
+ StatusBarNotification sbn2 = new StatusBarNotification(PKG, PKG, 0, "tag", mUid, 0,
+ nb2.build(), new UserHandle(mUid), null, 0);
+ NotificationRecord r2 =
+ new NotificationRecord(mContext, sbn2, mock(NotificationChannel.class));
+
+ assertFalse(mService.isVisuallyInterruptive(null, r2));
+ }
+
+ @Test
public void testHideAndUnhideNotificationsOnSuspendedPackageBroadcast() {
// post 2 notification from this package
final NotificationRecord notif1 = generateNotificationRecord(
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
index e286991..bd6416d 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
@@ -34,8 +34,6 @@
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.ActivityManager;
@@ -48,6 +46,7 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.graphics.Color;
+import android.graphics.drawable.Icon;
import android.media.AudioAttributes;
import android.metrics.LogMaker;
import android.net.Uri;
@@ -59,8 +58,8 @@
import android.service.notification.StatusBarNotification;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
-import android.util.Slog;
+import com.android.internal.R;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.server.UiServiceTestCase;
@@ -70,6 +69,7 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.ArrayList;
import java.util.Objects;
@SmallTest
@@ -697,4 +697,20 @@
record.calculateGrantableUris();
// should not throw
}
+
+ @Test
+ public void testSmartActions() {
+ StatusBarNotification sbn = getNotification(PKG_O, true /* noisy */,
+ true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
+ false /* lights */, false /* defaultLights */, groupId /* group */);
+ NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
+ assertNull(record.getSmartActions());
+
+ ArrayList<Notification.Action> smartActions = new ArrayList<>();
+ smartActions.add(new Notification.Action.Builder(
+ Icon.createWithResource(getContext(), R.drawable.btn_default),
+ "text", null).build());
+ record.setSmartActions(smartActions);
+ assertEquals(smartActions, record.getSmartActions());
+ }
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
new file mode 100644
index 0000000..02d5869
--- /dev/null
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -0,0 +1,1742 @@
+/*
+ * 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.notification;
+
+import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
+import static android.app.NotificationManager.IMPORTANCE_HIGH;
+import static android.app.NotificationManager.IMPORTANCE_LOW;
+import static android.app.NotificationManager.IMPORTANCE_MAX;
+import static android.app.NotificationManager.IMPORTANCE_NONE;
+import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
+
+import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.fail;
+
+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.mockito.ArgumentMatchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationChannelGroup;
+import android.app.NotificationManager;
+import android.content.ContentProvider;
+import android.content.Context;
+import android.content.IContentProvider;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.Signature;
+import android.content.res.Resources;
+import android.graphics.Color;
+import android.media.AudioAttributes;
+import android.net.Uri;
+import android.os.Build;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.provider.Settings.Secure;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.TestableContentResolver;
+import android.util.ArrayMap;
+import android.util.Xml;
+
+import com.android.internal.util.FastXmlSerializer;
+import com.android.server.UiServiceTestCase;
+
+import org.json.JSONArray;
+import org.json.JSONObject;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.ThreadLocalRandom;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class PreferencesHelperTest extends UiServiceTestCase {
+ private static final String PKG = "com.android.server.notification";
+ private static final int UID = 0;
+ private static final UserHandle USER = UserHandle.of(0);
+ private static final String UPDATED_PKG = "updatedPkg";
+ private static final int UID2 = 1111;
+ private static final String SYSTEM_PKG = "android";
+ private static final int SYSTEM_UID= 1000;
+ private static final UserHandle USER2 = UserHandle.of(10);
+ private static final String TEST_CHANNEL_ID = "test_channel_id";
+ private static final String TEST_AUTHORITY = "test";
+ private static final Uri SOUND_URI =
+ Uri.parse("content://" + TEST_AUTHORITY + "/internal/audio/media/10");
+ private static final Uri CANONICAL_SOUND_URI =
+ Uri.parse("content://" + TEST_AUTHORITY
+ + "/internal/audio/media/10?title=Test&canonical=1");
+
+ @Mock NotificationUsageStats mUsageStats;
+ @Mock RankingHandler mHandler;
+ @Mock PackageManager mPm;
+ @Mock IContentProvider mTestIContentProvider;
+ @Mock Context mContext;
+ @Mock ZenModeHelper mMockZenModeHelper;
+
+ private NotificationManager.Policy mTestNotificationPolicy;
+
+ private PreferencesHelper mHelper;
+ private AudioAttributes mAudioAttributes;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ UserHandle user = UserHandle.ALL;
+
+ final ApplicationInfo legacy = new ApplicationInfo();
+ legacy.targetSdkVersion = Build.VERSION_CODES.N_MR1;
+ final ApplicationInfo upgrade = new ApplicationInfo();
+ upgrade.targetSdkVersion = Build.VERSION_CODES.O;
+ when(mPm.getApplicationInfoAsUser(eq(PKG), anyInt(), anyInt())).thenReturn(legacy);
+ when(mPm.getApplicationInfoAsUser(eq(UPDATED_PKG), anyInt(), anyInt())).thenReturn(upgrade);
+ when(mPm.getApplicationInfoAsUser(eq(SYSTEM_PKG), anyInt(), anyInt())).thenReturn(upgrade);
+ when(mPm.getPackageUidAsUser(eq(PKG), anyInt())).thenReturn(UID);
+ when(mPm.getPackageUidAsUser(eq(UPDATED_PKG), anyInt())).thenReturn(UID2);
+ when(mPm.getPackageUidAsUser(eq(SYSTEM_PKG), anyInt())).thenReturn(SYSTEM_UID);
+ PackageInfo info = mock(PackageInfo.class);
+ info.signatures = new Signature[] {mock(Signature.class)};
+ when(mPm.getPackageInfoAsUser(eq(SYSTEM_PKG), anyInt(), anyInt())).thenReturn(info);
+ when(mPm.getPackageInfoAsUser(eq(PKG), anyInt(), anyInt()))
+ .thenReturn(mock(PackageInfo.class));
+ when(mContext.getResources()).thenReturn(
+ InstrumentationRegistry.getContext().getResources());
+ when(mContext.getContentResolver()).thenReturn(
+ InstrumentationRegistry.getContext().getContentResolver());
+ when(mContext.getPackageManager()).thenReturn(mPm);
+ when(mContext.getApplicationInfo()).thenReturn(legacy);
+ // most tests assume badging is enabled
+ TestableContentResolver contentResolver = getContext().getContentResolver();
+ contentResolver.setFallbackToExisting(false);
+ Secure.putIntForUser(contentResolver,
+ Secure.NOTIFICATION_BADGING, 1, UserHandle.getUserId(UID));
+
+ ContentProvider testContentProvider = mock(ContentProvider.class);
+ when(testContentProvider.getIContentProvider()).thenReturn(mTestIContentProvider);
+ contentResolver.addProvider(TEST_AUTHORITY, testContentProvider);
+
+ when(mTestIContentProvider.canonicalize(any(), eq(SOUND_URI)))
+ .thenReturn(CANONICAL_SOUND_URI);
+ when(mTestIContentProvider.canonicalize(any(), eq(CANONICAL_SOUND_URI)))
+ .thenReturn(CANONICAL_SOUND_URI);
+ when(mTestIContentProvider.uncanonicalize(any(), eq(CANONICAL_SOUND_URI)))
+ .thenReturn(SOUND_URI);
+
+ mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0,
+ NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND);
+ when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy);
+ mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper);
+ resetZenModeHelper();
+
+ mAudioAttributes = new AudioAttributes.Builder()
+ .setContentType(AudioAttributes.CONTENT_TYPE_UNKNOWN)
+ .setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE)
+ .setFlags(AudioAttributes.FLAG_AUDIBILITY_ENFORCED)
+ .build();
+ }
+
+ private NotificationChannel getDefaultChannel() {
+ return new NotificationChannel(NotificationChannel.DEFAULT_CHANNEL_ID, "name",
+ IMPORTANCE_LOW);
+ }
+
+ private ByteArrayOutputStream writeXmlAndPurge(String pkg, int uid, boolean forBackup,
+ String... channelIds)
+ throws Exception {
+ XmlSerializer serializer = new FastXmlSerializer();
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ serializer.setOutput(new BufferedOutputStream(baos), "utf-8");
+ serializer.startDocument(null, true);
+ mHelper.writeXml(serializer, forBackup);
+ serializer.endDocument();
+ serializer.flush();
+ for (String channelId : channelIds) {
+ mHelper.permanentlyDeleteNotificationChannel(pkg, uid, channelId);
+ }
+ return baos;
+ }
+
+ private void loadStreamXml(ByteArrayOutputStream stream, boolean forRestore) throws Exception {
+ loadByteArrayXml(stream.toByteArray(), forRestore);
+ }
+
+ private void loadByteArrayXml(byte[] byteArray, boolean forRestore) throws Exception {
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(new BufferedInputStream(new ByteArrayInputStream(byteArray)), null);
+ parser.nextTag();
+ mHelper.readXml(parser, forRestore);
+ }
+
+ private void compareChannels(NotificationChannel expected, NotificationChannel actual) {
+ assertEquals(expected.getId(), actual.getId());
+ assertEquals(expected.getName(), actual.getName());
+ assertEquals(expected.getDescription(), actual.getDescription());
+ assertEquals(expected.shouldVibrate(), actual.shouldVibrate());
+ assertEquals(expected.shouldShowLights(), actual.shouldShowLights());
+ assertEquals(expected.getImportance(), actual.getImportance());
+ assertEquals(expected.getLockscreenVisibility(), actual.getLockscreenVisibility());
+ assertEquals(expected.getSound(), actual.getSound());
+ assertEquals(expected.canBypassDnd(), actual.canBypassDnd());
+ assertTrue(Arrays.equals(expected.getVibrationPattern(), actual.getVibrationPattern()));
+ assertEquals(expected.getGroup(), actual.getGroup());
+ assertEquals(expected.getAudioAttributes(), actual.getAudioAttributes());
+ assertEquals(expected.getLightColor(), actual.getLightColor());
+ }
+
+ private void compareGroups(NotificationChannelGroup expected, NotificationChannelGroup actual) {
+ assertEquals(expected.getId(), actual.getId());
+ assertEquals(expected.getName(), actual.getName());
+ assertEquals(expected.getDescription(), actual.getDescription());
+ assertEquals(expected.isBlocked(), actual.isBlocked());
+ }
+
+ private NotificationChannel getChannel() {
+ return new NotificationChannel("id", "name", IMPORTANCE_LOW);
+ }
+
+ private NotificationChannel findChannel(List<NotificationChannel> channels, String id) {
+ for (NotificationChannel channel : channels) {
+ if (channel.getId().equals(id)) {
+ return channel;
+ }
+ }
+ return null;
+ }
+
+ private void resetZenModeHelper() {
+ reset(mMockZenModeHelper);
+ when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy);
+ }
+
+ @Test
+ public void testChannelXml() throws Exception {
+ NotificationChannelGroup ncg = new NotificationChannelGroup("1", "bye");
+ ncg.setBlocked(true);
+ ncg.setDescription("group desc");
+ NotificationChannelGroup ncg2 = new NotificationChannelGroup("2", "hello");
+ NotificationChannel channel1 =
+ new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
+ NotificationChannel channel2 =
+ new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
+ channel2.setDescription("descriptions for all");
+ channel2.setSound(new Uri.Builder().scheme("test").build(), mAudioAttributes);
+ channel2.enableLights(true);
+ channel2.setBypassDnd(true);
+ channel2.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
+ channel2.enableVibration(true);
+ channel2.setGroup(ncg.getId());
+ channel2.setVibrationPattern(new long[]{100, 67, 145, 156});
+ channel2.setLightColor(Color.BLUE);
+
+ mHelper.createNotificationChannelGroup(PKG, UID, ncg, true);
+ mHelper.createNotificationChannelGroup(PKG, UID, ncg2, true);
+ mHelper.createNotificationChannel(PKG, UID, channel1, true, false);
+ mHelper.createNotificationChannel(PKG, UID, channel2, false, false);
+
+ mHelper.setShowBadge(PKG, UID, true);
+ mHelper.setAppImportanceLocked(PKG, UID);
+
+ ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, false, channel1.getId(),
+ channel2.getId(), NotificationChannel.DEFAULT_CHANNEL_ID);
+ mHelper.onPackagesChanged(true, UserHandle.myUserId(), new String[]{PKG}, new int[]{UID});
+
+ loadStreamXml(baos, false);
+
+ assertTrue(mHelper.canShowBadge(PKG, UID));
+ assertTrue(mHelper.getIsAppImportanceLocked(PKG, UID));
+ assertEquals(channel1, mHelper.getNotificationChannel(PKG, UID, channel1.getId(), false));
+ compareChannels(channel2,
+ mHelper.getNotificationChannel(PKG, UID, channel2.getId(), false));
+
+ List<NotificationChannelGroup> actualGroups =
+ mHelper.getNotificationChannelGroups(PKG, UID, false, true).getList();
+ boolean foundNcg = false;
+ for (NotificationChannelGroup actual : actualGroups) {
+ if (ncg.getId().equals(actual.getId())) {
+ foundNcg = true;
+ compareGroups(ncg, actual);
+ } else if (ncg2.getId().equals(actual.getId())) {
+ compareGroups(ncg2, actual);
+ }
+ }
+ assertTrue(foundNcg);
+
+ boolean foundChannel2Group = false;
+ for (NotificationChannelGroup actual : actualGroups) {
+ if (channel2.getGroup().equals(actual.getChannels().get(0).getGroup())) {
+ foundChannel2Group = true;
+ break;
+ }
+ }
+ assertTrue(foundChannel2Group);
+ }
+
+ @Test
+ public void testChannelXmlForBackup() throws Exception {
+ NotificationChannelGroup ncg = new NotificationChannelGroup("1", "bye");
+ NotificationChannelGroup ncg2 = new NotificationChannelGroup("2", "hello");
+ NotificationChannel channel1 =
+ new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
+ NotificationChannel channel2 =
+ new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
+ channel2.setDescription("descriptions for all");
+ channel2.setSound(SOUND_URI, mAudioAttributes);
+ channel2.enableLights(true);
+ channel2.setBypassDnd(true);
+ channel2.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
+ channel2.enableVibration(false);
+ channel2.setGroup(ncg.getId());
+ channel2.setLightColor(Color.BLUE);
+ NotificationChannel channel3 = new NotificationChannel("id3", "NAM3", IMPORTANCE_HIGH);
+ channel3.enableVibration(true);
+
+ mHelper.createNotificationChannelGroup(PKG, UID, ncg, true);
+ mHelper.createNotificationChannelGroup(PKG, UID, ncg2, true);
+ mHelper.createNotificationChannel(PKG, UID, channel1, true, false);
+ mHelper.createNotificationChannel(PKG, UID, channel2, false, false);
+ mHelper.createNotificationChannel(PKG, UID, channel3, false, false);
+ mHelper.createNotificationChannel(UPDATED_PKG, UID2, getChannel(), true, false);
+
+ mHelper.setShowBadge(PKG, UID, true);
+
+ mHelper.setImportance(UPDATED_PKG, UID2, IMPORTANCE_NONE);
+
+ ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, true, channel1.getId(),
+ channel2.getId(), channel3.getId(), NotificationChannel.DEFAULT_CHANNEL_ID);
+ mHelper.onPackagesChanged(true, UserHandle.myUserId(), new String[]{PKG, UPDATED_PKG},
+ new int[]{UID, UID2});
+
+ mHelper.setShowBadge(UPDATED_PKG, UID2, true);
+
+ loadStreamXml(baos, true);
+
+ assertEquals(IMPORTANCE_NONE, mHelper.getImportance(UPDATED_PKG, UID2));
+ assertTrue(mHelper.canShowBadge(PKG, UID));
+ assertEquals(channel1, mHelper.getNotificationChannel(PKG, UID, channel1.getId(), false));
+ compareChannels(channel2,
+ mHelper.getNotificationChannel(PKG, UID, channel2.getId(), false));
+ compareChannels(channel3,
+ mHelper.getNotificationChannel(PKG, UID, channel3.getId(), false));
+
+ List<NotificationChannelGroup> actualGroups =
+ mHelper.getNotificationChannelGroups(PKG, UID, false, true).getList();
+ boolean foundNcg = false;
+ for (NotificationChannelGroup actual : actualGroups) {
+ if (ncg.getId().equals(actual.getId())) {
+ foundNcg = true;
+ compareGroups(ncg, actual);
+ } else if (ncg2.getId().equals(actual.getId())) {
+ compareGroups(ncg2, actual);
+ }
+ }
+ assertTrue(foundNcg);
+
+ boolean foundChannel2Group = false;
+ for (NotificationChannelGroup actual : actualGroups) {
+ if (channel2.getGroup().equals(actual.getChannels().get(0).getGroup())) {
+ foundChannel2Group = true;
+ break;
+ }
+ }
+ assertTrue(foundChannel2Group);
+ }
+
+ @Test
+ public void testBackupXml_backupCanonicalizedSoundUri() throws Exception {
+ NotificationChannel channel =
+ new NotificationChannel("id", "name", IMPORTANCE_LOW);
+ channel.setSound(SOUND_URI, mAudioAttributes);
+ mHelper.createNotificationChannel(PKG, UID, channel, true, false);
+
+ ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, true, channel.getId());
+
+ // Testing that in restore we are given the canonical version
+ loadStreamXml(baos, true);
+ verify(mTestIContentProvider).uncanonicalize(any(), eq(CANONICAL_SOUND_URI));
+ }
+
+ @Test
+ public void testRestoreXml_withExistentCanonicalizedSoundUri() throws Exception {
+ Uri localUri = Uri.parse("content://" + TEST_AUTHORITY + "/local/url");
+ Uri canonicalBasedOnLocal = localUri.buildUpon()
+ .appendQueryParameter("title", "Test")
+ .appendQueryParameter("canonical", "1")
+ .build();
+ when(mTestIContentProvider.canonicalize(any(), eq(CANONICAL_SOUND_URI)))
+ .thenReturn(canonicalBasedOnLocal);
+ when(mTestIContentProvider.uncanonicalize(any(), eq(CANONICAL_SOUND_URI)))
+ .thenReturn(localUri);
+ when(mTestIContentProvider.uncanonicalize(any(), eq(canonicalBasedOnLocal)))
+ .thenReturn(localUri);
+
+ NotificationChannel channel =
+ new NotificationChannel("id", "name", IMPORTANCE_LOW);
+ channel.setSound(SOUND_URI, mAudioAttributes);
+ mHelper.createNotificationChannel(PKG, UID, channel, true, false);
+ ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, true, channel.getId());
+
+ loadStreamXml(baos, true);
+
+ NotificationChannel actualChannel = mHelper.getNotificationChannel(
+ PKG, UID, channel.getId(), false);
+ assertEquals(localUri, actualChannel.getSound());
+ }
+
+ @Test
+ public void testRestoreXml_withNonExistentCanonicalizedSoundUri() throws Exception {
+ Thread.sleep(3000);
+ when(mTestIContentProvider.canonicalize(any(), eq(CANONICAL_SOUND_URI)))
+ .thenReturn(null);
+ when(mTestIContentProvider.uncanonicalize(any(), eq(CANONICAL_SOUND_URI)))
+ .thenReturn(null);
+
+ NotificationChannel channel =
+ new NotificationChannel("id", "name", IMPORTANCE_LOW);
+ channel.setSound(SOUND_URI, mAudioAttributes);
+ mHelper.createNotificationChannel(PKG, UID, channel, true, false);
+ ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, true, channel.getId());
+
+ loadStreamXml(baos, true);
+
+ NotificationChannel actualChannel = mHelper.getNotificationChannel(
+ PKG, UID, channel.getId(), false);
+ assertEquals(Settings.System.DEFAULT_NOTIFICATION_URI, actualChannel.getSound());
+ }
+
+
+ /**
+ * Although we don't make backups with uncanonicalized uris anymore, we used to, so we have to
+ * handle its restore properly.
+ */
+ @Test
+ public void testRestoreXml_withUncanonicalizedNonLocalSoundUri() throws Exception {
+ // Not a local uncanonicalized uri, simulating that it fails to exist locally
+ when(mTestIContentProvider.canonicalize(any(), eq(SOUND_URI))).thenReturn(null);
+ String id = "id";
+ String backupWithUncanonicalizedSoundUri = "<ranking version=\"1\">\n"
+ + "<package name=\"com.android.server.notification\" show_badge=\"true\">\n"
+ + "<channel id=\"" + id + "\" name=\"name\" importance=\"2\" "
+ + "sound=\"" + SOUND_URI + "\" "
+ + "usage=\"6\" content_type=\"0\" flags=\"1\" show_badge=\"true\" />\n"
+ + "<channel id=\"miscellaneous\" name=\"Uncategorized\" usage=\"5\" "
+ + "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n"
+ + "</package>\n"
+ + "</ranking>\n";
+
+ loadByteArrayXml(backupWithUncanonicalizedSoundUri.getBytes(), true);
+
+ NotificationChannel actualChannel = mHelper.getNotificationChannel(PKG, UID, id, false);
+ assertEquals(Settings.System.DEFAULT_NOTIFICATION_URI, actualChannel.getSound());
+ }
+
+ @Test
+ public void testBackupRestoreXml_withNullSoundUri() throws Exception {
+ NotificationChannel channel =
+ new NotificationChannel("id", "name", IMPORTANCE_LOW);
+ channel.setSound(null, mAudioAttributes);
+ mHelper.createNotificationChannel(PKG, UID, channel, true, false);
+ ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, true, channel.getId());
+
+ loadStreamXml(baos, true);
+
+ NotificationChannel actualChannel = mHelper.getNotificationChannel(
+ PKG, UID, channel.getId(), false);
+ assertEquals(null, actualChannel.getSound());
+ }
+
+ @Test
+ public void testChannelXml_backup() throws Exception {
+ NotificationChannelGroup ncg = new NotificationChannelGroup("1", "bye");
+ NotificationChannelGroup ncg2 = new NotificationChannelGroup("2", "hello");
+ NotificationChannel channel1 =
+ new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
+ NotificationChannel channel2 =
+ new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
+ NotificationChannel channel3 =
+ new NotificationChannel("id3", "name3", IMPORTANCE_LOW);
+ channel3.setGroup(ncg.getId());
+
+ mHelper.createNotificationChannelGroup(PKG, UID, ncg, true);
+ mHelper.createNotificationChannelGroup(PKG, UID, ncg2, true);
+ mHelper.createNotificationChannel(PKG, UID, channel1, true, false);
+ mHelper.createNotificationChannel(PKG, UID, channel2, false, false);
+ mHelper.createNotificationChannel(PKG, UID, channel3, true, false);
+
+ mHelper.deleteNotificationChannel(PKG, UID, channel1.getId());
+ mHelper.deleteNotificationChannelGroup(PKG, UID, ncg.getId());
+ assertEquals(channel2, mHelper.getNotificationChannel(PKG, UID, channel2.getId(), false));
+
+ ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, true, channel1.getId(),
+ channel2.getId(), channel3.getId(), NotificationChannel.DEFAULT_CHANNEL_ID);
+ mHelper.onPackagesChanged(true, UserHandle.myUserId(), new String[]{PKG}, new int[]{UID});
+
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(new BufferedInputStream(new ByteArrayInputStream(baos.toByteArray())),
+ null);
+ parser.nextTag();
+ mHelper.readXml(parser, true);
+
+ assertNull(mHelper.getNotificationChannel(PKG, UID, channel1.getId(), false));
+ assertNull(mHelper.getNotificationChannel(PKG, UID, channel3.getId(), false));
+ assertNull(mHelper.getNotificationChannelGroup(ncg.getId(), PKG, UID));
+ //assertEquals(ncg2, mHelper.getNotificationChannelGroup(ncg2.getId(), PKG, UID));
+ assertEquals(channel2, mHelper.getNotificationChannel(PKG, UID, channel2.getId(), false));
+ }
+
+ @Test
+ public void testChannelXml_defaultChannelLegacyApp_noUserSettings() throws Exception {
+ ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, false,
+ NotificationChannel.DEFAULT_CHANNEL_ID);
+
+ loadStreamXml(baos, false);
+
+ final NotificationChannel updated = mHelper.getNotificationChannel(PKG, UID,
+ NotificationChannel.DEFAULT_CHANNEL_ID, false);
+ assertEquals(NotificationManager.IMPORTANCE_UNSPECIFIED, updated.getImportance());
+ assertFalse(updated.canBypassDnd());
+ assertEquals(NotificationManager.VISIBILITY_NO_OVERRIDE, updated.getLockscreenVisibility());
+ assertEquals(0, updated.getUserLockedFields());
+ }
+
+ @Test
+ public void testChannelXml_defaultChannelUpdatedApp_userSettings() throws Exception {
+ final NotificationChannel defaultChannel = mHelper.getNotificationChannel(PKG, UID,
+ NotificationChannel.DEFAULT_CHANNEL_ID, false);
+ defaultChannel.setImportance(NotificationManager.IMPORTANCE_LOW);
+ mHelper.updateNotificationChannel(PKG, UID, defaultChannel, true);
+
+ ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, false,
+ NotificationChannel.DEFAULT_CHANNEL_ID);
+
+ loadStreamXml(baos, false);
+
+ assertEquals(NotificationManager.IMPORTANCE_LOW, mHelper.getNotificationChannel(
+ PKG, UID, NotificationChannel.DEFAULT_CHANNEL_ID, false).getImportance());
+ }
+
+ @Test
+ public void testChannelXml_upgradeCreateDefaultChannel() throws Exception {
+ final String preupgradeXml = "<ranking version=\"1\">\n"
+ + "<package name=\"" + PKG
+ + "\" importance=\"" + NotificationManager.IMPORTANCE_HIGH
+ + "\" priority=\"" + Notification.PRIORITY_MAX + "\" visibility=\""
+ + Notification.VISIBILITY_SECRET + "\"" +" uid=\"" + UID + "\" />\n"
+ + "<package name=\"" + UPDATED_PKG + "\" uid=\"" + UID2 + "\" visibility=\""
+ + Notification.VISIBILITY_PRIVATE + "\" />\n"
+ + "</ranking>";
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(new BufferedInputStream(new ByteArrayInputStream(preupgradeXml.getBytes())),
+ null);
+ parser.nextTag();
+ mHelper.readXml(parser, false);
+
+ final NotificationChannel updated1 =
+ mHelper.getNotificationChannel(PKG, UID, NotificationChannel.DEFAULT_CHANNEL_ID, false);
+ assertEquals(NotificationManager.IMPORTANCE_HIGH, updated1.getImportance());
+ assertTrue(updated1.canBypassDnd());
+ assertEquals(Notification.VISIBILITY_SECRET, updated1.getLockscreenVisibility());
+ assertEquals(NotificationChannel.USER_LOCKED_IMPORTANCE
+ | NotificationChannel.USER_LOCKED_PRIORITY
+ | NotificationChannel.USER_LOCKED_VISIBILITY,
+ updated1.getUserLockedFields());
+
+ // No Default Channel created for updated packages
+ assertEquals(null, mHelper.getNotificationChannel(UPDATED_PKG, UID2,
+ NotificationChannel.DEFAULT_CHANNEL_ID, false));
+ }
+
+ @Test
+ public void testChannelXml_upgradeDeletesDefaultChannel() throws Exception {
+ final NotificationChannel defaultChannel = mHelper.getNotificationChannel(
+ PKG, UID, NotificationChannel.DEFAULT_CHANNEL_ID, false);
+ assertTrue(defaultChannel != null);
+ ByteArrayOutputStream baos =
+ writeXmlAndPurge(PKG, UID, false, NotificationChannel.DEFAULT_CHANNEL_ID);
+ // Load package at higher sdk.
+ final ApplicationInfo upgraded = new ApplicationInfo();
+ upgraded.targetSdkVersion = Build.VERSION_CODES.N_MR1 + 1;
+ when(mPm.getApplicationInfoAsUser(eq(PKG), anyInt(), anyInt())).thenReturn(upgraded);
+ loadStreamXml(baos, false);
+
+ // Default Channel should be gone.
+ assertEquals(null, mHelper.getNotificationChannel(PKG, UID,
+ NotificationChannel.DEFAULT_CHANNEL_ID, false));
+ }
+
+ @Test
+ public void testDeletesDefaultChannelAfterChannelIsCreated() throws Exception {
+ mHelper.createNotificationChannel(PKG, UID,
+ new NotificationChannel("bananas", "bananas", IMPORTANCE_LOW), true, false);
+ ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, false,
+ NotificationChannel.DEFAULT_CHANNEL_ID, "bananas");
+
+ // Load package at higher sdk.
+ final ApplicationInfo upgraded = new ApplicationInfo();
+ upgraded.targetSdkVersion = Build.VERSION_CODES.N_MR1 + 1;
+ when(mPm.getApplicationInfoAsUser(eq(PKG), anyInt(), anyInt())).thenReturn(upgraded);
+ loadStreamXml(baos, false);
+
+ // Default Channel should be gone.
+ assertEquals(null, mHelper.getNotificationChannel(PKG, UID,
+ NotificationChannel.DEFAULT_CHANNEL_ID, false));
+ }
+
+ @Test
+ public void testLoadingOldChannelsDoesNotDeleteNewlyCreatedChannels() throws Exception {
+ ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, false,
+ NotificationChannel.DEFAULT_CHANNEL_ID, "bananas");
+ mHelper.createNotificationChannel(PKG, UID,
+ new NotificationChannel("bananas", "bananas", IMPORTANCE_LOW), true, false);
+
+ loadStreamXml(baos, false);
+
+ // Should still have the newly created channel that wasn't in the xml.
+ assertTrue(mHelper.getNotificationChannel(PKG, UID, "bananas", false) != null);
+ }
+
+ @Test
+ public void testCreateChannel_blocked() throws Exception {
+ mHelper.setImportance(PKG, UID, IMPORTANCE_NONE);
+
+ mHelper.createNotificationChannel(PKG, UID,
+ new NotificationChannel("bananas", "bananas", IMPORTANCE_LOW), true, false);
+ }
+
+ @Test
+ public void testCreateChannel_badImportance() throws Exception {
+ try {
+ mHelper.createNotificationChannel(PKG, UID,
+ new NotificationChannel("bananas", "bananas", IMPORTANCE_NONE - 1),
+ true, false);
+ fail("Was allowed to create a channel with invalid importance");
+ } catch (IllegalArgumentException e) {
+ // yay
+ }
+ try {
+ mHelper.createNotificationChannel(PKG, UID,
+ new NotificationChannel("bananas", "bananas", IMPORTANCE_UNSPECIFIED),
+ true, false);
+ fail("Was allowed to create a channel with invalid importance");
+ } catch (IllegalArgumentException e) {
+ // yay
+ }
+ try {
+ mHelper.createNotificationChannel(PKG, UID,
+ new NotificationChannel("bananas", "bananas", IMPORTANCE_MAX + 1),
+ true, false);
+ fail("Was allowed to create a channel with invalid importance");
+ } catch (IllegalArgumentException e) {
+ // yay
+ }
+ mHelper.createNotificationChannel(PKG, UID,
+ new NotificationChannel("bananas", "bananas", IMPORTANCE_NONE), true, false);
+ mHelper.createNotificationChannel(PKG, UID,
+ new NotificationChannel("bananas", "bananas", IMPORTANCE_MAX), true, false);
+ }
+
+
+ @Test
+ public void testUpdate() throws Exception {
+ // no fields locked by user
+ final NotificationChannel channel =
+ new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
+ channel.setSound(new Uri.Builder().scheme("test").build(), mAudioAttributes);
+ channel.enableLights(true);
+ channel.setBypassDnd(true);
+ channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
+
+ mHelper.createNotificationChannel(PKG, UID, channel, false, false);
+
+ // same id, try to update all fields
+ final NotificationChannel channel2 =
+ new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_HIGH);
+ channel2.setSound(new Uri.Builder().scheme("test2").build(), mAudioAttributes);
+ channel2.enableLights(false);
+ channel2.setBypassDnd(false);
+ channel2.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
+
+ mHelper.updateNotificationChannel(PKG, UID, channel2, true);
+
+ // all fields should be changed
+ assertEquals(channel2, mHelper.getNotificationChannel(PKG, UID, channel.getId(), false));
+
+ verify(mHandler, times(1)).requestSort();
+ }
+
+ @Test
+ public void testUpdate_preUpgrade_updatesAppFields() throws Exception {
+ mHelper.setImportance(PKG, UID, IMPORTANCE_UNSPECIFIED);
+ assertTrue(mHelper.canShowBadge(PKG, UID));
+ assertEquals(Notification.PRIORITY_DEFAULT, mHelper.getPackagePriority(PKG, UID));
+ assertEquals(NotificationManager.VISIBILITY_NO_OVERRIDE,
+ mHelper.getPackageVisibility(PKG, UID));
+ assertFalse(mHelper.getIsAppImportanceLocked(PKG, UID));
+
+ NotificationChannel defaultChannel = mHelper.getNotificationChannel(
+ PKG, UID, NotificationChannel.DEFAULT_CHANNEL_ID, false);
+
+ defaultChannel.setShowBadge(false);
+ defaultChannel.setImportance(IMPORTANCE_NONE);
+ defaultChannel.setBypassDnd(true);
+ defaultChannel.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
+
+ mHelper.setAppImportanceLocked(PKG, UID);
+ mHelper.updateNotificationChannel(PKG, UID, defaultChannel, true);
+
+ // ensure app level fields are changed
+ assertFalse(mHelper.canShowBadge(PKG, UID));
+ assertEquals(Notification.PRIORITY_MAX, mHelper.getPackagePriority(PKG, UID));
+ assertEquals(Notification.VISIBILITY_SECRET, mHelper.getPackageVisibility(PKG, UID));
+ assertEquals(IMPORTANCE_NONE, mHelper.getImportance(PKG, UID));
+ assertTrue(mHelper.getIsAppImportanceLocked(PKG, UID));
+ }
+
+ @Test
+ public void testUpdate_postUpgrade_noUpdateAppFields() throws Exception {
+ final NotificationChannel channel = new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
+
+ mHelper.createNotificationChannel(PKG, UID, channel, false, false);
+ assertTrue(mHelper.canShowBadge(PKG, UID));
+ assertEquals(Notification.PRIORITY_DEFAULT, mHelper.getPackagePriority(PKG, UID));
+ assertEquals(NotificationManager.VISIBILITY_NO_OVERRIDE,
+ mHelper.getPackageVisibility(PKG, UID));
+
+ channel.setShowBadge(false);
+ channel.setImportance(IMPORTANCE_NONE);
+ channel.setBypassDnd(true);
+ channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
+
+ mHelper.updateNotificationChannel(PKG, UID, channel, true);
+
+ // ensure app level fields are not changed
+ assertTrue(mHelper.canShowBadge(PKG, UID));
+ assertEquals(Notification.PRIORITY_DEFAULT, mHelper.getPackagePriority(PKG, UID));
+ assertEquals(NotificationManager.VISIBILITY_NO_OVERRIDE,
+ mHelper.getPackageVisibility(PKG, UID));
+ assertEquals(NotificationManager.IMPORTANCE_UNSPECIFIED, mHelper.getImportance(PKG, UID));
+ }
+
+ @Test
+ public void testGetNotificationChannel_ReturnsNullForUnknownChannel() throws Exception {
+ assertEquals(null, mHelper.getNotificationChannel(PKG, UID, "garbage", false));
+ }
+
+ @Test
+ public void testCreateChannel_CannotChangeHiddenFields() throws Exception {
+ final NotificationChannel channel =
+ new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
+ channel.setSound(new Uri.Builder().scheme("test").build(), mAudioAttributes);
+ channel.enableLights(true);
+ channel.setBypassDnd(true);
+ channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
+ channel.setShowBadge(true);
+ int lockMask = 0;
+ for (int i = 0; i < NotificationChannel.LOCKABLE_FIELDS.length; i++) {
+ lockMask |= NotificationChannel.LOCKABLE_FIELDS[i];
+ }
+ channel.lockFields(lockMask);
+
+ mHelper.createNotificationChannel(PKG, UID, channel, true, false);
+
+ NotificationChannel savedChannel =
+ mHelper.getNotificationChannel(PKG, UID, channel.getId(), false);
+
+ assertEquals(channel.getName(), savedChannel.getName());
+ assertEquals(channel.shouldShowLights(), savedChannel.shouldShowLights());
+ assertFalse(savedChannel.canBypassDnd());
+ assertFalse(Notification.VISIBILITY_SECRET == savedChannel.getLockscreenVisibility());
+ assertEquals(channel.canShowBadge(), savedChannel.canShowBadge());
+
+ verify(mHandler, never()).requestSort();
+ }
+
+ @Test
+ public void testCreateChannel_CannotChangeHiddenFieldsAssistant() throws Exception {
+ final NotificationChannel channel =
+ new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
+ channel.setSound(new Uri.Builder().scheme("test").build(), mAudioAttributes);
+ channel.enableLights(true);
+ channel.setBypassDnd(true);
+ channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
+ channel.setShowBadge(true);
+ int lockMask = 0;
+ for (int i = 0; i < NotificationChannel.LOCKABLE_FIELDS.length; i++) {
+ lockMask |= NotificationChannel.LOCKABLE_FIELDS[i];
+ }
+ channel.lockFields(lockMask);
+
+ mHelper.createNotificationChannel(PKG, UID, channel, true, false);
+
+ NotificationChannel savedChannel =
+ mHelper.getNotificationChannel(PKG, UID, channel.getId(), false);
+
+ assertEquals(channel.getName(), savedChannel.getName());
+ assertEquals(channel.shouldShowLights(), savedChannel.shouldShowLights());
+ assertFalse(savedChannel.canBypassDnd());
+ assertFalse(Notification.VISIBILITY_SECRET == savedChannel.getLockscreenVisibility());
+ assertEquals(channel.canShowBadge(), savedChannel.canShowBadge());
+ }
+
+ @Test
+ public void testClearLockedFields() throws Exception {
+ final NotificationChannel channel = getChannel();
+ mHelper.clearLockedFields(channel);
+ assertEquals(0, channel.getUserLockedFields());
+
+ channel.lockFields(NotificationChannel.USER_LOCKED_PRIORITY
+ | NotificationChannel.USER_LOCKED_IMPORTANCE);
+ mHelper.clearLockedFields(channel);
+ assertEquals(0, channel.getUserLockedFields());
+ }
+
+ @Test
+ public void testLockFields_soundAndVibration() throws Exception {
+ mHelper.createNotificationChannel(PKG, UID, getChannel(), true, false);
+
+ final NotificationChannel update1 = getChannel();
+ update1.setSound(new Uri.Builder().scheme("test").build(),
+ new AudioAttributes.Builder().build());
+ update1.lockFields(NotificationChannel.USER_LOCKED_PRIORITY);
+ mHelper.updateNotificationChannel(PKG, UID, update1, true);
+ assertEquals(NotificationChannel.USER_LOCKED_PRIORITY
+ | NotificationChannel.USER_LOCKED_SOUND,
+ mHelper.getNotificationChannel(PKG, UID, update1.getId(), false)
+ .getUserLockedFields());
+
+ NotificationChannel update2 = getChannel();
+ update2.enableVibration(true);
+ mHelper.updateNotificationChannel(PKG, UID, update2, true);
+ assertEquals(NotificationChannel.USER_LOCKED_PRIORITY
+ | NotificationChannel.USER_LOCKED_SOUND
+ | NotificationChannel.USER_LOCKED_VIBRATION,
+ mHelper.getNotificationChannel(PKG, UID, update2.getId(), false)
+ .getUserLockedFields());
+ }
+
+ @Test
+ public void testLockFields_vibrationAndLights() throws Exception {
+ mHelper.createNotificationChannel(PKG, UID, getChannel(), true, false);
+
+ final NotificationChannel update1 = getChannel();
+ update1.setVibrationPattern(new long[]{7945, 46 ,246});
+ mHelper.updateNotificationChannel(PKG, UID, update1, true);
+ assertEquals(NotificationChannel.USER_LOCKED_VIBRATION,
+ mHelper.getNotificationChannel(PKG, UID, update1.getId(), false)
+ .getUserLockedFields());
+
+ final NotificationChannel update2 = getChannel();
+ update2.enableLights(true);
+ mHelper.updateNotificationChannel(PKG, UID, update2, true);
+ assertEquals(NotificationChannel.USER_LOCKED_VIBRATION
+ | NotificationChannel.USER_LOCKED_LIGHTS,
+ mHelper.getNotificationChannel(PKG, UID, update2.getId(), false)
+ .getUserLockedFields());
+ }
+
+ @Test
+ public void testLockFields_lightsAndImportance() throws Exception {
+ mHelper.createNotificationChannel(PKG, UID, getChannel(), true, false);
+
+ final NotificationChannel update1 = getChannel();
+ update1.setLightColor(Color.GREEN);
+ mHelper.updateNotificationChannel(PKG, UID, update1, true);
+ assertEquals(NotificationChannel.USER_LOCKED_LIGHTS,
+ mHelper.getNotificationChannel(PKG, UID, update1.getId(), false)
+ .getUserLockedFields());
+
+ final NotificationChannel update2 = getChannel();
+ update2.setImportance(IMPORTANCE_DEFAULT);
+ mHelper.updateNotificationChannel(PKG, UID, update2, true);
+ assertEquals(NotificationChannel.USER_LOCKED_LIGHTS
+ | NotificationChannel.USER_LOCKED_IMPORTANCE,
+ mHelper.getNotificationChannel(PKG, UID, update2.getId(), false)
+ .getUserLockedFields());
+ }
+
+ @Test
+ public void testLockFields_visibilityAndDndAndBadge() throws Exception {
+ mHelper.createNotificationChannel(PKG, UID, getChannel(), true, false);
+ assertEquals(0,
+ mHelper.getNotificationChannel(PKG, UID, getChannel().getId(), false)
+ .getUserLockedFields());
+
+ final NotificationChannel update1 = getChannel();
+ update1.setBypassDnd(true);
+ mHelper.updateNotificationChannel(PKG, UID, update1, true);
+ assertEquals(NotificationChannel.USER_LOCKED_PRIORITY,
+ mHelper.getNotificationChannel(PKG, UID, update1.getId(), false)
+ .getUserLockedFields());
+
+ final NotificationChannel update2 = getChannel();
+ update2.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
+ mHelper.updateNotificationChannel(PKG, UID, update2, true);
+ assertEquals(NotificationChannel.USER_LOCKED_PRIORITY
+ | NotificationChannel.USER_LOCKED_VISIBILITY,
+ mHelper.getNotificationChannel(PKG, UID, update2.getId(), false)
+ .getUserLockedFields());
+
+ final NotificationChannel update3 = getChannel();
+ update3.setShowBadge(false);
+ mHelper.updateNotificationChannel(PKG, UID, update3, true);
+ assertEquals(NotificationChannel.USER_LOCKED_PRIORITY
+ | NotificationChannel.USER_LOCKED_VISIBILITY
+ | NotificationChannel.USER_LOCKED_SHOW_BADGE,
+ mHelper.getNotificationChannel(PKG, UID, update3.getId(), false)
+ .getUserLockedFields());
+ }
+
+ @Test
+ public void testDeleteNonExistentChannel() throws Exception {
+ mHelper.deleteNotificationChannelGroup(PKG, UID, "does not exist");
+ }
+
+ @Test
+ public void testGetDeletedChannel() throws Exception {
+ NotificationChannel channel = getChannel();
+ channel.setSound(new Uri.Builder().scheme("test").build(), mAudioAttributes);
+ channel.enableLights(true);
+ channel.setBypassDnd(true);
+ channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
+ channel.enableVibration(true);
+ channel.setVibrationPattern(new long[]{100, 67, 145, 156});
+
+ mHelper.createNotificationChannel(PKG, UID, channel, true, false);
+ mHelper.deleteNotificationChannel(PKG, UID, channel.getId());
+
+ // Does not return deleted channel
+ NotificationChannel response =
+ mHelper.getNotificationChannel(PKG, UID, channel.getId(), false);
+ assertNull(response);
+
+ // Returns deleted channel
+ response = mHelper.getNotificationChannel(PKG, UID, channel.getId(), true);
+ compareChannels(channel, response);
+ assertTrue(response.isDeleted());
+ }
+
+ @Test
+ public void testGetDeletedChannels() throws Exception {
+ Map<String, NotificationChannel> channelMap = new HashMap<>();
+ NotificationChannel channel =
+ new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
+ channel.setSound(new Uri.Builder().scheme("test").build(), mAudioAttributes);
+ channel.enableLights(true);
+ channel.setBypassDnd(true);
+ channel.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
+ channel.enableVibration(true);
+ channel.setVibrationPattern(new long[]{100, 67, 145, 156});
+ channelMap.put(channel.getId(), channel);
+ NotificationChannel channel2 =
+ new NotificationChannel("id4", "a", NotificationManager.IMPORTANCE_HIGH);
+ channelMap.put(channel2.getId(), channel2);
+ mHelper.createNotificationChannel(PKG, UID, channel, true, false);
+ mHelper.createNotificationChannel(PKG, UID, channel2, true, false);
+
+ mHelper.deleteNotificationChannel(PKG, UID, channel.getId());
+
+ // Returns only non-deleted channels
+ List<NotificationChannel> channels =
+ mHelper.getNotificationChannels(PKG, UID, false).getList();
+ assertEquals(2, channels.size()); // Default channel + non-deleted channel
+ for (NotificationChannel nc : channels) {
+ if (!NotificationChannel.DEFAULT_CHANNEL_ID.equals(nc.getId())) {
+ compareChannels(channel2, nc);
+ }
+ }
+
+ // Returns deleted channels too
+ channels = mHelper.getNotificationChannels(PKG, UID, true).getList();
+ assertEquals(3, channels.size()); // Includes default channel
+ for (NotificationChannel nc : channels) {
+ if (!NotificationChannel.DEFAULT_CHANNEL_ID.equals(nc.getId())) {
+ compareChannels(channelMap.get(nc.getId()), nc);
+ }
+ }
+ }
+
+ @Test
+ public void testGetDeletedChannelCount() throws Exception {
+ NotificationChannel channel =
+ new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
+ NotificationChannel channel2 =
+ new NotificationChannel("id4", "a", NotificationManager.IMPORTANCE_HIGH);
+ NotificationChannel channel3 =
+ new NotificationChannel("id5", "a", NotificationManager.IMPORTANCE_HIGH);
+ mHelper.createNotificationChannel(PKG, UID, channel, true, false);
+ mHelper.createNotificationChannel(PKG, UID, channel2, true, false);
+ mHelper.createNotificationChannel(PKG, UID, channel3, true, false);
+
+ mHelper.deleteNotificationChannel(PKG, UID, channel.getId());
+ mHelper.deleteNotificationChannel(PKG, UID, channel3.getId());
+
+ assertEquals(2, mHelper.getDeletedChannelCount(PKG, UID));
+ assertEquals(0, mHelper.getDeletedChannelCount("pkg2", UID2));
+ }
+
+ @Test
+ public void testGetBlockedChannelCount() throws Exception {
+ NotificationChannel channel =
+ new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
+ NotificationChannel channel2 =
+ new NotificationChannel("id4", "a", NotificationManager.IMPORTANCE_NONE);
+ NotificationChannel channel3 =
+ new NotificationChannel("id5", "a", NotificationManager.IMPORTANCE_NONE);
+ mHelper.createNotificationChannel(PKG, UID, channel, true, false);
+ mHelper.createNotificationChannel(PKG, UID, channel2, true, false);
+ mHelper.createNotificationChannel(PKG, UID, channel3, true, false);
+
+ mHelper.deleteNotificationChannel(PKG, UID, channel3.getId());
+
+ assertEquals(1, mHelper.getBlockedChannelCount(PKG, UID));
+ assertEquals(0, mHelper.getBlockedChannelCount("pkg2", UID2));
+ }
+
+ @Test
+ public void testCreateAndDeleteCanChannelsBypassDnd() throws Exception {
+ // create notification channel that can't bypass dnd
+ // expected result: areChannelsBypassingDnd = false
+ // setNotificationPolicy isn't called since areChannelsBypassingDnd was already false
+ NotificationChannel channel = new NotificationChannel("id1", "name1", IMPORTANCE_LOW);
+ mHelper.createNotificationChannel(PKG, UID, channel, true, false);
+ assertFalse(mHelper.areChannelsBypassingDnd());
+ verify(mMockZenModeHelper, never()).setNotificationPolicy(any());
+ resetZenModeHelper();
+
+ // create notification channel that can bypass dnd
+ // expected result: areChannelsBypassingDnd = true
+ NotificationChannel channel2 = new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
+ channel2.setBypassDnd(true);
+ mHelper.createNotificationChannel(PKG, UID, channel2, true, true);
+ assertTrue(mHelper.areChannelsBypassingDnd());
+ verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any());
+ resetZenModeHelper();
+
+ // delete channels
+ mHelper.deleteNotificationChannel(PKG, UID, channel.getId());
+ assertTrue(mHelper.areChannelsBypassingDnd()); // channel2 can still bypass DND
+ verify(mMockZenModeHelper, never()).setNotificationPolicy(any());
+ resetZenModeHelper();
+
+ mHelper.deleteNotificationChannel(PKG, UID, channel2.getId());
+ assertFalse(mHelper.areChannelsBypassingDnd());
+ verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any());
+ resetZenModeHelper();
+ }
+
+ @Test
+ public void testUpdateCanChannelsBypassDnd() throws Exception {
+ // create notification channel that can't bypass dnd
+ // expected result: areChannelsBypassingDnd = false
+ // setNotificationPolicy isn't called since areChannelsBypassingDnd was already false
+ NotificationChannel channel = new NotificationChannel("id1", "name1", IMPORTANCE_LOW);
+ mHelper.createNotificationChannel(PKG, UID, channel, true, false);
+ assertFalse(mHelper.areChannelsBypassingDnd());
+ verify(mMockZenModeHelper, never()).setNotificationPolicy(any());
+ resetZenModeHelper();
+
+ // update channel so it CAN bypass dnd:
+ // expected result: areChannelsBypassingDnd = true
+ channel.setBypassDnd(true);
+ mHelper.updateNotificationChannel(PKG, UID, channel, true);
+ assertTrue(mHelper.areChannelsBypassingDnd());
+ verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any());
+ resetZenModeHelper();
+
+ // update channel so it can't bypass dnd:
+ // expected result: areChannelsBypassingDnd = false
+ channel.setBypassDnd(false);
+ mHelper.updateNotificationChannel(PKG, UID, channel, true);
+ assertFalse(mHelper.areChannelsBypassingDnd());
+ verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any());
+ resetZenModeHelper();
+ }
+
+ @Test
+ public void testSetupNewZenModeHelper_canBypass() {
+ // start notification policy off with mAreChannelsBypassingDnd = true, but
+ // RankingHelper should change to false
+ mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0,
+ NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND);
+ when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy);
+ mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper);
+ assertFalse(mHelper.areChannelsBypassingDnd());
+ verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any());
+ resetZenModeHelper();
+ }
+
+ @Test
+ public void testSetupNewZenModeHelper_cannotBypass() {
+ // start notification policy off with mAreChannelsBypassingDnd = false
+ mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0, 0);
+ when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy);
+ mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper);
+ assertFalse(mHelper.areChannelsBypassingDnd());
+ verify(mMockZenModeHelper, never()).setNotificationPolicy(any());
+ resetZenModeHelper();
+ }
+
+ @Test
+ public void testCreateDeletedChannel() throws Exception {
+ long[] vibration = new long[]{100, 67, 145, 156};
+ NotificationChannel channel =
+ new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
+ channel.setVibrationPattern(vibration);
+
+ mHelper.createNotificationChannel(PKG, UID, channel, true, false);
+ mHelper.deleteNotificationChannel(PKG, UID, channel.getId());
+
+ NotificationChannel newChannel = new NotificationChannel(
+ channel.getId(), channel.getName(), NotificationManager.IMPORTANCE_HIGH);
+ newChannel.setVibrationPattern(new long[]{100});
+
+ mHelper.createNotificationChannel(PKG, UID, newChannel, true, false);
+
+ // No long deleted, using old settings
+ compareChannels(channel,
+ mHelper.getNotificationChannel(PKG, UID, newChannel.getId(), false));
+ }
+
+ @Test
+ public void testOnlyHasDefaultChannel() throws Exception {
+ assertTrue(mHelper.onlyHasDefaultChannel(PKG, UID));
+ assertFalse(mHelper.onlyHasDefaultChannel(UPDATED_PKG, UID2));
+
+ mHelper.createNotificationChannel(PKG, UID, getChannel(), true, false);
+ assertFalse(mHelper.onlyHasDefaultChannel(PKG, UID));
+ }
+
+ @Test
+ public void testCreateChannel_defaultChannelId() throws Exception {
+ try {
+ mHelper.createNotificationChannel(PKG, UID, new NotificationChannel(
+ NotificationChannel.DEFAULT_CHANNEL_ID, "ha", IMPORTANCE_HIGH), true, false);
+ fail("Allowed to create default channel");
+ } catch (IllegalArgumentException e) {
+ // pass
+ }
+ }
+
+ @Test
+ public void testCreateChannel_alreadyExists() throws Exception {
+ long[] vibration = new long[]{100, 67, 145, 156};
+ NotificationChannel channel =
+ new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
+ channel.setVibrationPattern(vibration);
+
+ mHelper.createNotificationChannel(PKG, UID, channel, true, false);
+
+ NotificationChannel newChannel = new NotificationChannel(
+ channel.getId(), channel.getName(), NotificationManager.IMPORTANCE_HIGH);
+ newChannel.setVibrationPattern(new long[]{100});
+
+ mHelper.createNotificationChannel(PKG, UID, newChannel, true, false);
+
+ // Old settings not overridden
+ compareChannels(channel,
+ mHelper.getNotificationChannel(PKG, UID, newChannel.getId(), false));
+ }
+
+ @Test
+ public void testCreateChannel_noOverrideSound() throws Exception {
+ Uri sound = new Uri.Builder().scheme("test").build();
+ final NotificationChannel channel = new NotificationChannel("id2", "name2",
+ NotificationManager.IMPORTANCE_DEFAULT);
+ channel.setSound(sound, mAudioAttributes);
+ mHelper.createNotificationChannel(PKG, UID, channel, true, false);
+ assertEquals(sound, mHelper.getNotificationChannel(
+ PKG, UID, channel.getId(), false).getSound());
+ }
+
+ @Test
+ public void testPermanentlyDeleteChannels() throws Exception {
+ NotificationChannel channel1 =
+ new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
+ NotificationChannel channel2 =
+ new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
+
+ mHelper.createNotificationChannel(PKG, UID, channel1, true, false);
+ mHelper.createNotificationChannel(PKG, UID, channel2, false, false);
+
+ mHelper.permanentlyDeleteNotificationChannels(PKG, UID);
+
+ // Only default channel remains
+ assertEquals(1, mHelper.getNotificationChannels(PKG, UID, true).getList().size());
+ }
+
+ @Test
+ public void testDeleteGroup() throws Exception {
+ NotificationChannelGroup notDeleted = new NotificationChannelGroup("not", "deleted");
+ NotificationChannelGroup deleted = new NotificationChannelGroup("totally", "deleted");
+ NotificationChannel nonGroupedNonDeletedChannel =
+ new NotificationChannel("no group", "so not deleted", IMPORTANCE_HIGH);
+ NotificationChannel groupedButNotDeleted =
+ new NotificationChannel("not deleted", "belongs to notDeleted", IMPORTANCE_DEFAULT);
+ groupedButNotDeleted.setGroup("not");
+ NotificationChannel groupedAndDeleted =
+ new NotificationChannel("deleted", "belongs to deleted", IMPORTANCE_DEFAULT);
+ groupedAndDeleted.setGroup("totally");
+
+ mHelper.createNotificationChannelGroup(PKG, UID, notDeleted, true);
+ mHelper.createNotificationChannelGroup(PKG, UID, deleted, true);
+ mHelper.createNotificationChannel(PKG, UID, nonGroupedNonDeletedChannel, true, false);
+ mHelper.createNotificationChannel(PKG, UID, groupedAndDeleted, true, false);
+ mHelper.createNotificationChannel(PKG, UID, groupedButNotDeleted, true, false);
+
+ mHelper.deleteNotificationChannelGroup(PKG, UID, deleted.getId());
+
+ assertNull(mHelper.getNotificationChannelGroup(deleted.getId(), PKG, UID));
+ assertNotNull(mHelper.getNotificationChannelGroup(notDeleted.getId(), PKG, UID));
+
+ assertNull(mHelper.getNotificationChannel(PKG, UID, groupedAndDeleted.getId(), false));
+ compareChannels(groupedAndDeleted,
+ mHelper.getNotificationChannel(PKG, UID, groupedAndDeleted.getId(), true));
+
+ compareChannels(groupedButNotDeleted,
+ mHelper.getNotificationChannel(PKG, UID, groupedButNotDeleted.getId(), false));
+ compareChannels(nonGroupedNonDeletedChannel, mHelper.getNotificationChannel(
+ PKG, UID, nonGroupedNonDeletedChannel.getId(), false));
+
+ // notDeleted
+ assertEquals(1, mHelper.getNotificationChannelGroups(PKG, UID).size());
+
+ verify(mHandler, never()).requestSort();
+ }
+
+ @Test
+ public void testOnUserRemoved() throws Exception {
+ int[] user0Uids = {98, 235, 16, 3782};
+ int[] user1Uids = new int[user0Uids.length];
+ for (int i = 0; i < user0Uids.length; i++) {
+ user1Uids[i] = UserHandle.PER_USER_RANGE + user0Uids[i];
+
+ final ApplicationInfo legacy = new ApplicationInfo();
+ legacy.targetSdkVersion = Build.VERSION_CODES.N_MR1;
+ when(mPm.getApplicationInfoAsUser(eq(PKG), anyInt(), anyInt())).thenReturn(legacy);
+
+ // create records with the default channel for all user 0 and user 1 uids
+ mHelper.getImportance(PKG, user0Uids[i]);
+ mHelper.getImportance(PKG, user1Uids[i]);
+ }
+
+ mHelper.onUserRemoved(1);
+
+ // user 0 records remain
+ for (int i = 0; i < user0Uids.length; i++) {
+ assertEquals(1,
+ mHelper.getNotificationChannels(PKG, user0Uids[i], false).getList().size());
+ }
+ // user 1 records are gone
+ for (int i = 0; i < user1Uids.length; i++) {
+ assertEquals(0,
+ mHelper.getNotificationChannels(PKG, user1Uids[i], false).getList().size());
+ }
+ }
+
+ @Test
+ public void testOnPackageChanged_packageRemoval() throws Exception {
+ // Deleted
+ NotificationChannel channel1 =
+ new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
+ mHelper.createNotificationChannel(PKG, UID, channel1, true, false);
+
+ mHelper.onPackagesChanged(true, UserHandle.USER_SYSTEM, new String[]{PKG}, new int[]{UID});
+
+ assertEquals(0, mHelper.getNotificationChannels(PKG, UID, true).getList().size());
+
+ // Not deleted
+ mHelper.createNotificationChannel(PKG, UID, channel1, true, false);
+
+ mHelper.onPackagesChanged(false, UserHandle.USER_SYSTEM, new String[]{PKG}, new int[]{UID});
+ assertEquals(2, mHelper.getNotificationChannels(PKG, UID, false).getList().size());
+ }
+
+ @Test
+ public void testOnPackageChanged_packageRemoval_importance() throws Exception {
+ mHelper.setImportance(PKG, UID, NotificationManager.IMPORTANCE_HIGH);
+
+ mHelper.onPackagesChanged(true, UserHandle.USER_SYSTEM, new String[]{PKG}, new int[]{UID});
+
+ assertEquals(NotificationManager.IMPORTANCE_UNSPECIFIED, mHelper.getImportance(PKG, UID));
+ }
+
+ @Test
+ public void testOnPackageChanged_packageRemoval_groups() throws Exception {
+ NotificationChannelGroup ncg = new NotificationChannelGroup("group1", "name1");
+ mHelper.createNotificationChannelGroup(PKG, UID, ncg, true);
+ NotificationChannelGroup ncg2 = new NotificationChannelGroup("group2", "name2");
+ mHelper.createNotificationChannelGroup(PKG, UID, ncg2, true);
+
+ mHelper.onPackagesChanged(true, UserHandle.USER_SYSTEM, new String[]{PKG}, new int[]{UID});
+
+ assertEquals(0,
+ mHelper.getNotificationChannelGroups(PKG, UID, true, true).getList().size());
+ }
+
+ @Test
+ public void testOnPackageChange_downgradeTargetSdk() throws Exception {
+ // create channel as api 26
+ mHelper.createNotificationChannel(UPDATED_PKG, UID2, getChannel(), true, false);
+
+ // install new app version targeting 25
+ final ApplicationInfo legacy = new ApplicationInfo();
+ legacy.targetSdkVersion = Build.VERSION_CODES.N_MR1;
+ when(mPm.getApplicationInfoAsUser(eq(UPDATED_PKG), anyInt(), anyInt())).thenReturn(legacy);
+ mHelper.onPackagesChanged(
+ false, UserHandle.USER_SYSTEM, new String[]{UPDATED_PKG}, new int[]{UID2});
+
+ // make sure the default channel was readded
+ //assertEquals(2, mHelper.getNotificationChannels(UPDATED_PKG, UID2, false).getList().size());
+ assertNotNull(mHelper.getNotificationChannel(
+ UPDATED_PKG, UID2, NotificationChannel.DEFAULT_CHANNEL_ID, false));
+ }
+
+ @Test
+ public void testRecordDefaults() throws Exception {
+ assertEquals(NotificationManager.IMPORTANCE_UNSPECIFIED, mHelper.getImportance(PKG, UID));
+ assertEquals(true, mHelper.canShowBadge(PKG, UID));
+ assertEquals(1, mHelper.getNotificationChannels(PKG, UID, false).getList().size());
+ }
+
+ @Test
+ public void testCreateGroup() throws Exception {
+ NotificationChannelGroup ncg = new NotificationChannelGroup("group1", "name1");
+ mHelper.createNotificationChannelGroup(PKG, UID, ncg, true);
+ assertEquals(ncg, mHelper.getNotificationChannelGroups(PKG, UID).iterator().next());
+ verify(mHandler, never()).requestSort();
+ }
+
+ @Test
+ public void testCannotCreateChannel_badGroup() throws Exception {
+ NotificationChannel channel1 =
+ new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
+ channel1.setGroup("garbage");
+ try {
+ mHelper.createNotificationChannel(PKG, UID, channel1, true, false);
+ fail("Created a channel with a bad group");
+ } catch (IllegalArgumentException e) {
+ }
+ }
+
+ @Test
+ public void testCannotCreateChannel_goodGroup() throws Exception {
+ NotificationChannelGroup ncg = new NotificationChannelGroup("group1", "name1");
+ mHelper.createNotificationChannelGroup(PKG, UID, ncg, true);
+ NotificationChannel channel1 =
+ new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
+ channel1.setGroup(ncg.getId());
+ mHelper.createNotificationChannel(PKG, UID, channel1, true, false);
+
+ assertEquals(ncg.getId(),
+ mHelper.getNotificationChannel(PKG, UID, channel1.getId(), false).getGroup());
+ }
+
+ @Test
+ public void testGetChannelGroups() throws Exception {
+ NotificationChannelGroup unused = new NotificationChannelGroup("unused", "s");
+ mHelper.createNotificationChannelGroup(PKG, UID, unused, true);
+ NotificationChannelGroup ncg = new NotificationChannelGroup("group1", "name1");
+ mHelper.createNotificationChannelGroup(PKG, UID, ncg, true);
+ NotificationChannelGroup ncg2 = new NotificationChannelGroup("group2", "name2");
+ mHelper.createNotificationChannelGroup(PKG, UID, ncg2, true);
+
+ NotificationChannel channel1 =
+ new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
+ channel1.setGroup(ncg.getId());
+ mHelper.createNotificationChannel(PKG, UID, channel1, true, false);
+ NotificationChannel channel1a =
+ new NotificationChannel("id1a", "name1", NotificationManager.IMPORTANCE_HIGH);
+ channel1a.setGroup(ncg.getId());
+ mHelper.createNotificationChannel(PKG, UID, channel1a, true, false);
+
+ NotificationChannel channel2 =
+ new NotificationChannel("id2", "name1", NotificationManager.IMPORTANCE_HIGH);
+ channel2.setGroup(ncg2.getId());
+ mHelper.createNotificationChannel(PKG, UID, channel2, true, false);
+
+ NotificationChannel channel3 =
+ new NotificationChannel("id3", "name1", NotificationManager.IMPORTANCE_HIGH);
+ mHelper.createNotificationChannel(PKG, UID, channel3, true, false);
+
+ List<NotificationChannelGroup> actual =
+ mHelper.getNotificationChannelGroups(PKG, UID, true, true).getList();
+ assertEquals(3, actual.size());
+ for (NotificationChannelGroup group : actual) {
+ if (group.getId() == null) {
+ assertEquals(2, group.getChannels().size()); // misc channel too
+ assertTrue(channel3.getId().equals(group.getChannels().get(0).getId())
+ || channel3.getId().equals(group.getChannels().get(1).getId()));
+ } else if (group.getId().equals(ncg.getId())) {
+ assertEquals(2, group.getChannels().size());
+ if (group.getChannels().get(0).getId().equals(channel1.getId())) {
+ assertTrue(group.getChannels().get(1).getId().equals(channel1a.getId()));
+ } else if (group.getChannels().get(0).getId().equals(channel1a.getId())) {
+ assertTrue(group.getChannels().get(1).getId().equals(channel1.getId()));
+ } else {
+ fail("expected channel not found");
+ }
+ } else if (group.getId().equals(ncg2.getId())) {
+ assertEquals(1, group.getChannels().size());
+ assertEquals(channel2.getId(), group.getChannels().get(0).getId());
+ }
+ }
+ }
+
+ @Test
+ public void testGetChannelGroups_noSideEffects() throws Exception {
+ NotificationChannelGroup ncg = new NotificationChannelGroup("group1", "name1");
+ mHelper.createNotificationChannelGroup(PKG, UID, ncg, true);
+
+ NotificationChannel channel1 =
+ new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
+ channel1.setGroup(ncg.getId());
+ mHelper.createNotificationChannel(PKG, UID, channel1, true, false);
+ mHelper.getNotificationChannelGroups(PKG, UID, true, true).getList();
+
+ channel1.setImportance(IMPORTANCE_LOW);
+ mHelper.updateNotificationChannel(PKG, UID, channel1, true);
+
+ List<NotificationChannelGroup> actual =
+ mHelper.getNotificationChannelGroups(PKG, UID, true, true).getList();
+
+ assertEquals(2, actual.size());
+ for (NotificationChannelGroup group : actual) {
+ if (Objects.equals(group.getId(), ncg.getId())) {
+ assertEquals(1, group.getChannels().size());
+ }
+ }
+ }
+
+ @Test
+ public void testCreateChannel_updateName() throws Exception {
+ NotificationChannel nc = new NotificationChannel("id", "hello", IMPORTANCE_DEFAULT);
+ mHelper.createNotificationChannel(PKG, UID, nc, true, false);
+ NotificationChannel actual = mHelper.getNotificationChannel(PKG, UID, "id", false);
+ assertEquals("hello", actual.getName());
+
+ nc = new NotificationChannel("id", "goodbye", IMPORTANCE_HIGH);
+ mHelper.createNotificationChannel(PKG, UID, nc, true, false);
+
+ actual = mHelper.getNotificationChannel(PKG, UID, "id", false);
+ assertEquals("goodbye", actual.getName());
+ assertEquals(IMPORTANCE_DEFAULT, actual.getImportance());
+
+ verify(mHandler, times(1)).requestSort();
+ }
+
+ @Test
+ public void testCreateChannel_addToGroup() throws Exception {
+ NotificationChannelGroup group = new NotificationChannelGroup("group", "");
+ mHelper.createNotificationChannelGroup(PKG, UID, group, true);
+ NotificationChannel nc = new NotificationChannel("id", "hello", IMPORTANCE_DEFAULT);
+ mHelper.createNotificationChannel(PKG, UID, nc, true, false);
+ NotificationChannel actual = mHelper.getNotificationChannel(PKG, UID, "id", false);
+ assertNull(actual.getGroup());
+
+ nc = new NotificationChannel("id", "hello", IMPORTANCE_HIGH);
+ nc.setGroup(group.getId());
+ mHelper.createNotificationChannel(PKG, UID, nc, true, false);
+
+ actual = mHelper.getNotificationChannel(PKG, UID, "id", false);
+ assertNotNull(actual.getGroup());
+ assertEquals(IMPORTANCE_DEFAULT, actual.getImportance());
+
+ verify(mHandler, times(1)).requestSort();
+ }
+
+ @Test
+ public void testDumpChannelsJson() throws Exception {
+ final ApplicationInfo upgrade = new ApplicationInfo();
+ upgrade.targetSdkVersion = Build.VERSION_CODES.O;
+ try {
+ when(mPm.getApplicationInfoAsUser(
+ anyString(), anyInt(), anyInt())).thenReturn(upgrade);
+ } catch (PackageManager.NameNotFoundException e) {
+ }
+ ArrayMap<String, Integer> expectedChannels = new ArrayMap<>();
+ int numPackages = ThreadLocalRandom.current().nextInt(1, 5);
+ for (int i = 0; i < numPackages; i++) {
+ String pkgName = "pkg" + i;
+ int numChannels = ThreadLocalRandom.current().nextInt(1, 10);
+ for (int j = 0; j < numChannels; j++) {
+ mHelper.createNotificationChannel(pkgName, UID,
+ new NotificationChannel("" + j, "a", IMPORTANCE_HIGH), true, false);
+ }
+ expectedChannels.put(pkgName, numChannels);
+ }
+
+ // delete the first channel of the first package
+ String pkg = expectedChannels.keyAt(0);
+ mHelper.deleteNotificationChannel("pkg" + 0, UID, "0");
+ // dump should not include deleted channels
+ int count = expectedChannels.get(pkg);
+ expectedChannels.put(pkg, count - 1);
+
+ JSONArray actual = mHelper.dumpChannelsJson(new NotificationManagerService.DumpFilter());
+ assertEquals(numPackages, actual.length());
+ for (int i = 0; i < numPackages; i++) {
+ JSONObject object = actual.getJSONObject(i);
+ assertTrue(expectedChannels.containsKey(object.get("packageName")));
+ assertEquals(expectedChannels.get(object.get("packageName")).intValue(),
+ object.getInt("channelCount"));
+ }
+ }
+
+ @Test
+ public void testBadgingOverrideTrue() throws Exception {
+ Secure.putIntForUser(getContext().getContentResolver(),
+ Secure.NOTIFICATION_BADGING, 1,
+ USER.getIdentifier());
+ mHelper.updateBadgingEnabled(); // would be called by settings observer
+ assertTrue(mHelper.badgingEnabled(USER));
+ }
+
+ @Test
+ public void testBadgingOverrideFalse() throws Exception {
+ Secure.putIntForUser(getContext().getContentResolver(),
+ Secure.NOTIFICATION_BADGING, 0,
+ USER.getIdentifier());
+ mHelper.updateBadgingEnabled(); // would be called by settings observer
+ assertFalse(mHelper.badgingEnabled(USER));
+ }
+
+ @Test
+ public void testBadgingForUserAll() throws Exception {
+ try {
+ mHelper.badgingEnabled(UserHandle.ALL);
+ } catch (Exception e) {
+ fail("just don't throw");
+ }
+ }
+
+ @Test
+ public void testBadgingOverrideUserIsolation() throws Exception {
+ Secure.putIntForUser(getContext().getContentResolver(),
+ Secure.NOTIFICATION_BADGING, 0,
+ USER.getIdentifier());
+ Secure.putIntForUser(getContext().getContentResolver(),
+ Secure.NOTIFICATION_BADGING, 1,
+ USER2.getIdentifier());
+ mHelper.updateBadgingEnabled(); // would be called by settings observer
+ assertFalse(mHelper.badgingEnabled(USER));
+ assertTrue(mHelper.badgingEnabled(USER2));
+ }
+
+ @Test
+ public void testOnLocaleChanged_updatesDefaultChannels() throws Exception {
+ String newLabel = "bananas!";
+ final NotificationChannel defaultChannel = mHelper.getNotificationChannel(PKG, UID,
+ NotificationChannel.DEFAULT_CHANNEL_ID, false);
+ assertFalse(newLabel.equals(defaultChannel.getName()));
+
+ Resources res = mock(Resources.class);
+ when(mContext.getResources()).thenReturn(res);
+ when(res.getString(com.android.internal.R.string.default_notification_channel_label))
+ .thenReturn(newLabel);
+
+ mHelper.onLocaleChanged(mContext, USER.getIdentifier());
+
+ assertEquals(newLabel, mHelper.getNotificationChannel(PKG, UID,
+ NotificationChannel.DEFAULT_CHANNEL_ID, false).getName());
+ }
+
+ @Test
+ public void testIsGroupBlocked_noGroup() throws Exception {
+ assertFalse(mHelper.isGroupBlocked(PKG, UID, null));
+
+ assertFalse(mHelper.isGroupBlocked(PKG, UID, "non existent group"));
+ }
+
+ @Test
+ public void testIsGroupBlocked_notBlocked() throws Exception {
+ NotificationChannelGroup group = new NotificationChannelGroup("id", "name");
+ mHelper.createNotificationChannelGroup(PKG, UID, group, true);
+
+ assertFalse(mHelper.isGroupBlocked(PKG, UID, group.getId()));
+ }
+
+ @Test
+ public void testIsGroupBlocked_blocked() throws Exception {
+ NotificationChannelGroup group = new NotificationChannelGroup("id", "name");
+ mHelper.createNotificationChannelGroup(PKG, UID, group, true);
+ group.setBlocked(true);
+ mHelper.createNotificationChannelGroup(PKG, UID, group, false);
+
+ assertTrue(mHelper.isGroupBlocked(PKG, UID, group.getId()));
+ }
+
+ @Test
+ public void testIsGroup_appCannotResetBlock() throws Exception {
+ NotificationChannelGroup group = new NotificationChannelGroup("id", "name");
+ mHelper.createNotificationChannelGroup(PKG, UID, group, true);
+ NotificationChannelGroup group2 = group.clone();
+ group2.setBlocked(true);
+ mHelper.createNotificationChannelGroup(PKG, UID, group2, false);
+ assertTrue(mHelper.isGroupBlocked(PKG, UID, group.getId()));
+
+ NotificationChannelGroup group3 = group.clone();
+ group3.setBlocked(false);
+ mHelper.createNotificationChannelGroup(PKG, UID, group3, true);
+ assertTrue(mHelper.isGroupBlocked(PKG, UID, group.getId()));
+ }
+
+ @Test
+ public void testGetNotificationChannelGroupWithChannels() throws Exception {
+ NotificationChannelGroup group = new NotificationChannelGroup("group", "");
+ NotificationChannelGroup other = new NotificationChannelGroup("something else", "");
+ mHelper.createNotificationChannelGroup(PKG, UID, group, true);
+ mHelper.createNotificationChannelGroup(PKG, UID, other, true);
+
+ NotificationChannel a = new NotificationChannel("a", "a", IMPORTANCE_DEFAULT);
+ a.setGroup(group.getId());
+ NotificationChannel b = new NotificationChannel("b", "b", IMPORTANCE_DEFAULT);
+ b.setGroup(other.getId());
+ NotificationChannel c = new NotificationChannel("c", "c", IMPORTANCE_DEFAULT);
+ c.setGroup(group.getId());
+ NotificationChannel d = new NotificationChannel("d", "d", IMPORTANCE_DEFAULT);
+
+ mHelper.createNotificationChannel(PKG, UID, a, true, false);
+ mHelper.createNotificationChannel(PKG, UID, b, true, false);
+ mHelper.createNotificationChannel(PKG, UID, c, true, false);
+ mHelper.createNotificationChannel(PKG, UID, d, true, false);
+ mHelper.deleteNotificationChannel(PKG, UID, c.getId());
+
+ NotificationChannelGroup retrieved = mHelper.getNotificationChannelGroupWithChannels(
+ PKG, UID, group.getId(), true);
+ assertEquals(2, retrieved.getChannels().size());
+ compareChannels(a, findChannel(retrieved.getChannels(), a.getId()));
+ compareChannels(c, findChannel(retrieved.getChannels(), c.getId()));
+
+ retrieved = mHelper.getNotificationChannelGroupWithChannels(
+ PKG, UID, group.getId(), false);
+ assertEquals(1, retrieved.getChannels().size());
+ compareChannels(a, findChannel(retrieved.getChannels(), a.getId()));
+ }
+
+ @Test
+ public void testAndroidPkgCannotBypassDnd_creation() {
+ NotificationChannel test = new NotificationChannel("A", "a", IMPORTANCE_LOW);
+ test.setBypassDnd(true);
+
+ mHelper.createNotificationChannel(SYSTEM_PKG, SYSTEM_UID, test, true, false);
+
+ assertFalse(mHelper.getNotificationChannel(SYSTEM_PKG, SYSTEM_UID, "A", false)
+ .canBypassDnd());
+ }
+
+ @Test
+ public void testDndPkgCanBypassDnd_creation() {
+ NotificationChannel test = new NotificationChannel("A", "a", IMPORTANCE_LOW);
+ test.setBypassDnd(true);
+
+ mHelper.createNotificationChannel(PKG, UID, test, true, true);
+
+ assertTrue(mHelper.getNotificationChannel(PKG, UID, "A", false).canBypassDnd());
+ }
+
+ @Test
+ public void testNormalPkgCannotBypassDnd_creation() {
+ NotificationChannel test = new NotificationChannel("A", "a", IMPORTANCE_LOW);
+ test.setBypassDnd(true);
+
+ mHelper.createNotificationChannel(PKG, 1000, test, true, false);
+
+ assertFalse(mHelper.getNotificationChannel(PKG, 1000, "A", false).canBypassDnd());
+ }
+
+ @Test
+ public void testAndroidPkgCannotBypassDnd_update() throws Exception {
+ NotificationChannel test = new NotificationChannel("A", "a", IMPORTANCE_LOW);
+ mHelper.createNotificationChannel(SYSTEM_PKG, SYSTEM_UID, test, true, false);
+
+ NotificationChannel update = new NotificationChannel("A", "a", IMPORTANCE_LOW);
+ update.setBypassDnd(true);
+ mHelper.createNotificationChannel(SYSTEM_PKG, SYSTEM_UID, update, true, false);
+
+ assertFalse(mHelper.getNotificationChannel(SYSTEM_PKG, SYSTEM_UID, "A", false)
+ .canBypassDnd());
+ }
+
+ @Test
+ public void testDndPkgCanBypassDnd_update() throws Exception {
+ NotificationChannel test = new NotificationChannel("A", "a", IMPORTANCE_LOW);
+ mHelper.createNotificationChannel(PKG, UID, test, true, true);
+
+ NotificationChannel update = new NotificationChannel("A", "a", IMPORTANCE_LOW);
+ update.setBypassDnd(true);
+ mHelper.createNotificationChannel(PKG, UID, update, true, true);
+
+ assertTrue(mHelper.getNotificationChannel(PKG, UID, "A", false).canBypassDnd());
+ }
+
+ @Test
+ public void testNormalPkgCannotBypassDnd_update() {
+ NotificationChannel test = new NotificationChannel("A", "a", IMPORTANCE_LOW);
+ mHelper.createNotificationChannel(PKG, 1000, test, true, false);
+ NotificationChannel update = new NotificationChannel("A", "a", IMPORTANCE_LOW);
+ update.setBypassDnd(true);
+ mHelper.createNotificationChannel(PKG, 1000, update, true, false);
+ assertFalse(mHelper.getNotificationChannel(PKG, 1000, "A", false).canBypassDnd());
+ }
+
+ @Test
+ public void testGetBlockedAppCount_noApps() {
+ assertEquals(0, mHelper.getBlockedAppCount(0));
+ }
+
+ @Test
+ public void testGetBlockedAppCount_noAppsForUserId() {
+ mHelper.setEnabled(PKG, 100, false);
+ assertEquals(0, mHelper.getBlockedAppCount(9));
+ }
+
+ @Test
+ public void testGetBlockedAppCount_appsForUserId() {
+ mHelper.setEnabled(PKG, 1020, false);
+ mHelper.setEnabled(PKG, 1030, false);
+ mHelper.setEnabled(PKG, 1060, false);
+ mHelper.setEnabled(PKG, 1000, true);
+ assertEquals(3, mHelper.getBlockedAppCount(0));
+ }
+}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
index 98c6ec4..7e0fcc9 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
@@ -15,35 +15,17 @@
*/
package com.android.server.notification;
-import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
-import static android.app.NotificationManager.IMPORTANCE_HIGH;
import static android.app.NotificationManager.IMPORTANCE_LOW;
-import static android.app.NotificationManager.IMPORTANCE_MAX;
-import static android.app.NotificationManager.IMPORTANCE_NONE;
-import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
-import static junit.framework.Assert.assertNull;
-import static junit.framework.Assert.fail;
-
-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.mockito.ArgumentMatchers.any;
import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.Notification;
import android.app.NotificationChannel;
-import android.app.NotificationChannelGroup;
import android.app.NotificationManager;
import android.content.ContentProvider;
import android.content.Context;
@@ -52,46 +34,26 @@
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.Signature;
-import android.content.res.Resources;
-import android.graphics.Color;
import android.media.AudioAttributes;
import android.net.Uri;
import android.os.Build;
import android.os.UserHandle;
-import android.provider.Settings;
import android.provider.Settings.Secure;
import android.service.notification.StatusBarNotification;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.TestableContentResolver;
-import android.util.ArrayMap;
-import android.util.Xml;
-import com.android.internal.util.FastXmlSerializer;
import com.android.server.UiServiceTestCase;
-import org.json.JSONArray;
-import org.json.JSONObject;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlSerializer;
-import java.io.BufferedInputStream;
-import java.io.BufferedOutputStream;
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.concurrent.ThreadLocalRandom;
@SmallTest
@RunWith(AndroidJUnit4.class)
@@ -118,6 +80,7 @@
@Mock IContentProvider mTestIContentProvider;
@Mock Context mContext;
@Mock ZenModeHelper mMockZenModeHelper;
+ @Mock RankingConfig mConfig;
private NotificationManager.Policy mTestNotificationPolicy;
private Notification mNotiGroupGSortA;
@@ -179,9 +142,8 @@
mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0,
NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND);
when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy);
- mHelper = new RankingHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
+ mHelper = new RankingHelper(getContext(), mHandler, mConfig, mMockZenModeHelper,
mUsageStats, new String[] {ImportanceExtractor.class.getName()});
- resetZenModeHelper();
mNotiGroupGSortA = new Notification.Builder(mContext, TEST_CHANNEL_ID)
.setContentTitle("A")
@@ -240,74 +202,6 @@
IMPORTANCE_LOW);
}
- private ByteArrayOutputStream writeXmlAndPurge(String pkg, int uid, boolean forBackup,
- String... channelIds)
- throws Exception {
- XmlSerializer serializer = new FastXmlSerializer();
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- serializer.setOutput(new BufferedOutputStream(baos), "utf-8");
- serializer.startDocument(null, true);
- mHelper.writeXml(serializer, forBackup);
- serializer.endDocument();
- serializer.flush();
- for (String channelId : channelIds) {
- mHelper.permanentlyDeleteNotificationChannel(pkg, uid, channelId);
- }
- return baos;
- }
-
- private void loadStreamXml(ByteArrayOutputStream stream, boolean forRestore) throws Exception {
- loadByteArrayXml(stream.toByteArray(), forRestore);
- }
-
- private void loadByteArrayXml(byte[] byteArray, boolean forRestore) throws Exception {
- XmlPullParser parser = Xml.newPullParser();
- parser.setInput(new BufferedInputStream(new ByteArrayInputStream(byteArray)), null);
- parser.nextTag();
- mHelper.readXml(parser, forRestore);
- }
-
- private void compareChannels(NotificationChannel expected, NotificationChannel actual) {
- assertEquals(expected.getId(), actual.getId());
- assertEquals(expected.getName(), actual.getName());
- assertEquals(expected.getDescription(), actual.getDescription());
- assertEquals(expected.shouldVibrate(), actual.shouldVibrate());
- assertEquals(expected.shouldShowLights(), actual.shouldShowLights());
- assertEquals(expected.getImportance(), actual.getImportance());
- assertEquals(expected.getLockscreenVisibility(), actual.getLockscreenVisibility());
- assertEquals(expected.getSound(), actual.getSound());
- assertEquals(expected.canBypassDnd(), actual.canBypassDnd());
- assertTrue(Arrays.equals(expected.getVibrationPattern(), actual.getVibrationPattern()));
- assertEquals(expected.getGroup(), actual.getGroup());
- assertEquals(expected.getAudioAttributes(), actual.getAudioAttributes());
- assertEquals(expected.getLightColor(), actual.getLightColor());
- }
-
- private void compareGroups(NotificationChannelGroup expected, NotificationChannelGroup actual) {
- assertEquals(expected.getId(), actual.getId());
- assertEquals(expected.getName(), actual.getName());
- assertEquals(expected.getDescription(), actual.getDescription());
- assertEquals(expected.isBlocked(), actual.isBlocked());
- }
-
- private NotificationChannel getChannel() {
- return new NotificationChannel("id", "name", IMPORTANCE_LOW);
- }
-
- private NotificationChannel findChannel(List<NotificationChannel> channels, String id) {
- for (NotificationChannel channel : channels) {
- if (channel.getId().equals(id)) {
- return channel;
- }
- }
- return null;
- }
-
- private void resetZenModeHelper() {
- reset(mMockZenModeHelper);
- when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy);
- }
-
@Test
public void testFindAfterRankingWithASplitGroup() throws Exception {
ArrayList<NotificationRecord> notificationList = new ArrayList<NotificationRecord>(3);
@@ -357,1496 +251,4 @@
ArrayList<NotificationRecord> notificationList = new ArrayList<NotificationRecord>();
mHelper.sort(notificationList);
}
-
- @Test
- public void testChannelXml() throws Exception {
- NotificationChannelGroup ncg = new NotificationChannelGroup("1", "bye");
- ncg.setBlocked(true);
- ncg.setDescription("group desc");
- NotificationChannelGroup ncg2 = new NotificationChannelGroup("2", "hello");
- NotificationChannel channel1 =
- new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
- NotificationChannel channel2 =
- new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
- channel2.setDescription("descriptions for all");
- channel2.setSound(new Uri.Builder().scheme("test").build(), mAudioAttributes);
- channel2.enableLights(true);
- channel2.setBypassDnd(true);
- channel2.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
- channel2.enableVibration(true);
- channel2.setGroup(ncg.getId());
- channel2.setVibrationPattern(new long[]{100, 67, 145, 156});
- channel2.setLightColor(Color.BLUE);
-
- mHelper.createNotificationChannelGroup(PKG, UID, ncg, true);
- mHelper.createNotificationChannelGroup(PKG, UID, ncg2, true);
- mHelper.createNotificationChannel(PKG, UID, channel1, true, false);
- mHelper.createNotificationChannel(PKG, UID, channel2, false, false);
-
- mHelper.setShowBadge(PKG, UID, true);
- mHelper.setAppImportanceLocked(PKG, UID);
-
- ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, false, channel1.getId(),
- channel2.getId(), NotificationChannel.DEFAULT_CHANNEL_ID);
- mHelper.onPackagesChanged(true, UserHandle.myUserId(), new String[]{PKG}, new int[]{UID});
-
- loadStreamXml(baos, false);
-
- assertTrue(mHelper.canShowBadge(PKG, UID));
- assertTrue(mHelper.getIsAppImportanceLocked(PKG, UID));
- assertEquals(channel1, mHelper.getNotificationChannel(PKG, UID, channel1.getId(), false));
- compareChannels(channel2,
- mHelper.getNotificationChannel(PKG, UID, channel2.getId(), false));
-
- List<NotificationChannelGroup> actualGroups =
- mHelper.getNotificationChannelGroups(PKG, UID, false, true).getList();
- boolean foundNcg = false;
- for (NotificationChannelGroup actual : actualGroups) {
- if (ncg.getId().equals(actual.getId())) {
- foundNcg = true;
- compareGroups(ncg, actual);
- } else if (ncg2.getId().equals(actual.getId())) {
- compareGroups(ncg2, actual);
- }
- }
- assertTrue(foundNcg);
-
- boolean foundChannel2Group = false;
- for (NotificationChannelGroup actual : actualGroups) {
- if (channel2.getGroup().equals(actual.getChannels().get(0).getGroup())) {
- foundChannel2Group = true;
- break;
- }
- }
- assertTrue(foundChannel2Group);
- }
-
- @Test
- public void testChannelXmlForBackup() throws Exception {
- NotificationChannelGroup ncg = new NotificationChannelGroup("1", "bye");
- NotificationChannelGroup ncg2 = new NotificationChannelGroup("2", "hello");
- NotificationChannel channel1 =
- new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
- NotificationChannel channel2 =
- new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
- channel2.setDescription("descriptions for all");
- channel2.setSound(SOUND_URI, mAudioAttributes);
- channel2.enableLights(true);
- channel2.setBypassDnd(true);
- channel2.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
- channel2.enableVibration(false);
- channel2.setGroup(ncg.getId());
- channel2.setLightColor(Color.BLUE);
- NotificationChannel channel3 = new NotificationChannel("id3", "NAM3", IMPORTANCE_HIGH);
- channel3.enableVibration(true);
-
- mHelper.createNotificationChannelGroup(PKG, UID, ncg, true);
- mHelper.createNotificationChannelGroup(PKG, UID, ncg2, true);
- mHelper.createNotificationChannel(PKG, UID, channel1, true, false);
- mHelper.createNotificationChannel(PKG, UID, channel2, false, false);
- mHelper.createNotificationChannel(PKG, UID, channel3, false, false);
- mHelper.createNotificationChannel(UPDATED_PKG, UID2, getChannel(), true, false);
-
- mHelper.setShowBadge(PKG, UID, true);
-
- mHelper.setImportance(UPDATED_PKG, UID2, IMPORTANCE_NONE);
-
- ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, true, channel1.getId(),
- channel2.getId(), channel3.getId(), NotificationChannel.DEFAULT_CHANNEL_ID);
- mHelper.onPackagesChanged(true, UserHandle.myUserId(), new String[]{PKG, UPDATED_PKG},
- new int[]{UID, UID2});
-
- mHelper.setShowBadge(UPDATED_PKG, UID2, true);
-
- loadStreamXml(baos, true);
-
- assertEquals(IMPORTANCE_NONE, mHelper.getImportance(UPDATED_PKG, UID2));
- assertTrue(mHelper.canShowBadge(PKG, UID));
- assertEquals(channel1, mHelper.getNotificationChannel(PKG, UID, channel1.getId(), false));
- compareChannels(channel2,
- mHelper.getNotificationChannel(PKG, UID, channel2.getId(), false));
- compareChannels(channel3,
- mHelper.getNotificationChannel(PKG, UID, channel3.getId(), false));
-
- List<NotificationChannelGroup> actualGroups =
- mHelper.getNotificationChannelGroups(PKG, UID, false, true).getList();
- boolean foundNcg = false;
- for (NotificationChannelGroup actual : actualGroups) {
- if (ncg.getId().equals(actual.getId())) {
- foundNcg = true;
- compareGroups(ncg, actual);
- } else if (ncg2.getId().equals(actual.getId())) {
- compareGroups(ncg2, actual);
- }
- }
- assertTrue(foundNcg);
-
- boolean foundChannel2Group = false;
- for (NotificationChannelGroup actual : actualGroups) {
- if (channel2.getGroup().equals(actual.getChannels().get(0).getGroup())) {
- foundChannel2Group = true;
- break;
- }
- }
- assertTrue(foundChannel2Group);
- }
-
- @Test
- public void testBackupXml_backupCanonicalizedSoundUri() throws Exception {
- NotificationChannel channel =
- new NotificationChannel("id", "name", IMPORTANCE_LOW);
- channel.setSound(SOUND_URI, mAudioAttributes);
- mHelper.createNotificationChannel(PKG, UID, channel, true, false);
-
- ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, true, channel.getId());
-
- // Testing that in restore we are given the canonical version
- loadStreamXml(baos, true);
- verify(mTestIContentProvider).uncanonicalize(any(), eq(CANONICAL_SOUND_URI));
- }
-
- @Test
- public void testRestoreXml_withExistentCanonicalizedSoundUri() throws Exception {
- Uri localUri = Uri.parse("content://" + TEST_AUTHORITY + "/local/url");
- Uri canonicalBasedOnLocal = localUri.buildUpon()
- .appendQueryParameter("title", "Test")
- .appendQueryParameter("canonical", "1")
- .build();
- when(mTestIContentProvider.canonicalize(any(), eq(CANONICAL_SOUND_URI)))
- .thenReturn(canonicalBasedOnLocal);
- when(mTestIContentProvider.uncanonicalize(any(), eq(CANONICAL_SOUND_URI)))
- .thenReturn(localUri);
- when(mTestIContentProvider.uncanonicalize(any(), eq(canonicalBasedOnLocal)))
- .thenReturn(localUri);
-
- NotificationChannel channel =
- new NotificationChannel("id", "name", IMPORTANCE_LOW);
- channel.setSound(SOUND_URI, mAudioAttributes);
- mHelper.createNotificationChannel(PKG, UID, channel, true, false);
- ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, true, channel.getId());
-
- loadStreamXml(baos, true);
-
- NotificationChannel actualChannel = mHelper.getNotificationChannel(
- PKG, UID, channel.getId(), false);
- assertEquals(localUri, actualChannel.getSound());
- }
-
- @Test
- public void testRestoreXml_withNonExistentCanonicalizedSoundUri() throws Exception {
- Thread.sleep(3000);
- when(mTestIContentProvider.canonicalize(any(), eq(CANONICAL_SOUND_URI)))
- .thenReturn(null);
- when(mTestIContentProvider.uncanonicalize(any(), eq(CANONICAL_SOUND_URI)))
- .thenReturn(null);
-
- NotificationChannel channel =
- new NotificationChannel("id", "name", IMPORTANCE_LOW);
- channel.setSound(SOUND_URI, mAudioAttributes);
- mHelper.createNotificationChannel(PKG, UID, channel, true, false);
- ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, true, channel.getId());
-
- loadStreamXml(baos, true);
-
- NotificationChannel actualChannel = mHelper.getNotificationChannel(
- PKG, UID, channel.getId(), false);
- assertEquals(Settings.System.DEFAULT_NOTIFICATION_URI, actualChannel.getSound());
- }
-
-
- /**
- * Although we don't make backups with uncanonicalized uris anymore, we used to, so we have to
- * handle its restore properly.
- */
- @Test
- public void testRestoreXml_withUncanonicalizedNonLocalSoundUri() throws Exception {
- // Not a local uncanonicalized uri, simulating that it fails to exist locally
- when(mTestIContentProvider.canonicalize(any(), eq(SOUND_URI))).thenReturn(null);
- String id = "id";
- String backupWithUncanonicalizedSoundUri = "<ranking version=\"1\">\n"
- + "<package name=\"com.android.server.notification\" show_badge=\"true\">\n"
- + "<channel id=\"" + id + "\" name=\"name\" importance=\"2\" "
- + "sound=\"" + SOUND_URI + "\" "
- + "usage=\"6\" content_type=\"0\" flags=\"1\" show_badge=\"true\" />\n"
- + "<channel id=\"miscellaneous\" name=\"Uncategorized\" usage=\"5\" "
- + "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n"
- + "</package>\n"
- + "</ranking>\n";
-
- loadByteArrayXml(backupWithUncanonicalizedSoundUri.getBytes(), true);
-
- NotificationChannel actualChannel = mHelper.getNotificationChannel(PKG, UID, id, false);
- assertEquals(Settings.System.DEFAULT_NOTIFICATION_URI, actualChannel.getSound());
- }
-
- @Test
- public void testBackupRestoreXml_withNullSoundUri() throws Exception {
- NotificationChannel channel =
- new NotificationChannel("id", "name", IMPORTANCE_LOW);
- channel.setSound(null, mAudioAttributes);
- mHelper.createNotificationChannel(PKG, UID, channel, true, false);
- ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, true, channel.getId());
-
- loadStreamXml(baos, true);
-
- NotificationChannel actualChannel = mHelper.getNotificationChannel(
- PKG, UID, channel.getId(), false);
- assertEquals(null, actualChannel.getSound());
- }
-
- @Test
- public void testChannelXml_backup() throws Exception {
- NotificationChannelGroup ncg = new NotificationChannelGroup("1", "bye");
- NotificationChannelGroup ncg2 = new NotificationChannelGroup("2", "hello");
- NotificationChannel channel1 =
- new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
- NotificationChannel channel2 =
- new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
- NotificationChannel channel3 =
- new NotificationChannel("id3", "name3", IMPORTANCE_LOW);
- channel3.setGroup(ncg.getId());
-
- mHelper.createNotificationChannelGroup(PKG, UID, ncg, true);
- mHelper.createNotificationChannelGroup(PKG, UID, ncg2, true);
- mHelper.createNotificationChannel(PKG, UID, channel1, true, false);
- mHelper.createNotificationChannel(PKG, UID, channel2, false, false);
- mHelper.createNotificationChannel(PKG, UID, channel3, true, false);
-
- mHelper.deleteNotificationChannel(PKG, UID, channel1.getId());
- mHelper.deleteNotificationChannelGroup(PKG, UID, ncg.getId());
- assertEquals(channel2, mHelper.getNotificationChannel(PKG, UID, channel2.getId(), false));
-
- ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, true, channel1.getId(),
- channel2.getId(), channel3.getId(), NotificationChannel.DEFAULT_CHANNEL_ID);
- mHelper.onPackagesChanged(true, UserHandle.myUserId(), new String[]{PKG}, new int[]{UID});
-
- XmlPullParser parser = Xml.newPullParser();
- parser.setInput(new BufferedInputStream(new ByteArrayInputStream(baos.toByteArray())),
- null);
- parser.nextTag();
- mHelper.readXml(parser, true);
-
- assertNull(mHelper.getNotificationChannel(PKG, UID, channel1.getId(), false));
- assertNull(mHelper.getNotificationChannel(PKG, UID, channel3.getId(), false));
- assertNull(mHelper.getNotificationChannelGroup(ncg.getId(), PKG, UID));
- //assertEquals(ncg2, mHelper.getNotificationChannelGroup(ncg2.getId(), PKG, UID));
- assertEquals(channel2, mHelper.getNotificationChannel(PKG, UID, channel2.getId(), false));
- }
-
- @Test
- public void testChannelXml_defaultChannelLegacyApp_noUserSettings() throws Exception {
- ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, false,
- NotificationChannel.DEFAULT_CHANNEL_ID);
-
- loadStreamXml(baos, false);
-
- final NotificationChannel updated = mHelper.getNotificationChannel(PKG, UID,
- NotificationChannel.DEFAULT_CHANNEL_ID, false);
- assertEquals(NotificationManager.IMPORTANCE_UNSPECIFIED, updated.getImportance());
- assertFalse(updated.canBypassDnd());
- assertEquals(NotificationManager.VISIBILITY_NO_OVERRIDE, updated.getLockscreenVisibility());
- assertEquals(0, updated.getUserLockedFields());
- }
-
- @Test
- public void testChannelXml_defaultChannelUpdatedApp_userSettings() throws Exception {
- final NotificationChannel defaultChannel = mHelper.getNotificationChannel(PKG, UID,
- NotificationChannel.DEFAULT_CHANNEL_ID, false);
- defaultChannel.setImportance(NotificationManager.IMPORTANCE_LOW);
- mHelper.updateNotificationChannel(PKG, UID, defaultChannel, true);
-
- ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, false,
- NotificationChannel.DEFAULT_CHANNEL_ID);
-
- loadStreamXml(baos, false);
-
- assertEquals(NotificationManager.IMPORTANCE_LOW, mHelper.getNotificationChannel(
- PKG, UID, NotificationChannel.DEFAULT_CHANNEL_ID, false).getImportance());
- }
-
- @Test
- public void testChannelXml_upgradeCreateDefaultChannel() throws Exception {
- final String preupgradeXml = "<ranking version=\"1\">\n"
- + "<package name=\"" + PKG
- + "\" importance=\"" + NotificationManager.IMPORTANCE_HIGH
- + "\" priority=\"" + Notification.PRIORITY_MAX + "\" visibility=\""
- + Notification.VISIBILITY_SECRET + "\"" +" uid=\"" + UID + "\" />\n"
- + "<package name=\"" + UPDATED_PKG + "\" uid=\"" + UID2 + "\" visibility=\""
- + Notification.VISIBILITY_PRIVATE + "\" />\n"
- + "</ranking>";
- XmlPullParser parser = Xml.newPullParser();
- parser.setInput(new BufferedInputStream(new ByteArrayInputStream(preupgradeXml.getBytes())),
- null);
- parser.nextTag();
- mHelper.readXml(parser, false);
-
- final NotificationChannel updated1 =
- mHelper.getNotificationChannel(PKG, UID, NotificationChannel.DEFAULT_CHANNEL_ID, false);
- assertEquals(NotificationManager.IMPORTANCE_HIGH, updated1.getImportance());
- assertTrue(updated1.canBypassDnd());
- assertEquals(Notification.VISIBILITY_SECRET, updated1.getLockscreenVisibility());
- assertEquals(NotificationChannel.USER_LOCKED_IMPORTANCE
- | NotificationChannel.USER_LOCKED_PRIORITY
- | NotificationChannel.USER_LOCKED_VISIBILITY,
- updated1.getUserLockedFields());
-
- // No Default Channel created for updated packages
- assertEquals(null, mHelper.getNotificationChannel(UPDATED_PKG, UID2,
- NotificationChannel.DEFAULT_CHANNEL_ID, false));
- }
-
- @Test
- public void testChannelXml_upgradeDeletesDefaultChannel() throws Exception {
- final NotificationChannel defaultChannel = mHelper.getNotificationChannel(
- PKG, UID, NotificationChannel.DEFAULT_CHANNEL_ID, false);
- assertTrue(defaultChannel != null);
- ByteArrayOutputStream baos =
- writeXmlAndPurge(PKG, UID, false, NotificationChannel.DEFAULT_CHANNEL_ID);
- // Load package at higher sdk.
- final ApplicationInfo upgraded = new ApplicationInfo();
- upgraded.targetSdkVersion = Build.VERSION_CODES.N_MR1 + 1;
- when(mPm.getApplicationInfoAsUser(eq(PKG), anyInt(), anyInt())).thenReturn(upgraded);
- loadStreamXml(baos, false);
-
- // Default Channel should be gone.
- assertEquals(null, mHelper.getNotificationChannel(PKG, UID,
- NotificationChannel.DEFAULT_CHANNEL_ID, false));
- }
-
- @Test
- public void testDeletesDefaultChannelAfterChannelIsCreated() throws Exception {
- mHelper.createNotificationChannel(PKG, UID,
- new NotificationChannel("bananas", "bananas", IMPORTANCE_LOW), true, false);
- ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, false,
- NotificationChannel.DEFAULT_CHANNEL_ID, "bananas");
-
- // Load package at higher sdk.
- final ApplicationInfo upgraded = new ApplicationInfo();
- upgraded.targetSdkVersion = Build.VERSION_CODES.N_MR1 + 1;
- when(mPm.getApplicationInfoAsUser(eq(PKG), anyInt(), anyInt())).thenReturn(upgraded);
- loadStreamXml(baos, false);
-
- // Default Channel should be gone.
- assertEquals(null, mHelper.getNotificationChannel(PKG, UID,
- NotificationChannel.DEFAULT_CHANNEL_ID, false));
- }
-
- @Test
- public void testLoadingOldChannelsDoesNotDeleteNewlyCreatedChannels() throws Exception {
- ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, false,
- NotificationChannel.DEFAULT_CHANNEL_ID, "bananas");
- mHelper.createNotificationChannel(PKG, UID,
- new NotificationChannel("bananas", "bananas", IMPORTANCE_LOW), true, false);
-
- loadStreamXml(baos, false);
-
- // Should still have the newly created channel that wasn't in the xml.
- assertTrue(mHelper.getNotificationChannel(PKG, UID, "bananas", false) != null);
- }
-
- @Test
- public void testCreateChannel_blocked() throws Exception {
- mHelper.setImportance(PKG, UID, IMPORTANCE_NONE);
-
- mHelper.createNotificationChannel(PKG, UID,
- new NotificationChannel("bananas", "bananas", IMPORTANCE_LOW), true, false);
- }
-
- @Test
- public void testCreateChannel_badImportance() throws Exception {
- try {
- mHelper.createNotificationChannel(PKG, UID,
- new NotificationChannel("bananas", "bananas", IMPORTANCE_NONE - 1),
- true, false);
- fail("Was allowed to create a channel with invalid importance");
- } catch (IllegalArgumentException e) {
- // yay
- }
- try {
- mHelper.createNotificationChannel(PKG, UID,
- new NotificationChannel("bananas", "bananas", IMPORTANCE_UNSPECIFIED),
- true, false);
- fail("Was allowed to create a channel with invalid importance");
- } catch (IllegalArgumentException e) {
- // yay
- }
- try {
- mHelper.createNotificationChannel(PKG, UID,
- new NotificationChannel("bananas", "bananas", IMPORTANCE_MAX + 1),
- true, false);
- fail("Was allowed to create a channel with invalid importance");
- } catch (IllegalArgumentException e) {
- // yay
- }
- mHelper.createNotificationChannel(PKG, UID,
- new NotificationChannel("bananas", "bananas", IMPORTANCE_NONE), true, false);
- mHelper.createNotificationChannel(PKG, UID,
- new NotificationChannel("bananas", "bananas", IMPORTANCE_MAX), true, false);
- }
-
-
- @Test
- public void testUpdate() throws Exception {
- // no fields locked by user
- final NotificationChannel channel =
- new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
- channel.setSound(new Uri.Builder().scheme("test").build(), mAudioAttributes);
- channel.enableLights(true);
- channel.setBypassDnd(true);
- channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
-
- mHelper.createNotificationChannel(PKG, UID, channel, false, false);
-
- // same id, try to update all fields
- final NotificationChannel channel2 =
- new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_HIGH);
- channel2.setSound(new Uri.Builder().scheme("test2").build(), mAudioAttributes);
- channel2.enableLights(false);
- channel2.setBypassDnd(false);
- channel2.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
-
- mHelper.updateNotificationChannel(PKG, UID, channel2, true);
-
- // all fields should be changed
- assertEquals(channel2, mHelper.getNotificationChannel(PKG, UID, channel.getId(), false));
-
- verify(mHandler, times(1)).requestSort();
- }
-
- @Test
- public void testUpdate_preUpgrade_updatesAppFields() throws Exception {
- mHelper.setImportance(PKG, UID, IMPORTANCE_UNSPECIFIED);
- assertTrue(mHelper.canShowBadge(PKG, UID));
- assertEquals(Notification.PRIORITY_DEFAULT, mHelper.getPackagePriority(PKG, UID));
- assertEquals(NotificationManager.VISIBILITY_NO_OVERRIDE,
- mHelper.getPackageVisibility(PKG, UID));
- assertFalse(mHelper.getIsAppImportanceLocked(PKG, UID));
-
- NotificationChannel defaultChannel = mHelper.getNotificationChannel(
- PKG, UID, NotificationChannel.DEFAULT_CHANNEL_ID, false);
-
- defaultChannel.setShowBadge(false);
- defaultChannel.setImportance(IMPORTANCE_NONE);
- defaultChannel.setBypassDnd(true);
- defaultChannel.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
-
- mHelper.setAppImportanceLocked(PKG, UID);
- mHelper.updateNotificationChannel(PKG, UID, defaultChannel, true);
-
- // ensure app level fields are changed
- assertFalse(mHelper.canShowBadge(PKG, UID));
- assertEquals(Notification.PRIORITY_MAX, mHelper.getPackagePriority(PKG, UID));
- assertEquals(Notification.VISIBILITY_SECRET, mHelper.getPackageVisibility(PKG, UID));
- assertEquals(IMPORTANCE_NONE, mHelper.getImportance(PKG, UID));
- assertTrue(mHelper.getIsAppImportanceLocked(PKG, UID));
- }
-
- @Test
- public void testUpdate_postUpgrade_noUpdateAppFields() throws Exception {
- final NotificationChannel channel = new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
-
- mHelper.createNotificationChannel(PKG, UID, channel, false, false);
- assertTrue(mHelper.canShowBadge(PKG, UID));
- assertEquals(Notification.PRIORITY_DEFAULT, mHelper.getPackagePriority(PKG, UID));
- assertEquals(NotificationManager.VISIBILITY_NO_OVERRIDE,
- mHelper.getPackageVisibility(PKG, UID));
-
- channel.setShowBadge(false);
- channel.setImportance(IMPORTANCE_NONE);
- channel.setBypassDnd(true);
- channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
-
- mHelper.updateNotificationChannel(PKG, UID, channel, true);
-
- // ensure app level fields are not changed
- assertTrue(mHelper.canShowBadge(PKG, UID));
- assertEquals(Notification.PRIORITY_DEFAULT, mHelper.getPackagePriority(PKG, UID));
- assertEquals(NotificationManager.VISIBILITY_NO_OVERRIDE,
- mHelper.getPackageVisibility(PKG, UID));
- assertEquals(NotificationManager.IMPORTANCE_UNSPECIFIED, mHelper.getImportance(PKG, UID));
- }
-
- @Test
- public void testGetNotificationChannel_ReturnsNullForUnknownChannel() throws Exception {
- assertEquals(null, mHelper.getNotificationChannel(PKG, UID, "garbage", false));
- }
-
- @Test
- public void testCreateChannel_CannotChangeHiddenFields() throws Exception {
- final NotificationChannel channel =
- new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
- channel.setSound(new Uri.Builder().scheme("test").build(), mAudioAttributes);
- channel.enableLights(true);
- channel.setBypassDnd(true);
- channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
- channel.setShowBadge(true);
- int lockMask = 0;
- for (int i = 0; i < NotificationChannel.LOCKABLE_FIELDS.length; i++) {
- lockMask |= NotificationChannel.LOCKABLE_FIELDS[i];
- }
- channel.lockFields(lockMask);
-
- mHelper.createNotificationChannel(PKG, UID, channel, true, false);
-
- NotificationChannel savedChannel =
- mHelper.getNotificationChannel(PKG, UID, channel.getId(), false);
-
- assertEquals(channel.getName(), savedChannel.getName());
- assertEquals(channel.shouldShowLights(), savedChannel.shouldShowLights());
- assertFalse(savedChannel.canBypassDnd());
- assertFalse(Notification.VISIBILITY_SECRET == savedChannel.getLockscreenVisibility());
- assertEquals(channel.canShowBadge(), savedChannel.canShowBadge());
-
- verify(mHandler, never()).requestSort();
- }
-
- @Test
- public void testCreateChannel_CannotChangeHiddenFieldsAssistant() throws Exception {
- final NotificationChannel channel =
- new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
- channel.setSound(new Uri.Builder().scheme("test").build(), mAudioAttributes);
- channel.enableLights(true);
- channel.setBypassDnd(true);
- channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
- channel.setShowBadge(true);
- int lockMask = 0;
- for (int i = 0; i < NotificationChannel.LOCKABLE_FIELDS.length; i++) {
- lockMask |= NotificationChannel.LOCKABLE_FIELDS[i];
- }
- channel.lockFields(lockMask);
-
- mHelper.createNotificationChannel(PKG, UID, channel, true, false);
-
- NotificationChannel savedChannel =
- mHelper.getNotificationChannel(PKG, UID, channel.getId(), false);
-
- assertEquals(channel.getName(), savedChannel.getName());
- assertEquals(channel.shouldShowLights(), savedChannel.shouldShowLights());
- assertFalse(savedChannel.canBypassDnd());
- assertFalse(Notification.VISIBILITY_SECRET == savedChannel.getLockscreenVisibility());
- assertEquals(channel.canShowBadge(), savedChannel.canShowBadge());
- }
-
- @Test
- public void testClearLockedFields() throws Exception {
- final NotificationChannel channel = getChannel();
- mHelper.clearLockedFields(channel);
- assertEquals(0, channel.getUserLockedFields());
-
- channel.lockFields(NotificationChannel.USER_LOCKED_PRIORITY
- | NotificationChannel.USER_LOCKED_IMPORTANCE);
- mHelper.clearLockedFields(channel);
- assertEquals(0, channel.getUserLockedFields());
- }
-
- @Test
- public void testLockFields_soundAndVibration() throws Exception {
- mHelper.createNotificationChannel(PKG, UID, getChannel(), true, false);
-
- final NotificationChannel update1 = getChannel();
- update1.setSound(new Uri.Builder().scheme("test").build(),
- new AudioAttributes.Builder().build());
- update1.lockFields(NotificationChannel.USER_LOCKED_PRIORITY);
- mHelper.updateNotificationChannel(PKG, UID, update1, true);
- assertEquals(NotificationChannel.USER_LOCKED_PRIORITY
- | NotificationChannel.USER_LOCKED_SOUND,
- mHelper.getNotificationChannel(PKG, UID, update1.getId(), false)
- .getUserLockedFields());
-
- NotificationChannel update2 = getChannel();
- update2.enableVibration(true);
- mHelper.updateNotificationChannel(PKG, UID, update2, true);
- assertEquals(NotificationChannel.USER_LOCKED_PRIORITY
- | NotificationChannel.USER_LOCKED_SOUND
- | NotificationChannel.USER_LOCKED_VIBRATION,
- mHelper.getNotificationChannel(PKG, UID, update2.getId(), false)
- .getUserLockedFields());
- }
-
- @Test
- public void testLockFields_vibrationAndLights() throws Exception {
- mHelper.createNotificationChannel(PKG, UID, getChannel(), true, false);
-
- final NotificationChannel update1 = getChannel();
- update1.setVibrationPattern(new long[]{7945, 46 ,246});
- mHelper.updateNotificationChannel(PKG, UID, update1, true);
- assertEquals(NotificationChannel.USER_LOCKED_VIBRATION,
- mHelper.getNotificationChannel(PKG, UID, update1.getId(), false)
- .getUserLockedFields());
-
- final NotificationChannel update2 = getChannel();
- update2.enableLights(true);
- mHelper.updateNotificationChannel(PKG, UID, update2, true);
- assertEquals(NotificationChannel.USER_LOCKED_VIBRATION
- | NotificationChannel.USER_LOCKED_LIGHTS,
- mHelper.getNotificationChannel(PKG, UID, update2.getId(), false)
- .getUserLockedFields());
- }
-
- @Test
- public void testLockFields_lightsAndImportance() throws Exception {
- mHelper.createNotificationChannel(PKG, UID, getChannel(), true, false);
-
- final NotificationChannel update1 = getChannel();
- update1.setLightColor(Color.GREEN);
- mHelper.updateNotificationChannel(PKG, UID, update1, true);
- assertEquals(NotificationChannel.USER_LOCKED_LIGHTS,
- mHelper.getNotificationChannel(PKG, UID, update1.getId(), false)
- .getUserLockedFields());
-
- final NotificationChannel update2 = getChannel();
- update2.setImportance(IMPORTANCE_DEFAULT);
- mHelper.updateNotificationChannel(PKG, UID, update2, true);
- assertEquals(NotificationChannel.USER_LOCKED_LIGHTS
- | NotificationChannel.USER_LOCKED_IMPORTANCE,
- mHelper.getNotificationChannel(PKG, UID, update2.getId(), false)
- .getUserLockedFields());
- }
-
- @Test
- public void testLockFields_visibilityAndDndAndBadge() throws Exception {
- mHelper.createNotificationChannel(PKG, UID, getChannel(), true, false);
- assertEquals(0,
- mHelper.getNotificationChannel(PKG, UID, getChannel().getId(), false)
- .getUserLockedFields());
-
- final NotificationChannel update1 = getChannel();
- update1.setBypassDnd(true);
- mHelper.updateNotificationChannel(PKG, UID, update1, true);
- assertEquals(NotificationChannel.USER_LOCKED_PRIORITY,
- mHelper.getNotificationChannel(PKG, UID, update1.getId(), false)
- .getUserLockedFields());
-
- final NotificationChannel update2 = getChannel();
- update2.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
- mHelper.updateNotificationChannel(PKG, UID, update2, true);
- assertEquals(NotificationChannel.USER_LOCKED_PRIORITY
- | NotificationChannel.USER_LOCKED_VISIBILITY,
- mHelper.getNotificationChannel(PKG, UID, update2.getId(), false)
- .getUserLockedFields());
-
- final NotificationChannel update3 = getChannel();
- update3.setShowBadge(false);
- mHelper.updateNotificationChannel(PKG, UID, update3, true);
- assertEquals(NotificationChannel.USER_LOCKED_PRIORITY
- | NotificationChannel.USER_LOCKED_VISIBILITY
- | NotificationChannel.USER_LOCKED_SHOW_BADGE,
- mHelper.getNotificationChannel(PKG, UID, update3.getId(), false)
- .getUserLockedFields());
- }
-
- @Test
- public void testDeleteNonExistentChannel() throws Exception {
- mHelper.deleteNotificationChannelGroup(PKG, UID, "does not exist");
- }
-
- @Test
- public void testGetDeletedChannel() throws Exception {
- NotificationChannel channel = getChannel();
- channel.setSound(new Uri.Builder().scheme("test").build(), mAudioAttributes);
- channel.enableLights(true);
- channel.setBypassDnd(true);
- channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
- channel.enableVibration(true);
- channel.setVibrationPattern(new long[]{100, 67, 145, 156});
-
- mHelper.createNotificationChannel(PKG, UID, channel, true, false);
- mHelper.deleteNotificationChannel(PKG, UID, channel.getId());
-
- // Does not return deleted channel
- NotificationChannel response =
- mHelper.getNotificationChannel(PKG, UID, channel.getId(), false);
- assertNull(response);
-
- // Returns deleted channel
- response = mHelper.getNotificationChannel(PKG, UID, channel.getId(), true);
- compareChannels(channel, response);
- assertTrue(response.isDeleted());
- }
-
- @Test
- public void testGetDeletedChannels() throws Exception {
- Map<String, NotificationChannel> channelMap = new HashMap<>();
- NotificationChannel channel =
- new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
- channel.setSound(new Uri.Builder().scheme("test").build(), mAudioAttributes);
- channel.enableLights(true);
- channel.setBypassDnd(true);
- channel.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
- channel.enableVibration(true);
- channel.setVibrationPattern(new long[]{100, 67, 145, 156});
- channelMap.put(channel.getId(), channel);
- NotificationChannel channel2 =
- new NotificationChannel("id4", "a", NotificationManager.IMPORTANCE_HIGH);
- channelMap.put(channel2.getId(), channel2);
- mHelper.createNotificationChannel(PKG, UID, channel, true, false);
- mHelper.createNotificationChannel(PKG, UID, channel2, true, false);
-
- mHelper.deleteNotificationChannel(PKG, UID, channel.getId());
-
- // Returns only non-deleted channels
- List<NotificationChannel> channels =
- mHelper.getNotificationChannels(PKG, UID, false).getList();
- assertEquals(2, channels.size()); // Default channel + non-deleted channel
- for (NotificationChannel nc : channels) {
- if (!NotificationChannel.DEFAULT_CHANNEL_ID.equals(nc.getId())) {
- compareChannels(channel2, nc);
- }
- }
-
- // Returns deleted channels too
- channels = mHelper.getNotificationChannels(PKG, UID, true).getList();
- assertEquals(3, channels.size()); // Includes default channel
- for (NotificationChannel nc : channels) {
- if (!NotificationChannel.DEFAULT_CHANNEL_ID.equals(nc.getId())) {
- compareChannels(channelMap.get(nc.getId()), nc);
- }
- }
- }
-
- @Test
- public void testGetDeletedChannelCount() throws Exception {
- NotificationChannel channel =
- new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
- NotificationChannel channel2 =
- new NotificationChannel("id4", "a", NotificationManager.IMPORTANCE_HIGH);
- NotificationChannel channel3 =
- new NotificationChannel("id5", "a", NotificationManager.IMPORTANCE_HIGH);
- mHelper.createNotificationChannel(PKG, UID, channel, true, false);
- mHelper.createNotificationChannel(PKG, UID, channel2, true, false);
- mHelper.createNotificationChannel(PKG, UID, channel3, true, false);
-
- mHelper.deleteNotificationChannel(PKG, UID, channel.getId());
- mHelper.deleteNotificationChannel(PKG, UID, channel3.getId());
-
- assertEquals(2, mHelper.getDeletedChannelCount(PKG, UID));
- assertEquals(0, mHelper.getDeletedChannelCount("pkg2", UID2));
- }
-
- @Test
- public void testGetBlockedChannelCount() throws Exception {
- NotificationChannel channel =
- new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
- NotificationChannel channel2 =
- new NotificationChannel("id4", "a", NotificationManager.IMPORTANCE_NONE);
- NotificationChannel channel3 =
- new NotificationChannel("id5", "a", NotificationManager.IMPORTANCE_NONE);
- mHelper.createNotificationChannel(PKG, UID, channel, true, false);
- mHelper.createNotificationChannel(PKG, UID, channel2, true, false);
- mHelper.createNotificationChannel(PKG, UID, channel3, true, false);
-
- mHelper.deleteNotificationChannel(PKG, UID, channel3.getId());
-
- assertEquals(1, mHelper.getBlockedChannelCount(PKG, UID));
- assertEquals(0, mHelper.getBlockedChannelCount("pkg2", UID2));
- }
-
- @Test
- public void testCreateAndDeleteCanChannelsBypassDnd() throws Exception {
- // create notification channel that can't bypass dnd
- // expected result: areChannelsBypassingDnd = false
- // setNotificationPolicy isn't called since areChannelsBypassingDnd was already false
- NotificationChannel channel = new NotificationChannel("id1", "name1", IMPORTANCE_LOW);
- mHelper.createNotificationChannel(PKG, UID, channel, true, false);
- assertFalse(mHelper.areChannelsBypassingDnd());
- verify(mMockZenModeHelper, never()).setNotificationPolicy(any());
- resetZenModeHelper();
-
- // create notification channel that can bypass dnd
- // expected result: areChannelsBypassingDnd = true
- NotificationChannel channel2 = new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
- channel2.setBypassDnd(true);
- mHelper.createNotificationChannel(PKG, UID, channel2, true, true);
- assertTrue(mHelper.areChannelsBypassingDnd());
- verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any());
- resetZenModeHelper();
-
- // delete channels
- mHelper.deleteNotificationChannel(PKG, UID, channel.getId());
- assertTrue(mHelper.areChannelsBypassingDnd()); // channel2 can still bypass DND
- verify(mMockZenModeHelper, never()).setNotificationPolicy(any());
- resetZenModeHelper();
-
- mHelper.deleteNotificationChannel(PKG, UID, channel2.getId());
- assertFalse(mHelper.areChannelsBypassingDnd());
- verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any());
- resetZenModeHelper();
- }
-
- @Test
- public void testUpdateCanChannelsBypassDnd() throws Exception {
- // create notification channel that can't bypass dnd
- // expected result: areChannelsBypassingDnd = false
- // setNotificationPolicy isn't called since areChannelsBypassingDnd was already false
- NotificationChannel channel = new NotificationChannel("id1", "name1", IMPORTANCE_LOW);
- mHelper.createNotificationChannel(PKG, UID, channel, true, false);
- assertFalse(mHelper.areChannelsBypassingDnd());
- verify(mMockZenModeHelper, never()).setNotificationPolicy(any());
- resetZenModeHelper();
-
- // update channel so it CAN bypass dnd:
- // expected result: areChannelsBypassingDnd = true
- channel.setBypassDnd(true);
- mHelper.updateNotificationChannel(PKG, UID, channel, true);
- assertTrue(mHelper.areChannelsBypassingDnd());
- verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any());
- resetZenModeHelper();
-
- // update channel so it can't bypass dnd:
- // expected result: areChannelsBypassingDnd = false
- channel.setBypassDnd(false);
- mHelper.updateNotificationChannel(PKG, UID, channel, true);
- assertFalse(mHelper.areChannelsBypassingDnd());
- verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any());
- resetZenModeHelper();
- }
-
- @Test
- public void testSetupNewZenModeHelper_canBypass() {
- // start notification policy off with mAreChannelsBypassingDnd = true, but
- // RankingHelper should change to false
- mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0,
- NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND);
- when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy);
- mHelper = new RankingHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mUsageStats, new String[] {ImportanceExtractor.class.getName()});
- assertFalse(mHelper.areChannelsBypassingDnd());
- verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any());
- resetZenModeHelper();
- }
-
- @Test
- public void testSetupNewZenModeHelper_cannotBypass() {
- // start notification policy off with mAreChannelsBypassingDnd = false
- mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0, 0);
- when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy);
- mHelper = new RankingHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mUsageStats, new String[] {ImportanceExtractor.class.getName()});
- assertFalse(mHelper.areChannelsBypassingDnd());
- verify(mMockZenModeHelper, never()).setNotificationPolicy(any());
- resetZenModeHelper();
- }
-
- @Test
- public void testCreateDeletedChannel() throws Exception {
- long[] vibration = new long[]{100, 67, 145, 156};
- NotificationChannel channel =
- new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
- channel.setVibrationPattern(vibration);
-
- mHelper.createNotificationChannel(PKG, UID, channel, true, false);
- mHelper.deleteNotificationChannel(PKG, UID, channel.getId());
-
- NotificationChannel newChannel = new NotificationChannel(
- channel.getId(), channel.getName(), NotificationManager.IMPORTANCE_HIGH);
- newChannel.setVibrationPattern(new long[]{100});
-
- mHelper.createNotificationChannel(PKG, UID, newChannel, true, false);
-
- // No long deleted, using old settings
- compareChannels(channel,
- mHelper.getNotificationChannel(PKG, UID, newChannel.getId(), false));
- }
-
- @Test
- public void testOnlyHasDefaultChannel() throws Exception {
- assertTrue(mHelper.onlyHasDefaultChannel(PKG, UID));
- assertFalse(mHelper.onlyHasDefaultChannel(UPDATED_PKG, UID2));
-
- mHelper.createNotificationChannel(PKG, UID, getChannel(), true, false);
- assertFalse(mHelper.onlyHasDefaultChannel(PKG, UID));
- }
-
- @Test
- public void testCreateChannel_defaultChannelId() throws Exception {
- try {
- mHelper.createNotificationChannel(PKG, UID, new NotificationChannel(
- NotificationChannel.DEFAULT_CHANNEL_ID, "ha", IMPORTANCE_HIGH), true, false);
- fail("Allowed to create default channel");
- } catch (IllegalArgumentException e) {
- // pass
- }
- }
-
- @Test
- public void testCreateChannel_alreadyExists() throws Exception {
- long[] vibration = new long[]{100, 67, 145, 156};
- NotificationChannel channel =
- new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
- channel.setVibrationPattern(vibration);
-
- mHelper.createNotificationChannel(PKG, UID, channel, true, false);
-
- NotificationChannel newChannel = new NotificationChannel(
- channel.getId(), channel.getName(), NotificationManager.IMPORTANCE_HIGH);
- newChannel.setVibrationPattern(new long[]{100});
-
- mHelper.createNotificationChannel(PKG, UID, newChannel, true, false);
-
- // Old settings not overridden
- compareChannels(channel,
- mHelper.getNotificationChannel(PKG, UID, newChannel.getId(), false));
- }
-
- @Test
- public void testCreateChannel_noOverrideSound() throws Exception {
- Uri sound = new Uri.Builder().scheme("test").build();
- final NotificationChannel channel = new NotificationChannel("id2", "name2",
- NotificationManager.IMPORTANCE_DEFAULT);
- channel.setSound(sound, mAudioAttributes);
- mHelper.createNotificationChannel(PKG, UID, channel, true, false);
- assertEquals(sound, mHelper.getNotificationChannel(
- PKG, UID, channel.getId(), false).getSound());
- }
-
- @Test
- public void testPermanentlyDeleteChannels() throws Exception {
- NotificationChannel channel1 =
- new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
- NotificationChannel channel2 =
- new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
-
- mHelper.createNotificationChannel(PKG, UID, channel1, true, false);
- mHelper.createNotificationChannel(PKG, UID, channel2, false, false);
-
- mHelper.permanentlyDeleteNotificationChannels(PKG, UID);
-
- // Only default channel remains
- assertEquals(1, mHelper.getNotificationChannels(PKG, UID, true).getList().size());
- }
-
- @Test
- public void testDeleteGroup() throws Exception {
- NotificationChannelGroup notDeleted = new NotificationChannelGroup("not", "deleted");
- NotificationChannelGroup deleted = new NotificationChannelGroup("totally", "deleted");
- NotificationChannel nonGroupedNonDeletedChannel =
- new NotificationChannel("no group", "so not deleted", IMPORTANCE_HIGH);
- NotificationChannel groupedButNotDeleted =
- new NotificationChannel("not deleted", "belongs to notDeleted", IMPORTANCE_DEFAULT);
- groupedButNotDeleted.setGroup("not");
- NotificationChannel groupedAndDeleted =
- new NotificationChannel("deleted", "belongs to deleted", IMPORTANCE_DEFAULT);
- groupedAndDeleted.setGroup("totally");
-
- mHelper.createNotificationChannelGroup(PKG, UID, notDeleted, true);
- mHelper.createNotificationChannelGroup(PKG, UID, deleted, true);
- mHelper.createNotificationChannel(PKG, UID, nonGroupedNonDeletedChannel, true, false);
- mHelper.createNotificationChannel(PKG, UID, groupedAndDeleted, true, false);
- mHelper.createNotificationChannel(PKG, UID, groupedButNotDeleted, true, false);
-
- mHelper.deleteNotificationChannelGroup(PKG, UID, deleted.getId());
-
- assertNull(mHelper.getNotificationChannelGroup(deleted.getId(), PKG, UID));
- assertNotNull(mHelper.getNotificationChannelGroup(notDeleted.getId(), PKG, UID));
-
- assertNull(mHelper.getNotificationChannel(PKG, UID, groupedAndDeleted.getId(), false));
- compareChannels(groupedAndDeleted,
- mHelper.getNotificationChannel(PKG, UID, groupedAndDeleted.getId(), true));
-
- compareChannels(groupedButNotDeleted,
- mHelper.getNotificationChannel(PKG, UID, groupedButNotDeleted.getId(), false));
- compareChannels(nonGroupedNonDeletedChannel, mHelper.getNotificationChannel(
- PKG, UID, nonGroupedNonDeletedChannel.getId(), false));
-
- // notDeleted
- assertEquals(1, mHelper.getNotificationChannelGroups(PKG, UID).size());
-
- verify(mHandler, never()).requestSort();
- }
-
- @Test
- public void testOnUserRemoved() throws Exception {
- int[] user0Uids = {98, 235, 16, 3782};
- int[] user1Uids = new int[user0Uids.length];
- for (int i = 0; i < user0Uids.length; i++) {
- user1Uids[i] = UserHandle.PER_USER_RANGE + user0Uids[i];
-
- final ApplicationInfo legacy = new ApplicationInfo();
- legacy.targetSdkVersion = Build.VERSION_CODES.N_MR1;
- when(mPm.getApplicationInfoAsUser(eq(PKG), anyInt(), anyInt())).thenReturn(legacy);
-
- // create records with the default channel for all user 0 and user 1 uids
- mHelper.getImportance(PKG, user0Uids[i]);
- mHelper.getImportance(PKG, user1Uids[i]);
- }
-
- mHelper.onUserRemoved(1);
-
- // user 0 records remain
- for (int i = 0; i < user0Uids.length; i++) {
- assertEquals(1,
- mHelper.getNotificationChannels(PKG, user0Uids[i], false).getList().size());
- }
- // user 1 records are gone
- for (int i = 0; i < user1Uids.length; i++) {
- assertEquals(0,
- mHelper.getNotificationChannels(PKG, user1Uids[i], false).getList().size());
- }
- }
-
- @Test
- public void testOnPackageChanged_packageRemoval() throws Exception {
- // Deleted
- NotificationChannel channel1 =
- new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
- mHelper.createNotificationChannel(PKG, UID, channel1, true, false);
-
- mHelper.onPackagesChanged(true, UserHandle.USER_SYSTEM, new String[]{PKG}, new int[]{UID});
-
- assertEquals(0, mHelper.getNotificationChannels(PKG, UID, true).getList().size());
-
- // Not deleted
- mHelper.createNotificationChannel(PKG, UID, channel1, true, false);
-
- mHelper.onPackagesChanged(false, UserHandle.USER_SYSTEM, new String[]{PKG}, new int[]{UID});
- assertEquals(2, mHelper.getNotificationChannels(PKG, UID, false).getList().size());
- }
-
- @Test
- public void testOnPackageChanged_packageRemoval_importance() throws Exception {
- mHelper.setImportance(PKG, UID, NotificationManager.IMPORTANCE_HIGH);
-
- mHelper.onPackagesChanged(true, UserHandle.USER_SYSTEM, new String[]{PKG}, new int[]{UID});
-
- assertEquals(NotificationManager.IMPORTANCE_UNSPECIFIED, mHelper.getImportance(PKG, UID));
- }
-
- @Test
- public void testOnPackageChanged_packageRemoval_groups() throws Exception {
- NotificationChannelGroup ncg = new NotificationChannelGroup("group1", "name1");
- mHelper.createNotificationChannelGroup(PKG, UID, ncg, true);
- NotificationChannelGroup ncg2 = new NotificationChannelGroup("group2", "name2");
- mHelper.createNotificationChannelGroup(PKG, UID, ncg2, true);
-
- mHelper.onPackagesChanged(true, UserHandle.USER_SYSTEM, new String[]{PKG}, new int[]{UID});
-
- assertEquals(0,
- mHelper.getNotificationChannelGroups(PKG, UID, true, true).getList().size());
- }
-
- @Test
- public void testOnPackageChange_downgradeTargetSdk() throws Exception {
- // create channel as api 26
- mHelper.createNotificationChannel(UPDATED_PKG, UID2, getChannel(), true, false);
-
- // install new app version targeting 25
- final ApplicationInfo legacy = new ApplicationInfo();
- legacy.targetSdkVersion = Build.VERSION_CODES.N_MR1;
- when(mPm.getApplicationInfoAsUser(eq(UPDATED_PKG), anyInt(), anyInt())).thenReturn(legacy);
- mHelper.onPackagesChanged(
- false, UserHandle.USER_SYSTEM, new String[]{UPDATED_PKG}, new int[]{UID2});
-
- // make sure the default channel was readded
- //assertEquals(2, mHelper.getNotificationChannels(UPDATED_PKG, UID2, false).getList().size());
- assertNotNull(mHelper.getNotificationChannel(
- UPDATED_PKG, UID2, NotificationChannel.DEFAULT_CHANNEL_ID, false));
- }
-
- @Test
- public void testRecordDefaults() throws Exception {
- assertEquals(NotificationManager.IMPORTANCE_UNSPECIFIED, mHelper.getImportance(PKG, UID));
- assertEquals(true, mHelper.canShowBadge(PKG, UID));
- assertEquals(1, mHelper.getNotificationChannels(PKG, UID, false).getList().size());
- }
-
- @Test
- public void testCreateGroup() throws Exception {
- NotificationChannelGroup ncg = new NotificationChannelGroup("group1", "name1");
- mHelper.createNotificationChannelGroup(PKG, UID, ncg, true);
- assertEquals(ncg, mHelper.getNotificationChannelGroups(PKG, UID).iterator().next());
- verify(mHandler, never()).requestSort();
- }
-
- @Test
- public void testCannotCreateChannel_badGroup() throws Exception {
- NotificationChannel channel1 =
- new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
- channel1.setGroup("garbage");
- try {
- mHelper.createNotificationChannel(PKG, UID, channel1, true, false);
- fail("Created a channel with a bad group");
- } catch (IllegalArgumentException e) {
- }
- }
-
- @Test
- public void testCannotCreateChannel_goodGroup() throws Exception {
- NotificationChannelGroup ncg = new NotificationChannelGroup("group1", "name1");
- mHelper.createNotificationChannelGroup(PKG, UID, ncg, true);
- NotificationChannel channel1 =
- new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
- channel1.setGroup(ncg.getId());
- mHelper.createNotificationChannel(PKG, UID, channel1, true, false);
-
- assertEquals(ncg.getId(),
- mHelper.getNotificationChannel(PKG, UID, channel1.getId(), false).getGroup());
- }
-
- @Test
- public void testGetChannelGroups() throws Exception {
- NotificationChannelGroup unused = new NotificationChannelGroup("unused", "s");
- mHelper.createNotificationChannelGroup(PKG, UID, unused, true);
- NotificationChannelGroup ncg = new NotificationChannelGroup("group1", "name1");
- mHelper.createNotificationChannelGroup(PKG, UID, ncg, true);
- NotificationChannelGroup ncg2 = new NotificationChannelGroup("group2", "name2");
- mHelper.createNotificationChannelGroup(PKG, UID, ncg2, true);
-
- NotificationChannel channel1 =
- new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
- channel1.setGroup(ncg.getId());
- mHelper.createNotificationChannel(PKG, UID, channel1, true, false);
- NotificationChannel channel1a =
- new NotificationChannel("id1a", "name1", NotificationManager.IMPORTANCE_HIGH);
- channel1a.setGroup(ncg.getId());
- mHelper.createNotificationChannel(PKG, UID, channel1a, true, false);
-
- NotificationChannel channel2 =
- new NotificationChannel("id2", "name1", NotificationManager.IMPORTANCE_HIGH);
- channel2.setGroup(ncg2.getId());
- mHelper.createNotificationChannel(PKG, UID, channel2, true, false);
-
- NotificationChannel channel3 =
- new NotificationChannel("id3", "name1", NotificationManager.IMPORTANCE_HIGH);
- mHelper.createNotificationChannel(PKG, UID, channel3, true, false);
-
- List<NotificationChannelGroup> actual =
- mHelper.getNotificationChannelGroups(PKG, UID, true, true).getList();
- assertEquals(3, actual.size());
- for (NotificationChannelGroup group : actual) {
- if (group.getId() == null) {
- assertEquals(2, group.getChannels().size()); // misc channel too
- assertTrue(channel3.getId().equals(group.getChannels().get(0).getId())
- || channel3.getId().equals(group.getChannels().get(1).getId()));
- } else if (group.getId().equals(ncg.getId())) {
- assertEquals(2, group.getChannels().size());
- if (group.getChannels().get(0).getId().equals(channel1.getId())) {
- assertTrue(group.getChannels().get(1).getId().equals(channel1a.getId()));
- } else if (group.getChannels().get(0).getId().equals(channel1a.getId())) {
- assertTrue(group.getChannels().get(1).getId().equals(channel1.getId()));
- } else {
- fail("expected channel not found");
- }
- } else if (group.getId().equals(ncg2.getId())) {
- assertEquals(1, group.getChannels().size());
- assertEquals(channel2.getId(), group.getChannels().get(0).getId());
- }
- }
- }
-
- @Test
- public void testGetChannelGroups_noSideEffects() throws Exception {
- NotificationChannelGroup ncg = new NotificationChannelGroup("group1", "name1");
- mHelper.createNotificationChannelGroup(PKG, UID, ncg, true);
-
- NotificationChannel channel1 =
- new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
- channel1.setGroup(ncg.getId());
- mHelper.createNotificationChannel(PKG, UID, channel1, true, false);
- mHelper.getNotificationChannelGroups(PKG, UID, true, true).getList();
-
- channel1.setImportance(IMPORTANCE_LOW);
- mHelper.updateNotificationChannel(PKG, UID, channel1, true);
-
- List<NotificationChannelGroup> actual =
- mHelper.getNotificationChannelGroups(PKG, UID, true, true).getList();
-
- assertEquals(2, actual.size());
- for (NotificationChannelGroup group : actual) {
- if (Objects.equals(group.getId(), ncg.getId())) {
- assertEquals(1, group.getChannels().size());
- }
- }
- }
-
- @Test
- public void testCreateChannel_updateName() throws Exception {
- NotificationChannel nc = new NotificationChannel("id", "hello", IMPORTANCE_DEFAULT);
- mHelper.createNotificationChannel(PKG, UID, nc, true, false);
- NotificationChannel actual = mHelper.getNotificationChannel(PKG, UID, "id", false);
- assertEquals("hello", actual.getName());
-
- nc = new NotificationChannel("id", "goodbye", IMPORTANCE_HIGH);
- mHelper.createNotificationChannel(PKG, UID, nc, true, false);
-
- actual = mHelper.getNotificationChannel(PKG, UID, "id", false);
- assertEquals("goodbye", actual.getName());
- assertEquals(IMPORTANCE_DEFAULT, actual.getImportance());
-
- verify(mHandler, times(1)).requestSort();
- }
-
- @Test
- public void testCreateChannel_addToGroup() throws Exception {
- NotificationChannelGroup group = new NotificationChannelGroup("group", "");
- mHelper.createNotificationChannelGroup(PKG, UID, group, true);
- NotificationChannel nc = new NotificationChannel("id", "hello", IMPORTANCE_DEFAULT);
- mHelper.createNotificationChannel(PKG, UID, nc, true, false);
- NotificationChannel actual = mHelper.getNotificationChannel(PKG, UID, "id", false);
- assertNull(actual.getGroup());
-
- nc = new NotificationChannel("id", "hello", IMPORTANCE_HIGH);
- nc.setGroup(group.getId());
- mHelper.createNotificationChannel(PKG, UID, nc, true, false);
-
- actual = mHelper.getNotificationChannel(PKG, UID, "id", false);
- assertNotNull(actual.getGroup());
- assertEquals(IMPORTANCE_DEFAULT, actual.getImportance());
-
- verify(mHandler, times(1)).requestSort();
- }
-
- @Test
- public void testDumpChannelsJson() throws Exception {
- final ApplicationInfo upgrade = new ApplicationInfo();
- upgrade.targetSdkVersion = Build.VERSION_CODES.O;
- try {
- when(mPm.getApplicationInfoAsUser(
- anyString(), anyInt(), anyInt())).thenReturn(upgrade);
- } catch (PackageManager.NameNotFoundException e) {
- }
- ArrayMap<String, Integer> expectedChannels = new ArrayMap<>();
- int numPackages = ThreadLocalRandom.current().nextInt(1, 5);
- for (int i = 0; i < numPackages; i++) {
- String pkgName = "pkg" + i;
- int numChannels = ThreadLocalRandom.current().nextInt(1, 10);
- for (int j = 0; j < numChannels; j++) {
- mHelper.createNotificationChannel(pkgName, UID,
- new NotificationChannel("" + j, "a", IMPORTANCE_HIGH), true, false);
- }
- expectedChannels.put(pkgName, numChannels);
- }
-
- // delete the first channel of the first package
- String pkg = expectedChannels.keyAt(0);
- mHelper.deleteNotificationChannel("pkg" + 0, UID, "0");
- // dump should not include deleted channels
- int count = expectedChannels.get(pkg);
- expectedChannels.put(pkg, count - 1);
-
- JSONArray actual = mHelper.dumpChannelsJson(new NotificationManagerService.DumpFilter());
- assertEquals(numPackages, actual.length());
- for (int i = 0; i < numPackages; i++) {
- JSONObject object = actual.getJSONObject(i);
- assertTrue(expectedChannels.containsKey(object.get("packageName")));
- assertEquals(expectedChannels.get(object.get("packageName")).intValue(),
- object.getInt("channelCount"));
- }
- }
-
- @Test
- public void testBadgingOverrideTrue() throws Exception {
- Secure.putIntForUser(getContext().getContentResolver(),
- Secure.NOTIFICATION_BADGING, 1,
- USER.getIdentifier());
- mHelper.updateBadgingEnabled(); // would be called by settings observer
- assertTrue(mHelper.badgingEnabled(USER));
- }
-
- @Test
- public void testBadgingOverrideFalse() throws Exception {
- Secure.putIntForUser(getContext().getContentResolver(),
- Secure.NOTIFICATION_BADGING, 0,
- USER.getIdentifier());
- mHelper.updateBadgingEnabled(); // would be called by settings observer
- assertFalse(mHelper.badgingEnabled(USER));
- }
-
- @Test
- public void testBadgingForUserAll() throws Exception {
- try {
- mHelper.badgingEnabled(UserHandle.ALL);
- } catch (Exception e) {
- fail("just don't throw");
- }
- }
-
- @Test
- public void testBadgingOverrideUserIsolation() throws Exception {
- Secure.putIntForUser(getContext().getContentResolver(),
- Secure.NOTIFICATION_BADGING, 0,
- USER.getIdentifier());
- Secure.putIntForUser(getContext().getContentResolver(),
- Secure.NOTIFICATION_BADGING, 1,
- USER2.getIdentifier());
- mHelper.updateBadgingEnabled(); // would be called by settings observer
- assertFalse(mHelper.badgingEnabled(USER));
- assertTrue(mHelper.badgingEnabled(USER2));
- }
-
- @Test
- public void testOnLocaleChanged_updatesDefaultChannels() throws Exception {
- String newLabel = "bananas!";
- final NotificationChannel defaultChannel = mHelper.getNotificationChannel(PKG, UID,
- NotificationChannel.DEFAULT_CHANNEL_ID, false);
- assertFalse(newLabel.equals(defaultChannel.getName()));
-
- Resources res = mock(Resources.class);
- when(mContext.getResources()).thenReturn(res);
- when(res.getString(com.android.internal.R.string.default_notification_channel_label))
- .thenReturn(newLabel);
-
- mHelper.onLocaleChanged(mContext, USER.getIdentifier());
-
- assertEquals(newLabel, mHelper.getNotificationChannel(PKG, UID,
- NotificationChannel.DEFAULT_CHANNEL_ID, false).getName());
- }
-
- @Test
- public void testIsGroupBlocked_noGroup() throws Exception {
- assertFalse(mHelper.isGroupBlocked(PKG, UID, null));
-
- assertFalse(mHelper.isGroupBlocked(PKG, UID, "non existent group"));
- }
-
- @Test
- public void testIsGroupBlocked_notBlocked() throws Exception {
- NotificationChannelGroup group = new NotificationChannelGroup("id", "name");
- mHelper.createNotificationChannelGroup(PKG, UID, group, true);
-
- assertFalse(mHelper.isGroupBlocked(PKG, UID, group.getId()));
- }
-
- @Test
- public void testIsGroupBlocked_blocked() throws Exception {
- NotificationChannelGroup group = new NotificationChannelGroup("id", "name");
- mHelper.createNotificationChannelGroup(PKG, UID, group, true);
- group.setBlocked(true);
- mHelper.createNotificationChannelGroup(PKG, UID, group, false);
-
- assertTrue(mHelper.isGroupBlocked(PKG, UID, group.getId()));
- }
-
- @Test
- public void testIsGroup_appCannotResetBlock() throws Exception {
- NotificationChannelGroup group = new NotificationChannelGroup("id", "name");
- mHelper.createNotificationChannelGroup(PKG, UID, group, true);
- NotificationChannelGroup group2 = group.clone();
- group2.setBlocked(true);
- mHelper.createNotificationChannelGroup(PKG, UID, group2, false);
- assertTrue(mHelper.isGroupBlocked(PKG, UID, group.getId()));
-
- NotificationChannelGroup group3 = group.clone();
- group3.setBlocked(false);
- mHelper.createNotificationChannelGroup(PKG, UID, group3, true);
- assertTrue(mHelper.isGroupBlocked(PKG, UID, group.getId()));
- }
-
- @Test
- public void testGetNotificationChannelGroupWithChannels() throws Exception {
- NotificationChannelGroup group = new NotificationChannelGroup("group", "");
- NotificationChannelGroup other = new NotificationChannelGroup("something else", "");
- mHelper.createNotificationChannelGroup(PKG, UID, group, true);
- mHelper.createNotificationChannelGroup(PKG, UID, other, true);
-
- NotificationChannel a = new NotificationChannel("a", "a", IMPORTANCE_DEFAULT);
- a.setGroup(group.getId());
- NotificationChannel b = new NotificationChannel("b", "b", IMPORTANCE_DEFAULT);
- b.setGroup(other.getId());
- NotificationChannel c = new NotificationChannel("c", "c", IMPORTANCE_DEFAULT);
- c.setGroup(group.getId());
- NotificationChannel d = new NotificationChannel("d", "d", IMPORTANCE_DEFAULT);
-
- mHelper.createNotificationChannel(PKG, UID, a, true, false);
- mHelper.createNotificationChannel(PKG, UID, b, true, false);
- mHelper.createNotificationChannel(PKG, UID, c, true, false);
- mHelper.createNotificationChannel(PKG, UID, d, true, false);
- mHelper.deleteNotificationChannel(PKG, UID, c.getId());
-
- NotificationChannelGroup retrieved = mHelper.getNotificationChannelGroupWithChannels(
- PKG, UID, group.getId(), true);
- assertEquals(2, retrieved.getChannels().size());
- compareChannels(a, findChannel(retrieved.getChannels(), a.getId()));
- compareChannels(c, findChannel(retrieved.getChannels(), c.getId()));
-
- retrieved = mHelper.getNotificationChannelGroupWithChannels(
- PKG, UID, group.getId(), false);
- assertEquals(1, retrieved.getChannels().size());
- compareChannels(a, findChannel(retrieved.getChannels(), a.getId()));
- }
-
- @Test
- public void testAndroidPkgCannotBypassDnd_creation() {
- NotificationChannel test = new NotificationChannel("A", "a", IMPORTANCE_LOW);
- test.setBypassDnd(true);
-
- mHelper.createNotificationChannel(SYSTEM_PKG, SYSTEM_UID, test, true, false);
-
- assertFalse(mHelper.getNotificationChannel(SYSTEM_PKG, SYSTEM_UID, "A", false)
- .canBypassDnd());
- }
-
- @Test
- public void testDndPkgCanBypassDnd_creation() {
- NotificationChannel test = new NotificationChannel("A", "a", IMPORTANCE_LOW);
- test.setBypassDnd(true);
-
- mHelper.createNotificationChannel(PKG, UID, test, true, true);
-
- assertTrue(mHelper.getNotificationChannel(PKG, UID, "A", false).canBypassDnd());
- }
-
- @Test
- public void testNormalPkgCannotBypassDnd_creation() {
- NotificationChannel test = new NotificationChannel("A", "a", IMPORTANCE_LOW);
- test.setBypassDnd(true);
-
- mHelper.createNotificationChannel(PKG, 1000, test, true, false);
-
- assertFalse(mHelper.getNotificationChannel(PKG, 1000, "A", false).canBypassDnd());
- }
-
- @Test
- public void testAndroidPkgCannotBypassDnd_update() throws Exception {
- NotificationChannel test = new NotificationChannel("A", "a", IMPORTANCE_LOW);
- mHelper.createNotificationChannel(SYSTEM_PKG, SYSTEM_UID, test, true, false);
-
- NotificationChannel update = new NotificationChannel("A", "a", IMPORTANCE_LOW);
- update.setBypassDnd(true);
- mHelper.createNotificationChannel(SYSTEM_PKG, SYSTEM_UID, update, true, false);
-
- assertFalse(mHelper.getNotificationChannel(SYSTEM_PKG, SYSTEM_UID, "A", false)
- .canBypassDnd());
- }
-
- @Test
- public void testDndPkgCanBypassDnd_update() throws Exception {
- NotificationChannel test = new NotificationChannel("A", "a", IMPORTANCE_LOW);
- mHelper.createNotificationChannel(PKG, UID, test, true, true);
-
- NotificationChannel update = new NotificationChannel("A", "a", IMPORTANCE_LOW);
- update.setBypassDnd(true);
- mHelper.createNotificationChannel(PKG, UID, update, true, true);
-
- assertTrue(mHelper.getNotificationChannel(PKG, UID, "A", false).canBypassDnd());
- }
-
- @Test
- public void testNormalPkgCannotBypassDnd_update() {
- NotificationChannel test = new NotificationChannel("A", "a", IMPORTANCE_LOW);
- mHelper.createNotificationChannel(PKG, 1000, test, true, false);
- NotificationChannel update = new NotificationChannel("A", "a", IMPORTANCE_LOW);
- update.setBypassDnd(true);
- mHelper.createNotificationChannel(PKG, 1000, update, true, false);
- assertFalse(mHelper.getNotificationChannel(PKG, 1000, "A", false).canBypassDnd());
- }
-
- @Test
- public void testGetBlockedAppCount_noApps() {
- assertEquals(0, mHelper.getBlockedAppCount(0));
- }
-
- @Test
- public void testGetBlockedAppCount_noAppsForUserId() {
- mHelper.setEnabled(PKG, 100, false);
- assertEquals(0, mHelper.getBlockedAppCount(9));
- }
-
- @Test
- public void testGetBlockedAppCount_appsForUserId() {
- mHelper.setEnabled(PKG, 1020, false);
- mHelper.setEnabled(PKG, 1030, false);
- mHelper.setEnabled(PKG, 1060, false);
- mHelper.setEnabled(PKG, 1000, true);
- assertEquals(3, mHelper.getBlockedAppCount(0));
- }
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ScheduleCalendarTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ScheduleCalendarTest.java
index 942a07a..96ac935 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ScheduleCalendarTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ScheduleCalendarTest.java
@@ -121,7 +121,7 @@
cal.set(Calendar.MINUTE, 15);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
- mScheduleInfo.days = new int[] {getTodayDay(), getTodayDay() + 1};
+ mScheduleInfo.days = new int[] {getTodayDay(), getTodayDay(1)};
mScheduleInfo.startHour = 1;
mScheduleInfo.endHour = 3;
mScheduleInfo.startMinute = 15;
@@ -149,7 +149,7 @@
cal.set(Calendar.MINUTE, 15);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
- mScheduleInfo.days = new int[] {getTodayDay(), getTodayDay() + 1};
+ mScheduleInfo.days = new int[] {getTodayDay(), getTodayDay(1)};
mScheduleInfo.startHour = 22;
mScheduleInfo.endHour = 3;
mScheduleInfo.startMinute = 15;
@@ -250,7 +250,7 @@
calAlarm.add(Calendar.DATE, 1); // add a day
// ScheduleInfo: day 1, day 2: 9pm-7am
- mScheduleInfo.days = new int[] {getTodayDay(), getTodayDay() + 1};
+ mScheduleInfo.days = new int[] {getTodayDay(), getTodayDay(1)};
mScheduleInfo.startHour = 21;
mScheduleInfo.endHour = 7;
mScheduleInfo.startMinute = 0;
@@ -418,7 +418,7 @@
now.set(Calendar.MILLISECOND, 0);
now.add(Calendar.DATE, 1); // add a day
- mScheduleInfo.days = new int[] {getTodayDay(), getTodayDay() + 1};
+ mScheduleInfo.days = new int[] {getTodayDay(), getTodayDay(1)};
mScheduleInfo.startHour = 22;
mScheduleInfo.startMinute = 15;
mScheduleInfo.endHour = 3;
@@ -446,7 +446,7 @@
now.set(Calendar.MILLISECOND, 0);
now.add(Calendar.DATE, 1); // add a day
- mScheduleInfo.days = new int[] {getTodayDay(), getTodayDay() + 1};
+ mScheduleInfo.days = new int[] {getTodayDay(), getTodayDay(1)};
mScheduleInfo.startHour = 22;
mScheduleInfo.startMinute = 15;
mScheduleInfo.endHour = 3;
@@ -464,4 +464,10 @@
private int getTodayDay() {
return new GregorianCalendar().get(Calendar.DAY_OF_WEEK);
}
+
+ private int getTodayDay(int offset) {
+ Calendar cal = new GregorianCalendar();
+ cal.add(Calendar.DATE, offset);
+ return cal.get(Calendar.DAY_OF_WEEK);
+ }
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index afc1263..2ecda48 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -648,7 +648,7 @@
String xml = "<zen version=\"6\" user=\"0\">\n"
+ "<allow calls=\"false\" repeatCallers=\"false\" messages=\"true\" "
+ "reminders=\"false\" events=\"false\" callsFrom=\"1\" messagesFrom=\"2\" "
- + "visualScreenOff=\"false\" alarms=\"true\" "
+ + "visualScreenOff=\"true\" alarms=\"true\" "
+ "media=\"true\" system=\"false\" />\n"
+ "<disallow visualEffects=\"511\" />"
+ "</zen>";
@@ -664,7 +664,7 @@
xml = "<zen version=\"6\" user=\"0\">\n"
+ "<allow calls=\"false\" repeatCallers=\"false\" messages=\"true\" "
+ "reminders=\"false\" events=\"false\" callsFrom=\"1\" messagesFrom=\"2\" "
- + "visualScreenOn=\"false\" alarms=\"true\" "
+ + "visualScreenOn=\"true\" alarms=\"true\" "
+ "media=\"true\" system=\"false\" />\n"
+ "<disallow visualEffects=\"511\" />"
+ "</zen>";
@@ -683,7 +683,7 @@
String xml = "<zen version=\"6\" user=\"0\">\n"
+ "<allow calls=\"false\" repeatCallers=\"false\" messages=\"true\" "
+ "reminders=\"false\" events=\"false\" callsFrom=\"1\" messagesFrom=\"2\" "
- + "visualScreenOff=\"false\" visualScreenOn=\"false\" alarms=\"true\" "
+ + "visualScreenOff=\"true\" visualScreenOn=\"true\" alarms=\"true\" "
+ "media=\"true\" system=\"false\" />\n"
+ "<disallow visualEffects=\"511\" />"
+ "</zen>";
@@ -702,7 +702,7 @@
String xml = "<zen version=\"6\" user=\"0\">\n"
+ "<allow calls=\"false\" repeatCallers=\"false\" messages=\"true\" "
+ "reminders=\"false\" events=\"false\" callsFrom=\"1\" messagesFrom=\"2\" "
- + "visualScreenOff=\"true\" visualScreenOn=\"true\" alarms=\"true\" "
+ + "visualScreenOff=\"false\" visualScreenOn=\"false\" alarms=\"true\" "
+ "media=\"true\" system=\"false\" />\n"
+ "<disallow visualEffects=\"511\" />"
+ "</zen>";
@@ -721,7 +721,7 @@
xml = "<zen version=\"6\" user=\"0\">\n"
+ "<allow calls=\"false\" repeatCallers=\"false\" messages=\"true\" "
+ "reminders=\"false\" events=\"false\" callsFrom=\"1\" messagesFrom=\"2\" "
- + "visualScreenOff=\"false\" visualScreenOn=\"true\" alarms=\"true\" "
+ + "visualScreenOff=\"true\" visualScreenOn=\"false\" alarms=\"true\" "
+ "media=\"true\" system=\"false\" />\n"
+ "<disallow visualEffects=\"511\" />"
+ "</zen>";
@@ -737,7 +737,7 @@
xml = "<zen version=\"6\" user=\"0\">\n"
+ "<allow calls=\"false\" repeatCallers=\"false\" messages=\"true\" "
+ "reminders=\"false\" events=\"false\" callsFrom=\"1\" messagesFrom=\"2\" "
- + "visualScreenOff=\"true\" visualScreenOn=\"false\" alarms=\"true\" "
+ + "visualScreenOff=\"false\" visualScreenOn=\"true\" alarms=\"true\" "
+ "media=\"true\" system=\"false\" />\n"
+ "<disallow visualEffects=\"511\" />"
+ "</zen>";
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 3c4e333..82e0fbe 100644
--- a/services/tests/uiservicestests/src/com/android/server/slice/PinnedSliceStateTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/slice/PinnedSliceStateTest.java
@@ -1,16 +1,16 @@
package com.android.server.slice;
+import static android.testing.TestableContentResolver.UNSTABLE;
+
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -71,11 +71,12 @@
mSliceService = mock(SliceManagerService.class);
when(mSliceService.getContext()).thenReturn(mContext);
when(mSliceService.getLock()).thenReturn(new Object());
- when(mSliceService.getHandler()).thenReturn(new Handler(TestableLooper.get(this).getLooper()));
+ when(mSliceService.getHandler()).thenReturn(
+ new Handler(TestableLooper.get(this).getLooper()));
mContentProvider = mock(ContentProvider.class);
mIContentProvider = mock(IContentProvider.class);
when(mContentProvider.getIContentProvider()).thenReturn(mIContentProvider);
- mContext.getContentResolver().addProvider(AUTH, mContentProvider);
+ mContext.getContentResolver().addProvider(AUTH, mContentProvider, UNSTABLE);
mPinnedSliceManager = new PinnedSliceState(mSliceService, TEST_URI, "pkg");
}
diff --git a/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java
index 43a4e27..1db8967 100644
--- a/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java
@@ -18,6 +18,7 @@
import static android.content.pm.PackageManager.PERMISSION_DENIED;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
@@ -59,7 +60,7 @@
public class SliceManagerServiceTest extends UiServiceTestCase {
private static final String AUTH = "com.android.services.uitests";
- private static final Uri TEST_URI = maybeAddUserId(Uri.parse("content://" + AUTH + "/path"), 0);
+ private static final Uri TEST_URI = Uri.parse("content://" + AUTH + "/path");
private static final SliceSpec[] EMPTY_SPECS = new SliceSpec[]{
};
@@ -93,7 +94,7 @@
mService.pinSlice("pkg", TEST_URI, EMPTY_SPECS, mToken);
mService.pinSlice("pkg", TEST_URI, EMPTY_SPECS, mToken);
- verify(mService, times(1)).createPinnedSlice(eq(TEST_URI), anyString());
+ verify(mService, times(1)).createPinnedSlice(eq(maybeAddUserId(TEST_URI, 0)), anyString());
}
@Test
@@ -126,4 +127,19 @@
verify(mContextSpy).checkPermission(eq("perm2"), eq(Process.myPid()), eq(Process.myUid()));
}
+ @Test(expected = IllegalStateException.class)
+ public void testNoPinThrow() throws Exception {
+ mService.getPinnedSpecs(TEST_URI, "pkg");
+ }
+
+ @Test
+ public void testGetPinnedSpecs() throws Exception {
+ SliceSpec[] specs = new SliceSpec[] {
+ new SliceSpec("Something", 1) };
+ mService.pinSlice("pkg", TEST_URI, specs, mToken);
+
+ when(mCreatedSliceState.getSpecs()).thenReturn(specs);
+ assertEquals(specs, mService.getPinnedSpecs(TEST_URI, "pkg"));
+ }
+
}
diff --git a/services/usage/java/com/android/server/usage/IntervalStats.java b/services/usage/java/com/android/server/usage/IntervalStats.java
index 0dce738..4b7e21f 100644
--- a/services/usage/java/com/android/server/usage/IntervalStats.java
+++ b/services/usage/java/com/android/server/usage/IntervalStats.java
@@ -27,7 +27,9 @@
import java.util.List;
-class IntervalStats {
+import com.android.internal.annotations.VisibleForTesting;
+
+public class IntervalStats {
public long beginTime;
public long endTime;
public long lastTimeSaved;
@@ -149,7 +151,11 @@
&& eventType != UsageEvents.Event.STANDBY_BUCKET_CHANGED;
}
- void update(String packageName, long timeStamp, int eventType) {
+ /**
+ * @hide
+ */
+ @VisibleForTesting
+ public void update(String packageName, long timeStamp, int eventType) {
UsageStats usageStats = getOrCreateUsageStats(packageName);
// TODO(adamlesinski): Ensure that we recover from incorrect event sequences
diff --git a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
index 9705469..5ab5dc2 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
@@ -42,7 +42,7 @@
/**
* Provides an interface to query for UsageStat data from an XML database.
*/
-class UsageStatsDatabase {
+public class UsageStatsDatabase {
private static final int CURRENT_VERSION = 3;
// Current version of the backup schema
@@ -369,7 +369,7 @@
/**
* Figures out what to extract from the given IntervalStats object.
*/
- interface StatCombiner<T> {
+ public interface StatCombiner<T> {
/**
* Implementations should extract interesting from <code>stats</code> and add it
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index b8dfd38..dd1ddfa 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -712,13 +712,13 @@
if (mPackageManagerInternal.getPackageUid(pkg, /*flags=*/ 0,
callingUserId) != callingUid) {
- throw new SecurityException("Calling uid " + pkg + " cannot query events"
+ throw new SecurityException("Calling uid " + callingUid + " cannot query events"
+ "for package " + pkg);
}
}
private boolean isCallingUidSystem() {
- final int uid = Binder.getCallingUid();
+ final int uid = UserHandle.getAppId(Binder.getCallingUid()); // ignores user
return uid == Process.SYSTEM_UID;
}
@@ -924,8 +924,7 @@
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
- final int packageUid = mPackageManagerInternal.getPackageUid(packageName,
- PackageManager.MATCH_ANY_USER, userId);
+ final int packageUid = mPackageManagerInternal.getPackageUid(packageName, 0, userId);
// If the calling app is asking about itself, continue, else check for permission.
if (packageUid != callingUid) {
if (!hasPermission(callingPackage)) {
@@ -974,7 +973,8 @@
final long token = Binder.clearCallingIdentity();
try {
final int packageUid = mPackageManagerInternal.getPackageUid(packageName,
- PackageManager.MATCH_ANY_USER, userId);
+ PackageManager.MATCH_ANY_USER | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
+ | PackageManager.MATCH_DIRECT_BOOT_AWARE, userId);
// Caller cannot set their own standby state
if (packageUid == callingUid) {
throw new IllegalArgumentException("Cannot set your own standby bucket");
diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
index 4efe0b5..9b194e9 100644
--- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
@@ -64,6 +64,9 @@
private String mLastBackgroundedPackage;
private final int mUserId;
+ // STOPSHIP: Temporary member variable for debugging b/110930764.
+ private UsageEvents.Event mLastEvent;
+
private static final long[] INTERVAL_LENGTH = new long[] {
UnixCalendar.DAY_IN_MILLIS, UnixCalendar.WEEK_IN_MILLIS,
UnixCalendar.MONTH_IN_MILLIS, UnixCalendar.YEAR_IN_MILLIS
@@ -156,6 +159,8 @@
+ eventToString(event.mEventType));
}
+ mLastEvent = new UsageEvents.Event(event);
+
if (event.mTimeStamp >= mDailyExpiryDate.getTimeInMillis()) {
// Need to rollover
rolloverStats(event.mTimeStamp);
@@ -306,6 +311,36 @@
Slog.d(TAG, mLogPrefix + "Requesting stats after " + beginTime + " but latest is "
+ currentStats.endTime);
}
+
+ // STOPSHIP: Temporary logging for b/110930764.
+ if (intervalType == UsageStatsManager.INTERVAL_DAILY
+ && mLastEvent != null && mLastEvent.mTimeStamp >= beginTime) {
+ final IntervalStats diskStats = mDatabase.getLatestUsageStats(
+ UsageStatsManager.INTERVAL_DAILY);
+ StringBuilder sb = new StringBuilder(256);
+ sb.append("Last 24 hours of UsageStats missing! timeRange : ");
+ sb.append(beginTime);
+ sb.append(", ");
+ sb.append(endTime);
+ sb.append("\nLast reported Usage Event time : ");
+ sb.append(mLastEvent.mTimeStamp);
+ if (currentStats == null) {
+ sb.append("\nNo in memory event stats available.");
+ } else {
+ sb.append("\nLast in memory event time : ");
+ sb.append(currentStats.endTime);
+ sb.append("\nLast save time: ");
+ sb.append(currentStats.lastTimeSaved);
+ }
+ if (diskStats == null) {
+ sb.append("\nNo on disk event stats available.");
+ } else {
+ sb.append("\nLast on disk event time : ");
+ sb.append(diskStats.endTime);
+ }
+ Slog.wtf(TAG, sb.toString());
+ }
+
// Nothing newer available.
return null;
}
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index a919d38..63945a9 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -20,8 +20,8 @@
import static com.android.internal.util.dump.DumpUtils.writeStringIfNotNull;
import android.app.ActivityManager;
-import android.app.ActivityManagerInternal;
-import android.app.ActivityTaskManagerInternal;
+
+import com.android.server.wm.ActivityTaskManagerInternal;
import android.app.KeyguardManager;
import android.app.Notification;
import android.app.NotificationChannel;
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbDescriptor.java b/services/usb/java/com/android/server/usb/descriptors/UsbDescriptor.java
index 3fc5fe3..ff67667 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbDescriptor.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbDescriptor.java
@@ -126,6 +126,9 @@
public static final int REQUEST_GET_CONFIGURATION = 0x08;
public static final int REQUEST_SET_CONFIGURATION = 0x09;
+ // USB control transfer timeout
+ public static final int USB_CONTROL_TRANSFER_TIMEOUT_MS = 200;
+
/**
* @throws IllegalArgumentException
*/
@@ -224,7 +227,7 @@
0,
sStringBuffer,
0xFF,
- 0);
+ USB_CONTROL_TRANSFER_TIMEOUT_MS);
if (rdo >= 0) {
usbStr = new String(sStringBuffer, 2, rdo - 2, "UTF-16LE");
} else {
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index cd1b835..c5d6dc7 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -19,7 +19,7 @@
import android.Manifest;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
-import android.app.ActivityTaskManagerInternal;
+import com.android.server.wm.ActivityTaskManagerInternal;
import android.app.AppGlobals;
import android.content.ComponentName;
import android.content.ContentResolver;
@@ -433,11 +433,11 @@
if (hasComponent) {
mShortcutServiceInternal.setShortcutHostPackage(TAG,
serviceComponent.getPackageName(), mCurUser);
- mAmInternal.setAllowAppSwitches(TAG,
+ mAtmInternal.setAllowAppSwitches(TAG,
serviceInfo.applicationInfo.uid, mCurUser);
} else {
mShortcutServiceInternal.setShortcutHostPackage(TAG, null, mCurUser);
- mAmInternal.setAllowAppSwitches(TAG, -1, mCurUser);
+ mAtmInternal.setAllowAppSwitches(TAG, -1, mCurUser);
}
}
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
index 54f99a6..57e9f66 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
@@ -23,10 +23,9 @@
import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
import android.app.ActivityManager;
-import android.app.ActivityManagerInternal;
import android.app.ActivityOptions;
import android.app.ActivityTaskManager;
-import android.app.ActivityTaskManagerInternal;
+import com.android.server.wm.ActivityTaskManagerInternal;
import android.app.IActivityManager;
import android.app.IActivityTaskManager;
import android.content.BroadcastReceiver;
@@ -50,7 +49,6 @@
import android.util.Slog;
import android.view.IWindowManager;
-import com.android.internal.app.IVoiceInteractionSessionListener;
import com.android.internal.app.IVoiceInteractionSessionShowCallback;
import com.android.internal.app.IVoiceInteractor;
import com.android.server.LocalServices;
@@ -246,7 +244,7 @@
Slog.w(TAG, "setKeepAwake does not match active session");
return;
}
- mAm.setVoiceKeepAwake(mActiveSession.mSession, keepAwake);
+ mAtm.setVoiceKeepAwake(mActiveSession.mSession, keepAwake);
} catch (RemoteException e) {
throw new IllegalStateException("Unexpected remote error", e);
}
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
index 4eaed34..01f5d9c 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
@@ -16,9 +16,9 @@
package com.android.server.voiceinteraction;
-import static android.app.ActivityTaskManagerInternal.ASSIST_KEY_CONTENT;
-import static android.app.ActivityTaskManagerInternal.ASSIST_KEY_DATA;
-import static android.app.ActivityTaskManagerInternal.ASSIST_KEY_STRUCTURE;
+import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_CONTENT;
+import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_DATA;
+import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_STRUCTURE;
import static android.app.AppOpsManager.OP_ASSIST_SCREENSHOT;
import static android.app.AppOpsManager.OP_ASSIST_STRUCTURE;
import static android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION;
@@ -144,7 +144,7 @@
mIWindowManager = IWindowManager.Stub.asInterface(
ServiceManager.getService(Context.WINDOW_SERVICE));
mAppOps = context.getSystemService(AppOpsManager.class);
- mAssistDataRequester = new AssistDataRequester(mContext, mAm, mIWindowManager,
+ mAssistDataRequester = new AssistDataRequester(mContext, mIWindowManager,
(AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE),
this, mLock, OP_ASSIST_STRUCTURE, OP_ASSIST_SCREENSHOT);
IBinder permOwner = null;
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 3bf951d..22958d4 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -45,6 +45,7 @@
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
+import java.nio.channels.Channels;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -853,6 +854,8 @@
private final OutputStreamWriter mPipeToInCall;
private final ParcelFileDescriptor mFdFromInCall;
private final ParcelFileDescriptor mFdToInCall;
+
+ private final FileInputStream mFromInCallFileInputStream;
private char[] mReadBuffer = new char[READ_BUFFER_SIZE];
/**
@@ -861,8 +864,11 @@
public RttTextStream(ParcelFileDescriptor toInCall, ParcelFileDescriptor fromInCall) {
mFdFromInCall = fromInCall;
mFdToInCall = toInCall;
+ mFromInCallFileInputStream = new FileInputStream(fromInCall.getFileDescriptor());
+
+ // Wrap the FileInputStream in a Channel so that it's interruptible.
mPipeFromInCall = new InputStreamReader(
- new FileInputStream(fromInCall.getFileDescriptor()));
+ Channels.newInputStream(Channels.newChannel(mFromInCallFileInputStream)));
mPipeToInCall = new OutputStreamWriter(
new FileOutputStream(toInCall.getFileDescriptor()));
}
@@ -910,7 +916,7 @@
* not entered any new text yet.
*/
public String readImmediately() throws IOException {
- if (mPipeFromInCall.ready()) {
+ if (mFromInCallFileInputStream.available() > 0) {
return read();
} else {
return null;
@@ -2804,9 +2810,21 @@
public void onReject(String replyMessage) {}
/**
- * Notifies the Connection of a request to silence the ringer.
- *
- * @hide
+ * Notifies this Connection of a request to silence the ringer.
+ * <p>
+ * The ringer may be silenced by any of the following methods:
+ * <ul>
+ * <li>{@link TelecomManager#silenceRinger()}</li>
+ * <li>The user presses the volume-down button while a call is ringing.</li>
+ * </ul>
+ * <p>
+ * Self-managed {@link ConnectionService} implementations should override this method in their
+ * {@link Connection} implementation and implement logic to silence their app's ringtone. If
+ * your app set the ringtone as part of the incoming call {@link Notification} (see
+ * {@link #onShowIncomingCallUi()}), it should re-post the notification now, except call
+ * {@link android.app.Notification.Builder#setOnlyAlertOnce(boolean)} with {@code true}. This
+ * will ensure the ringtone sound associated with your {@link android.app.NotificationChannel}
+ * stops playing.
*/
public void onSilence() {}
@@ -2883,7 +2901,29 @@
* <p>
* You should trigger the display of the incoming call user interface for your application by
* showing a {@link Notification} with a full-screen {@link Intent} specified.
- * For example:
+ *
+ * In your application code, you should create a {@link android.app.NotificationChannel} for
+ * incoming call notifications from your app:
+ * <pre><code>
+ * NotificationChannel channel = new NotificationChannel(YOUR_CHANNEL_ID, "Incoming Calls",
+ * NotificationManager.IMPORTANCE_MAX);
+ * // other channel setup stuff goes here.
+ *
+ * // We'll use the default system ringtone for our incoming call notification channel. You can
+ * // use your own audio resource here.
+ * Uri ringtoneUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE);
+ * channel.setSound(ringtoneUri, new AudioAttributes.Builder()
+ * // Setting the AudioAttributes is important as it identifies the purpose of your
+ * // notification sound.
+ * .setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE)
+ * .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
+ * .build());
+ *
+ * NotificationManager mgr = getSystemService(NotificationManager.class);
+ * mgr.createNotificationChannel(channel);
+ * </code></pre>
+ * When it comes time to post a notification for your incoming call, ensure it uses your
+ * incoming call {@link android.app.NotificationChannel}.
* <pre><code>
* // Create an intent which triggers your fullscreen incoming call user interface.
* Intent intent = new Intent(Intent.ACTION_MAIN, null);
@@ -2909,11 +2949,14 @@
* builder.setContentTitle("Your notification title");
* builder.setContentText("Your notification content.");
*
- * // Use builder.addAction(..) to add buttons to answer or reject the call.
+ * // Set notification as insistent to cause your ringtone to loop.
+ * Notification notification = builder.build();
+ * notification.flags |= Notification.FLAG_INSISTENT;
*
+ * // Use builder.addAction(..) to add buttons to answer or reject the call.
* NotificationManager notificationManager = mContext.getSystemService(
* NotificationManager.class);
- * notificationManager.notify(YOUR_TAG, YOUR_ID, builder.build());
+ * notificationManager.notify(YOUR_CHANNEL_ID, YOUR_TAG, YOUR_ID, notification);
* </code></pre>
*/
public void onShowIncomingCallUi() {}
diff --git a/telecomm/java/android/telecom/InCallService.java b/telecomm/java/android/telecom/InCallService.java
index bd25ab2..1aeeca7 100644
--- a/telecomm/java/android/telecom/InCallService.java
+++ b/telecomm/java/android/telecom/InCallService.java
@@ -47,13 +47,19 @@
* before the telecom service will bind to its {@code InCallService} implementation.
* <p>
* Below is an example manifest registration for an {@code InCallService}. The meta-data
- * ({@link TelecomManager#METADATA_IN_CALL_SERVICE_UI}) indicates that this particular
+ * {@link TelecomManager#METADATA_IN_CALL_SERVICE_UI} indicates that this particular
* {@code InCallService} implementation intends to replace the built-in in-call UI.
+ * The meta-data {@link TelecomManager#METADATA_IN_CALL_SERVICE_RINGING} indicates that this
+ * {@link InCallService} will play the ringtone for incoming calls. See
+ * <a href="#incomingCallNotification">below</a> for more information on showing the incoming call
+ * UI and playing the ringtone in your app.
* <pre>
* {@code
* <service android:name="your.package.YourInCallServiceImplementation"
* android:permission="android.permission.BIND_INCALL_SERVICE">
* <meta-data android:name="android.telecom.IN_CALL_SERVICE_UI" android:value="true" />
+ * <meta-data android:name="android.telecom.IN_CALL_SERVICE_RINGING"
+ * android:value="true" />
* <intent-filter>
* <action android:name="android.telecom.InCallService"/>
* </intent-filter>
@@ -80,6 +86,72 @@
* to see if they would like your application to be the new default phone app. See the
* {@link TelecomManager#ACTION_CHANGE_DEFAULT_DIALER} intent documentation for more information on
* how to do this.
+ * <p id="incomingCallNotification">
+ * <h2>Showing the Incoming Call Notification</h2>
+ * When your app receives a new incoming call via {@link InCallService#onCallAdded(Call)}, it is
+ * responsible for displaying an incoming call UI for the incoming call. It should do this using
+ * {@link android.app.NotificationManager} APIs to post a new incoming call notification.
+ * <p>
+ * Where your app declares the meta-data {@link TelecomManager#METADATA_IN_CALL_SERVICE_RINGING}, it
+ * is responsible for playing the ringtone for incoming calls. Your app should create a
+ * {@link android.app.NotificationChannel} which specifies the desired ringtone. For example:
+ * <pre><code>
+ * NotificationChannel channel = new NotificationChannel(YOUR_CHANNEL_ID, "Incoming Calls",
+ * NotificationManager.IMPORTANCE_MAX);
+ * // other channel setup stuff goes here.
+ *
+ * // We'll use the default system ringtone for our incoming call notification channel. You can
+ * // use your own audio resource here.
+ * Uri ringtoneUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE);
+ * channel.setSound(ringtoneUri, new AudioAttributes.Builder()
+ * // Setting the AudioAttributes is important as it identifies the purpose of your
+ * // notification sound.
+ * .setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE)
+ * .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
+ * .build());
+ *
+ * NotificationManager mgr = getSystemService(NotificationManager.class);
+ * mgr.createNotificationChannel(channel);
+ * </code></pre>
+ * <p>
+ * When your app receives a new incoming call, it creates a {@link android.app.Notification} for the
+ * incoming call and associates it with your incoming call notification channel. You can specify a
+ * {@link android.app.PendingIntent} on the notification which will launch your full screen
+ * incoming call UI. The notification manager framework will display your notification as a
+ * heads-up notification if the user is actively using the phone. When the user is not using the
+ * phone, your full-screen incoming call UI is used instead.
+ * For example:
+ * <pre><code>
+ * // Create an intent which triggers your fullscreen incoming call user interface.
+ * Intent intent = new Intent(Intent.ACTION_MAIN, null);
+ * intent.setFlags(Intent.FLAG_ACTIVITY_NO_USER_ACTION | Intent.FLAG_ACTIVITY_NEW_TASK);
+ * intent.setClass(context, YourIncomingCallActivity.class);
+ * PendingIntent pendingIntent = PendingIntent.getActivity(context, 1, intent, 0);
+ *
+ * // Build the notification as an ongoing high priority item; this ensures it will show as
+ * // a heads up notification which slides down over top of the current content.
+ * final Notification.Builder builder = new Notification.Builder(context);
+ * builder.setOngoing(true);
+ * builder.setPriority(Notification.PRIORITY_HIGH);
+ *
+ * // Set notification content intent to take user to the fullscreen UI if user taps on the
+ * // notification body.
+ * builder.setContentIntent(pendingIntent);
+ * // Set full screen intent to trigger display of the fullscreen UI when the notification
+ * // manager deems it appropriate.
+ * builder.setFullScreenIntent(pendingIntent, true);
+ *
+ * // Setup notification content.
+ * builder.setSmallIcon( yourIconResourceId );
+ * builder.setContentTitle("Your notification title");
+ * builder.setContentText("Your notification content.");
+ *
+ * // Use builder.addAction(..) to add buttons to answer or reject the call.
+ *
+ * NotificationManager notificationManager = mContext.getSystemService(
+ * NotificationManager.class);
+ * notificationManager.notify(YOUR_CHANNEL_ID, YOUR_TAG, YOUR_ID, builder.build());
+ * </code></pre>
*/
public abstract class InCallService extends Service {
diff --git a/telephony/java/android/provider/Telephony.java b/telephony/java/android/provider/Telephony.java
index 2d096c0..8283e97 100644
--- a/telephony/java/android/provider/Telephony.java
+++ b/telephony/java/android/provider/Telephony.java
@@ -3542,6 +3542,12 @@
public static final String ICCID_PREFIX = "iccid_prefix";
/**
+ * Certificate for carrier privilege access rules.
+ * <P>Type: TEXT in hex string </P>
+ */
+ public static final String PRIVILEGE_ACCESS_RULE = "privilege_access_rule";
+
+ /**
* The {@code content://} URI for this table.
*/
public static final Uri CONTENT_URI = Uri.parse("content://carrier_id/all");
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index f60332e..cabf444 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -80,6 +80,30 @@
public static final String
KEY_CARRIER_VOLTE_PROVISIONED_BOOL = "carrier_volte_provisioned_bool";
+ /**
+ * Boolean indicating if the "Call forwarding" item is visible in the Call Settings menu.
+ * true means visible. false means gone.
+ * @hide
+ */
+ public static final String KEY_CALL_FORWARDING_VISIBILITY_BOOL =
+ "call_forwarding_visibility_bool";
+
+ /**
+ * Boolean indicating if the "Caller ID" item is visible in the Additional Settings menu.
+ * true means visible. false means gone.
+ * @hide
+ */
+ public static final String KEY_ADDITIONAL_SETTINGS_CALLER_ID_VISIBILITY_BOOL =
+ "additional_settings_caller_id_visibility_bool";
+
+ /**
+ * Boolean indicating if the "Call Waiting" item is visible in the Additional Settings menu.
+ * true means visible. false means gone.
+ * @hide
+ */
+ public static final String KEY_ADDITIONAL_SETTINGS_CALL_WAITING_VISIBILITY_BOOL =
+ "additional_settings_call_waiting_visibility_bool";
+
/**
* Boolean indicating if the "Call barring" item is visible in the Call Settings menu.
* true means visible. false means gone.
@@ -116,6 +140,14 @@
public static final String
KEY_SIM_NETWORK_UNLOCK_ALLOW_DISMISS_BOOL = "sim_network_unlock_allow_dismiss_bool";
+ /**
+ * Flag indicating whether or not sending emergency SMS messages over IMS
+ * is supported when in LTE/limited LTE (Emergency only) service mode..
+ *
+ */
+ public static final String
+ KEY_SUPPORT_EMERGENCY_SMS_OVER_IMS_BOOL = "support_emergency_sms_over_ims_bool";
+
/** Flag indicating if the phone is a world phone */
public static final String KEY_WORLD_PHONE_BOOL = "world_phone_bool";
@@ -1034,6 +1066,26 @@
public static final String KEY_CARRIER_NAME_STRING = "carrier_name_string";
/**
+ * Override the registered PLMN name using #KEY_CDMA_HOME_REGISTERED_PLMN_NAME_STRING.
+ *
+ * If true, then the registered PLMN name (only for CDMA/CDMA-LTE and only when not roaming)
+ * will be #KEY_CDMA_HOME_REGISTERED_PLMN_NAME_STRING. If false, or if phone type is not
+ * CDMA/CDMA-LTE or if roaming, then #KEY_CDMA_HOME_REGISTERED_PLMN_NAME_STRING will be ignored.
+ * @hide
+ */
+ public static final String KEY_CDMA_HOME_REGISTERED_PLMN_NAME_OVERRIDE_BOOL =
+ "cdma_home_registered_plmn_name_override_bool";
+
+ /**
+ * String to identify registered PLMN name in CarrierConfig app. This string overrides
+ * registered PLMN name if #KEY_CDMA_HOME_REGISTERED_PLMN_NAME_OVERRIDE_BOOL is true, phone type
+ * is CDMA/CDMA-LTE and device is not in roaming state; otherwise, it will be ignored.
+ * @hide
+ */
+ public static final String KEY_CDMA_HOME_REGISTERED_PLMN_NAME_STRING =
+ "cdma_home_registered_plmn_name_string";
+
+ /**
* If this is true, the SIM card (through Customer Service Profile EF file) will be able to
* prevent manual operator selection. If false, this SIM setting will be ignored and manual
* operator selection will always be available. See CPHS4_2.WW6, CPHS B.4.7.1 for more
@@ -1548,7 +1600,7 @@
public static final String KEY_EDITABLE_WFC_ROAMING_MODE_BOOL =
"editable_wfc_roaming_mode_bool";
- /**
+ /**
* Determine whether current lpp_mode used for E-911 needs to be kept persistently.
* {@code false} - not keeping the lpp_mode means using default configuration of gps.conf
* when sim is not presented.
@@ -2018,6 +2070,9 @@
sDefaults.putBoolean(KEY_CARRIER_VOLTE_PROVISIONED_BOOL, false);
sDefaults.putBoolean(KEY_CALL_BARRING_VISIBILITY_BOOL, false);
+ sDefaults.putBoolean(KEY_CALL_FORWARDING_VISIBILITY_BOOL, true);
+ sDefaults.putBoolean(KEY_ADDITIONAL_SETTINGS_CALLER_ID_VISIBILITY_BOOL, true);
+ sDefaults.putBoolean(KEY_ADDITIONAL_SETTINGS_CALL_WAITING_VISIBILITY_BOOL, true);
sDefaults.putBoolean(KEY_IGNORE_SIM_NETWORK_LOCKED_EVENTS_BOOL, false);
sDefaults.putBoolean(KEY_MDN_IS_ADDITIONAL_VOICEMAIL_NUMBER_BOOL, false);
sDefaults.putBoolean(KEY_OPERATOR_SELECTION_EXPAND_BOOL, true);
@@ -2025,6 +2080,7 @@
sDefaults.putBoolean(KEY_SHOW_APN_SETTING_CDMA_BOOL, false);
sDefaults.putBoolean(KEY_SHOW_CDMA_CHOICES_BOOL, false);
sDefaults.putBoolean(KEY_SMS_REQUIRES_DESTINATION_NUMBER_CONVERSION_BOOL, false);
+ sDefaults.putBoolean(KEY_SUPPORT_EMERGENCY_SMS_OVER_IMS_BOOL, false);
sDefaults.putBoolean(KEY_SHOW_ONSCREEN_DIAL_BUTTON_BOOL, true);
sDefaults.putBoolean(KEY_SIM_NETWORK_UNLOCK_ALLOW_DISMISS_BOOL, true);
sDefaults.putBoolean(KEY_SUPPORT_PAUSE_IMS_VIDEO_CALLS_BOOL, false);
@@ -2132,6 +2188,8 @@
sDefaults.putBoolean(KEY_CONFIG_WIFI_DISABLE_IN_ECBM, false);
sDefaults.putBoolean(KEY_CARRIER_NAME_OVERRIDE_BOOL, false);
sDefaults.putString(KEY_CARRIER_NAME_STRING, "");
+ sDefaults.putBoolean(KEY_CDMA_HOME_REGISTERED_PLMN_NAME_OVERRIDE_BOOL, false);
+ sDefaults.putString(KEY_CDMA_HOME_REGISTERED_PLMN_NAME_STRING, "");
sDefaults.putBoolean(KEY_SUPPORT_DIRECT_FDN_DIALING_BOOL, false);
sDefaults.putBoolean(KEY_CARRIER_DEFAULT_DATA_ROAMING_ENABLED_BOOL, false);
sDefaults.putBoolean(KEY_SKIP_CF_FAIL_TO_DISABLE_DIALOG_BOOL, false);
diff --git a/telephony/java/android/telephony/CellIdentity.java b/telephony/java/android/telephony/CellIdentity.java
index 890a6ea..2a41829 100644
--- a/telephony/java/android/telephony/CellIdentity.java
+++ b/telephony/java/android/telephony/CellIdentity.java
@@ -175,7 +175,10 @@
}
CellIdentity o = (CellIdentity) other;
- return TextUtils.equals(mAlphaLong, o.mAlphaLong)
+ return mType == o.mType
+ && TextUtils.equals(mMccStr, o.mMccStr)
+ && TextUtils.equals(mMncStr, o.mMncStr)
+ && TextUtils.equals(mAlphaLong, o.mAlphaLong)
&& TextUtils.equals(mAlphaShort, o.mAlphaShort);
}
@@ -233,4 +236,4 @@
protected void log(String s) {
Rlog.w(mTag, s);
}
-}
\ No newline at end of file
+}
diff --git a/telephony/java/android/telephony/CellIdentityTdscdma.java b/telephony/java/android/telephony/CellIdentityTdscdma.java
index 18ab6d4..b99fe46 100644
--- a/telephony/java/android/telephony/CellIdentityTdscdma.java
+++ b/telephony/java/android/telephony/CellIdentityTdscdma.java
@@ -16,9 +16,7 @@
package android.telephony;
-import android.annotation.Nullable;
import android.os.Parcel;
-import android.text.TextUtils;
import java.util.Objects;
@@ -35,6 +33,8 @@
private final int mCid;
// 8-bit Cell Parameters ID described in TS 25.331, 0..127, INT_MAX if unknown.
private final int mCpid;
+ // 16-bit UMTS Absolute RF Channel Number described in TS 25.101 sec. 5.4.3
+ private final int mUarfcn;
/**
* @hide
@@ -44,6 +44,7 @@
mLac = Integer.MAX_VALUE;
mCid = Integer.MAX_VALUE;
mCpid = Integer.MAX_VALUE;
+ mUarfcn = Integer.MAX_VALUE;
}
/**
@@ -52,11 +53,12 @@
* @param lac 16-bit Location Area Code, 0..65535, INT_MAX if unknown
* @param cid 28-bit UMTS Cell Identity described in TS 25.331, 0..268435455, INT_MAX if unknown
* @param cpid 8-bit Cell Parameters ID described in TS 25.331, 0..127, INT_MAX if unknown
+ * @param uarfcn 16-bit UMTS Absolute RF Channel Number described in TS 25.101 sec. 5.4.3
*
* @hide
*/
- public CellIdentityTdscdma(int mcc, int mnc, int lac, int cid, int cpid) {
- this(String.valueOf(mcc), String.valueOf(mnc), lac, cid, cpid, null, null);
+ public CellIdentityTdscdma(int mcc, int mnc, int lac, int cid, int cpid, int uarfcn) {
+ this(String.valueOf(mcc), String.valueOf(mnc), lac, cid, cpid, uarfcn, null, null);
}
/**
@@ -65,39 +67,24 @@
* @param lac 16-bit Location Area Code, 0..65535, INT_MAX if unknown
* @param cid 28-bit UMTS Cell Identity described in TS 25.331, 0..268435455, INT_MAX if unknown
* @param cpid 8-bit Cell Parameters ID described in TS 25.331, 0..127, INT_MAX if unknown
- *
- * FIXME: This is a temporary constructor to facilitate migration.
- * @hide
- */
- public CellIdentityTdscdma(String mcc, String mnc, int lac, int cid, int cpid) {
- super(TAG, TYPE_TDSCDMA, mcc, mnc, null, null);
- mLac = lac;
- mCid = cid;
- mCpid = cpid;
- }
-
- /**
- * @param mcc 3-digit Mobile Country Code in string format
- * @param mnc 2 or 3-digit Mobile Network Code in string format
- * @param lac 16-bit Location Area Code, 0..65535, INT_MAX if unknown
- * @param cid 28-bit UMTS Cell Identity described in TS 25.331, 0..268435455, INT_MAX if unknown
- * @param cpid 8-bit Cell Parameters ID described in TS 25.331, 0..127, INT_MAX if unknown
+ * @param uarfcn 16-bit UMTS Absolute RF Channel Number described in TS 25.101 sec. 5.4.3
* @param alphal long alpha Operator Name String or Enhanced Operator Name String
* @param alphas short alpha Operator Name String or Enhanced Operator Name String
*
* @hide
*/
- public CellIdentityTdscdma(String mcc, String mnc, int lac, int cid, int cpid,
+ public CellIdentityTdscdma(String mcc, String mnc, int lac, int cid, int cpid, int uarfcn,
String alphal, String alphas) {
super(TAG, TYPE_TDSCDMA, mcc, mnc, alphal, alphas);
mLac = lac;
mCid = cid;
mCpid = cpid;
+ mUarfcn = uarfcn;
}
private CellIdentityTdscdma(CellIdentityTdscdma cid) {
this(cid.mMccStr, cid.mMncStr, cid.mLac, cid.mCid,
- cid.mCpid, cid.mAlphaLong, cid.mAlphaShort);
+ cid.mCpid, cid.mUarfcn, cid.mAlphaLong, cid.mAlphaShort);
}
CellIdentityTdscdma copy() {
@@ -141,9 +128,10 @@
return mCpid;
}
+ /** @hide */
@Override
- public int hashCode() {
- return Objects.hash(mLac, mCid, mCpid, super.hashCode());
+ public int getChannelNumber() {
+ return mUarfcn;
}
@Override
@@ -157,24 +145,29 @@
}
CellIdentityTdscdma o = (CellIdentityTdscdma) other;
- return TextUtils.equals(mMccStr, o.mMccStr)
- && TextUtils.equals(mMncStr, o.mMncStr)
- && mLac == o.mLac
+ return mLac == o.mLac
&& mCid == o.mCid
&& mCpid == o.mCpid
+ && mUarfcn == o.mUarfcn
&& super.equals(other);
}
@Override
+ public int hashCode() {
+ return Objects.hash(mLac, mCid, mCpid, mUarfcn, super.hashCode());
+ }
+
+ @Override
public String toString() {
return new StringBuilder(TAG)
.append(":{ mMcc=").append(mMccStr)
.append(" mMnc=").append(mMncStr)
+ .append(" mAlphaLong=").append(mAlphaLong)
+ .append(" mAlphaShort=").append(mAlphaShort)
.append(" mLac=").append(mLac)
.append(" mCid=").append(mCid)
.append(" mCpid=").append(mCpid)
- .append(" mAlphaLong=").append(mAlphaLong)
- .append(" mAlphaShort=").append(mAlphaShort)
+ .append(" mUarfcn=").append(mUarfcn)
.append("}").toString();
}
@@ -186,6 +179,7 @@
dest.writeInt(mLac);
dest.writeInt(mCid);
dest.writeInt(mCpid);
+ dest.writeInt(mUarfcn);
}
/** Construct from Parcel, type has already been processed */
@@ -194,7 +188,7 @@
mLac = in.readInt();
mCid = in.readInt();
mCpid = in.readInt();
-
+ mUarfcn = in.readInt();
if (DBG) log(toString());
}
diff --git a/telephony/java/android/telephony/CellIdentityWcdma.java b/telephony/java/android/telephony/CellIdentityWcdma.java
index 984483e..43f9406 100644
--- a/telephony/java/android/telephony/CellIdentityWcdma.java
+++ b/telephony/java/android/telephony/CellIdentityWcdma.java
@@ -35,7 +35,7 @@
private final int mCid;
// 9-bit UMTS Primary Scrambling Code described in TS 25.331, 0..511
private final int mPsc;
- // 16-bit UMTS Absolute RF Channel Number
+ // 16-bit UMTS Absolute RF Channel Number described in TS 25.101 sec. 5.4.4
private final int mUarfcn;
/**
@@ -70,7 +70,7 @@
* @param lac 16-bit Location Area Code, 0..65535
* @param cid 28-bit UMTS Cell Identity
* @param psc 9-bit UMTS Primary Scrambling Code
- * @param uarfcn 16-bit UMTS Absolute RF Channel Number
+ * @param uarfcn 16-bit UMTS Absolute RF Channel Number described in TS 25.101 sec. 5.4.3
*
* @hide
*/
@@ -83,7 +83,7 @@
* @param lac 16-bit Location Area Code, 0..65535
* @param cid 28-bit UMTS Cell Identity
* @param psc 9-bit UMTS Primary Scrambling Code
- * @param uarfcn 16-bit UMTS Absolute RF Channel Number
+ * @param uarfcn 16-bit UMTS Absolute RF Channel Number described in TS 25.101 sec. 5.4.3
* @param mccStr 3-digit Mobile Country Code in string format
* @param mncStr 2 or 3-digit Mobile Network Code in string format
* @param alphal long alpha Operator Name String or Enhanced Operator Name String
diff --git a/telephony/java/android/telephony/CellInfo.java b/telephony/java/android/telephony/CellInfo.java
index 9232ed7..3aab3fc 100644
--- a/telephony/java/android/telephony/CellInfo.java
+++ b/telephony/java/android/telephony/CellInfo.java
@@ -19,6 +19,7 @@
import android.annotation.IntDef;
import android.os.Parcel;
import android.os.Parcelable;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -36,6 +37,8 @@
protected static final int TYPE_LTE = 3;
/** @hide */
protected static final int TYPE_WCDMA = 4;
+ /** @hide */
+ protected static final int TYPE_TDCDMA = 5;
// Type to distinguish where time stamp gets recorded.
@@ -260,6 +263,7 @@
case TYPE_CDMA: return CellInfoCdma.createFromParcelBody(in);
case TYPE_LTE: return CellInfoLte.createFromParcelBody(in);
case TYPE_WCDMA: return CellInfoWcdma.createFromParcelBody(in);
+ case TYPE_TDCDMA: return CellInfoTdscdma.createFromParcelBody(in);
default: throw new RuntimeException("Bad CellInfo Parcel");
}
}
diff --git a/telephony/java/android/telephony/CellInfoCdma.java b/telephony/java/android/telephony/CellInfoCdma.java
index 6f2f1f6..6403bc5 100644
--- a/telephony/java/android/telephony/CellInfoCdma.java
+++ b/telephony/java/android/telephony/CellInfoCdma.java
@@ -21,7 +21,7 @@
import android.telephony.Rlog;
/**
- * Immutable cell information from a point in time.
+ * A {@link CellInfo} representing a CDMA cell that provides identity and measurement info.
*/
public final class CellInfoCdma extends CellInfo implements Parcelable {
diff --git a/telephony/java/android/telephony/CellInfoGsm.java b/telephony/java/android/telephony/CellInfoGsm.java
index 1bedddb..a3a9b31 100644
--- a/telephony/java/android/telephony/CellInfoGsm.java
+++ b/telephony/java/android/telephony/CellInfoGsm.java
@@ -21,7 +21,7 @@
import android.telephony.Rlog;
/**
- * Immutable cell information from a point in time.
+ * A {@link CellInfo} representing a GSM cell that provides identity and measurement info.
*/
public final class CellInfoGsm extends CellInfo implements Parcelable {
diff --git a/telephony/java/android/telephony/CellInfoLte.java b/telephony/java/android/telephony/CellInfoLte.java
index 287c9f0..b892e89 100644
--- a/telephony/java/android/telephony/CellInfoLte.java
+++ b/telephony/java/android/telephony/CellInfoLte.java
@@ -21,7 +21,7 @@
import android.telephony.Rlog;
/**
- * Immutable cell information from a point in time.
+ * A {@link CellInfo} representing an LTE cell that provides identity and measurement info.
*/
public final class CellInfoLte extends CellInfo implements Parcelable {
diff --git a/telephony/java/android/telephony/CellInfoTdscdma.java b/telephony/java/android/telephony/CellInfoTdscdma.java
new file mode 100644
index 0000000..7084c51
--- /dev/null
+++ b/telephony/java/android/telephony/CellInfoTdscdma.java
@@ -0,0 +1,151 @@
+/*
+ * 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;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * A {@link CellInfo} representing a TD-SCDMA cell that provides identity and measurement info.
+ *
+ * @hide
+ */
+public final class CellInfoTdscdma extends CellInfo implements Parcelable {
+
+ private static final String LOG_TAG = "CellInfoTdscdma";
+ private static final boolean DBG = false;
+
+ private CellIdentityTdscdma mCellIdentityTdscdma;
+ private CellSignalStrengthTdscdma mCellSignalStrengthTdscdma;
+
+ /** @hide */
+ public CellInfoTdscdma() {
+ super();
+ mCellIdentityTdscdma = new CellIdentityTdscdma();
+ mCellSignalStrengthTdscdma = new CellSignalStrengthTdscdma();
+ }
+
+ /** @hide */
+ public CellInfoTdscdma(CellInfoTdscdma ci) {
+ super(ci);
+ this.mCellIdentityTdscdma = ci.mCellIdentityTdscdma.copy();
+ this.mCellSignalStrengthTdscdma = ci.mCellSignalStrengthTdscdma.copy();
+ }
+
+ public CellIdentityTdscdma getCellIdentity() {
+ return mCellIdentityTdscdma;
+ }
+ /** @hide */
+ public void setCellIdentity(CellIdentityTdscdma cid) {
+ mCellIdentityTdscdma = cid;
+ }
+
+ public CellSignalStrengthTdscdma getCellSignalStrength() {
+ return mCellSignalStrengthTdscdma;
+ }
+ /** @hide */
+ public void setCellSignalStrength(CellSignalStrengthTdscdma css) {
+ mCellSignalStrengthTdscdma = css;
+ }
+
+ /**
+ * @return hash code
+ */
+ @Override
+ public int hashCode() {
+ return Objects.hash(super.hashCode(), mCellIdentityTdscdma, mCellSignalStrengthTdscdma);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (!super.equals(other)) {
+ return false;
+ }
+ try {
+ CellInfoTdscdma o = (CellInfoTdscdma) other;
+ return mCellIdentityTdscdma.equals(o.mCellIdentityTdscdma)
+ && mCellSignalStrengthTdscdma.equals(o.mCellSignalStrengthTdscdma);
+ } catch (ClassCastException e) {
+ return false;
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+
+ sb.append("CellInfoTdscdma:{");
+ sb.append(super.toString());
+ sb.append(" ").append(mCellIdentityTdscdma);
+ sb.append(" ").append(mCellSignalStrengthTdscdma);
+ sb.append("}");
+
+ return sb.toString();
+ }
+
+ /** Implement the Parcelable interface */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /** Implement the Parcelable interface */
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags, TYPE_TDCDMA);
+ mCellIdentityTdscdma.writeToParcel(dest, flags);
+ mCellSignalStrengthTdscdma.writeToParcel(dest, flags);
+ }
+
+ /**
+ * Construct a CellInfoTdscdma object from the given parcel
+ * where the token is already been processed.
+ */
+ private CellInfoTdscdma(Parcel in) {
+ super(in);
+ mCellIdentityTdscdma = CellIdentityTdscdma.CREATOR.createFromParcel(in);
+ mCellSignalStrengthTdscdma = CellSignalStrengthTdscdma.CREATOR.createFromParcel(in);
+ }
+
+ /** Implement the Parcelable interface */
+ public static final Creator<CellInfoTdscdma> CREATOR = new Creator<CellInfoTdscdma>() {
+ @Override
+ public CellInfoTdscdma createFromParcel(Parcel in) {
+ in.readInt(); // Skip past token, we know what it is
+ return createFromParcelBody(in);
+ }
+
+ @Override
+ public CellInfoTdscdma[] newArray(int size) {
+ return new CellInfoTdscdma[size];
+ }
+ };
+
+ /** @hide */
+ protected static CellInfoTdscdma createFromParcelBody(Parcel in) {
+ return new CellInfoTdscdma(in);
+ }
+
+ /**
+ * log
+ */
+ private static void log(String s) {
+ Rlog.w(LOG_TAG, s);
+ }
+}
diff --git a/telephony/java/android/telephony/CellInfoWcdma.java b/telephony/java/android/telephony/CellInfoWcdma.java
index 0615702..005f3d3 100644
--- a/telephony/java/android/telephony/CellInfoWcdma.java
+++ b/telephony/java/android/telephony/CellInfoWcdma.java
@@ -20,8 +20,10 @@
import android.os.Parcelable;
import android.telephony.Rlog;
+import java.util.Objects;
+
/**
- * Immutable cell information from a point in time.
+ * A {@link CellInfo} representing a WCDMA cell that provides identity and measurement info.
*/
public final class CellInfoWcdma extends CellInfo implements Parcelable {
@@ -66,7 +68,7 @@
*/
@Override
public int hashCode() {
- return super.hashCode() + mCellIdentityWcdma.hashCode() + mCellSignalStrengthWcdma.hashCode();
+ return Objects.hash(super.hashCode(), mCellIdentityWcdma, mCellSignalStrengthWcdma);
}
@Override
diff --git a/telephony/java/android/telephony/CellSignalStrengthCdma.java b/telephony/java/android/telephony/CellSignalStrengthCdma.java
index 183f96d..aa6b207 100644
--- a/telephony/java/android/telephony/CellSignalStrengthCdma.java
+++ b/telephony/java/android/telephony/CellSignalStrengthCdma.java
@@ -104,7 +104,10 @@
}
/**
- * Get signal level as an int from 0..4
+ * Retrieve an abstract level value for the overall signal strength.
+ *
+ * @return a single integer from 0 to 4 representing the general signal quality.
+ * 0 represents very poor signal strength while 4 represents a very strong signal strength.
*/
@Override
public int getLevel() {
diff --git a/telephony/java/android/telephony/CellSignalStrengthGsm.java b/telephony/java/android/telephony/CellSignalStrengthGsm.java
index 8687cd1..cff159b 100644
--- a/telephony/java/android/telephony/CellSignalStrengthGsm.java
+++ b/telephony/java/android/telephony/CellSignalStrengthGsm.java
@@ -82,7 +82,10 @@
}
/**
- * Get signal level as an int from 0..4
+ * Retrieve an abstract level value for the overall signal strength.
+ *
+ * @return a single integer from 0 to 4 representing the general signal quality.
+ * 0 represents very poor signal strength while 4 represents a very strong signal strength.
*/
@Override
public int getLevel() {
diff --git a/telephony/java/android/telephony/CellSignalStrengthLte.java b/telephony/java/android/telephony/CellSignalStrengthLte.java
index 7e86966..2f059f4 100644
--- a/telephony/java/android/telephony/CellSignalStrengthLte.java
+++ b/telephony/java/android/telephony/CellSignalStrengthLte.java
@@ -86,7 +86,10 @@
}
/**
- * Get signal level as an int from 0..4
+ * Retrieve an abstract level value for the overall signal strength.
+ *
+ * @return a single integer from 0 to 4 representing the general signal quality.
+ * 0 represents very poor signal strength while 4 represents a very strong signal strength.
*/
@Override
public int getLevel() {
@@ -121,6 +124,8 @@
/**
* Get reference signal received quality
+ *
+ * @return the RSRQ if available or Integer.MAX_VALUE if unavailable.
*/
public int getRsrq() {
return mRsrq;
@@ -128,13 +133,17 @@
/**
* Get reference signal signal-to-noise ratio
+ *
+ * @return the RSSNR if available or Integer.MAX_VALUE if unavailable.
*/
public int getRssnr() {
return mRssnr;
}
/**
- * Get reference signal received power
+ * Get reference signal received power in dBm
+ *
+ * @return the RSRP of the measured cell.
*/
public int getRsrp() {
return mRsrp;
@@ -142,13 +151,17 @@
/**
* Get channel quality indicator
+ *
+ * @return the CQI if available or Integer.MAX_VALUE if unavailable.
*/
public int getCqi() {
return mCqi;
}
/**
- * Get signal strength as dBm
+ * Get signal strength in dBm
+ *
+ * @return the RSRP of the measured cell.
*/
@Override
public int getDbm() {
@@ -175,7 +188,8 @@
* Get the timing advance value for LTE, as a value in range of 0..1282.
* Integer.MAX_VALUE is reported when there is no active RRC
* connection. Refer to 3GPP 36.213 Sec 4.2.3
- * @return the LTE timing advance, if available.
+ *
+ * @return the LTE timing advance if available or Integer.MAX_VALUE if unavailable.
*/
public int getTimingAdvance() {
return mTimingAdvance;
diff --git a/telephony/java/android/telephony/CellSignalStrengthTdscdma.java b/telephony/java/android/telephony/CellSignalStrengthTdscdma.java
new file mode 100644
index 0000000..41859a3
--- /dev/null
+++ b/telephony/java/android/telephony/CellSignalStrengthTdscdma.java
@@ -0,0 +1,228 @@
+/*
+ * 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;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * Tdscdma signal strength related information.
+ *
+ * @hide
+ */
+public final class CellSignalStrengthTdscdma extends CellSignalStrength implements Parcelable {
+
+ private static final String LOG_TAG = "CellSignalStrengthTdscdma";
+ private static final boolean DBG = false;
+
+ private static final int TDSCDMA_SIGNAL_STRENGTH_GREAT = 12;
+ private static final int TDSCDMA_SIGNAL_STRENGTH_GOOD = 8;
+ private static final int TDSCDMA_SIGNAL_STRENGTH_MODERATE = 5;
+
+ private int mSignalStrength; // in ASU; Valid values are (0-31, 99) as defined in TS 27.007 8.5
+ // or Integer.MAX_VALUE if unknown
+ private int mBitErrorRate; // bit error rate (0-7, 99) as defined in TS 27.007 8.5 or
+ // Integer.MAX_VALUE if unknown
+ private int mRscp; // Pilot power (0-96, 255) as defined in TS 27.007 8.69 or Integer.MAX_VALUE
+ // if unknown
+
+ /** @hide */
+ public CellSignalStrengthTdscdma() {
+ setDefaultValues();
+ }
+
+ /** @hide */
+ public CellSignalStrengthTdscdma(int ss, int ber, int rscp) {
+ mSignalStrength = ss;
+ mBitErrorRate = ber;
+ mRscp = rscp;
+ }
+
+ /** @hide */
+ public CellSignalStrengthTdscdma(CellSignalStrengthTdscdma s) {
+ copyFrom(s);
+ }
+
+ /** @hide */
+ protected void copyFrom(CellSignalStrengthTdscdma s) {
+ mSignalStrength = s.mSignalStrength;
+ mBitErrorRate = s.mBitErrorRate;
+ mRscp = s.mRscp;
+ }
+
+ /** @hide */
+ @Override
+ public CellSignalStrengthTdscdma copy() {
+ return new CellSignalStrengthTdscdma(this);
+ }
+
+ /** @hide */
+ @Override
+ public void setDefaultValues() {
+ mSignalStrength = Integer.MAX_VALUE;
+ mBitErrorRate = Integer.MAX_VALUE;
+ mRscp = Integer.MAX_VALUE;
+ }
+
+ /**
+ * Retrieve an abstract level value for the overall signal strength.
+ *
+ * @return a single integer from 0 to 4 representing the general signal quality.
+ * 0 represents very poor signal strength while 4 represents a very strong signal strength.
+ */
+ @Override
+ public int getLevel() {
+ int level;
+
+ // ASU ranges from 0 to 31 - TS 27.007 Sec 8.5
+ // asu = 0 (-113dB or less) is very weak
+ // signal, its better to show 0 bars to the user in such cases.
+ // asu = 99 is a special case, where the signal strength is unknown.
+ int asu = mSignalStrength;
+ if (asu <= 2 || asu == 99) {
+ level = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+ } else if (asu >= TDSCDMA_SIGNAL_STRENGTH_GREAT) {
+ level = SIGNAL_STRENGTH_GREAT;
+ } else if (asu >= TDSCDMA_SIGNAL_STRENGTH_GOOD) {
+ level = SIGNAL_STRENGTH_GOOD;
+ } else if (asu >= TDSCDMA_SIGNAL_STRENGTH_MODERATE) {
+ level = SIGNAL_STRENGTH_MODERATE;
+ } else {
+ level = SIGNAL_STRENGTH_POOR;
+ }
+ if (DBG) log("getLevel=" + level);
+ return level;
+ }
+
+ /**
+ * Get the signal strength as dBm
+ */
+ @Override
+ public int getDbm() {
+ int dBm;
+
+ int level = mSignalStrength;
+ int asu = (level == 99 ? Integer.MAX_VALUE : level);
+ if (asu != Integer.MAX_VALUE) {
+ dBm = -113 + (2 * asu);
+ } else {
+ dBm = Integer.MAX_VALUE;
+ }
+ if (DBG) log("getDbm=" + dBm);
+ return dBm;
+ }
+
+ /**
+ * Get the signal level as an asu value between 0..31, 99 is unknown
+ * Asu is calculated based on 3GPP RSRP. Refer to 3GPP 27.007 (Ver 10.3.0) Sec 8.69
+ */
+ @Override
+ public int getAsuLevel() {
+ // ASU ranges from 0 to 31 - TS 27.007 Sec 8.5
+ // asu = 0 (-113dB or less) is very weak
+ // signal, its better to show 0 bars to the user in such cases.
+ // asu = 99 is a special case, where the signal strength is unknown.
+ int level = mSignalStrength;
+ if (DBG) log("getAsuLevel=" + level);
+ return level;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mSignalStrength, mBitErrorRate);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ CellSignalStrengthTdscdma s;
+
+ try {
+ s = (CellSignalStrengthTdscdma) o;
+ } catch (ClassCastException ex) {
+ return false;
+ }
+
+ if (o == null) {
+ return false;
+ }
+
+ return mSignalStrength == s.mSignalStrength
+ && mBitErrorRate == s.mBitErrorRate
+ && mRscp == s.mRscp;
+ }
+
+ /**
+ * @return string representation.
+ */
+ @Override
+ public String toString() {
+ return "CellSignalStrengthTdscdma:"
+ + " ss=" + mSignalStrength
+ + " ber=" + mBitErrorRate
+ + " rscp=" + mRscp;
+ }
+
+ /** Implement the Parcelable interface */
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ if (DBG) log("writeToParcel(Parcel, int): " + toString());
+ dest.writeInt(mSignalStrength);
+ dest.writeInt(mBitErrorRate);
+ dest.writeInt(mRscp);
+ }
+
+ /**
+ * Construct a SignalStrength object from the given parcel
+ * where the token is already been processed.
+ */
+ private CellSignalStrengthTdscdma(Parcel in) {
+ mSignalStrength = in.readInt();
+ mBitErrorRate = in.readInt();
+ mRscp = in.readInt();
+ if (DBG) log("CellSignalStrengthTdscdma(Parcel): " + toString());
+ }
+
+ /** Implement the Parcelable interface */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /** Implement the Parcelable interface */
+ @SuppressWarnings("hiding")
+ public static final Parcelable.Creator<CellSignalStrengthTdscdma> CREATOR =
+ new Parcelable.Creator<CellSignalStrengthTdscdma>() {
+ @Override
+ public CellSignalStrengthTdscdma createFromParcel(Parcel in) {
+ return new CellSignalStrengthTdscdma(in);
+ }
+
+ @Override
+ public CellSignalStrengthTdscdma[] newArray(int size) {
+ return new CellSignalStrengthTdscdma[size];
+ }
+ };
+
+ /**
+ * log
+ */
+ private static void log(String s) {
+ Rlog.w(LOG_TAG, s);
+ }
+}
diff --git a/telephony/java/android/telephony/CellSignalStrengthWcdma.java b/telephony/java/android/telephony/CellSignalStrengthWcdma.java
index dd32a96..21cf0be 100644
--- a/telephony/java/android/telephony/CellSignalStrengthWcdma.java
+++ b/telephony/java/android/telephony/CellSignalStrengthWcdma.java
@@ -35,7 +35,13 @@
private static final int WCDMA_SIGNAL_STRENGTH_MODERATE = 5;
private int mSignalStrength; // in ASU; Valid values are (0-31, 99) as defined in TS 27.007 8.5
- private int mBitErrorRate; // bit error rate (0-7, 99) as defined in TS 27.007 8.5
+ // or Integer.MAX_VALUE if unknown
+ private int mBitErrorRate; // bit error rate (0-7, 99) as defined in TS 27.007 8.5 or
+ // Integer.MAX_VALUE if unknown
+ private int mRscp; // bit error rate (0-96, 255) as defined in TS 27.007 8.69 or
+ // Integer.MAX_VALUE if unknown
+ private int mEcNo; // signal to noise radio (0-49, 255) as defined in TS 27.007 8.69 or
+ // Integer.MAX_VALUE if unknown
/** @hide */
public CellSignalStrengthWcdma() {
@@ -43,9 +49,11 @@
}
/** @hide */
- public CellSignalStrengthWcdma(int ss, int ber) {
+ public CellSignalStrengthWcdma(int ss, int ber, int rscp, int ecno) {
mSignalStrength = ss;
mBitErrorRate = ber;
+ mRscp = rscp;
+ mEcNo = ecno;
}
/** @hide */
@@ -57,6 +65,8 @@
protected void copyFrom(CellSignalStrengthWcdma s) {
mSignalStrength = s.mSignalStrength;
mBitErrorRate = s.mBitErrorRate;
+ mRscp = s.mRscp;
+ mEcNo = s.mEcNo;
}
/** @hide */
@@ -70,10 +80,15 @@
public void setDefaultValues() {
mSignalStrength = Integer.MAX_VALUE;
mBitErrorRate = Integer.MAX_VALUE;
+ mRscp = Integer.MAX_VALUE;
+ mEcNo = Integer.MAX_VALUE;
}
/**
- * Get signal level as an int from 0..4
+ * Retrieve an abstract level value for the overall signal strength.
+ *
+ * @return a single integer from 0 to 4 representing the general signal quality.
+ * 0 represents very poor signal strength while 4 represents a very strong signal strength.
*/
@Override
public int getLevel() {
@@ -145,7 +160,10 @@
return false;
}
- return mSignalStrength == s.mSignalStrength && mBitErrorRate == s.mBitErrorRate;
+ return mSignalStrength == s.mSignalStrength
+ && mBitErrorRate == s.mBitErrorRate
+ && mRscp == s.mRscp
+ && mEcNo == s.mEcNo;
}
/**
@@ -155,7 +173,9 @@
public String toString() {
return "CellSignalStrengthWcdma:"
+ " ss=" + mSignalStrength
- + " ber=" + mBitErrorRate;
+ + " ber=" + mBitErrorRate
+ + " rscp=" + mRscp
+ + " ecno=" + mEcNo;
}
/** Implement the Parcelable interface */
@@ -164,6 +184,8 @@
if (DBG) log("writeToParcel(Parcel, int): " + toString());
dest.writeInt(mSignalStrength);
dest.writeInt(mBitErrorRate);
+ dest.writeInt(mRscp);
+ dest.writeInt(mEcNo);
}
/**
@@ -173,6 +195,8 @@
private CellSignalStrengthWcdma(Parcel in) {
mSignalStrength = in.readInt();
mBitErrorRate = in.readInt();
+ mRscp = in.readInt();
+ mEcNo = in.readInt();
if (DBG) log("CellSignalStrengthWcdma(Parcel): " + toString());
}
diff --git a/telephony/java/android/telephony/LocationAccessPolicy.java b/telephony/java/android/telephony/LocationAccessPolicy.java
index 6db8e82..53d69f4 100644
--- a/telephony/java/android/telephony/LocationAccessPolicy.java
+++ b/telephony/java/android/telephony/LocationAccessPolicy.java
@@ -39,7 +39,8 @@
* @hide
*/
public final class LocationAccessPolicy {
- private static final String LOG_TAG = LocationAccessPolicy.class.getSimpleName();
+ private static final String TAG = "LocationAccessPolicy";
+ private static final boolean DBG = false;
/**
* API to determine if the caller has permissions to get cell location.
@@ -52,12 +53,13 @@
*/
public static boolean canAccessCellLocation(@NonNull Context context, @NonNull String pkgName,
int uid, int pid, boolean throwOnDeniedPermission) throws SecurityException {
- Trace.beginSection("TelephonyLohcationCheck");
+ Trace.beginSection("TelephonyLocationCheck");
try {
- // Always allow the phone process to access location. This avoid breaking legacy code
- // that rely on public-facing APIs to access cell location, and it doesn't create a
- // info leak risk because the cell location is stored in the phone process anyway.
- if (uid == Process.PHONE_UID) {
+ // Always allow the phone process and system server to access location. This avoid
+ // breaking legacy code that rely on public-facing APIs to access cell location, and
+ // it doesn't create an info leak risk because the cell location is stored in the phone
+ // process anyway, and the system server already has location access.
+ if (uid == Process.PHONE_UID || uid == Process.SYSTEM_UID || uid == Process.ROOT_UID) {
return true;
}
@@ -72,15 +74,18 @@
pid, uid, "canAccessCellLocation");
} else if (context.checkPermission(Manifest.permission.ACCESS_COARSE_LOCATION,
pid, uid) == PackageManager.PERMISSION_DENIED) {
+ if (DBG) Log.w(TAG, "Permission checked failed (" + pid + "," + uid + ")");
return false;
}
final int opCode = AppOpsManager.permissionToOpCode(
Manifest.permission.ACCESS_COARSE_LOCATION);
if (opCode != AppOpsManager.OP_NONE && context.getSystemService(AppOpsManager.class)
.noteOpNoThrow(opCode, uid, pkgName) != AppOpsManager.MODE_ALLOWED) {
+ if (DBG) Log.w(TAG, "AppOp check failed (" + uid + "," + pkgName + ")");
return false;
}
if (!isLocationModeEnabled(context, UserHandle.getUserId(uid))) {
+ if (DBG) Log.w(TAG, "Location disabled, failed, (" + uid + ")");
return false;
}
// If the user or profile is current, permission is granted.
@@ -94,7 +99,7 @@
private static boolean isLocationModeEnabled(@NonNull Context context, @UserIdInt int userId) {
LocationManager locationManager = context.getSystemService(LocationManager.class);
if (locationManager == null) {
- Log.w(LOG_TAG, "Couldn't get location manager, denying location access");
+ Log.w(TAG, "Couldn't get location manager, denying location access");
return false;
}
return locationManager.isLocationEnabledForUser(UserHandle.of(userId));
diff --git a/telephony/java/android/telephony/NetworkRegistrationState.java b/telephony/java/android/telephony/NetworkRegistrationState.java
index bba779d..c393155 100644
--- a/telephony/java/android/telephony/NetworkRegistrationState.java
+++ b/telephony/java/android/telephony/NetworkRegistrationState.java
@@ -21,6 +21,7 @@
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
+import android.telephony.AccessNetworkConstants.TransportType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -59,15 +60,15 @@
/** Not registered. The device is not currently searching a new operator to register */
public static final int REG_STATE_NOT_REG_NOT_SEARCHING = 0;
/** Registered on home network */
- public static final int REG_STATE_HOME = 1;
+ public static final int REG_STATE_HOME = 1;
/** Not registered. The device is currently searching a new operator to register */
- public static final int REG_STATE_NOT_REG_SEARCHING = 2;
+ public static final int REG_STATE_NOT_REG_SEARCHING = 2;
/** Registration denied */
- public static final int REG_STATE_DENIED = 3;
+ public static final int REG_STATE_DENIED = 3;
/** Registration state is unknown */
- public static final int REG_STATE_UNKNOWN = 4;
+ public static final int REG_STATE_UNKNOWN = 4;
/** Registered on roaming network */
- public static final int REG_STATE_ROAMING = 5;
+ public static final int REG_STATE_ROAMING = 5;
/**
* Supported service type
@@ -79,24 +80,24 @@
SERVICE_TYPE_EMERGENCY})
public @interface ServiceType {}
- public static final int SERVICE_TYPE_VOICE = 1;
- public static final int SERVICE_TYPE_DATA = 2;
- public static final int SERVICE_TYPE_SMS = 3;
- public static final int SERVICE_TYPE_VIDEO = 4;
- public static final int SERVICE_TYPE_EMERGENCY = 5;
-
- /** {@link AccessNetworkConstants.TransportType}*/
- private final int mTransportType;
+ public static final int SERVICE_TYPE_VOICE = 1;
+ public static final int SERVICE_TYPE_DATA = 2;
+ public static final int SERVICE_TYPE_SMS = 3;
+ public static final int SERVICE_TYPE_VIDEO = 4;
+ public static final int SERVICE_TYPE_EMERGENCY = 5;
@Domain
private final int mDomain;
+ /** {@link TransportType} */
+ private final int mTransportType;
+
@RegState
private final int mRegState;
private final int mAccessNetworkTechnology;
- private final int mReasonForDenial;
+ private final int mRejectCause;
private final boolean mEmergencyOnly;
@@ -112,22 +113,35 @@
private DataSpecificRegistrationStates mDataSpecificStates;
/**
- * @param transportType Transport type. Must be {@link AccessNetworkConstants.TransportType}
- * @param domain Network domain. Must be DOMAIN_CS or DOMAIN_PS.
- * @param regState Network registration state.
- * @param accessNetworkTechnology See TelephonyManager NETWORK_TYPE_XXXX.
- * @param reasonForDenial Reason for denial if the registration state is DENIED.
- * @param availableServices The supported service.
- * @param cellIdentity The identity representing a unique cell
+ * @param domain Network domain. Must be a {@link Domain}. For {@link TransportType#WLAN}
+ * transport, this must set to {@link #DOMAIN_PS}.
+ * @param transportType Transport type. Must be one of the{@link TransportType}.
+ * @param regState Network registration state. Must be one of the {@link RegState}. For
+ * {@link TransportType#WLAN} transport, only {@link #REG_STATE_HOME} and
+ * {@link #REG_STATE_NOT_REG_NOT_SEARCHING} are valid states.
+ * @param accessNetworkTechnology Access network technology. Must be one of TelephonyManager
+ * NETWORK_TYPE_XXXX. For {@link TransportType#WLAN} transport, set to
+ * {@link TelephonyManager#NETWORK_TYPE_IWLAN}.
+ * @param rejectCause Reason for denial if the registration state is {@link #REG_STATE_DENIED}.
+ * Depending on {@code accessNetworkTechnology}, the values are defined in 3GPP TS 24.008
+ * 10.5.3.6 for UMTS, 3GPP TS 24.301 9.9.3.9 for LTE, and 3GPP2 A.S0001 6.2.2.44 for CDMA. If
+ * the reject cause is not supported or unknown, set it to 0.
+ * // TODO: Add IWLAN reject cause reference
+ * @param emergencyOnly True if this registration is for emergency only.
+ * @param availableServices The list of the supported services. Each element must be one of
+ * the {@link ServiceType}.
+ * @param cellIdentity The identity representing a unique cell or wifi AP. Set to null if the
+ * information is not available.
*/
- public NetworkRegistrationState(int transportType, int domain, int regState,
- int accessNetworkTechnology, int reasonForDenial, boolean emergencyOnly,
- int[] availableServices, @Nullable CellIdentity cellIdentity) {
- mTransportType = transportType;
+ public NetworkRegistrationState(@Domain int domain, int transportType, @RegState int regState,
+ int accessNetworkTechnology, int rejectCause,
+ boolean emergencyOnly, int[] availableServices,
+ @Nullable CellIdentity cellIdentity) {
mDomain = domain;
+ mTransportType = transportType;
mRegState = regState;
mAccessNetworkTechnology = accessNetworkTechnology;
- mReasonForDenial = reasonForDenial;
+ mRejectCause = rejectCause;
mAvailableServices = availableServices;
mCellIdentity = cellIdentity;
mEmergencyOnly = emergencyOnly;
@@ -137,12 +151,14 @@
* Constructor for voice network registration states.
* @hide
*/
- public NetworkRegistrationState(int transportType, int domain, int regState,
- int accessNetworkTechnology, int reasonForDenial, boolean emergencyOnly,
- int[] availableServices, @Nullable CellIdentity cellIdentity, boolean cssSupported,
- int roamingIndicator, int systemIsInPrl, int defaultRoamingIndicator) {
- this(transportType, domain, regState, accessNetworkTechnology,
- reasonForDenial, emergencyOnly, availableServices, cellIdentity);
+ public NetworkRegistrationState(int domain, int transportType, int regState,
+ int accessNetworkTechnology, int rejectCause,
+ boolean emergencyOnly, int[] availableServices,
+ @Nullable CellIdentity cellIdentity, boolean cssSupported,
+ int roamingIndicator, int systemIsInPrl,
+ int defaultRoamingIndicator) {
+ this(domain, transportType, regState, accessNetworkTechnology, rejectCause, emergencyOnly,
+ availableServices, cellIdentity);
mVoiceSpecificStates = new VoiceSpecificRegistrationStates(cssSupported, roamingIndicator,
systemIsInPrl, defaultRoamingIndicator);
@@ -152,21 +168,22 @@
* Constructor for data network registration states.
* @hide
*/
- public NetworkRegistrationState(int transportType, int domain, int regState,
- int accessNetworkTechnology, int reasonForDenial, boolean emergencyOnly,
- int[] availableServices, @Nullable CellIdentity cellIdentity, int maxDataCalls) {
- this(transportType, domain, regState, accessNetworkTechnology,
- reasonForDenial, emergencyOnly, availableServices, cellIdentity);
+ public NetworkRegistrationState(int domain, int transportType, int regState,
+ int accessNetworkTechnology, int rejectCause,
+ boolean emergencyOnly, int[] availableServices,
+ @Nullable CellIdentity cellIdentity, int maxDataCalls) {
+ this(domain, transportType, regState, accessNetworkTechnology, rejectCause, emergencyOnly,
+ availableServices, cellIdentity);
mDataSpecificStates = new DataSpecificRegistrationStates(maxDataCalls);
}
protected NetworkRegistrationState(Parcel source) {
- mTransportType = source.readInt();
mDomain = source.readInt();
+ mTransportType = source.readInt();
mRegState = source.readInt();
mAccessNetworkTechnology = source.readInt();
- mReasonForDenial = source.readInt();
+ mRejectCause = source.readInt();
mEmergencyOnly = source.readBoolean();
mAvailableServices = source.createIntArray();
mCellIdentity = source.readParcelable(CellIdentity.class.getClassLoader());
@@ -211,10 +228,10 @@
}
/**
- * @return Reason for denial from network.
+ * @return Network reject cause
*/
- public int getReasonForDenial() {
- return mReasonForDenial;
+ public int getRejectCause() {
+ return mRejectCause;
}
/**
@@ -260,12 +277,12 @@
@Override
public String toString() {
return new StringBuilder("NetworkRegistrationState{")
- .append("transportType=").append(mTransportType)
.append(" domain=").append((mDomain == DOMAIN_CS) ? "CS" : "PS")
+ .append("transportType=").append(mTransportType)
.append(" regState=").append(regStateToString(mRegState))
.append(" accessNetworkTechnology=")
.append(TelephonyManager.getNetworkTypeName(mAccessNetworkTechnology))
- .append(" reasonForDenial=").append(mReasonForDenial)
+ .append(" rejectCause=").append(mRejectCause)
.append(" emergencyEnabled=").append(mEmergencyOnly)
.append(" supportedServices=").append(mAvailableServices)
.append(" cellIdentity=").append(mCellIdentity)
@@ -276,8 +293,8 @@
@Override
public int hashCode() {
- return Objects.hash(mTransportType, mDomain, mRegState, mAccessNetworkTechnology,
- mReasonForDenial, mEmergencyOnly, mAvailableServices, mCellIdentity,
+ return Objects.hash(mDomain, mTransportType, mRegState, mAccessNetworkTechnology,
+ mRejectCause, mEmergencyOnly, mAvailableServices, mCellIdentity,
mVoiceSpecificStates, mDataSpecificStates);
}
@@ -290,11 +307,11 @@
}
NetworkRegistrationState other = (NetworkRegistrationState) o;
- return mTransportType == other.mTransportType
- && mDomain == other.mDomain
+ return mDomain == other.mDomain
+ && mTransportType == other.mTransportType
&& mRegState == other.mRegState
&& mAccessNetworkTechnology == other.mAccessNetworkTechnology
- && mReasonForDenial == other.mReasonForDenial
+ && mRejectCause == other.mRejectCause
&& mEmergencyOnly == other.mEmergencyOnly
&& (mAvailableServices == other.mAvailableServices
|| Arrays.equals(mAvailableServices, other.mAvailableServices))
@@ -305,11 +322,11 @@
@Override
public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(mTransportType);
dest.writeInt(mDomain);
+ dest.writeInt(mTransportType);
dest.writeInt(mRegState);
dest.writeInt(mAccessNetworkTechnology);
- dest.writeInt(mReasonForDenial);
+ dest.writeInt(mRejectCause);
dest.writeBoolean(mEmergencyOnly);
dest.writeIntArray(mAvailableServices);
dest.writeParcelable(mCellIdentity, 0);
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index fa7988d..9e8529e 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -30,6 +30,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.Objects;
/**
* Contains phone state and service related information.
@@ -723,38 +724,40 @@
@Override
public int hashCode() {
- return ((mVoiceRegState * 31)
- + (mDataRegState * 37)
- + mVoiceRoamingType
- + mDataRoamingType
- + mChannelNumber
- + Arrays.hashCode(mCellBandwidths)
- + (mIsManualNetworkSelection ? 1 : 0)
- + ((null == mVoiceOperatorAlphaLong) ? 0 : mVoiceOperatorAlphaLong.hashCode())
- + ((null == mVoiceOperatorAlphaShort) ? 0 : mVoiceOperatorAlphaShort.hashCode())
- + ((null == mVoiceOperatorNumeric) ? 0 : mVoiceOperatorNumeric.hashCode())
- + ((null == mDataOperatorAlphaLong) ? 0 : mDataOperatorAlphaLong.hashCode())
- + ((null == mDataOperatorAlphaShort) ? 0 : mDataOperatorAlphaShort.hashCode())
- + ((null == mDataOperatorNumeric) ? 0 : mDataOperatorNumeric.hashCode())
- + mCdmaRoamingIndicator
- + mCdmaDefaultRoamingIndicator
- + (mIsEmergencyOnly ? 1 : 0)
- + (mIsDataRoamingFromRegistration ? 1 : 0));
+ return Objects.hash(
+ mVoiceRegState,
+ mDataRegState,
+ mVoiceRoamingType,
+ mDataRoamingType,
+ mChannelNumber,
+ mCellBandwidths,
+ mVoiceOperatorAlphaLong,
+ mVoiceOperatorAlphaShort,
+ mVoiceOperatorNumeric,
+ mDataOperatorAlphaLong,
+ mDataOperatorAlphaShort,
+ mDataOperatorNumeric,
+ mIsManualNetworkSelection,
+ mRilVoiceRadioTechnology,
+ mRilDataRadioTechnology,
+ mCssIndicator,
+ mNetworkId,
+ mSystemId,
+ mCdmaRoamingIndicator,
+ mCdmaDefaultRoamingIndicator,
+ mCdmaEriIconIndex,
+ mCdmaEriIconMode,
+ mIsEmergencyOnly,
+ mIsDataRoamingFromRegistration,
+ mIsUsingCarrierAggregation,
+ mLteEarfcnRsrpBoost,
+ mNetworkRegistrationStates);
}
@Override
public boolean equals (Object o) {
- ServiceState s;
-
- try {
- s = (ServiceState) o;
- } catch (ClassCastException ex) {
- return false;
- }
-
- if (o == null) {
- return false;
- }
+ if (!(o instanceof ServiceState)) return false;
+ ServiceState s = (ServiceState) o;
return (mVoiceRegState == s.mVoiceRegState
&& mDataRegState == s.mDataRegState
@@ -1566,13 +1569,14 @@
/**
* Get the network registration states with given transport type and domain.
*
+ * @param domain The network domain. Must be {@link NetworkRegistrationState#DOMAIN_CS} or
+ * {@link NetworkRegistrationState#DOMAIN_PS}.
* @param transportType The transport type. See {@link AccessNetworkConstants.TransportType}
- * @param domain The network domain. Must be DOMAIN_CS or DOMAIN_PS.
* @return The matching NetworkRegistrationState.
* @hide
*/
@SystemApi
- public NetworkRegistrationState getNetworkRegistrationStates(int transportType, int domain) {
+ public NetworkRegistrationState getNetworkRegistrationStates(int domain, int transportType) {
synchronized (mNetworkRegistrationStates) {
for (NetworkRegistrationState networkRegistrationState : mNetworkRegistrationStates) {
if (networkRegistrationState.getTransportType() == transportType
diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java
index 936505c..d76e39b 100644
--- a/telephony/java/android/telephony/SubscriptionInfo.java
+++ b/telephony/java/android/telephony/SubscriptionInfo.java
@@ -33,9 +33,9 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.util.DisplayMetrics;
+import android.util.Log;
import java.util.Arrays;
-import java.util.ArrayList;
import java.util.List;
/**
@@ -105,12 +105,12 @@
/**
* Mobile Country Code
*/
- private int mMcc;
+ private String mMcc;
/**
* Mobile Network Code
*/
- private int mMnc;
+ private String mMnc;
/**
* ISO Country code for the subscription's provider
@@ -138,11 +138,11 @@
* @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) {
+ CharSequence carrierName, int nameSource, int iconTint, String number, int roaming,
+ Bitmap icon, String mcc, String mnc, String countryIso) {
this(id, iccId, simSlotIndex, displayName, carrierName, nameSource, iconTint, number,
- roaming, icon, mcc, mnc, countryIso, false /* isEmbedded */,
- null /* accessRules */, null /* accessRules */);
+ roaming, icon, mcc, mnc, countryIso, false /* isEmbedded */,
+ null /* accessRules */, null /* accessRules */);
}
/**
@@ -150,7 +150,7 @@
*/
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,
+ Bitmap icon, String mcc, String mnc, String countryIso, boolean isEmbedded,
@Nullable UiccAccessRule[] accessRules) {
this(id, iccId, simSlotIndex, displayName, carrierName, nameSource, iconTint, number,
roaming, icon, mcc, mnc, countryIso, isEmbedded, accessRules, null /* cardId */);
@@ -161,7 +161,7 @@
*/
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,
+ Bitmap icon, String mcc, String mnc, String countryIso, boolean isEmbedded,
@Nullable UiccAccessRule[] accessRules, String cardId) {
this.mId = id;
this.mIccId = iccId;
@@ -316,15 +316,43 @@
/**
* @return the MCC.
+ * @deprecated Use {@link #getMccString()} instead.
*/
+ @Deprecated
public int getMcc() {
- return this.mMcc;
+ try {
+ return this.mMcc == null ? 0 : Integer.valueOf(this.mMcc);
+ } catch (NumberFormatException e) {
+ Log.w(SubscriptionInfo.class.getSimpleName(), "MCC string is not a number");
+ return 0;
+ }
}
/**
* @return the MNC.
+ * @deprecated Use {@link #getMncString()} instead.
*/
+ @Deprecated
public int getMnc() {
+ try {
+ return this.mMnc == null ? 0 : Integer.valueOf(this.mMnc);
+ } catch (NumberFormatException e) {
+ Log.w(SubscriptionInfo.class.getSimpleName(), "MNC string is not a number");
+ return 0;
+ }
+ }
+
+ /**
+ * @return The MCC, as a string.
+ */
+ public String getMccString() {
+ return this.mMcc;
+ }
+
+ /**
+ * @return The MNC, as a string.
+ */
+ public String getMncString() {
return this.mMnc;
}
@@ -425,8 +453,8 @@
int iconTint = source.readInt();
String number = source.readString();
int dataRoaming = source.readInt();
- int mcc = source.readInt();
- int mnc = source.readInt();
+ String mcc = source.readString();
+ String mnc = source.readString();
String countryIso = source.readString();
Bitmap iconBitmap = Bitmap.CREATOR.createFromParcel(source);
boolean isEmbedded = source.readBoolean();
@@ -455,8 +483,8 @@
dest.writeInt(mIconTint);
dest.writeString(mNumber);
dest.writeInt(mDataRoaming);
- dest.writeInt(mMcc);
- dest.writeInt(mMnc);
+ dest.writeString(mMcc);
+ dest.writeString(mMnc);
dest.writeString(mCountryIso);
mIconBitmap.writeToParcel(dest, flags);
dest.writeBoolean(mIsEmbedded);
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index ece646c..17e7c49 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -253,6 +253,20 @@
public static final int SIM_PROVISIONED = 0;
/**
+ * TelephonyProvider column name for the MCC associated with a SIM, stored as a string.
+ * <P>Type: TEXT (String)</P>
+ * @hide
+ */
+ public static final String MCC_STRING = "mcc_string";
+
+ /**
+ * TelephonyProvider column name for the MNC associated with a SIM, stored as a string.
+ * <P>Type: TEXT (String)</P>
+ * @hide
+ */
+ public static final String MNC_STRING = "mnc_string";
+
+ /**
* TelephonyProvider column name for the MCC associated with a SIM.
* <P>Type: INTEGER (int)</P>
* @hide
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index d7568b4..9e23c5c 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -335,11 +335,18 @@
* <p>
* The {@link #EXTRA_STATE} extra indicates the new call state.
* If a receiving app has {@link android.Manifest.permission#READ_CALL_LOG} permission, a second
- * extra {@link #EXTRA_INCOMING_NUMBER} provides the phone number for incoming and outoing calls
- * as a String. Note: If the receiving app has
+ * extra {@link #EXTRA_INCOMING_NUMBER} provides the phone number for incoming and outgoing
+ * calls as a String.
+ * <p>
+ * If the receiving app has
* {@link android.Manifest.permission#READ_CALL_LOG} and
* {@link android.Manifest.permission#READ_PHONE_STATE} permission, it will receive the
- * broadcast twice; one with the phone number and another without it.
+ * broadcast twice; one with the {@link #EXTRA_INCOMING_NUMBER} populated with the phone number,
+ * and another with it blank. Due to the nature of broadcasts, you cannot assume the order
+ * in which these broadcasts will arrive, however you are guaranteed to receive two in this
+ * case. Apps which are interested in the {@link #EXTRA_INCOMING_NUMBER} can ignore the
+ * broadcasts where {@link #EXTRA_INCOMING_NUMBER} is not present in the extras (e.g. where
+ * {@link Intent#hasExtra(String)} returns {@code false}).
* <p class="note">
* This was a {@link android.content.Context#sendStickyBroadcast sticky}
* broadcast in version 1.0, but it is no longer sticky.
@@ -488,10 +495,19 @@
public static final String EXTRA_STATE_OFFHOOK = PhoneConstants.State.OFFHOOK.toString();
/**
- * The lookup key used with the {@link #ACTION_PHONE_STATE_CHANGED} broadcast
- * for a String containing the incoming phone number.
- * Only valid when the new call state is RINGING.
- *
+ * Extra key used with the {@link #ACTION_PHONE_STATE_CHANGED} broadcast
+ * for a String containing the incoming or outgoing phone number.
+ * <p>
+ * This extra is only populated for receivers of the {@link #ACTION_PHONE_STATE_CHANGED}
+ * broadcast which have been granted the {@link android.Manifest.permission#READ_CALL_LOG} and
+ * {@link android.Manifest.permission#READ_PHONE_STATE} permissions.
+ * <p>
+ * For incoming calls, the phone number is only guaranteed to be populated when the
+ * {@link #EXTRA_STATE} changes from {@link #EXTRA_STATE_IDLE} to {@link #EXTRA_STATE_RINGING}.
+ * If the incoming caller is from an unknown number, the extra will be populated with an empty
+ * string.
+ * For outgoing calls, the phone number is only guaranteed to be populated when the
+ * {@link #EXTRA_STATE} changes from {@link #EXTRA_STATE_IDLE} to {@link #EXTRA_STATE_OFFHOOK}.
* <p class="note">
* Retrieve with
* {@link android.content.Intent#getStringExtra(String)}.
@@ -1296,6 +1312,33 @@
}
/**
+ * Returns the Type Allocation Code from the IMEI. Return null if Type Allocation Code is not
+ * available.
+ */
+ public String getTypeAllocationCode() {
+ return getTypeAllocationCode(getSlotIndex());
+ }
+
+ /**
+ * Returns the Type Allocation Code from the IMEI. Return null if Type Allocation Code is not
+ * available.
+ *
+ * @param slotIndex of which Type Allocation Code is returned
+ */
+ public String getTypeAllocationCode(int slotIndex) {
+ ITelephony telephony = getITelephony();
+ if (telephony == null) return null;
+
+ try {
+ return telephony.getTypeAllocationCodeForSlot(slotIndex);
+ } catch (RemoteException ex) {
+ return null;
+ } catch (NullPointerException ex) {
+ return null;
+ }
+ }
+
+ /**
* Returns the MEID (Mobile Equipment Identifier). Return null if MEID is not available.
*
* <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
@@ -1331,6 +1374,33 @@
}
/**
+ * Returns the Manufacturer Code from the MEID. Return null if Manufacturer Code is not
+ * available.
+ */
+ public String getManufacturerCode() {
+ return getManufacturerCode(getSlotIndex());
+ }
+
+ /**
+ * Returns the Manufacturer Code from the MEID. Return null if Manufacturer Code is not
+ * available.
+ *
+ * @param slotIndex of which Type Allocation Code is returned
+ */
+ public String getManufacturerCode(int slotIndex) {
+ ITelephony telephony = getITelephony();
+ if (telephony == null) return null;
+
+ try {
+ return telephony.getManufacturerCodeForSlot(slotIndex);
+ } catch (RemoteException ex) {
+ return null;
+ } catch (NullPointerException ex) {
+ return null;
+ }
+ }
+
+ /**
* Returns the Network Access Identifier (NAI). Return null if NAI is not available.
*
* <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
@@ -2767,7 +2837,8 @@
}
/**
- * Gets all the UICC slots.
+ * Gets all the UICC slots. The objects in the array can be null if the slot info is not
+ * available, which is possible between phone process starting and getting slot info from modem.
*
* @return UiccSlotInfo array.
*
@@ -5432,23 +5503,6 @@
}
/**
- * @return true if the IMS resolver is busy resolving a binding and should not be considered
- * available, false if the IMS resolver is idle.
- * @hide
- */
- public boolean isResolvingImsBinding() {
- try {
- ITelephony telephony = getITelephony();
- if (telephony != null) {
- return telephony.isResolvingImsBinding();
- }
- } catch (RemoteException e) {
- Rlog.e(TAG, "isResolvingImsBinding, RemoteException: " + e.getMessage());
- }
- return false;
- }
-
- /**
* Set IMS registration state
*
* @param Registration state
diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java
index f875a98..5dc0cff 100644
--- a/telephony/java/android/telephony/data/ApnSetting.java
+++ b/telephony/java/android/telephony/data/ApnSetting.java
@@ -70,7 +70,12 @@
private static final String UNSPECIFIED_STRING = "";
/**
- * All APN types.
+ * APN type for none. Should only be used for initialization.
+ * @hide
+ */
+ public static final int TYPE_NONE = ApnTypes.NONE;
+ /**
+ * APN type for all APNs.
* @hide
*/
public static final int TYPE_ALL = ApnTypes.ALL;
diff --git a/telephony/java/android/telephony/ims/ImsExternalCallState.java b/telephony/java/android/telephony/ims/ImsExternalCallState.java
index e82c115..e158fa8 100644
--- a/telephony/java/android/telephony/ims/ImsExternalCallState.java
+++ b/telephony/java/android/telephony/ims/ImsExternalCallState.java
@@ -45,6 +45,7 @@
private int mCallId;
// Number
private Uri mAddress;
+ private Uri mLocalAddress;
private boolean mIsPullable;
// CALL_STATE_CONFIRMED / CALL_STATE_TERMINATED
private int mCallState;
@@ -69,10 +70,24 @@
}
/** @hide */
+ public ImsExternalCallState(int callId, Uri address, Uri localAddress,
+ boolean isPullable, int callState, int callType, boolean isCallheld) {
+ mCallId = callId;
+ mAddress = address;
+ mLocalAddress = localAddress;
+ mIsPullable = isPullable;
+ mCallState = callState;
+ mCallType = callType;
+ mIsHeld = isCallheld;
+ Rlog.d(TAG, "ImsExternalCallState = " + this);
+ }
+
+ /** @hide */
public ImsExternalCallState(Parcel in) {
mCallId = in.readInt();
ClassLoader classLoader = ImsExternalCallState.class.getClassLoader();
mAddress = in.readParcelable(classLoader);
+ mLocalAddress = in.readParcelable(classLoader);
mIsPullable = (in.readInt() != 0);
mCallState = in.readInt();
mCallType = in.readInt();
@@ -89,6 +104,7 @@
public void writeToParcel(Parcel out, int flags) {
out.writeInt(mCallId);
out.writeParcelable(mAddress, 0);
+ out.writeParcelable(mLocalAddress, 0);
out.writeInt(mIsPullable ? 1 : 0);
out.writeInt(mCallState);
out.writeInt(mCallType);
@@ -117,6 +133,11 @@
return mAddress;
}
+ /** @hide */
+ public Uri getLocalAddress() {
+ return mLocalAddress;
+ }
+
public boolean isCallPullable() {
return mIsPullable;
}
@@ -137,6 +158,7 @@
public String toString() {
return "ImsExternalCallState { mCallId = " + mCallId +
", mAddress = " + Log.pii(mAddress) +
+ ", mLocalAddress = " + Log.pii(mLocalAddress) +
", mIsPullable = " + mIsPullable +
", mCallState = " + mCallState +
", mCallType = " + mCallType +
diff --git a/telephony/java/android/telephony/ims/feature/ImsFeature.java b/telephony/java/android/telephony/ims/feature/ImsFeature.java
index d537699..b77881e 100644
--- a/telephony/java/android/telephony/ims/feature/ImsFeature.java
+++ b/telephony/java/android/telephony/ims/feature/ImsFeature.java
@@ -341,15 +341,15 @@
}
}
+ /** @hide */
+ protected Context mContext;
+ /** @hide */
+ protected final Object mLock = new Object();
+
private final Set<IImsFeatureStatusCallback> mStatusCallbacks = Collections.newSetFromMap(
new WeakHashMap<IImsFeatureStatusCallback, Boolean>());
private @ImsState int mState = STATE_UNAVAILABLE;
private int mSlotId = SubscriptionManager.INVALID_SIM_SLOT_INDEX;
- /**
- * @hide
- */
- protected Context mContext;
- private final Object mLock = new Object();
private final RemoteCallbackList<IImsCapabilityCallback> mCapabilityCallbacks
= new RemoteCallbackList<>();
private Capabilities mCapabilityStatus = new Capabilities();
diff --git a/telephony/java/android/telephony/ims/feature/MmTelFeature.java b/telephony/java/android/telephony/ims/feature/MmTelFeature.java
index bc790fb..7681aef 100644
--- a/telephony/java/android/telephony/ims/feature/MmTelFeature.java
+++ b/telephony/java/android/telephony/ims/feature/MmTelFeature.java
@@ -22,25 +22,25 @@
import android.os.Message;
import android.os.RemoteException;
import android.telecom.TelecomManager;
-import android.telephony.ims.stub.ImsRegistrationImplBase;
-import android.telephony.ims.stub.ImsCallSessionImplBase;
-import android.telephony.ims.stub.ImsSmsImplBase;
+import android.telephony.ims.ImsCallProfile;
+import android.telephony.ims.ImsCallSession;
+import android.telephony.ims.ImsReasonInfo;
import android.telephony.ims.aidl.IImsCapabilityCallback;
import android.telephony.ims.aidl.IImsMmTelFeature;
import android.telephony.ims.aidl.IImsMmTelListener;
import android.telephony.ims.aidl.IImsSmsListener;
+import android.telephony.ims.stub.ImsCallSessionImplBase;
import android.telephony.ims.stub.ImsEcbmImplBase;
import android.telephony.ims.stub.ImsMultiEndpointImplBase;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
+import android.telephony.ims.stub.ImsSmsImplBase;
import android.telephony.ims.stub.ImsUtImplBase;
import android.util.Log;
-import android.telephony.ims.ImsCallProfile;
-import android.telephony.ims.ImsReasonInfo;
import com.android.ims.internal.IImsCallSession;
import com.android.ims.internal.IImsEcbm;
import com.android.ims.internal.IImsMultiEndpoint;
import com.android.ims.internal.IImsUt;
-import android.telephony.ims.ImsCallSession;
import com.android.internal.annotations.VisibleForTesting;
import java.lang.annotation.Retention;
@@ -61,20 +61,16 @@
private final IImsMmTelFeature mImsMMTelBinder = new IImsMmTelFeature.Stub() {
@Override
- public void setListener(IImsMmTelListener l) throws RemoteException {
- synchronized (mLock) {
- MmTelFeature.this.setListener(l);
- }
+ public void setListener(IImsMmTelListener l) {
+ MmTelFeature.this.setListener(l);
}
@Override
public int getFeatureState() throws RemoteException {
- synchronized (mLock) {
- try {
- return MmTelFeature.this.getFeatureState();
- } catch (Exception e) {
- throw new RemoteException(e.getMessage());
- }
+ try {
+ return MmTelFeature.this.getFeatureState();
+ } catch (Exception e) {
+ throw new RemoteException(e.getMessage());
}
}
@@ -138,10 +134,8 @@
}
@Override
- public int queryCapabilityStatus() throws RemoteException {
- synchronized (mLock) {
- return MmTelFeature.this.queryCapabilityStatus().mCapabilities;
- }
+ public int queryCapabilityStatus() {
+ return MmTelFeature.this.queryCapabilityStatus().mCapabilities;
}
@Override
@@ -158,7 +152,7 @@
@Override
public void changeCapabilitiesConfiguration(CapabilityChangeRequest request,
- IImsCapabilityCallback c) throws RemoteException {
+ IImsCapabilityCallback c) {
synchronized (mLock) {
MmTelFeature.this.requestChangeEnabledCapabilities(request, c);
}
@@ -173,10 +167,8 @@
}
@Override
- public void setSmsListener(IImsSmsListener l) throws RemoteException {
- synchronized (mLock) {
- MmTelFeature.this.setSmsListener(l);
- }
+ public void setSmsListener(IImsSmsListener l) {
+ MmTelFeature.this.setSmsListener(l);
}
@Override
@@ -364,9 +356,6 @@
@Retention(RetentionPolicy.SOURCE)
public @interface ProcessCallResult {}
-
- // Lock for feature synchronization
- private final Object mLock = new Object();
private IImsMmTelListener mListener;
/**
@@ -376,9 +365,9 @@
private void setListener(IImsMmTelListener listener) {
synchronized (mLock) {
mListener = listener;
- }
- if (mListener != null) {
- onFeatureReady();
+ if (mListener != null) {
+ onFeatureReady();
+ }
}
}
diff --git a/telephony/java/com/android/internal/telephony/CarrierAppUtils.java b/telephony/java/com/android/internal/telephony/CarrierAppUtils.java
index bcad554..a1bea4d 100644
--- a/telephony/java/com/android/internal/telephony/CarrierAppUtils.java
+++ b/telephony/java/com/android/internal/telephony/CarrierAppUtils.java
@@ -21,7 +21,6 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
-import android.content.res.Resources;
import android.os.RemoteException;
import android.provider.Settings;
import android.telephony.TelephonyManager;
@@ -145,6 +144,18 @@
telephonyManager.checkCarrierPrivilegesForPackageAnyPhone(packageName) ==
TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
+ // add hiddenUntilInstalled flag for carrier apps and associated apps
+ packageManager.setSystemAppHiddenUntilInstalled(packageName, true);
+ List<ApplicationInfo> associatedAppList = associatedApps.get(packageName);
+ if (associatedAppList != null) {
+ for (ApplicationInfo associatedApp : associatedAppList) {
+ packageManager.setSystemAppHiddenUntilInstalled(
+ associatedApp.packageName,
+ true
+ );
+ }
+ }
+
if (hasPrivileges) {
// Only update enabled state for the app on /system. Once it has been
// updated we shouldn't touch it.
@@ -152,9 +163,14 @@
&& (ai.enabledSetting ==
PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
|| ai.enabledSetting ==
- PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED)) {
+ PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED
+ || (ai.flags & ApplicationInfo.FLAG_INSTALLED) == 0)) {
Slog.i(TAG, "Update state(" + packageName + "): ENABLED for user "
+ userId);
+ packageManager.setSystemAppInstallState(
+ packageName,
+ true /*installed*/,
+ userId);
packageManager.setApplicationEnabledSetting(
packageName,
PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
@@ -164,15 +180,20 @@
}
// Also enable any associated apps for this carrier app.
- List<ApplicationInfo> associatedAppList = associatedApps.get(packageName);
if (associatedAppList != null) {
for (ApplicationInfo associatedApp : associatedAppList) {
if (associatedApp.enabledSetting ==
PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
|| associatedApp.enabledSetting ==
- PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) {
+ PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED
+ || (associatedApp.flags
+ & ApplicationInfo.FLAG_INSTALLED) == 0) {
Slog.i(TAG, "Update associated state(" + associatedApp.packageName
+ "): ENABLED for user " + userId);
+ packageManager.setSystemAppInstallState(
+ associatedApp.packageName,
+ true /*installed*/,
+ userId);
packageManager.setApplicationEnabledSetting(
associatedApp.packageName,
PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
@@ -190,36 +211,33 @@
// updated we shouldn't touch it.
if (!ai.isUpdatedSystemApp()
&& ai.enabledSetting ==
- PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
+ PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
+ && (ai.flags & ApplicationInfo.FLAG_INSTALLED) != 0) {
Slog.i(TAG, "Update state(" + packageName
+ "): DISABLED_UNTIL_USED for user " + userId);
- packageManager.setApplicationEnabledSetting(
+ packageManager.setSystemAppInstallState(
packageName,
- PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED,
- 0,
- userId,
- callingPackage);
+ false /*installed*/,
+ userId);
}
// Also disable any associated apps for this carrier app if this is the first
// run. We avoid doing this a second time because it is brittle to rely on the
// distinction between "default" and "enabled".
if (!hasRunOnce) {
- List<ApplicationInfo> associatedAppList = associatedApps.get(packageName);
if (associatedAppList != null) {
for (ApplicationInfo associatedApp : associatedAppList) {
if (associatedApp.enabledSetting
- == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
+ == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
+ && (associatedApp.flags
+ & ApplicationInfo.FLAG_INSTALLED) != 0) {
Slog.i(TAG,
"Update associated state(" + associatedApp.packageName
+ "): DISABLED_UNTIL_USED for user " + userId);
- packageManager.setApplicationEnabledSetting(
+ packageManager.setSystemAppInstallState(
associatedApp.packageName,
- PackageManager
- .COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED,
- 0,
- userId,
- callingPackage);
+ false /*installed*/,
+ userId);
}
}
}
@@ -357,7 +375,8 @@
String packageName) {
try {
ApplicationInfo ai = packageManager.getApplicationInfo(packageName,
- PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS, userId);
+ PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS
+ | PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS, userId);
if (ai != null && ai.isSystemApp()) {
return ai;
}
diff --git a/telephony/java/com/android/internal/telephony/DctConstants.java b/telephony/java/com/android/internal/telephony/DctConstants.java
index d999c13..86cb1b7 100644
--- a/telephony/java/com/android/internal/telephony/DctConstants.java
+++ b/telephony/java/com/android/internal/telephony/DctConstants.java
@@ -63,34 +63,25 @@
public static final int EVENT_RADIO_AVAILABLE = BASE + 1;
public static final int EVENT_RECORDS_LOADED = BASE + 2;
public static final int EVENT_TRY_SETUP_DATA = BASE + 3;
- public static final int EVENT_DATA_STATE_CHANGED = BASE + 4;
- public static final int EVENT_POLL_PDP = BASE + 5;
public static final int EVENT_RADIO_OFF_OR_NOT_AVAILABLE = BASE + 6;
public static final int EVENT_VOICE_CALL_STARTED = BASE + 7;
public static final int EVENT_VOICE_CALL_ENDED = BASE + 8;
public static final int EVENT_DATA_CONNECTION_DETACHED = BASE + 9;
- public static final int EVENT_LINK_STATE_CHANGED = BASE + 10;
public static final int EVENT_ROAMING_ON = BASE + 11;
public static final int EVENT_ROAMING_OFF = BASE + 12;
public static final int EVENT_ENABLE_NEW_APN = BASE + 13;
- public static final int EVENT_RESTORE_DEFAULT_APN = BASE + 14;
public static final int EVENT_DISCONNECT_DONE = BASE + 15;
public static final int EVENT_DATA_CONNECTION_ATTACHED = BASE + 16;
public static final int EVENT_DATA_STALL_ALARM = BASE + 17;
public static final int EVENT_DO_RECOVERY = BASE + 18;
public static final int EVENT_APN_CHANGED = BASE + 19;
- public static final int EVENT_CDMA_DATA_DETACHED = BASE + 20;
- public static final int EVENT_CDMA_SUBSCRIPTION_SOURCE_CHANGED = BASE + 21;
public static final int EVENT_PS_RESTRICT_ENABLED = BASE + 22;
public static final int EVENT_PS_RESTRICT_DISABLED = BASE + 23;
public static final int EVENT_CLEAN_UP_CONNECTION = BASE + 24;
- public static final int EVENT_CDMA_OTA_PROVISION = BASE + 25;
public static final int EVENT_RESTART_RADIO = BASE + 26;
public static final int EVENT_SET_INTERNAL_DATA_ENABLE = BASE + 27;
- public static final int EVENT_RESET_DONE = BASE + 28;
public static final int EVENT_CLEAN_UP_ALL_CONNECTIONS = BASE + 29;
public static final int CMD_SET_USER_DATA_ENABLE = BASE + 30;
- public static final int CMD_SET_DEPENDENCY_MET = BASE + 31;
public static final int CMD_SET_POLICY_DATA_ENABLE = BASE + 32;
public static final int EVENT_ICC_CHANGED = BASE + 33;
public static final int EVENT_DISCONNECT_DC_RETRYING = BASE + 34;
@@ -112,19 +103,6 @@
/***** Constants *****/
- public static final int APN_INVALID_ID = -1;
- public static final int APN_DEFAULT_ID = 0;
- public static final int APN_MMS_ID = 1;
- public static final int APN_SUPL_ID = 2;
- public static final int APN_DUN_ID = 3;
- public static final int APN_HIPRI_ID = 4;
- public static final int APN_IMS_ID = 5;
- public static final int APN_FOTA_ID = 6;
- public static final int APN_CBS_ID = 7;
- public static final int APN_IA_ID = 8;
- public static final int APN_EMERGENCY_ID = 9;
- public static final int APN_NUM_TYPES = 10;
-
public static final int INVALID = -1;
public static final int DISABLED = 0;
public static final int ENABLED = 1;
diff --git a/telephony/java/com/android/internal/telephony/ISmsBaseImpl.java b/telephony/java/com/android/internal/telephony/ISmsBaseImpl.java
new file mode 100644
index 0000000..cc1d105
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/ISmsBaseImpl.java
@@ -0,0 +1,199 @@
+/* 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;
+
+import android.app.PendingIntent;
+import android.net.Uri;
+import java.lang.UnsupportedOperationException;
+import java.util.List;
+
+public class ISmsBaseImpl extends ISms.Stub {
+
+ @Override
+ public List<SmsRawData> getAllMessagesFromIccEfForSubscriber(int subId, String callingPkg) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean updateMessageOnIccEfForSubscriber(int subId, String callingPkg,
+ int messageIndex, int newStatus, byte[] pdu) throws UnsupportedOperationException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean copyMessageToIccEfForSubscriber(int subId, String callingPkg, int status,
+ byte[] pdu, byte[] smsc) throws UnsupportedOperationException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void sendDataForSubscriber(int subId, String callingPkg, String destAddr,
+ String scAddr, int destPort, byte[] data, PendingIntent sentIntent,
+ PendingIntent deliveryIntent) throws UnsupportedOperationException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void sendDataForSubscriberWithSelfPermissions(int subId, String callingPkg,
+ String destAddr, String scAddr, int destPort, byte[] data,
+ PendingIntent sentIntent, PendingIntent deliveryIntent)
+ throws UnsupportedOperationException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void sendTextForSubscriber(int subId, String callingPkg, String destAddr,
+ String scAddr, String text, PendingIntent sentIntent,
+ PendingIntent deliveryIntent, boolean persistMessageForNonDefaultSmsApp)
+ throws UnsupportedOperationException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void sendTextForSubscriberWithSelfPermissions(int subId, String callingPkg,
+ String destAddr, String scAddr, String text, PendingIntent sentIntent,
+ PendingIntent deliveryIntent, boolean persistMessage)
+ throws UnsupportedOperationException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void sendTextForSubscriberWithOptions(int subId, String callingPkg, String destAddr,
+ String scAddr, String text, PendingIntent sentIntent,
+ PendingIntent deliveryIntent, boolean persistMessageForNonDefaultSmsApp,
+ int priority, boolean expectMore, int validityPeriod)
+ throws UnsupportedOperationException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void injectSmsPduForSubscriber(
+ int subId, byte[] pdu, String format, PendingIntent receivedIntent)
+ throws UnsupportedOperationException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void sendMultipartTextForSubscriber(int subId, String callingPkg,
+ String destinationAddress, String scAddress,
+ List<String> parts, List<PendingIntent> sentIntents,
+ List<PendingIntent> deliveryIntents, boolean persistMessageForNonDefaultSmsApp)
+ throws UnsupportedOperationException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void sendMultipartTextForSubscriberWithOptions(int subId, String callingPkg,
+ String destinationAddress, String scAddress,
+ List<String> parts, List<PendingIntent> sentIntents,
+ List<PendingIntent> deliveryIntents, boolean persistMessageForNonDefaultSmsApp,
+ int priority, boolean expectMore, int validityPeriod)
+ throws UnsupportedOperationException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean enableCellBroadcastForSubscriber(int subId, int messageIdentifier, int ranType)
+ throws UnsupportedOperationException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean disableCellBroadcastForSubscriber(int subId, int messageIdentifier, int ranType)
+ throws UnsupportedOperationException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean enableCellBroadcastRangeForSubscriber(int subId, int startMessageId,
+ int endMessageId, int ranType) throws UnsupportedOperationException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean disableCellBroadcastRangeForSubscriber(int subId, int startMessageId,
+ int endMessageId, int ranType) throws UnsupportedOperationException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int getPremiumSmsPermission(String packageName) throws UnsupportedOperationException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int getPremiumSmsPermissionForSubscriber(int subId, String packageName)
+ throws UnsupportedOperationException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void setPremiumSmsPermission(String packageName, int permission) throws UnsupportedOperationException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void setPremiumSmsPermissionForSubscriber(int subId, String packageName,
+ int permission) throws UnsupportedOperationException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean isImsSmsSupportedForSubscriber(int subId) throws UnsupportedOperationException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean isSmsSimPickActivityNeeded(int subId) throws UnsupportedOperationException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int getPreferredSmsSubscription() throws UnsupportedOperationException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String getImsSmsFormatForSubscriber(int subId) throws UnsupportedOperationException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean isSMSPromptEnabled() throws UnsupportedOperationException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void sendStoredText(int subId, String callingPkg, Uri messageUri, String scAddress,
+ PendingIntent sentIntent, PendingIntent deliveryIntent)
+ throws UnsupportedOperationException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void sendStoredMultipartText(int subId, String callingPkg, Uri messageUri,
+ String scAddress, List<PendingIntent> sentIntents,
+ List<PendingIntent> deliveryIntents) throws UnsupportedOperationException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String createAppSpecificSmsToken(int subId, String callingPkg, PendingIntent intent)
+ throws UnsupportedOperationException {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 6ea8ccb..d850fbc 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -817,12 +817,6 @@
IImsConfig getImsConfig(int slotId, int feature);
/**
- * @return true if the IMS resolver is busy resolving a binding and should not be considered
- * available, false if the IMS resolver is idle.
- */
- boolean isResolvingImsBinding();
-
- /**
* @return true if the ImsService to bind to for the slot id specified was set, false otherwise.
*/
boolean setImsService(int slotId, boolean isCarrierImsService, String packageName);
@@ -1201,6 +1195,13 @@
String getImeiForSlot(int slotIndex, String callingPackage);
/**
+ * Returns the Type Allocation Code from the IMEI for the given slot.
+ *
+ * @param slotIndex - Which slot to retrieve the Type Allocation Code from.
+ */
+ String getTypeAllocationCodeForSlot(int slotIndex);
+
+ /**
* Returns the MEID for the given slot.
*
* @param slotIndex - device slot.
@@ -1211,6 +1212,13 @@
String getMeidForSlot(int slotIndex, String callingPackage);
/**
+ * Returns the Manufacturer Code from the MEID for the given slot.
+ *
+ * @param slotIndex - Which slot to retrieve the Manufacturer Code from.
+ */
+ String getManufacturerCodeForSlot(int slotIndex);
+
+ /**
* Returns the device software version.
*
* @param slotIndex - device slot.
diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index 49fbd8f..3a26350 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -16,15 +16,6 @@
package com.android.internal.telephony;
-/**
- * TODO: This should probably not be an interface see
- * http://www.javaworld.com/javaworld/javaqa/2001-06/01-qa-0608-constants.html and google with
- * http://www.google.com/search?q=interface+constants&ie=utf-8&oe=utf-8&aq=t&rls=com.ubuntu:en-US:unofficial&client=firefox-a
- *
- * Also they should all probably be static final.
- */
-
-import android.os.SystemProperties;
import android.telephony.TelephonyManager;
/**
@@ -231,13 +222,6 @@
int LCE_STOPPED = 0;
int LCE_ACTIVE = 1;
-/*
-cat include/telephony/ril.h | \
- egrep '^#define' | \
- sed -re 's/^#define +([^ ]+)* +([^ ]+)/ int \1 = \2;/' \
- >>java/android/com.android.internal.telephony/gsm/RILConstants.java
-*/
-
/**
* No restriction at all including voice/SMS/USSD/SS/AV64
* and packet data.
@@ -272,6 +256,18 @@
public static final int DATA_PROFILE_OEM_BASE = 1000;
public static final int DATA_PROFILE_INVALID = 0xFFFFFFFF;
+ /**
+ * The request/response/unsol message IDs below match RIL.h through Android O-MR1.
+ *
+ * RIL.h is at hardware/ril/include/telephony.ril.h; RIL support is deprecated and may
+ * be removed in the future.
+ *
+ * Messages defined after O-MR1 have no corresponding definition in RIL.h.
+ * P-and-later messages start at RIL_REQUEST_HAL_NON_RIL_BASE and
+ * RIL_UNSOL_HAL_NON_RIL_BASE.
+ */
+
+ /* Requests begin */
int RIL_REQUEST_GET_SIM_STATUS = 1;
int RIL_REQUEST_ENTER_SIM_PIN = 2;
int RIL_REQUEST_ENTER_SIM_PUK = 3;
@@ -415,15 +411,20 @@
int RIL_REQUEST_SET_CARRIER_INFO_IMSI_ENCRYPTION = 141;
int RIL_REQUEST_START_NETWORK_SCAN = 142;
int RIL_REQUEST_STOP_NETWORK_SCAN = 143;
- int RIL_REQUEST_GET_SLOT_STATUS = 144;
- int RIL_REQUEST_SET_LOGICAL_TO_PHYSICAL_SLOT_MAPPING = 145;
- int RIL_REQUEST_START_KEEPALIVE = 146;
- int RIL_REQUEST_STOP_KEEPALIVE = 147;
- int RIL_REQUEST_SET_SIGNAL_STRENGTH_REPORTING_CRITERIA = 148;
- int RIL_REQUEST_SET_LINK_CAPACITY_REPORTING_CRITERIA = 149;
+ int RIL_REQUEST_START_KEEPALIVE = 144;
+ int RIL_REQUEST_STOP_KEEPALIVE = 145;
+ /* The following requests are not defined in RIL.h */
+ int RIL_REQUEST_HAL_NON_RIL_BASE = 200;
+ int RIL_REQUEST_GET_SLOT_STATUS = 200;
+ int RIL_REQUEST_SET_LOGICAL_TO_PHYSICAL_SLOT_MAPPING = 201;
+ int RIL_REQUEST_SET_SIGNAL_STRENGTH_REPORTING_CRITERIA = 202;
+ int RIL_REQUEST_SET_LINK_CAPACITY_REPORTING_CRITERIA = 203;
+
+ /* Responses begin */
int RIL_RESPONSE_ACKNOWLEDGEMENT = 800;
+ /* Unsols begin */
int RIL_UNSOL_RESPONSE_BASE = 1000;
int RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED = 1000;
int RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED = 1001;
@@ -475,7 +476,10 @@
int RIL_UNSOL_MODEM_RESTART = 1047;
int RIL_UNSOL_CARRIER_INFO_IMSI_ENCRYPTION = 1048;
int RIL_UNSOL_NETWORK_SCAN_RESULT = 1049;
- int RIL_UNSOL_ICC_SLOT_STATUS = 1050;
- int RIL_UNSOL_KEEPALIVE_STATUS = 1051;
- int RIL_UNSOL_PHYSICAL_CHANNEL_CONFIG = 1052;
+ int RIL_UNSOL_KEEPALIVE_STATUS = 1050;
+
+ /* The following unsols are not defined in RIL.h */
+ int RIL_UNSOL_HAL_NON_RIL_BASE = 1100;
+ int RIL_UNSOL_ICC_SLOT_STATUS = 1100;
+ int RIL_UNSOL_PHYSICAL_CHANNEL_CONFIG = 1101;
}
diff --git a/test-base/Android.bp b/test-base/Android.bp
index a0e3985..d25b477 100644
--- a/test-base/Android.bp
+++ b/test-base/Android.bp
@@ -21,6 +21,7 @@
// Also contains the com.android.internal.util.Predicate[s] classes.
java_library {
name: "android.test.base",
+ installable: true,
srcs: ["src/**/*.java"],
@@ -42,6 +43,7 @@
// Also contains the com.android.internal.util.Predicate[s] classes.
java_library {
name: "legacy-test",
+ installable: true,
sdk_version: "current",
static_libs: ["android.test.base"],
@@ -115,4 +117,5 @@
},
},
sdk_version: "current",
+ compile_dex: true,
}
diff --git a/test-mock/Android.bp b/test-mock/Android.bp
index 51fa86b..8d3faae 100644
--- a/test-mock/Android.bp
+++ b/test-mock/Android.bp
@@ -18,6 +18,7 @@
// ===================================
java_library {
name: "android.test.mock",
+ installable: true,
java_version: "1.8",
srcs: ["src/**/*.java"],
@@ -91,6 +92,7 @@
enabled: false,
},
},
+ compile_dex: true,
}
java_library_static {
@@ -104,4 +106,5 @@
enabled: false,
},
},
+ compile_dex: true,
}
diff --git a/test-runner/Android.bp b/test-runner/Android.bp
index b50ba3b..2caa6c4 100644
--- a/test-runner/Android.bp
+++ b/test-runner/Android.bp
@@ -18,6 +18,7 @@
// =====================================
java_library {
name: "android.test.runner",
+ installable: true,
// Needs to be consistent with the repackaged version of this make target.
java_version: "1.8",
@@ -120,4 +121,5 @@
},
},
sdk_version: "current",
+ compile_dex: true,
}
diff --git a/tests/TouchLatency/app/src/main/java/com/prefabulated/touchlatency/TouchLatencyActivity.java b/tests/TouchLatency/app/src/main/java/com/prefabulated/touchlatency/TouchLatencyActivity.java
index b763c78..b4b5ca7 100644
--- a/tests/TouchLatency/app/src/main/java/com/prefabulated/touchlatency/TouchLatencyActivity.java
+++ b/tests/TouchLatency/app/src/main/java/com/prefabulated/touchlatency/TouchLatencyActivity.java
@@ -30,6 +30,7 @@
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
+import android.os.Trace;
import java.util.ArrayList;
import java.util.Collections;
@@ -42,6 +43,7 @@
public TouchLatencyView(Context context, AttributeSet attrs) {
super(context, attrs);
+ Trace.beginSection("TouchLatencyView constructor");
setOnTouchListener(this);
setWillNotDraw(false);
mBluePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
@@ -63,48 +65,56 @@
mBallY = 100.0f;
mVelocityX = 7.0f;
mVelocityY = 7.0f;
+ Trace.endSection();
}
@Override
public boolean onTouch(View view, MotionEvent event) {
+ Trace.beginSection("TouchLatencyView onTouch");
int action = event.getActionMasked();
if (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_MOVE) {
mTouching = true;
invalidate();
+
+ mTouchX = event.getX();
+ mTouchY = event.getY();
} else if (action == MotionEvent.ACTION_UP) {
mTouching = false;
invalidate();
- return true;
- } else {
- return true;
}
- mTouchX = event.getX();
- mTouchY = event.getY();
+ Trace.endSection();
return true;
}
private void drawTouch(Canvas canvas) {
- if (!mTouching) {
- Log.d(LOG_TAG, "Filling background");
+ Trace.beginSection("TouchLatencyView drawTouch");
+
+ try {
+ if (!mTouching) {
+ Log.d(LOG_TAG, "Filling background");
+ canvas.drawColor(BACKGROUND_COLOR);
+ return;
+ }
+
+ float deltaX = (mTouchX - mLastDrawnX);
+ float deltaY = (mTouchY - mLastDrawnY);
+ float scaleFactor = (float) Math.sqrt(deltaX * deltaX + deltaY * deltaY) * 1.5f;
+
+ mLastDrawnX = mTouchX;
+ mLastDrawnY = mTouchY;
+
canvas.drawColor(BACKGROUND_COLOR);
- return;
+ canvas.drawCircle(mTouchX, mTouchY, INNER_RADIUS + 3 * scaleFactor, mRedPaint);
+ canvas.drawCircle(mTouchX, mTouchY, INNER_RADIUS + 2 * scaleFactor, mYellowPaint);
+ canvas.drawCircle(mTouchX, mTouchY, INNER_RADIUS + scaleFactor, mGreenPaint);
+ canvas.drawCircle(mTouchX, mTouchY, INNER_RADIUS, mBluePaint);
+ } finally {
+ Trace.endSection();
}
-
- float deltaX = (mTouchX - mLastDrawnX);
- float deltaY = (mTouchY - mLastDrawnY);
- float scaleFactor = (float) Math.sqrt(deltaX * deltaX + deltaY * deltaY) * 1.5f;
-
- mLastDrawnX = mTouchX;
- mLastDrawnY = mTouchY;
-
- canvas.drawColor(BACKGROUND_COLOR);
- canvas.drawCircle(mTouchX, mTouchY, INNER_RADIUS + 3 * scaleFactor, mRedPaint);
- canvas.drawCircle(mTouchX, mTouchY, INNER_RADIUS + 2 * scaleFactor, mYellowPaint);
- canvas.drawCircle(mTouchX, mTouchY, INNER_RADIUS + scaleFactor, mGreenPaint);
- canvas.drawCircle(mTouchX, mTouchY, INNER_RADIUS, mBluePaint);
}
private void drawBall(Canvas canvas) {
+ Trace.beginSection("TouchLatencyView drawBall");
int width = canvas.getWidth();
int height = canvas.getHeight();
@@ -141,25 +151,29 @@
canvas.drawColor(BACKGROUND_COLOR);
canvas.drawOval(left, top, right, bottom, mYellowPaint);
invalidate();
+ Trace.endSection();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
-
+ Trace.beginSection("TouchLatencyView onDraw");
if (mMode == 0) {
drawTouch(canvas);
} else {
drawBall(canvas);
}
+ Trace.endSection();
}
public void changeMode(MenuItem item) {
+ Trace.beginSection("TouchLatencyView changeMode");
final int NUM_MODES = 2;
final String modes[] = {"Touch", "Ball"};
mMode = (mMode + 1) % NUM_MODES;
invalidate();
item.setTitle(modes[mMode]);
+ Trace.endSection();
}
private Paint mBluePaint, mGreenPaint, mYellowPaint, mRedPaint;
@@ -178,21 +192,26 @@
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ Trace.beginSection("TouchLatencyActivity onCreate");
setContentView(R.layout.activity_touch_latency);
mTouchView = findViewById(R.id.canvasView);
+ Trace.endSection();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
+ Trace.beginSection("TouchLatencyActivity onCreateOptionsMenu");
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_touch_latency, menu);
+ Trace.endSection();
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
+ Trace.beginSection("TouchLatencyActivity onOptionsItemSelected");
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
@@ -203,6 +222,7 @@
mTouchView.changeMode(item);
}
+ Trace.endSection();
return super.onOptionsItemSelected(item);
}
diff --git a/core/tests/HdmiCec/Android.mk b/tests/UsageStatsPerfTests/Android.mk
similarity index 70%
copy from core/tests/HdmiCec/Android.mk
copy to tests/UsageStatsPerfTests/Android.mk
index 450068b..cd29b51 100644
--- a/core/tests/HdmiCec/Android.mk
+++ b/tests/UsageStatsPerfTests/Android.mk
@@ -13,18 +13,22 @@
# limitations under the License.
LOCAL_PATH := $(call my-dir)
-
include $(CLEAR_VARS)
-# Include all test java files
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
LOCAL_MODULE_TAGS := tests
-LOCAL_PACKAGE_NAME := HdmiCecTests
-LOCAL_SDK_VERSION := current
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+LOCAL_SRC_FILES := \
+ $(call all-java-files-under, src)
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ android-support-test \
+ apct-perftests-utils \
+ services.usage
+
+LOCAL_PACKAGE_NAME := UsageStatsPerfTests
+LOCAL_PRIVATE_PLATFORM_APIS := true
+
+# For android.permission.FORCE_STOP_PACKAGES permission
LOCAL_CERTIFICATE := platform
-LOCAL_COMPATIBILITY_SUITE := device-tests
include $(BUILD_PACKAGE)
diff --git a/core/tests/HdmiCec/AndroidManifest.xml b/tests/UsageStatsPerfTests/AndroidManifest.xml
similarity index 72%
copy from core/tests/HdmiCec/AndroidManifest.xml
copy to tests/UsageStatsPerfTests/AndroidManifest.xml
index 80129d0d..596a79c 100644
--- a/core/tests/HdmiCec/AndroidManifest.xml
+++ b/tests/UsageStatsPerfTests/AndroidManifest.xml
@@ -1,9 +1,12 @@
<?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.
@@ -11,13 +14,16 @@
limitations under the License.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="android.test.example.helloworld"
- android:sharedUserId="android.uid.system" >
- <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="21" />
+ package="com.android.frameworks.perftests.usage">
+ <uses-sdk
+ android:minSdkVersion="21" />
+ <uses-permission android:name="android.permission.DUMP" />
+ <uses-permission android:name="android.permission.FORCE_STOP_PACKAGES" />
+
<application>
<uses-library android:name="android.test.runner" />
</application>
+
<instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
- android:targetPackage="android.test.example.helloworld"
- android:label="Hello World Test"/>
+ android:targetPackage="com.android.frameworks.perftests.usage"/>
</manifest>
diff --git a/core/tests/HdmiCec/AndroidTest.xml b/tests/UsageStatsPerfTests/AndroidTest.xml
similarity index 61%
copy from core/tests/HdmiCec/AndroidTest.xml
copy to tests/UsageStatsPerfTests/AndroidTest.xml
index 5af30fb..c9b51dc 100644
--- a/core/tests/HdmiCec/AndroidTest.xml
+++ b/tests/UsageStatsPerfTests/AndroidTest.xml
@@ -1,28 +1,28 @@
<?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.
-->
-<configuration description="Runs sample instrumentation test.">
- <option name="test-suite-tag" value="apct"/>
- <option name="test-suite-tag" value="apct-instrumentation"/>
- <target_preparer class="com.android.tradefed.targetprep.TestFilePushSetup"/>
+<configuration description="Runs UsageStats Performance Tests">
<target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
- <option name="test-file-name" value="HelloWorldTests.apk"/>
+ <option name="test-file-name" value="UsageStatsPerfTests.apk"/>
+ <option name="cleanup-apks" value="true"/>
</target_preparer>
- <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"/>
- <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"/>
+
<option name="test-suite-tag" value="apct"/>
- <option name="test-tag" value="SampleInstrumentationTest"/>
+ <option name="test-tag" value="UsageStatsPerfTests"/>
<test class="com.android.tradefed.testtype.AndroidJUnitTest">
- <option name="package" value="android.test.example.helloworld"/>
+ <option name="package" value="com.android.frameworks.perftests.usage"/>
<option name="runner" value="android.support.test.runner.AndroidJUnitRunner"/>
</test>
-</configuration>
+</configuration>
\ No newline at end of file
diff --git a/tests/UsageStatsPerfTests/src/com/android/frameworks/perftests/usage/tests/UsageStatsDatabasePerfTest.java b/tests/UsageStatsPerfTests/src/com/android/frameworks/perftests/usage/tests/UsageStatsDatabasePerfTest.java
new file mode 100644
index 0000000..8467bee
--- /dev/null
+++ b/tests/UsageStatsPerfTests/src/com/android/frameworks/perftests/usage/tests/UsageStatsDatabasePerfTest.java
@@ -0,0 +1,183 @@
+/*
+ * 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.frameworks.perftests.usage.tests;
+
+import static junit.framework.Assert.assertEquals;
+
+import com.android.server.usage.UsageStatsDatabase;
+import com.android.server.usage.UsageStatsDatabase.StatCombiner;
+import com.android.server.usage.IntervalStats;
+
+import android.app.usage.EventList;
+import android.app.usage.UsageEvents;
+import android.app.usage.UsageStatsManager;
+import android.content.Context;
+import android.os.SystemClock;
+import android.perftests.utils.ManualBenchmarkState;
+import android.perftests.utils.PerfManualStatusReporter;
+import android.support.test.filters.LargeTest;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class UsageStatsDatabasePerfTest {
+ protected static Context sContext;
+ private static UsageStatsDatabase sUsageStatsDatabase;
+ private static File mTestDir;
+
+ // Represents how many apps might have used in a day by a user with a few apps
+ final static int FEW_PKGS = 10;
+ // Represent how many apps might have used in a day by a user with many apps
+ final static int MANY_PKGS = 50;
+ // Represents how many usage events per app a device might have with light usage
+ final static int LIGHT_USE = 10;
+ // Represents how many usage events per app a device might have with heavy usage
+ final static int HEAVY_USE = 50;
+
+ private static final StatCombiner<UsageEvents.Event> sUsageStatsCombiner =
+ new StatCombiner<UsageEvents.Event>() {
+ @Override
+ public void combine(IntervalStats stats, boolean mutable,
+ List<UsageEvents.Event> accResult) {
+ final int size = stats.events.size();
+ for (int i = 0; i < size; i++) {
+ accResult.add(stats.events.get(i));
+ }
+ }
+ };
+
+
+ @Rule
+ public PerfManualStatusReporter mPerfManualStatusReporter = new PerfManualStatusReporter();
+
+ @BeforeClass
+ public static void setUpOnce() {
+ sContext = InstrumentationRegistry.getTargetContext();
+ mTestDir = new File(sContext.getFilesDir(), "UsageStatsDatabasePerfTest");
+ sUsageStatsDatabase = new UsageStatsDatabase(mTestDir);
+ sUsageStatsDatabase.init(1);
+ }
+
+ private static void populateIntervalStats(IntervalStats intervalStats, int packageCount,
+ int eventsPerPackage) {
+ if (intervalStats.events == null) {
+ intervalStats.events = new EventList();
+ }
+ for (int pkg = 0; pkg < packageCount; pkg++) {
+ UsageEvents.Event event = new UsageEvents.Event();
+ event.mPackage = "fake.package.name" + pkg;
+ event.mTimeStamp = 1;
+ event.mEventType = UsageEvents.Event.MOVE_TO_FOREGROUND;
+ for (int evt = 0; evt < eventsPerPackage; evt++) {
+ intervalStats.events.insert(event);
+ intervalStats.update(event.mPackage, event.mTimeStamp, event.mEventType);
+ }
+ }
+ }
+
+ private static void clearUsageStatsFiles() {
+ File[] intervalDirs = mTestDir.listFiles();
+ for (File intervalDir : intervalDirs) {
+ if (intervalDir.isDirectory()) {
+ File[] usageFiles = intervalDir.listFiles();
+ for (File f : usageFiles) {
+ f.delete();
+ }
+ }
+ }
+ }
+
+ private void runQueryUsageStatsTest(int packageCount, int eventsPerPackage) throws IOException {
+ final ManualBenchmarkState benchmarkState = mPerfManualStatusReporter.getBenchmarkState();
+ IntervalStats intervalStats = new IntervalStats();
+ populateIntervalStats(intervalStats, packageCount, eventsPerPackage);
+ sUsageStatsDatabase.putUsageStats(0, intervalStats);
+ long elapsedTimeNs = 0;
+ while (benchmarkState.keepRunning(elapsedTimeNs)) {
+ final long startTime = SystemClock.elapsedRealtimeNanos();
+ List<UsageEvents.Event> temp = sUsageStatsDatabase.queryUsageStats(
+ UsageStatsManager.INTERVAL_DAILY, 0, 2, sUsageStatsCombiner);
+ final long endTime = SystemClock.elapsedRealtimeNanos();
+ elapsedTimeNs = endTime - startTime;
+ assertEquals(packageCount * eventsPerPackage, temp.size());
+ }
+ }
+
+ private void runPutUsageStatsTest(int packageCount, int eventsPerPackage) throws IOException {
+ final ManualBenchmarkState benchmarkState = mPerfManualStatusReporter.getBenchmarkState();
+ IntervalStats intervalStats = new IntervalStats();
+ populateIntervalStats(intervalStats, packageCount, eventsPerPackage);
+ long elapsedTimeNs = 0;
+ while (benchmarkState.keepRunning(elapsedTimeNs)) {
+ final long startTime = SystemClock.elapsedRealtimeNanos();
+ sUsageStatsDatabase.putUsageStats(0, intervalStats);
+ final long endTime = SystemClock.elapsedRealtimeNanos();
+ elapsedTimeNs = endTime - startTime;
+ clearUsageStatsFiles();
+ }
+ }
+
+ @Test
+ public void testQueryUsageStats_FewPkgsLightUse() throws IOException {
+ runQueryUsageStatsTest(FEW_PKGS, LIGHT_USE);
+ }
+
+ @Test
+ public void testPutUsageStats_FewPkgsLightUse() throws IOException {
+ runPutUsageStatsTest(FEW_PKGS, LIGHT_USE);
+ }
+
+ @Test
+ public void testQueryUsageStats_FewPkgsHeavyUse() throws IOException {
+ runQueryUsageStatsTest(FEW_PKGS, HEAVY_USE);
+ }
+
+ @Test
+ public void testPutUsageStats_FewPkgsHeavyUse() throws IOException {
+ runPutUsageStatsTest(FEW_PKGS, HEAVY_USE);
+ }
+
+ @Test
+ public void testQueryUsageStats_ManyPkgsLightUse() throws IOException {
+ runQueryUsageStatsTest(MANY_PKGS, LIGHT_USE);
+ }
+
+ @Test
+ public void testPutUsageStats_ManyPkgsLightUse() throws IOException {
+ runPutUsageStatsTest(MANY_PKGS, LIGHT_USE);
+ }
+
+ @Test
+ public void testQueryUsageStats_ManyPkgsHeavyUse() throws IOException {
+ runQueryUsageStatsTest(MANY_PKGS, HEAVY_USE);
+ }
+
+ @Test
+ public void testPutUsageStats_ManyPkgsHeavyUse() throws IOException {
+ runPutUsageStatsTest(MANY_PKGS, HEAVY_USE);
+ }
+}
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 2208580..dbf81d6 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -57,6 +57,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
@@ -95,6 +96,7 @@
import android.net.ConnectivityThread;
import android.net.INetworkPolicyManager;
import android.net.INetworkStatsService;
+import android.net.InterfaceConfiguration;
import android.net.IpPrefix;
import android.net.LinkAddress;
import android.net.LinkProperties;
@@ -125,6 +127,7 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.os.Process;
+import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.Settings;
@@ -132,6 +135,7 @@
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import android.test.mock.MockContentResolver;
+import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Log;
@@ -145,6 +149,7 @@
import com.android.server.connectivity.DnsManager;
import com.android.server.connectivity.IpConnectivityMetrics;
import com.android.server.connectivity.MockableSystemProperties;
+import com.android.server.connectivity.Nat464Xlat;
import com.android.server.connectivity.NetworkAgentInfo;
import com.android.server.connectivity.NetworkMonitor;
import com.android.server.connectivity.Vpn;
@@ -161,10 +166,13 @@
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;
+import java.net.Inet4Address;
import java.net.InetAddress;
+import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
+import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
@@ -190,6 +198,7 @@
private static final int TIMEOUT_MS = 500;
private static final int TEST_LINGER_DELAY_MS = 120;
+ private static final String CLAT_PREFIX = "v4-";
private static final String MOBILE_IFNAME = "test_rmnet_data0";
private static final String WIFI_IFNAME = "test_wlan0";
@@ -950,6 +959,10 @@
return monitor;
}
+ public Nat464Xlat getNat464Xlat(MockNetworkAgent mna) {
+ return getNetworkAgentInfoForNetwork(mna.getNetwork()).clatd;
+ }
+
@Override
public MultinetworkPolicyTracker createMultinetworkPolicyTracker(
Context c, Handler h, Runnable r) {
@@ -4422,4 +4435,97 @@
mMockVpn.disconnect();
}
+
+ /**
+ * Make simulated InterfaceConfig for Nat464Xlat to query clat lower layer info.
+ */
+ private InterfaceConfiguration getClatInterfaceConfig(LinkAddress la) {
+ InterfaceConfiguration cfg = new InterfaceConfiguration();
+ cfg.setHardwareAddress("11:22:33:44:55:66");
+ cfg.setLinkAddress(la);
+ return cfg;
+ }
+
+ /**
+ * Make expected stack link properties, copied from Nat464Xlat.
+ */
+ private LinkProperties makeClatLinkProperties(LinkAddress la) {
+ LinkAddress clatAddress = la;
+ LinkProperties stacked = new LinkProperties();
+ stacked.setInterfaceName(CLAT_PREFIX + MOBILE_IFNAME);
+ RouteInfo ipv4Default = new RouteInfo(
+ new LinkAddress(Inet4Address.ANY, 0),
+ clatAddress.getAddress(), CLAT_PREFIX + MOBILE_IFNAME);
+ stacked.addRoute(ipv4Default);
+ stacked.addLinkAddress(clatAddress);
+ return stacked;
+ }
+
+ @Test
+ public void testStackedLinkProperties() throws UnknownHostException, RemoteException {
+ final LinkAddress myIpv4 = new LinkAddress("1.2.3.4/24");
+ final LinkAddress myIpv6 = new LinkAddress("2001:db8:1::1/64");
+ final NetworkRequest networkRequest = new NetworkRequest.Builder()
+ .addTransportType(TRANSPORT_CELLULAR)
+ .addCapability(NET_CAPABILITY_INTERNET)
+ .build();
+ final TestNetworkCallback networkCallback = new TestNetworkCallback();
+ mCm.registerNetworkCallback(networkRequest, networkCallback);
+
+ // Prepare ipv6 only link properties and connect.
+ mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ final LinkProperties cellLp = new LinkProperties();
+ cellLp.setInterfaceName(MOBILE_IFNAME);
+ cellLp.addLinkAddress(myIpv6);
+ cellLp.addRoute(new RouteInfo((IpPrefix) null, myIpv6.getAddress(), MOBILE_IFNAME));
+ cellLp.addRoute(new RouteInfo(myIpv6, null, MOBILE_IFNAME));
+ reset(mNetworkManagementService);
+ when(mNetworkManagementService.getInterfaceConfig(CLAT_PREFIX + MOBILE_IFNAME))
+ .thenReturn(getClatInterfaceConfig(myIpv4));
+
+ // Connect with ipv6 link properties, then expect clat setup ipv4 and update link
+ // properties properly.
+ mCellNetworkAgent.sendLinkProperties(cellLp);
+ mCellNetworkAgent.connect(true);
+ networkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ verify(mNetworkManagementService, times(1)).startClatd(MOBILE_IFNAME);
+ Nat464Xlat clat = mService.getNat464Xlat(mCellNetworkAgent);
+
+ // Clat iface up, expect stack link updated.
+ clat.interfaceLinkStateChanged(CLAT_PREFIX + MOBILE_IFNAME, true);
+ waitForIdle();
+ List<LinkProperties> stackedLps = mCm.getLinkProperties(mCellNetworkAgent.getNetwork())
+ .getStackedLinks();
+ assertEquals(makeClatLinkProperties(myIpv4), stackedLps.get(0));
+
+ // Change trivial linkproperties and see if stacked link is preserved.
+ cellLp.addDnsServer(InetAddress.getByName("8.8.8.8"));
+ mCellNetworkAgent.sendLinkProperties(cellLp);
+ waitForIdle();
+ networkCallback.expectCallback(CallbackState.LINK_PROPERTIES, mCellNetworkAgent);
+
+ List<LinkProperties> stackedLpsAfterChange =
+ mCm.getLinkProperties(mCellNetworkAgent.getNetwork()).getStackedLinks();
+ assertNotEquals(stackedLpsAfterChange, Collections.EMPTY_LIST);
+ assertEquals(makeClatLinkProperties(myIpv4), stackedLpsAfterChange.get(0));
+
+ // Add ipv4 address, expect stacked linkproperties be cleaned up
+ cellLp.addLinkAddress(myIpv4);
+ cellLp.addRoute(new RouteInfo(myIpv4, null, MOBILE_IFNAME));
+ mCellNetworkAgent.sendLinkProperties(cellLp);
+ waitForIdle();
+ networkCallback.expectCallback(CallbackState.LINK_PROPERTIES, mCellNetworkAgent);
+ verify(mNetworkManagementService, times(1)).stopClatd(MOBILE_IFNAME);
+
+ // Clat iface removed, expect linkproperties revert to original one
+ clat.interfaceRemoved(CLAT_PREFIX + MOBILE_IFNAME);
+ waitForIdle();
+ networkCallback.expectCallback(CallbackState.LINK_PROPERTIES, mCellNetworkAgent);
+ LinkProperties actualLpAfterIpv4 = mCm.getLinkProperties(mCellNetworkAgent.getNetwork());
+ assertEquals(cellLp, actualLpAfterIpv4);
+
+ // Clean up
+ mCellNetworkAgent.disconnect();
+ mCm.unregisterNetworkCallback(networkCallback);
+ }
}
diff --git a/tests/net/java/com/android/server/connectivity/TetheringTest.java b/tests/net/java/com/android/server/connectivity/TetheringTest.java
index 34ecde8..0d3b8e4 100644
--- a/tests/net/java/com/android/server/connectivity/TetheringTest.java
+++ b/tests/net/java/com/android/server/connectivity/TetheringTest.java
@@ -71,6 +71,7 @@
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
+import android.net.NetworkRequest;
import android.net.NetworkState;
import android.net.NetworkUtils;
import android.net.RouteInfo;
@@ -128,6 +129,10 @@
private static final String TEST_USB_IFNAME = "test_rndis0";
private static final String TEST_WLAN_IFNAME = "test_wlan0";
+ // Actual contents of the request don't matter for this test. The lack of
+ // any specific TRANSPORT_* is sufficient to identify this request.
+ private static final NetworkRequest mDefaultRequest = new NetworkRequest.Builder().build();
+
@Mock private ApplicationInfo mApplicationInfo;
@Mock private Context mContext;
@Mock private INetworkManagementService mNMService;
@@ -238,6 +243,11 @@
isTetheringSupportedCalls++;
return true;
}
+
+ @Override
+ public NetworkRequest getDefaultNetworkRequest() {
+ return mDefaultRequest;
+ }
}
private static NetworkState buildMobileUpstreamState(boolean withIPv4, boolean withIPv6,
@@ -305,6 +315,8 @@
.thenReturn(new String[0]);
when(mResources.getIntArray(com.android.internal.R.array.config_tether_upstream_types))
.thenReturn(new int[0]);
+ when(mResources.getBoolean(com.android.internal.R.bool.config_tether_upstream_automatic))
+ .thenReturn(false);
when(mNMService.listInterfaces())
.thenReturn(new String[] {
TEST_MOBILE_IFNAME, TEST_WLAN_IFNAME, TEST_USB_IFNAME});
@@ -458,6 +470,7 @@
}
private void prepareUsbTethering(NetworkState upstreamState) {
+ when(mUpstreamNetworkMonitor.getCurrentPreferredUpstream()).thenReturn(upstreamState);
when(mUpstreamNetworkMonitor.selectPreferredUpstreamType(any()))
.thenReturn(upstreamState);
@@ -519,7 +532,7 @@
TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_LOCAL_ONLY);
verifyNoMoreInteractions(mWifiManager);
verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_ACTIVE_LOCAL_ONLY);
- verify(mUpstreamNetworkMonitor, times(1)).start();
+ verify(mUpstreamNetworkMonitor, times(1)).start(any(NetworkRequest.class));
// TODO: Figure out why this isn't exactly once, for sendTetherStateChangedBroadcast().
assertTrue(1 <= mTetheringDependencies.isTetheringSupportedCalls);
@@ -656,6 +669,24 @@
}
@Test
+ public void configTetherUpstreamAutomaticIgnoresConfigTetherUpstreamTypes() throws Exception {
+ when(mResources.getBoolean(com.android.internal.R.bool.config_tether_upstream_automatic))
+ .thenReturn(true);
+ sendConfigurationChanged();
+
+ // Setup IPv6
+ final NetworkState upstreamState = buildMobileIPv6UpstreamState();
+ runUsbTethering(upstreamState);
+
+ // UpstreamNetworkMonitor should choose upstream automatically
+ // (in this specific case: choose the default network).
+ verify(mUpstreamNetworkMonitor, times(1)).getCurrentPreferredUpstream();
+ verify(mUpstreamNetworkMonitor, never()).selectPreferredUpstreamType(any());
+
+ verify(mUpstreamNetworkMonitor, times(1)).setCurrentUpstream(upstreamState.network);
+ }
+
+ @Test
public void workingLocalOnlyHotspotEnrichedApBroadcastWithIfaceChanged() throws Exception {
workingLocalOnlyHotspotEnrichedApBroadcast(true);
}
@@ -718,7 +749,7 @@
TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_TETHERED);
verifyNoMoreInteractions(mWifiManager);
verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_ACTIVE_TETHER);
- verify(mUpstreamNetworkMonitor, times(1)).start();
+ verify(mUpstreamNetworkMonitor, times(1)).start(any(NetworkRequest.class));
// In tethering mode, in the default configuration, an explicit request
// for a mobile network is also made.
verify(mUpstreamNetworkMonitor, times(1)).registerMobileNetworkRequest();
diff --git a/tests/net/java/com/android/server/connectivity/tethering/SimChangeListenerTest.java b/tests/net/java/com/android/server/connectivity/tethering/SimChangeListenerTest.java
deleted file mode 100644
index f58ea7e..0000000
--- a/tests/net/java/com/android/server/connectivity/tethering/SimChangeListenerTest.java
+++ /dev/null
@@ -1,132 +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.connectivity.tethering;
-
-import static com.android.internal.telephony.IccCardConstants.INTENT_VALUE_ICC_ABSENT;
-import static com.android.internal.telephony.IccCardConstants.INTENT_VALUE_ICC_LOADED;
-import static com.android.internal.telephony.IccCardConstants.INTENT_KEY_ICC_STATE;
-import static com.android.internal.telephony.TelephonyIntents.ACTION_SIM_STATE_CHANGED;
-
-import static org.junit.Assert.assertEquals;
-import static org.mockito.Mockito.reset;
-
-import android.content.Context;
-import android.content.Intent;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.UserHandle;
-
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-
-import com.android.internal.util.test.BroadcastInterceptingContext;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.BeforeClass;
-import org.junit.runner.RunWith;
-import org.junit.Test;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.mockito.MockitoAnnotations;
-
-
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-public class SimChangeListenerTest {
- @Mock private Context mContext;
- private BroadcastInterceptingContext mServiceContext;
- private Handler mHandler;
- private SimChangeListener mSCL;
- private int mCallbackCount;
-
- private void doCallback() { mCallbackCount++; }
-
- private class MockContext extends BroadcastInterceptingContext {
- MockContext(Context base) {
- super(base);
- }
- }
-
- @BeforeClass
- public static void setUpBeforeClass() throws Exception {
- if (Looper.myLooper() == null) {
- Looper.prepare();
- }
- }
-
- @Before public void setUp() throws Exception {
- MockitoAnnotations.initMocks(this);
- reset(mContext);
- mServiceContext = new MockContext(mContext);
- mHandler = new Handler(Looper.myLooper());
- mCallbackCount = 0;
- mSCL = new SimChangeListener(mServiceContext, mHandler, () -> doCallback());
- }
-
- @After public void tearDown() throws Exception {
- if (mSCL != null) {
- mSCL.stopListening();
- mSCL = null;
- }
- }
-
- private void sendSimStateChangeIntent(String state) {
- final Intent intent = new Intent(ACTION_SIM_STATE_CHANGED);
- intent.putExtra(INTENT_KEY_ICC_STATE, state);
- mServiceContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
- }
-
- @Test
- public void testNotSeenFollowedBySeenCallsCallback() {
- mSCL.startListening();
-
- sendSimStateChangeIntent(INTENT_VALUE_ICC_ABSENT);
- sendSimStateChangeIntent(INTENT_VALUE_ICC_LOADED);
- assertEquals(1, mCallbackCount);
-
- sendSimStateChangeIntent(INTENT_VALUE_ICC_ABSENT);
- sendSimStateChangeIntent(INTENT_VALUE_ICC_LOADED);
- assertEquals(2, mCallbackCount);
-
- mSCL.stopListening();
- }
-
- @Test
- public void testNotListeningDoesNotCallback() {
- sendSimStateChangeIntent(INTENT_VALUE_ICC_ABSENT);
- sendSimStateChangeIntent(INTENT_VALUE_ICC_LOADED);
- assertEquals(0, mCallbackCount);
-
- sendSimStateChangeIntent(INTENT_VALUE_ICC_ABSENT);
- sendSimStateChangeIntent(INTENT_VALUE_ICC_LOADED);
- assertEquals(0, mCallbackCount);
- }
-
- @Test
- public void testSeenOnlyDoesNotCallback() {
- mSCL.startListening();
-
- sendSimStateChangeIntent(INTENT_VALUE_ICC_LOADED);
- assertEquals(0, mCallbackCount);
-
- sendSimStateChangeIntent(INTENT_VALUE_ICC_LOADED);
- assertEquals(0, mCallbackCount);
-
- mSCL.stopListening();
- }
-}
diff --git a/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java b/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java
index 9661dc2..3e21a2c 100644
--- a/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java
+++ b/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java
@@ -31,6 +31,7 @@
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.anyString;
+import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
@@ -73,6 +74,7 @@
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
@@ -84,6 +86,10 @@
private static final boolean INCLUDES = true;
private static final boolean EXCLUDES = false;
+ // Actual contents of the request don't matter for this test. The lack of
+ // any specific TRANSPORT_* is sufficient to identify this request.
+ private static final NetworkRequest mDefaultRequest = new NetworkRequest.Builder().build();
+
@Mock private Context mContext;
@Mock private IConnectivityManager mCS;
@Mock private SharedLog mLog;
@@ -113,6 +119,13 @@
}
@Test
+ public void testStopWithoutStartIsNonFatal() {
+ mUNM.stop();
+ mUNM.stop();
+ mUNM.stop();
+ }
+
+ @Test
public void testDoesNothingBeforeStarted() {
assertTrue(mCM.hasNoCallbacks());
assertFalse(mUNM.mobileNetworkRequested());
@@ -127,7 +140,7 @@
public void testDefaultNetworkIsTracked() throws Exception {
assertEquals(0, mCM.trackingDefault.size());
- mUNM.start();
+ mUNM.start(mDefaultRequest);
assertEquals(1, mCM.trackingDefault.size());
mUNM.stop();
@@ -138,7 +151,7 @@
public void testListensForAllNetworks() throws Exception {
assertTrue(mCM.listening.isEmpty());
- mUNM.start();
+ mUNM.start(mDefaultRequest);
assertFalse(mCM.listening.isEmpty());
assertTrue(mCM.isListeningForAll());
@@ -148,9 +161,11 @@
@Test
public void testCallbacksRegistered() {
- mUNM.start();
- verify(mCM, times(1)).registerNetworkCallback(any(), any(), any());
- verify(mCM, times(1)).registerDefaultNetworkCallback(any(), any());
+ mUNM.start(mDefaultRequest);
+ verify(mCM, times(1)).registerNetworkCallback(
+ any(NetworkRequest.class), any(NetworkCallback.class), any(Handler.class));
+ verify(mCM, times(1)).requestNetwork(
+ eq(mDefaultRequest), any(NetworkCallback.class), any(Handler.class));
mUNM.stop();
verify(mCM, times(2)).unregisterNetworkCallback(any(NetworkCallback.class));
@@ -161,7 +176,7 @@
assertFalse(mUNM.mobileNetworkRequested());
assertEquals(0, mCM.requested.size());
- mUNM.start();
+ mUNM.start(mDefaultRequest);
assertFalse(mUNM.mobileNetworkRequested());
assertEquals(0, mCM.requested.size());
@@ -184,17 +199,17 @@
assertFalse(mUNM.mobileNetworkRequested());
assertEquals(0, mCM.requested.size());
- mUNM.start();
- verify(mCM, Mockito.times(1)).registerNetworkCallback(
+ mUNM.start(mDefaultRequest);
+ verify(mCM, times(1)).registerNetworkCallback(
any(NetworkRequest.class), any(NetworkCallback.class), any(Handler.class));
- verify(mCM, Mockito.times(1)).registerDefaultNetworkCallback(
- any(NetworkCallback.class), any(Handler.class));
+ verify(mCM, times(1)).requestNetwork(
+ eq(mDefaultRequest), any(NetworkCallback.class), any(Handler.class));
assertFalse(mUNM.mobileNetworkRequested());
assertEquals(0, mCM.requested.size());
mUNM.updateMobileRequiresDun(true);
mUNM.registerMobileNetworkRequest();
- verify(mCM, Mockito.times(1)).requestNetwork(
+ verify(mCM, times(1)).requestNetwork(
any(NetworkRequest.class), any(NetworkCallback.class), anyInt(), anyInt(),
any(Handler.class));
@@ -222,7 +237,7 @@
assertFalse(mUNM.mobileNetworkRequested());
assertEquals(0, mCM.requested.size());
- mUNM.start();
+ mUNM.start(mDefaultRequest);
assertFalse(mUNM.mobileNetworkRequested());
assertEquals(0, mCM.requested.size());
@@ -242,7 +257,7 @@
@Test
public void testUpdateMobileRequiresDun() throws Exception {
- mUNM.start();
+ mUNM.start(mDefaultRequest);
// Test going from no-DUN to DUN correctly re-registers callbacks.
mUNM.updateMobileRequiresDun(false);
@@ -270,7 +285,7 @@
final Collection<Integer> preferredTypes = new ArrayList<>();
preferredTypes.add(TYPE_WIFI);
- mUNM.start();
+ mUNM.start(mDefaultRequest);
// There are no networks, so there is nothing to select.
assertSatisfiesLegacyType(TYPE_NONE, mUNM.selectPreferredUpstreamType(preferredTypes));
@@ -334,8 +349,47 @@
}
@Test
+ public void testGetCurrentPreferredUpstream() throws Exception {
+ mUNM.start(mDefaultRequest);
+ mUNM.updateMobileRequiresDun(false);
+
+ // [0] Mobile connects, DUN not required -> mobile selected.
+ final TestNetworkAgent cellAgent = new TestNetworkAgent(mCM, TRANSPORT_CELLULAR);
+ cellAgent.fakeConnect();
+ mCM.makeDefaultNetwork(cellAgent);
+ assertEquals(cellAgent.networkId, mUNM.getCurrentPreferredUpstream().network);
+
+ // [1] WiFi connects but not validated/promoted to default -> mobile selected.
+ final TestNetworkAgent wifiAgent = new TestNetworkAgent(mCM, TRANSPORT_WIFI);
+ wifiAgent.fakeConnect();
+ assertEquals(cellAgent.networkId, mUNM.getCurrentPreferredUpstream().network);
+
+ // [2] WiFi validates and is promoted to the default network -> WiFi selected.
+ mCM.makeDefaultNetwork(wifiAgent);
+ assertEquals(wifiAgent.networkId, mUNM.getCurrentPreferredUpstream().network);
+
+ // [3] DUN required, no other changes -> WiFi still selected
+ mUNM.updateMobileRequiresDun(true);
+ assertEquals(wifiAgent.networkId, mUNM.getCurrentPreferredUpstream().network);
+
+ // [4] WiFi no longer validated, mobile becomes default, DUN required -> null selected.
+ mCM.makeDefaultNetwork(cellAgent);
+ assertEquals(null, mUNM.getCurrentPreferredUpstream());
+ // TODO: make sure that a DUN request has been filed. This is currently
+ // triggered by code over in Tethering, but once that has been moved
+ // into UNM we should test for this here.
+
+ // [5] DUN network arrives -> DUN selected
+ final TestNetworkAgent dunAgent = new TestNetworkAgent(mCM, TRANSPORT_CELLULAR);
+ dunAgent.networkCapabilities.addCapability(NET_CAPABILITY_DUN);
+ dunAgent.networkCapabilities.removeCapability(NET_CAPABILITY_INTERNET);
+ dunAgent.fakeConnect();
+ assertEquals(dunAgent.networkId, mUNM.getCurrentPreferredUpstream().network);
+ }
+
+ @Test
public void testLocalPrefixes() throws Exception {
- mUNM.start();
+ mUNM.start(mDefaultRequest);
// [0] Test minimum set of local prefixes.
Set<IpPrefix> local = mUNM.getLocalPrefixes();
@@ -345,7 +399,7 @@
// [1] Pretend Wi-Fi connects.
final TestNetworkAgent wifiAgent = new TestNetworkAgent(mCM, TRANSPORT_WIFI);
- final LinkProperties wifiLp = new LinkProperties();
+ final LinkProperties wifiLp = wifiAgent.linkProperties;
wifiLp.setInterfaceName("wlan0");
final String[] WIFI_ADDRS = {
"fe80::827a:bfff:fe6f:374d", "100.112.103.18",
@@ -358,7 +412,7 @@
wifiLp.addLinkAddress(new LinkAddress(addrStr + cidr));
}
wifiAgent.fakeConnect();
- wifiAgent.sendLinkProperties(wifiLp);
+ wifiAgent.sendLinkProperties();
local = mUNM.getLocalPrefixes();
assertPrefixSet(local, INCLUDES, alreadySeen);
@@ -372,7 +426,7 @@
// [2] Pretend mobile connects.
final TestNetworkAgent cellAgent = new TestNetworkAgent(mCM, TRANSPORT_CELLULAR);
- final LinkProperties cellLp = new LinkProperties();
+ final LinkProperties cellLp = cellAgent.linkProperties;
cellLp.setInterfaceName("rmnet_data0");
final String[] CELL_ADDRS = {
"10.102.211.48", "2001:db8:0:1:b50e:70d9:10c9:433d",
@@ -382,7 +436,7 @@
cellLp.addLinkAddress(new LinkAddress(addrStr + cidr));
}
cellAgent.fakeConnect();
- cellAgent.sendLinkProperties(cellLp);
+ cellAgent.sendLinkProperties();
local = mUNM.getLocalPrefixes();
assertPrefixSet(local, INCLUDES, alreadySeen);
@@ -394,17 +448,18 @@
// [3] Pretend DUN connects.
final TestNetworkAgent dunAgent = new TestNetworkAgent(mCM, TRANSPORT_CELLULAR);
dunAgent.networkCapabilities.addCapability(NET_CAPABILITY_DUN);
- final LinkProperties dunLp = new LinkProperties();
+ dunAgent.networkCapabilities.removeCapability(NET_CAPABILITY_INTERNET);
+ final LinkProperties dunLp = dunAgent.linkProperties;
dunLp.setInterfaceName("rmnet_data1");
final String[] DUN_ADDRS = {
"192.0.2.48", "2001:db8:1:2:b50e:70d9:10c9:433d",
};
for (String addrStr : DUN_ADDRS) {
final String cidr = addrStr.contains(":") ? "/64" : "/27";
- cellLp.addLinkAddress(new LinkAddress(addrStr + cidr));
+ dunLp.addLinkAddress(new LinkAddress(addrStr + cidr));
}
dunAgent.fakeConnect();
- dunAgent.sendLinkProperties(dunLp);
+ dunAgent.sendLinkProperties();
local = mUNM.getLocalPrefixes();
assertPrefixSet(local, INCLUDES, alreadySeen);
@@ -442,6 +497,7 @@
public static class TestConnectivityManager extends ConnectivityManager {
public Map<NetworkCallback, Handler> allCallbacks = new HashMap<>();
public Set<NetworkCallback> trackingDefault = new HashSet<>();
+ public TestNetworkAgent defaultNetwork = null;
public Map<NetworkCallback, NetworkRequest> listening = new HashMap<>();
public Map<NetworkCallback, NetworkRequest> requested = new HashMap<>();
public Map<NetworkCallback, Integer> legacyTypeMap = new HashMap<>();
@@ -483,12 +539,34 @@
int getNetworkId() { return ++mNetworkId; }
+ void makeDefaultNetwork(TestNetworkAgent agent) {
+ if (Objects.equals(defaultNetwork, agent)) return;
+
+ final TestNetworkAgent formerDefault = defaultNetwork;
+ defaultNetwork = agent;
+
+ for (NetworkCallback cb : trackingDefault) {
+ if (defaultNetwork != null) {
+ cb.onAvailable(defaultNetwork.networkId);
+ cb.onCapabilitiesChanged(
+ defaultNetwork.networkId, defaultNetwork.networkCapabilities);
+ cb.onLinkPropertiesChanged(
+ defaultNetwork.networkId, defaultNetwork.linkProperties);
+ }
+ }
+ }
+
@Override
public void requestNetwork(NetworkRequest req, NetworkCallback cb, Handler h) {
assertFalse(allCallbacks.containsKey(cb));
allCallbacks.put(cb, h);
- assertFalse(requested.containsKey(cb));
- requested.put(cb, req);
+ if (mDefaultRequest.equals(req)) {
+ assertFalse(trackingDefault.contains(cb));
+ trackingDefault.add(cb);
+ } else {
+ assertFalse(requested.containsKey(cb));
+ requested.put(cb, req);
+ }
}
@Override
@@ -524,10 +602,7 @@
@Override
public void registerDefaultNetworkCallback(NetworkCallback cb, Handler h) {
- assertFalse(allCallbacks.containsKey(cb));
- allCallbacks.put(cb, h);
- assertFalse(trackingDefault.contains(cb));
- trackingDefault.add(cb);
+ fail("Should never be called.");
}
@Override
@@ -561,6 +636,7 @@
public final Network networkId;
public final int transportType;
public final NetworkCapabilities networkCapabilities;
+ public final LinkProperties linkProperties;
public TestNetworkAgent(TestConnectivityManager cm, int transportType) {
this.cm = cm;
@@ -569,12 +645,14 @@
networkCapabilities = new NetworkCapabilities();
networkCapabilities.addTransportType(transportType);
networkCapabilities.addCapability(NET_CAPABILITY_INTERNET);
+ linkProperties = new LinkProperties();
}
public void fakeConnect() {
for (NetworkCallback cb : cm.listening.keySet()) {
cb.onAvailable(networkId);
cb.onCapabilitiesChanged(networkId, copy(networkCapabilities));
+ cb.onLinkPropertiesChanged(networkId, copy(linkProperties));
}
}
@@ -584,11 +662,16 @@
}
}
- public void sendLinkProperties(LinkProperties lp) {
+ public void sendLinkProperties() {
for (NetworkCallback cb : cm.listening.keySet()) {
- cb.onLinkPropertiesChanged(networkId, lp);
+ cb.onLinkPropertiesChanged(networkId, copy(linkProperties));
}
}
+
+ @Override
+ public String toString() {
+ return String.format("TestNetworkAgent: %s %s", networkId, networkCapabilities);
+ }
}
public static class TestStateMachine extends StateMachine {
@@ -618,6 +701,10 @@
return new NetworkCapabilities(nc);
}
+ static LinkProperties copy(LinkProperties lp) {
+ return new LinkProperties(lp);
+ }
+
static void assertPrefixSet(Set<IpPrefix> prefixes, boolean expectation, String... expected) {
final Set<String> expectedSet = new HashSet<>();
Collections.addAll(expectedSet, expected);
diff --git a/tests/testables/src/android/testing/TestableContentResolver.java b/tests/testables/src/android/testing/TestableContentResolver.java
index 0850916..a0afef8 100644
--- a/tests/testables/src/android/testing/TestableContentResolver.java
+++ b/tests/testables/src/android/testing/TestableContentResolver.java
@@ -20,6 +20,7 @@
import android.content.IContentProvider;
import android.database.ContentObserver;
import android.net.Uri;
+import android.util.ArrayMap;
import android.util.ArraySet;
import com.google.android.collect.Maps;
@@ -35,7 +36,11 @@
*/
public class TestableContentResolver extends ContentResolver {
- private final Map<String, ContentProvider> mProviders = Maps.newHashMap();
+ public static final int STABLE = 1;
+ public static final int UNSTABLE = 2;
+
+ private final Map<String, ContentProvider> mProviders = new ArrayMap<>();
+ private final Map<String, ContentProvider> mUnstableProviders = new ArrayMap<>();
private final ContentResolver mParent;
private final ArraySet<ContentProvider> mInUse = new ArraySet<>();
private boolean mFallbackToExisting;
@@ -62,7 +67,23 @@
* subclasses, or null.
*/
public void addProvider(String name, ContentProvider provider) {
- mProviders.put(name, provider);
+ addProvider(name, provider, STABLE | UNSTABLE);
+ }
+
+ /**
+ * Adds access to a provider based on its authority
+ *
+ * @param name The authority name associated with the provider.
+ * @param provider An instance of {@link android.content.ContentProvider} or one of its
+ * subclasses, or null.
+ */
+ public void addProvider(String name, ContentProvider provider, int flags) {
+ if ((flags & STABLE) != 0) {
+ mProviders.put(name, provider);
+ }
+ if ((flags & UNSTABLE) != 0) {
+ mUnstableProviders.put(name, provider);
+ }
}
@Override
@@ -98,7 +119,7 @@
@Override
protected IContentProvider acquireUnstableProvider(Context c, String name) {
- final ContentProvider provider = mProviders.get(name);
+ final ContentProvider provider = mUnstableProviders.get(name);
if (provider != null) {
return provider.getIContentProvider();
} else {
@@ -128,7 +149,8 @@
@Override
public void notifyChange(Uri uri, ContentObserver observer, boolean syncToNetwork) {
if (!mFallbackToExisting) return;
- if (!mProviders.containsKey(uri.getAuthority())) {
+ if (!mProviders.containsKey(uri.getAuthority())
+ && !mUnstableProviders.containsKey(uri.getAuthority())) {
super.notifyChange(uri, observer, syncToNetwork);
}
}
diff --git a/tests/testables/tests/src/android/testing/TestableContentResolverTest.java b/tests/testables/tests/src/android/testing/TestableContentResolverTest.java
new file mode 100644
index 0000000..71afda0
--- /dev/null
+++ b/tests/testables/tests/src/android/testing/TestableContentResolverTest.java
@@ -0,0 +1,61 @@
+package android.testing;
+
+import android.content.ContentProvider;
+import android.content.IContentProvider;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mockito;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class TestableContentResolverTest {
+
+ @Rule
+ public TestableContext mContext = new TestableContext(InstrumentationRegistry.getContext());
+ private TestableContentResolver mContentResolver;
+
+ @Before
+ public void setup() {
+ mContentResolver = new TestableContentResolver(mContext);
+ mContentResolver.setFallbackToExisting(false);
+ }
+
+ @Test
+ public void testDefaultContentProvider() {
+ ContentProvider provider = Mockito.mock(ContentProvider.class);
+ IContentProvider iprovider = Mockito.mock(IContentProvider.class);
+ Mockito.when(provider.getIContentProvider()).thenReturn(iprovider);
+ mContentResolver.addProvider("test", provider);
+
+ Assert.assertEquals(iprovider, mContentResolver.acquireProvider(mContext, "test"));
+ Assert.assertEquals(iprovider, mContentResolver.acquireUnstableProvider(mContext, "test"));
+ }
+
+ @Test
+ public void testStableContentProvider() {
+ ContentProvider provider = Mockito.mock(ContentProvider.class);
+ IContentProvider iprovider = Mockito.mock(IContentProvider.class);
+ Mockito.when(provider.getIContentProvider()).thenReturn(iprovider);
+ mContentResolver.addProvider("test", provider, TestableContentResolver.STABLE);
+
+ Assert.assertEquals(iprovider, mContentResolver.acquireProvider(mContext, "test"));
+ Assert.assertNull(mContentResolver.acquireUnstableProvider(mContext, "test"));
+ }
+
+ @Test
+ public void testUnstableContentProvider() {
+ ContentProvider provider = Mockito.mock(ContentProvider.class);
+ IContentProvider iprovider = Mockito.mock(IContentProvider.class);
+ Mockito.when(provider.getIContentProvider()).thenReturn(iprovider);
+ mContentResolver.addProvider("test", provider, TestableContentResolver.UNSTABLE);
+
+ Assert.assertEquals(iprovider, mContentResolver.acquireUnstableProvider(mContext, "test"));
+ Assert.assertNull(mContentResolver.acquireProvider(mContext, "test"));
+ }
+}
diff --git a/tools/aapt2/Android.bp b/tools/aapt2/Android.bp
index 7a33de0..2ecf25b 100644
--- a/tools/aapt2/Android.bp
+++ b/tools/aapt2/Android.bp
@@ -15,6 +15,7 @@
//
toolSources = [
+ "cmd/Command.cpp",
"cmd/Compile.cpp",
"cmd/Convert.cpp",
"cmd/Diff.cpp",
@@ -124,7 +125,6 @@
"ConfigDescription.cpp",
"Debug.cpp",
"DominatorTree.cpp",
- "Flags.cpp",
"java/AnnotationProcessor.cpp",
"java/ClassDefinition.cpp",
"java/JavaClassGenerator.cpp",
diff --git a/tools/aapt2/AppInfo.h b/tools/aapt2/AppInfo.h
index d6f5995..7512353 100644
--- a/tools/aapt2/AppInfo.h
+++ b/tools/aapt2/AppInfo.h
@@ -31,9 +31,12 @@
// The app's minimum SDK version, if it is defined.
Maybe<int> min_sdk_version;
- // The app's version code, if it is defined.
+ // The app's version code (the lower 32 bits of the long version code), if it is defined.
Maybe<uint32_t> version_code;
+ // The app's version code major (the upper 32 bits of the long version code), if it is defined.
+ Maybe<uint32_t> version_code_major;
+
// The app's revision code, if it is defined.
Maybe<uint32_t> revision_code;
diff --git a/tools/aapt2/Flags.cpp b/tools/aapt2/Flags.cpp
deleted file mode 100644
index 84977ab..0000000
--- a/tools/aapt2/Flags.cpp
+++ /dev/null
@@ -1,189 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "Flags.h"
-
-#include <iomanip>
-#include <iostream>
-#include <string>
-#include <vector>
-
-#include "androidfw/StringPiece.h"
-
-#include "util/Util.h"
-
-using android::StringPiece;
-
-namespace aapt {
-
-Flags& Flags::RequiredFlag(const StringPiece& name,
- const StringPiece& description, std::string* value) {
- auto func = [value](const StringPiece& arg) -> bool {
- *value = arg.to_string();
- return true;
- };
-
- flags_.push_back(Flag{name.to_string(), description.to_string(), func, true, 1, false});
- return *this;
-}
-
-Flags& Flags::RequiredFlagList(const StringPiece& name,
- const StringPiece& description,
- std::vector<std::string>* value) {
- auto func = [value](const StringPiece& arg) -> bool {
- value->push_back(arg.to_string());
- return true;
- };
-
- flags_.push_back(Flag{name.to_string(), description.to_string(), func, true, 1, false});
- return *this;
-}
-
-Flags& Flags::OptionalFlag(const StringPiece& name,
- const StringPiece& description,
- Maybe<std::string>* value) {
- auto func = [value](const StringPiece& arg) -> bool {
- *value = arg.to_string();
- return true;
- };
-
- flags_.push_back(Flag{name.to_string(), description.to_string(), func, false, 1, false});
- return *this;
-}
-
-Flags& Flags::OptionalFlagList(const StringPiece& name,
- const StringPiece& description,
- std::vector<std::string>* value) {
- auto func = [value](const StringPiece& arg) -> bool {
- value->push_back(arg.to_string());
- return true;
- };
-
- flags_.push_back(Flag{name.to_string(), description.to_string(), func, false, 1, false});
- return *this;
-}
-
-Flags& Flags::OptionalFlagList(const StringPiece& name,
- const StringPiece& description,
- std::unordered_set<std::string>* value) {
- auto func = [value](const StringPiece& arg) -> bool {
- value->insert(arg.to_string());
- return true;
- };
-
- flags_.push_back(Flag{name.to_string(), description.to_string(), func, false, 1, false});
- return *this;
-}
-
-Flags& Flags::OptionalSwitch(const StringPiece& name,
- const StringPiece& description, bool* value) {
- auto func = [value](const StringPiece& arg) -> bool {
- *value = true;
- return true;
- };
-
- flags_.push_back(Flag{name.to_string(), description.to_string(), func, false, 0, false});
- return *this;
-}
-
-void Flags::Usage(const StringPiece& command, std::ostream* out) {
- constexpr size_t kWidth = 50;
-
- *out << command << " [options]";
- for (const Flag& flag : flags_) {
- if (flag.required) {
- *out << " " << flag.name << " arg";
- }
- }
-
- *out << " files...\n\nOptions:\n";
-
- for (const Flag& flag : flags_) {
- std::string argline = flag.name;
- if (flag.num_args > 0) {
- argline += " arg";
- }
-
- // Split the description by newlines and write out the argument (which is
- // empty after
- // the first line) followed by the description line. This will make sure
- // that multiline
- // descriptions are still right justified and aligned.
- for (StringPiece line : util::Tokenize(flag.description, '\n')) {
- *out << " " << std::setw(kWidth) << std::left << argline << line << "\n";
- argline = " ";
- }
- }
- *out << " " << std::setw(kWidth) << std::left << "-h"
- << "Displays this help menu\n";
- out->flush();
-}
-
-bool Flags::Parse(const StringPiece& command,
- const std::vector<StringPiece>& args,
- std::ostream* out_error) {
- for (size_t i = 0; i < args.size(); i++) {
- StringPiece arg = args[i];
- if (*(arg.data()) != '-') {
- args_.push_back(arg.to_string());
- continue;
- }
-
- if (arg == "-h" || arg == "--help") {
- Usage(command, out_error);
- return false;
- }
-
- bool match = false;
- for (Flag& flag : flags_) {
- if (arg == flag.name) {
- if (flag.num_args > 0) {
- i++;
- if (i >= args.size()) {
- *out_error << flag.name << " missing argument.\n\n";
- Usage(command, out_error);
- return false;
- }
- flag.action(args[i]);
- } else {
- flag.action({});
- }
- flag.parsed = true;
- match = true;
- break;
- }
- }
-
- if (!match) {
- *out_error << "unknown option '" << arg << "'.\n\n";
- Usage(command, out_error);
- return false;
- }
- }
-
- for (const Flag& flag : flags_) {
- if (flag.required && !flag.parsed) {
- *out_error << "missing required flag " << flag.name << "\n\n";
- Usage(command, out_error);
- return false;
- }
- }
- return true;
-}
-
-const std::vector<std::string>& Flags::GetArgs() { return args_; }
-
-} // namespace aapt
diff --git a/tools/aapt2/Flags.h b/tools/aapt2/Flags.h
deleted file mode 100644
index 3b3ae71..0000000
--- a/tools/aapt2/Flags.h
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef AAPT_FLAGS_H
-#define AAPT_FLAGS_H
-
-#include <functional>
-#include <ostream>
-#include <string>
-#include <unordered_set>
-#include <vector>
-
-#include "androidfw/StringPiece.h"
-
-#include "util/Maybe.h"
-
-namespace aapt {
-
-class Flags {
- public:
- Flags& RequiredFlag(const android::StringPiece& name, const android::StringPiece& description,
- std::string* value);
- Flags& RequiredFlagList(const android::StringPiece& name, const android::StringPiece& description,
- std::vector<std::string>* value);
- Flags& OptionalFlag(const android::StringPiece& name, const android::StringPiece& description,
- Maybe<std::string>* value);
- Flags& OptionalFlagList(const android::StringPiece& name, const android::StringPiece& description,
- std::vector<std::string>* value);
- Flags& OptionalFlagList(const android::StringPiece& name, const android::StringPiece& description,
- std::unordered_set<std::string>* value);
- Flags& OptionalSwitch(const android::StringPiece& name, const android::StringPiece& description,
- bool* value);
-
- void Usage(const android::StringPiece& command, std::ostream* out);
-
- bool Parse(const android::StringPiece& command, const std::vector<android::StringPiece>& args,
- std::ostream* outError);
-
- const std::vector<std::string>& GetArgs();
-
- private:
- struct Flag {
- std::string name;
- std::string description;
- std::function<bool(const android::StringPiece& value)> action;
- bool required;
- size_t num_args;
-
- bool parsed;
- };
-
- std::vector<Flag> flags_;
- std::vector<std::string> args_;
-};
-
-} // namespace aapt
-
-#endif // AAPT_FLAGS_H
diff --git a/tools/aapt2/Main.cpp b/tools/aapt2/Main.cpp
index 808b29c..23903c9e 100644
--- a/tools/aapt2/Main.cpp
+++ b/tools/aapt2/Main.cpp
@@ -29,6 +29,13 @@
#include "androidfw/StringPiece.h"
#include "Diagnostics.h"
+#include "cmd/Command.h"
+#include "cmd/Compile.h"
+#include "cmd/Convert.h"
+#include "cmd/Diff.h"
+#include "cmd/Dump.h"
+#include "cmd/Link.h"
+#include "cmd/Optimize.h"
#include "util/Files.h"
#include "util/Util.h"
@@ -43,114 +50,121 @@
// Update minor version whenever a feature or flag is added.
static const char* sMinorVersion = "19";
-static void PrintVersion() {
- std::cerr << StringPrintf("Android Asset Packaging Tool (aapt) %s:%s", sMajorVersion,
- sMinorVersion)
- << std::endl;
-}
+/** Prints the version information of AAPT2. */
+class VersionCommand : public Command {
+ public:
+ explicit VersionCommand() : Command("version") {
+ SetDescription("Prints the version of aapt.");
+ }
-static void PrintUsage() {
- std::cerr << "\nusage: aapt2 [compile|link|dump|diff|optimize|convert|version] ..." << std::endl;
-}
-
-extern int Compile(const std::vector<StringPiece>& args, IDiagnostics* diagnostics);
-extern int Link(const std::vector<StringPiece>& args, IDiagnostics* diagnostics);
-extern int Dump(const std::vector<StringPiece>& args);
-extern int Diff(const std::vector<StringPiece>& args);
-extern int Optimize(const std::vector<StringPiece>& args);
-extern int Convert(const std::vector<StringPiece>& args);
-
-static int ExecuteCommand(const StringPiece& command, const std::vector<StringPiece>& args,
- IDiagnostics* diagnostics) {
- if (command == "compile" || command == "c") {
- return Compile(args, diagnostics);
- } else if (command == "link" || command == "l") {
- return Link(args, diagnostics);
- } else if (command == "dump" || command == "d") {
- return Dump(args);
- } else if (command == "diff") {
- return Diff(args);
- } else if (command == "optimize") {
- return Optimize(args);
- } else if (command == "convert") {
- return Convert(args);
- } else if (command == "version") {
- PrintVersion();
+ int Action(const std::vector<std::string>& /* args */) override {
+ std::cerr << StringPrintf("Android Asset Packaging Tool (aapt) %s:%s", sMajorVersion,
+ sMinorVersion)
+ << std::endl;
return 0;
}
- diagnostics->Error(DiagMessage() << "unknown command '" << command << "'");
- return -1;
-}
+};
-static void RunDaemon(IDiagnostics* diagnostics) {
- std::cout << "Ready" << std::endl;
-
- // Run in daemon mode. The first line of input is the command. This can be 'quit' which ends
- // the daemon mode. Each subsequent line is a single parameter to the command. The end of a
- // invocation is signaled by providing an empty line. At any point, an EOF signal or the
- // command 'quit' will end the daemon mode.
- while (true) {
- std::vector<std::string> raw_args;
- for (std::string line; std::getline(std::cin, line) && !line.empty();) {
- raw_args.push_back(line);
- }
-
- if (!std::cin) {
- break;
- }
-
- // An empty command does nothing.
- if (raw_args.empty()) {
- continue;
- }
-
- if (raw_args[0] == "quit") {
- break;
- }
-
- std::vector<StringPiece> args;
- args.insert(args.end(), ++raw_args.begin(), raw_args.end());
- int ret = ExecuteCommand(raw_args[0], args, diagnostics);
- if (ret != 0) {
- std::cerr << "Error" << std::endl;
- }
- std::cerr << "Done" << std::endl;
+/** The main entry point of AAPT. */
+class MainCommand : public Command {
+ public:
+ explicit MainCommand(IDiagnostics* diagnostics) : Command("aapt2"), diagnostics_(diagnostics) {
+ AddOptionalSubcommand(util::make_unique<CompileCommand>(diagnostics));
+ AddOptionalSubcommand(util::make_unique<LinkCommand>(diagnostics));
+ AddOptionalSubcommand(util::make_unique<DumpCommand>());
+ AddOptionalSubcommand(util::make_unique<DiffCommand>());
+ AddOptionalSubcommand(util::make_unique<OptimizeCommand>());
+ AddOptionalSubcommand(util::make_unique<ConvertCommand>());
+ AddOptionalSubcommand(util::make_unique<VersionCommand>());
}
- std::cout << "Exiting daemon" << std::endl;
-}
+
+ int Action(const std::vector<std::string>& args) override {
+ if (args.size() == 0) {
+ diagnostics_->Error(DiagMessage() << "no subcommand specified");
+ } else {
+ diagnostics_->Error(DiagMessage() << "unknown subcommand '" << args[0] << "'");
+ }
+
+ Usage(&std::cerr);
+ return -1;
+ }
+
+ private:
+ IDiagnostics* diagnostics_;
+};
+
+/*
+ * Run in daemon mode. The first line of input is the command. This can be 'quit' which ends
+ * the daemon mode. Each subsequent line is a single parameter to the command. The end of a
+ * invocation is signaled by providing an empty line. At any point, an EOF signal or the
+ * command 'quit' will end the daemon mode.
+ */
+class DaemonCommand : public Command {
+ public:
+ explicit DaemonCommand(IDiagnostics* diagnostics) : Command("daemon", "m"),
+ diagnostics_(diagnostics) {
+ SetDescription("Runs aapt in daemon mode. Each subsequent line is a single parameter to the\n"
+ "command. The end of an invocation is signaled by providing an empty line.");
+ }
+
+ int Action(const std::vector<std::string>& /* args */) override {
+ std::cout << "Ready" << std::endl;
+
+ while (true) {
+ std::vector<std::string> raw_args;
+ for (std::string line; std::getline(std::cin, line) && !line.empty();) {
+ raw_args.push_back(line);
+ }
+
+ if (!std::cin) {
+ break;
+ }
+
+ // An empty command does nothing.
+ if (raw_args.empty()) {
+ continue;
+ }
+
+ // End the dameon
+ if (raw_args[0] == "quit") {
+ break;
+ }
+
+ std::vector<StringPiece> args;
+ args.insert(args.end(), raw_args.begin(), raw_args.end());
+ if (MainCommand(diagnostics_).Execute(args, &std::cerr) != 0) {
+ std::cerr << "Error" << std::endl;
+ }
+ std::cerr << "Done" << std::endl;
+ }
+ std::cout << "Exiting daemon" << std::endl;
+
+ return 0;
+ }
+
+ private:
+ IDiagnostics* diagnostics_;
+};
} // namespace aapt
int MainImpl(int argc, char** argv) {
- if (argc < 2) {
- std::cerr << "no command specified\n";
- aapt::PrintUsage();
+ if (argc < 1) {
return -1;
}
- argv += 1;
- argc -= 1;
-
- aapt::StdErrDiagnostics diagnostics;
-
// Collect the arguments starting after the program name and command name.
std::vector<StringPiece> args;
for (int i = 1; i < argc; i++) {
args.push_back(argv[i]);
}
- const StringPiece command(argv[0]);
- if (command != "daemon" && command != "m") {
- // Single execution.
- const int result = aapt::ExecuteCommand(command, args, &diagnostics);
- if (result < 0) {
- aapt::PrintUsage();
- }
- return result;
- }
+ // Add the daemon subcommand here so it cannot be called while executing the daemon
+ aapt::StdErrDiagnostics diagnostics;
+ auto main_command = new aapt::MainCommand(&diagnostics);
+ main_command->AddOptionalSubcommand(aapt::util::make_unique<aapt::DaemonCommand>(&diagnostics));
- aapt::RunDaemon(&diagnostics);
- return 0;
+ return main_command->Execute(args, &std::cerr);
}
int main(int argc, char** argv) {
diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp
index ef5912b..8719a23 100644
--- a/tools/aapt2/ResourceParser.cpp
+++ b/tools/aapt2/ResourceParser.cpp
@@ -208,6 +208,15 @@
}
};
+// A chunk of text in the XML string within a CDATA tags.
+class CdataSegmentNode : public SegmentNode {
+ public:
+
+ void Build(StringBuilder* builder) const override {
+ builder->AppendText(data, /* preserve_spaces */ true);
+ }
+};
+
// A tag that will be encoded into the final flattened string. Tags like <b> or <i>.
class SpanNode : public Node {
public:
@@ -244,6 +253,7 @@
std::vector<Node*> node_stack;
node_stack.push_back(&root);
+ bool cdata_block = false;
bool saw_span_node = false;
SegmentNode* first_segment = nullptr;
SegmentNode* last_segment = nullptr;
@@ -253,11 +263,15 @@
const xml::XmlPullParser::Event event = parser->event();
// First take care of any SegmentNodes that should be created.
- if (event == xml::XmlPullParser::Event::kStartElement ||
- event == xml::XmlPullParser::Event::kEndElement) {
+ if (event == xml::XmlPullParser::Event::kStartElement
+ || event == xml::XmlPullParser::Event::kEndElement
+ || event == xml::XmlPullParser::Event::kCdataStart
+ || event == xml::XmlPullParser::Event::kCdataEnd) {
if (!current_text.empty()) {
- std::unique_ptr<SegmentNode> segment_node = util::make_unique<SegmentNode>();
+ std::unique_ptr<SegmentNode> segment_node = (cdata_block)
+ ? util::make_unique<CdataSegmentNode>() : util::make_unique<SegmentNode>();
segment_node->data = std::move(current_text);
+
last_segment = node_stack.back()->AddChild(std::move(segment_node));
if (first_segment == nullptr) {
first_segment = last_segment;
@@ -333,6 +347,16 @@
}
} break;
+ case xml::XmlPullParser::Event::kCdataStart: {
+ cdata_block = true;
+ break;
+ }
+
+ case xml::XmlPullParser::Event::kCdataEnd: {
+ cdata_block = false;
+ break;
+ }
+
default:
// ignore.
break;
diff --git a/tools/aapt2/ResourceParser_test.cpp b/tools/aapt2/ResourceParser_test.cpp
index 5711dc3..ee496d5 100644
--- a/tools/aapt2/ResourceParser_test.cpp
+++ b/tools/aapt2/ResourceParser_test.cpp
@@ -989,4 +989,40 @@
ASSERT_FALSE(TestParse(input));
}
+TEST_F(ResourceParserTest, ParseCData) {
+ std::string input = R"(
+ <string name="foo"><![CDATA[some text and ' apostrophe]]></string>)";
+
+ ASSERT_TRUE(TestParse(input));
+ String* output = test::GetValue<String>(&table_, "string/foo");
+ ASSERT_THAT(output, NotNull());
+ EXPECT_THAT(*output, StrValueEq("some text and ' apostrophe"));
+
+ // Double quotes should not change the state of whitespace processing
+ input = R"(<string name="foo2">Hello<![CDATA[ "</string>' ]]> World</string>)";
+ ASSERT_TRUE(TestParse(input));
+ output = test::GetValue<String>(&table_, "string/foo2");
+ ASSERT_THAT(output, NotNull());
+ EXPECT_THAT(*output, StrValueEq(std::string("Hello \"</string>' World").data()));
+
+ // Cdata blocks should not have their whitespace trimmed
+ input = R"(<string name="foo3"> <![CDATA[ text ]]> </string>)";
+ ASSERT_TRUE(TestParse(input));
+ output = test::GetValue<String>(&table_, "string/foo3");
+ ASSERT_THAT(output, NotNull());
+ EXPECT_THAT(*output, StrValueEq(std::string(" text ").data()));
+
+ input = R"(<string name="foo4"> <![CDATA[]]> </string>)";
+ ASSERT_TRUE(TestParse(input));
+ output = test::GetValue<String>(&table_, "string/foo4");
+ ASSERT_THAT(output, NotNull());
+ EXPECT_THAT(*output, StrValueEq(std::string("").data()));
+
+ input = R"(<string name="foo5"> <![CDATA[ ]]> </string>)";
+ ASSERT_TRUE(TestParse(input));
+ output = test::GetValue<String>(&table_, "string/foo5");
+ ASSERT_THAT(output, NotNull());
+ EXPECT_THAT(*output, StrValueEq(std::string(" ").data()));
+}
+
} // namespace aapt
diff --git a/tools/aapt2/ResourceUtils.cpp b/tools/aapt2/ResourceUtils.cpp
index 560077c..c48765b 100644
--- a/tools/aapt2/ResourceUtils.cpp
+++ b/tools/aapt2/ResourceUtils.cpp
@@ -797,16 +797,20 @@
: preserve_spaces_(preserve_spaces), quote_(preserve_spaces) {
}
-StringBuilder& StringBuilder::AppendText(const std::string& text) {
+StringBuilder& StringBuilder::AppendText(const std::string& text, bool preserve_spaces) {
if (!error_.empty()) {
return *this;
}
+ // Enable preserving spaces if it is enabled for this append or the StringBuilder was constructed
+ // to preserve spaces
+ preserve_spaces = (preserve_spaces) ? preserve_spaces : preserve_spaces_;
+
const size_t previous_len = xml_string_.text.size();
Utf8Iterator iter(text);
while (iter.HasNext()) {
char32_t codepoint = iter.Next();
- if (!quote_ && iswspace(codepoint)) {
+ if (!preserve_spaces && !quote_ && iswspace(codepoint)) {
if (!last_codepoint_was_space_) {
// Emit a space if it's the first.
xml_string_.text += ' ';
@@ -827,7 +831,6 @@
case U't':
xml_string_.text += '\t';
break;
-
case U'n':
xml_string_.text += '\n';
break;
@@ -855,12 +858,12 @@
break;
}
}
- } else if (!preserve_spaces_ && codepoint == U'"') {
+ } else if (!preserve_spaces && codepoint == U'"') {
// Only toggle the quote state when we are not preserving spaces.
quote_ = !quote_;
- } else if (!quote_ && codepoint == U'\'') {
- // This should be escaped.
+ } else if (!preserve_spaces && !quote_ && codepoint == U'\'') {
+ // This should be escaped when we are not preserving spaces
error_ = StringPrintf("unescaped apostrophe in string\n\"%s\"", text.c_str());
return *this;
diff --git a/tools/aapt2/ResourceUtils.h b/tools/aapt2/ResourceUtils.h
index 7af2fe0..410ef28 100644
--- a/tools/aapt2/ResourceUtils.h
+++ b/tools/aapt2/ResourceUtils.h
@@ -267,8 +267,10 @@
// single quotations can be used without escaping them.
explicit StringBuilder(bool preserve_spaces = false);
- // Appends a chunk of text.
- StringBuilder& AppendText(const std::string& text);
+ // Appends a chunk of text. If preserve_spaces is true, whitespace removal is not performed, and
+ // single quotations can be used without escaping them for this append. Otherwise, the
+ // StringBuilder will behave as it was constructed.
+ StringBuilder& AppendText(const std::string& text, bool preserve_spaces = false);
// Starts a Span (tag) with the given name. The name is expected to be of the form:
// "tag_name;attr1=value;attr2=value;"
diff --git a/tools/aapt2/ResourceUtils_test.cpp b/tools/aapt2/ResourceUtils_test.cpp
index 11f3fa3..5ce4640 100644
--- a/tools/aapt2/ResourceUtils_test.cpp
+++ b/tools/aapt2/ResourceUtils_test.cpp
@@ -254,6 +254,29 @@
TEST(ResourceUtilsTest, StringBuilderPreserveSpaces) {
EXPECT_THAT(ResourceUtils::StringBuilder(true /*preserve_spaces*/).AppendText("\"").to_string(),
Eq("\""));
+
+ // Single quotes should be able to be used without escaping them when preserving spaces and the
+ // spaces should not be trimmed
+ EXPECT_THAT(ResourceUtils::StringBuilder()
+ .AppendText(" hey guys ")
+ .AppendText(" 'this is so cool' ", /* preserve_spaces */ true)
+ .AppendText(" wow ")
+ .to_string(),
+ Eq(" hey guys 'this is so cool' wow "));
+
+ // Reading a double quote while preserving spaces should not change the quote state
+ EXPECT_THAT(ResourceUtils::StringBuilder()
+ .AppendText(" hey guys ")
+ .AppendText(" \"this is so cool' ", /* preserve_spaces */ true)
+ .AppendText(" wow ")
+ .to_string(),
+ Eq(" hey guys \"this is so cool' wow "));
+ EXPECT_THAT(ResourceUtils::StringBuilder()
+ .AppendText(" hey guys\" ")
+ .AppendText(" \"this is so cool' ", /* preserve_spaces */ true)
+ .AppendText(" wow \" ")
+ .to_string(),
+ Eq(" hey guys \"this is so cool' wow "));
}
} // namespace aapt
diff --git a/tools/aapt2/StringPool.cpp b/tools/aapt2/StringPool.cpp
index b37e1fb..8eabd32 100644
--- a/tools/aapt2/StringPool.cpp
+++ b/tools/aapt2/StringPool.cpp
@@ -367,7 +367,7 @@
static bool EncodeString(const std::string& str, const bool utf8, BigBuffer* out,
IDiagnostics* diag) {
if (utf8) {
- const std::string& encoded = str;
+ const std::string& encoded = util::Utf8ToModifiedUtf8(str);
const ssize_t utf16_length = utf8_to_utf16_length(
reinterpret_cast<const uint8_t*>(encoded.data()), encoded.size());
CHECK(utf16_length >= 0);
diff --git a/tools/aapt2/StringPool_test.cpp b/tools/aapt2/StringPool_test.cpp
index 4b3afe2..0778564 100644
--- a/tools/aapt2/StringPool_test.cpp
+++ b/tools/aapt2/StringPool_test.cpp
@@ -303,6 +303,25 @@
}
}
+TEST(StringPoolTest, FlattenModifiedUTF8) {
+ using namespace android; // For NO_ERROR on Windows.
+ StdErrDiagnostics diag;
+ StringPool pool;
+ StringPool::Ref ref_a = pool.MakeRef("\xF0\x90\x90\x80"); // 𐐀 (U+10400)
+ StringPool::Ref ref_b = pool.MakeRef("foo \xF0\x90\x90\xB7 bar"); // 𐐷 (U+10437)
+ StringPool::Ref ref_c = pool.MakeRef("\xF0\x90\x90\x80\xF0\x90\x90\xB7");
+
+ BigBuffer buffer(1024);
+ StringPool::FlattenUtf8(&buffer, pool, &diag);
+ std::unique_ptr<uint8_t[]> data = util::Copy(buffer);
+
+ // Check that the 4 byte utf-8 codepoint is encoded using 2 3 byte surrogate pairs
+ ResStringPool test;
+ ASSERT_EQ(test.setTo(data.get(), buffer.size()), NO_ERROR);
+ EXPECT_THAT(util::GetString(test, 0), Eq("\xED\xA0\x81\xED\xB0\x80"));
+ EXPECT_THAT(util::GetString(test, 1), Eq("foo \xED\xA0\x81\xED\xB0\xB7 bar"));
+ EXPECT_THAT(util::GetString(test, 2), Eq("\xED\xA0\x81\xED\xB0\x80\xED\xA0\x81\xED\xB0\xB7"));
+}
TEST(StringPoolTest, MaxEncodingLength) {
StdErrDiagnostics diag;
diff --git a/tools/aapt2/cmd/Command.cpp b/tools/aapt2/cmd/Command.cpp
new file mode 100644
index 0000000..09411b9
--- /dev/null
+++ b/tools/aapt2/cmd/Command.cpp
@@ -0,0 +1,222 @@
+/*
+ * 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 "Command.h"
+
+#include <iomanip>
+#include <iostream>
+#include <string>
+#include <vector>
+
+#include "androidfw/StringPiece.h"
+
+#include "util/Util.h"
+
+using android::StringPiece;
+
+namespace aapt {
+
+void Command::AddRequiredFlag(const StringPiece& name,
+ const StringPiece& description, std::string* value) {
+ auto func = [value](const StringPiece& arg) -> bool {
+ *value = arg.to_string();
+ return true;
+ };
+
+ flags_.push_back(Flag{name.to_string(), description.to_string(), func, true, 1, false});
+}
+
+void Command::AddRequiredFlagList(const StringPiece& name,
+ const StringPiece& description,
+ std::vector<std::string>* value) {
+ auto func = [value](const StringPiece& arg) -> bool {
+ value->push_back(arg.to_string());
+ return true;
+ };
+
+ flags_.push_back(Flag{name.to_string(), description.to_string(), func, true, 1, false});
+}
+
+void Command::AddOptionalFlag(const StringPiece& name,
+ const StringPiece& description,
+ Maybe<std::string>* value) {
+ auto func = [value](const StringPiece& arg) -> bool {
+ *value = arg.to_string();
+ return true;
+ };
+
+ flags_.push_back(Flag{name.to_string(), description.to_string(), func, false, 1, false});
+}
+
+void Command::AddOptionalFlagList(const StringPiece& name,
+ const StringPiece& description,
+ std::vector<std::string>* value) {
+ auto func = [value](const StringPiece& arg) -> bool {
+ value->push_back(arg.to_string());
+ return true;
+ };
+
+ flags_.push_back(Flag{name.to_string(), description.to_string(), func, false, 1, false});
+}
+
+void Command::AddOptionalFlagList(const StringPiece& name,
+ const StringPiece& description,
+ std::unordered_set<std::string>* value) {
+ auto func = [value](const StringPiece& arg) -> bool {
+ value->insert(arg.to_string());
+ return true;
+ };
+
+ flags_.push_back(Flag{name.to_string(), description.to_string(), func, false, 1, false});
+}
+
+void Command::AddOptionalSwitch(const StringPiece& name,
+ const StringPiece& description, bool* value) {
+ auto func = [value](const StringPiece& arg) -> bool {
+ *value = true;
+ return true;
+ };
+
+ flags_.push_back(Flag{name.to_string(), description.to_string(), func, false, 0, false});
+}
+
+void Command::AddOptionalSubcommand(std::unique_ptr<Command>&& subcommand) {
+ subcommand->fullname_ = name_ + " " + subcommand->name_;
+ subcommands_.push_back(std::move(subcommand));
+}
+
+void Command::SetDescription(const android::StringPiece& description) {
+ description_ = description.to_string();
+}
+
+void Command::Usage(std::ostream* out) {
+ constexpr size_t kWidth = 50;
+
+ *out << fullname_;
+
+ if (!subcommands_.empty()) {
+ *out << " [subcommand]";
+ }
+
+ *out << " [options]";
+ for (const Flag& flag : flags_) {
+ if (flag.required) {
+ *out << " " << flag.name << " arg";
+ }
+ }
+
+ *out << " files...\n";
+
+ if (!subcommands_.empty()) {
+ *out << "\nSubcommands:\n";
+ for (auto& subcommand : subcommands_) {
+ std::string argline = subcommand->name_;
+
+ // Split the description by newlines and write out the argument (which is
+ // empty after the first line) followed by the description line. This will make sure
+ // that multiline descriptions are still right justified and aligned.
+ for (StringPiece line : util::Tokenize(subcommand->description_, '\n')) {
+ *out << " " << std::setw(kWidth) << std::left << argline << line << "\n";
+ argline = " ";
+ }
+ }
+ }
+
+ *out << "\nOptions:\n";
+
+ for (const Flag& flag : flags_) {
+ std::string argline = flag.name;
+ if (flag.num_args > 0) {
+ argline += " arg";
+ }
+
+ // Split the description by newlines and write out the argument (which is
+ // empty after the first line) followed by the description line. This will make sure
+ // that multiline descriptions are still right justified and aligned.
+ for (StringPiece line : util::Tokenize(flag.description, '\n')) {
+ *out << " " << std::setw(kWidth) << std::left << argline << line << "\n";
+ argline = " ";
+ }
+ }
+ *out << " " << std::setw(kWidth) << std::left << "-h"
+ << "Displays this help menu\n";
+ out->flush();
+}
+
+int Command::Execute(const std::vector<android::StringPiece>& args, std::ostream* out_error) {
+ std::vector<std::string> file_args;
+
+ for (size_t i = 0; i < args.size(); i++) {
+ StringPiece arg = args[i];
+ if (*(arg.data()) != '-') {
+ // Continue parsing as the sub command if the first argument matches one of the subcommands
+ if (i == 0) {
+ for (auto& subcommand : subcommands_) {
+ if (arg == subcommand->name_ || arg==subcommand->short_name_) {
+ return subcommand->Execute(
+ std::vector<android::StringPiece>(args.begin() + 1, args.end()), out_error);
+ }
+ }
+ }
+
+ file_args.push_back(arg.to_string());
+ continue;
+ }
+
+ if (arg == "-h" || arg == "--help") {
+ Usage(out_error);
+ return 1;
+ }
+
+ bool match = false;
+ for (Flag& flag : flags_) {
+ if (arg == flag.name) {
+ if (flag.num_args > 0) {
+ i++;
+ if (i >= args.size()) {
+ *out_error << flag.name << " missing argument.\n\n";
+ Usage(out_error);
+ return false;
+ }
+ flag.action(args[i]);
+ } else {
+ flag.action({});
+ }
+ flag.parsed = true;
+ match = true;
+ break;
+ }
+ }
+
+ if (!match) {
+ *out_error << "unknown option '" << arg << "'.\n\n";
+ Usage(out_error);
+ return 1;
+ }
+ }
+
+ for (const Flag& flag : flags_) {
+ if (flag.required && !flag.parsed) {
+ *out_error << "missing required flag " << flag.name << "\n\n";
+ Usage(out_error);
+ return 1;
+ }
+ }
+
+ return Action(file_args);
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/cmd/Command.h b/tools/aapt2/cmd/Command.h
new file mode 100644
index 0000000..71dc6fe
--- /dev/null
+++ b/tools/aapt2/cmd/Command.h
@@ -0,0 +1,90 @@
+/*
+ * 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.
+ */
+
+#ifndef AAPT_COMMAND_H
+#define AAPT_COMMAND_H
+
+#include <functional>
+#include <ostream>
+#include <string>
+#include <unordered_set>
+#include <vector>
+
+#include "androidfw/StringPiece.h"
+
+#include "util/Maybe.h"
+
+namespace aapt {
+
+class Command {
+ public:
+ explicit Command(const android::StringPiece& name) : name_(name.to_string()),
+ fullname_(name.to_string()) { }
+ explicit Command(const android::StringPiece& name, const android::StringPiece& short_name)
+ : name_(name.to_string()), short_name_(short_name.to_string()), fullname_(name.to_string()) {}
+ virtual ~Command() = default;
+
+ void AddRequiredFlag(const android::StringPiece& name, const android::StringPiece& description,
+ std::string* value);
+ void AddRequiredFlagList(const android::StringPiece& name, const android::StringPiece&
+ description, std::vector<std::string>* value);
+ void AddOptionalFlag(const android::StringPiece& name, const android::StringPiece& description,
+ Maybe<std::string>* value);
+ void AddOptionalFlagList(const android::StringPiece& name,
+ const android::StringPiece& description, std::vector<std::string>* value);
+ void AddOptionalFlagList(const android::StringPiece& name,
+ const android::StringPiece& description, std::unordered_set<std::string>* value);
+ void AddOptionalSwitch(const android::StringPiece& name, const android::StringPiece& description,
+ bool* value);
+ void AddOptionalSubcommand(std::unique_ptr<Command>&& subcommand);
+
+ void SetDescription(const android::StringPiece& name);
+
+ /** Prints the help menu of the command. */
+ void Usage(std::ostream* out);
+
+ /**
+ * Parses the command line arguments, sets the flag variable values, and runs the action of
+ * the command. If the arguments fail to parse to the command and its subcommands, then the action
+ * will not be run and the usage will be printed instead.
+ **/
+ int Execute(const std::vector<android::StringPiece>& args, std::ostream* outError);
+
+ /** The action to preform when the command is executed. */
+ virtual int Action(const std::vector<std::string>& args) = 0;
+
+ private:
+ struct Flag {
+ std::string name;
+ std::string description;
+ std::function<bool(const android::StringPiece& value)> action;
+ bool required;
+ size_t num_args;
+
+ bool parsed;
+ };
+
+ std::string description_;
+ std::string name_;
+ std::string short_name_;
+ std::string fullname_;
+ std::vector<Flag> flags_;
+ std::vector<std::unique_ptr<Command>> subcommands_;
+};
+
+} // namespace aapt
+
+#endif // AAPT_COMMAND_H
diff --git a/tools/aapt2/cmd/Compile.cpp b/tools/aapt2/cmd/Compile.cpp
index a17a0d3..8c1fa9a 100644
--- a/tools/aapt2/cmd/Compile.cpp
+++ b/tools/aapt2/cmd/Compile.cpp
@@ -14,8 +14,9 @@
* limitations under the License.
*/
-#include <dirent.h>
+#include "Compile.h"
+#include <dirent.h>
#include <string>
#include "android-base/errors.h"
@@ -27,7 +28,6 @@
#include "ConfigDescription.h"
#include "Diagnostics.h"
-#include "Flags.h"
#include "ResourceParser.h"
#include "ResourceTable.h"
#include "cmd/Util.h"
@@ -121,17 +121,6 @@
extension.to_string(), config_str.to_string(), config};
}
-struct CompileOptions {
- std::string output_path;
- Maybe<std::string> res_dir;
- Maybe<std::string> generate_text_symbols_path;
- Maybe<Visibility::Level> visibility;
- bool pseudolocalize = false;
- bool no_png_crunch = false;
- bool legacy_mode = false;
- bool verbose = false;
-};
-
static std::string BuildIntermediateContainerFilename(const ResourcePathData& data) {
std::stringstream name;
name << data.resource_dir;
@@ -712,50 +701,21 @@
bool verbose_ = false;
};
-// Entry point for compilation phase. Parses arguments and dispatches to the correct steps.
-int Compile(const std::vector<StringPiece>& args, IDiagnostics* diagnostics) {
- CompileContext context(diagnostics);
- CompileOptions options;
+int CompileCommand::Action(const std::vector<std::string>& args) {
+ CompileContext context(diagnostic_);
+ context.SetVerbose(options_.verbose);
- bool verbose = false;
- Maybe<std::string> visibility;
- Flags flags =
- Flags()
- .RequiredFlag("-o", "Output path", &options.output_path)
- .OptionalFlag("--dir", "Directory to scan for resources", &options.res_dir)
- .OptionalFlag("--output-text-symbols",
- "Generates a text file containing the resource symbols in the\n"
- "specified file",
- &options.generate_text_symbols_path)
- .OptionalSwitch("--pseudo-localize",
- "Generate resources for pseudo-locales "
- "(en-XA and ar-XB)",
- &options.pseudolocalize)
- .OptionalSwitch("--no-crunch", "Disables PNG processing", &options.no_png_crunch)
- .OptionalSwitch("--legacy", "Treat errors that used to be valid in AAPT as warnings",
- &options.legacy_mode)
- .OptionalSwitch("-v", "Enables verbose logging", &verbose)
- .OptionalFlag("--visibility",
- "Sets the visibility of the compiled resources to the specified\n"
- "level. Accepted levels: public, private, default",
- &visibility);
- if (!flags.Parse("aapt2 compile", args, &std::cerr)) {
- return 1;
- }
-
- context.SetVerbose(verbose);
-
- if (visibility) {
- if (visibility.value() == "public") {
- options.visibility = Visibility::Level::kPublic;
- } else if (visibility.value() == "private") {
- options.visibility = Visibility::Level::kPrivate;
- } else if (visibility.value() == "default") {
- options.visibility = Visibility::Level::kUndefined;
+ if (visibility_) {
+ if (visibility_.value() == "public") {
+ options_.visibility = Visibility::Level::kPublic;
+ } else if (visibility_.value() == "private") {
+ options_.visibility = Visibility::Level::kPrivate;
+ } else if (visibility_.value() == "default") {
+ options_.visibility = Visibility::Level::kUndefined;
} else {
context.GetDiagnostics()->Error(
DiagMessage() << "Unrecognized visibility level passes to --visibility: '"
- << visibility.value() << "'. Accepted levels: public, private, default");
+ << visibility_.value() << "'. Accepted levels: public, private, default");
return 1;
}
}
@@ -763,25 +723,25 @@
std::unique_ptr<IArchiveWriter> archive_writer;
std::vector<ResourcePathData> input_data;
- if (options.res_dir) {
- if (!flags.GetArgs().empty()) {
+ if (options_.res_dir) {
+ if (!args.empty()) {
// Can't have both files and a resource directory.
context.GetDiagnostics()->Error(DiagMessage() << "files given but --dir specified");
- flags.Usage("aapt2 compile", &std::cerr);
+ Usage(&std::cerr);
return 1;
}
- if (!LoadInputFilesFromDir(&context, options, &input_data)) {
+ if (!LoadInputFilesFromDir(&context, options_, &input_data)) {
return 1;
}
- archive_writer = CreateZipFileArchiveWriter(context.GetDiagnostics(), options.output_path);
+ archive_writer = CreateZipFileArchiveWriter(context.GetDiagnostics(), options_.output_path);
} else {
- input_data.reserve(flags.GetArgs().size());
+ input_data.reserve(args.size());
// Collect data from the path for each input file.
- for (const std::string& arg : flags.GetArgs()) {
+ for (const std::string& arg : args) {
std::string error_str;
if (Maybe<ResourcePathData> path_data = ExtractResourcePathData(arg, &error_str)) {
input_data.push_back(std::move(path_data.value()));
@@ -791,7 +751,7 @@
}
}
- archive_writer = CreateDirectoryArchiveWriter(context.GetDiagnostics(), options.output_path);
+ archive_writer = CreateDirectoryArchiveWriter(context.GetDiagnostics(), options_.output_path);
}
if (!archive_writer) {
@@ -800,7 +760,7 @@
bool error = false;
for (ResourcePathData& path_data : input_data) {
- if (options.verbose) {
+ if (options_.verbose) {
context.GetDiagnostics()->Note(DiagMessage(path_data.source) << "processing");
}
@@ -821,21 +781,21 @@
if (*type != ResourceType::kRaw) {
if (path_data.extension == "xml") {
compile_func = &CompileXml;
- } else if ((!options.no_png_crunch && path_data.extension == "png")
+ } else if ((!options_.no_png_crunch && path_data.extension == "png")
|| path_data.extension == "9.png") {
compile_func = &CompilePng;
}
}
} else {
context.GetDiagnostics()->Error(DiagMessage()
- << "invalid file path '" << path_data.source << "'");
+ << "invalid file path '" << path_data.source << "'");
error = true;
continue;
}
// Treat periods as a reserved character that should not be present in a file name
// Legacy support for AAPT which did not reserve periods
- if (compile_func != &CompileFile && !options.legacy_mode
+ if (compile_func != &CompileFile && !options_.legacy_mode
&& std::count(path_data.name.begin(), path_data.name.end(), '.') != 0) {
error = true;
context.GetDiagnostics()->Error(DiagMessage() << "resource file '" << path_data.source.path
@@ -846,7 +806,7 @@
// Compile the file.
const std::string out_path = BuildIntermediateContainerFilename(path_data);
- error |= !compile_func(&context, options, path_data, archive_writer.get(), out_path);
+ error |= !compile_func(&context, options_, path_data, archive_writer.get(), out_path);
}
return error ? 1 : 0;
}
diff --git a/tools/aapt2/cmd/Compile.h b/tools/aapt2/cmd/Compile.h
index d95cf1c..4151952 100644
--- a/tools/aapt2/cmd/Compile.h
+++ b/tools/aapt2/cmd/Compile.h
@@ -1,13 +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.
+ */
+
#ifndef AAPT2_COMPILE_H
#define AAPT2_COMPILE_H
#include "androidfw/StringPiece.h"
+#include "Command.h"
#include "Diagnostics.h"
+#include "ResourceTable.h"
namespace aapt {
- int Compile(const std::vector<android::StringPiece>& args, IDiagnostics* diagnostics);
+struct CompileOptions {
+ std::string output_path;
+ Maybe<std::string> res_dir;
+ Maybe<std::string> generate_text_symbols_path;
+ Maybe<Visibility::Level> visibility;
+ bool pseudolocalize = false;
+ bool no_png_crunch = false;
+ bool legacy_mode = false;
+ bool verbose = false;
+};
+
+class CompileCommand : public Command {
+ public:
+ explicit CompileCommand(IDiagnostics* diagnostic) : Command("compile", "c"),
+ diagnostic_(diagnostic) {
+ SetDescription("Compiles resources to be linked into an apk.");
+ AddRequiredFlag("-o", "Output path", &options_.output_path);
+ AddOptionalFlag("--dir", "Directory to scan for resources", &options_.res_dir);
+ AddOptionalFlag("--output-text-symbols",
+ "Generates a text file containing the resource symbols in the\n"
+ "specified file", &options_.generate_text_symbols_path);
+ AddOptionalSwitch("--pseudo-localize", "Generate resources for pseudo-locales "
+ "(en-XA and ar-XB)", &options_.pseudolocalize);
+ AddOptionalSwitch("--no-crunch", "Disables PNG processing", &options_.no_png_crunch);
+ AddOptionalSwitch("--legacy", "Treat errors that used to be valid in AAPT as warnings",
+ &options_.legacy_mode);
+ AddOptionalSwitch("-v", "Enables verbose logging", &options_.verbose);
+ AddOptionalFlag("--visibility",
+ "Sets the visibility of the compiled resources to the specified\n"
+ "level. Accepted levels: public, private, default", &visibility_);
+ }
+
+ int Action(const std::vector<std::string>& args) override;
+
+ private:
+ IDiagnostics* diagnostic_;
+ CompileOptions options_;
+ Maybe<std::string> visibility_;
+};
}// namespace aapt
diff --git a/tools/aapt2/cmd/Compile_test.cpp b/tools/aapt2/cmd/Compile_test.cpp
index 212f2cf..d21addf 100644
--- a/tools/aapt2/cmd/Compile_test.cpp
+++ b/tools/aapt2/cmd/Compile_test.cpp
@@ -23,7 +23,8 @@
namespace aapt {
-int TestCompile(std::string path, std::string outDir, bool legacy, StdErrDiagnostics& diag) {
+int TestCompile(const std::string& path, const std::string& outDir, bool legacy,
+ StdErrDiagnostics& diag) {
std::vector<android::StringPiece> args;
args.push_back(path);
args.push_back("-o");
@@ -32,7 +33,7 @@
if (legacy) {
args.push_back("--legacy");
}
- return aapt::Compile(args, &diag);
+ return CompileCommand(&diag).Execute(args, &std::cerr);
}
TEST(CompilerTest, MultiplePeriods) {
diff --git a/tools/aapt2/cmd/Convert.cpp b/tools/aapt2/cmd/Convert.cpp
index 3c8beaa..4b82eef 100644
--- a/tools/aapt2/cmd/Convert.cpp
+++ b/tools/aapt2/cmd/Convert.cpp
@@ -14,12 +14,13 @@
* limitations under the License.
*/
+#include "Convert.h"
+
#include <vector>
#include "android-base/macros.h"
#include "androidfw/StringPiece.h"
-#include "Flags.h"
#include "LoadedApk.h"
#include "ValueVisitor.h"
#include "cmd/Util.h"
@@ -325,37 +326,18 @@
StdErrDiagnostics diag_;
};
-int Convert(const vector<StringPiece>& args) {
+const char* ConvertCommand::kOutputFormatProto = "proto";
+const char* ConvertCommand::kOutputFormatBinary = "binary";
- static const char* kOutputFormatProto = "proto";
- static const char* kOutputFormatBinary = "binary";
+int ConvertCommand::Action(const std::vector<std::string>& args) {
+ if (args.size() != 1) {
+ std::cerr << "must supply a single proto APK\n";
+ Usage(&std::cerr);
+ return 1;
+ }
Context context;
- std::string output_path;
- Maybe<std::string> output_format;
- TableFlattenerOptions options;
- Flags flags =
- Flags()
- .RequiredFlag("-o", "Output path", &output_path)
- .OptionalFlag("--output-format", StringPrintf("Format of the output. Accepted values are "
- "'%s' and '%s'. When not set, defaults to '%s'.", kOutputFormatProto,
- kOutputFormatBinary, kOutputFormatBinary), &output_format)
- .OptionalSwitch("--enable-sparse-encoding",
- "Enables encoding sparse entries using a binary search tree.\n"
- "This decreases APK size at the cost of resource retrieval performance.",
- &options.use_sparse_entries)
- .OptionalSwitch("-v", "Enables verbose logging", &context.verbose_);
- if (!flags.Parse("aapt2 convert", args, &std::cerr)) {
- return 1;
- }
-
- if (flags.GetArgs().size() != 1) {
- std::cerr << "must supply a single proto APK\n";
- flags.Usage("aapt2 convert", &std::cerr);
- return 1;
- }
-
- const StringPiece& path = flags.GetArgs()[0];
+ const StringPiece& path = args[0];
unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(path, context.GetDiagnostics());
if (apk == nullptr) {
context.GetDiagnostics()->Error(DiagMessage(path) << "failed to load APK");
@@ -371,24 +353,24 @@
context.package_ = app_info.value().package;
unique_ptr<IArchiveWriter> writer =
- CreateZipFileArchiveWriter(context.GetDiagnostics(), output_path);
+ CreateZipFileArchiveWriter(context.GetDiagnostics(), output_path_);
if (writer == nullptr) {
return 1;
}
unique_ptr<IApkSerializer> serializer;
- if (!output_format || output_format.value() == kOutputFormatBinary) {
- serializer.reset(new BinaryApkSerializer(&context, apk->GetSource(), options));
- } else if (output_format.value() == kOutputFormatProto) {
+ if (!output_format_ || output_format_.value() == ConvertCommand::kOutputFormatBinary) {
+
+ serializer.reset(new BinaryApkSerializer(&context, apk->GetSource(), options_));
+ } else if (output_format_.value() == ConvertCommand::kOutputFormatProto) {
serializer.reset(new ProtoApkSerializer(&context, apk->GetSource()));
} else {
context.GetDiagnostics()->Error(DiagMessage(path)
- << "Invalid value for flag --output-format: "
- << output_format.value());
+ << "Invalid value for flag --output-format: "
+ << output_format_.value());
return 1;
}
-
return ConvertApk(&context, std::move(apk), serializer.get(), writer.get()) ? 0 : 1;
}
diff --git a/tools/aapt2/cmd/Convert.h b/tools/aapt2/cmd/Convert.h
new file mode 100644
index 0000000..fcec23d
--- /dev/null
+++ b/tools/aapt2/cmd/Convert.h
@@ -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.
+ */
+
+#ifndef AAPT2_CONVERT_H
+#define AAPT2_CONVERT_H
+
+#include "Command.h"
+#include "format/binary/TableFlattener.h"
+
+namespace aapt {
+
+class ConvertCommand : public Command {
+ public:
+ explicit ConvertCommand() : Command("convert") {
+ SetDescription("Converts an apk between binary and proto formats.");
+ AddRequiredFlag("-o", "Output path", &output_path_);
+ AddOptionalFlag("--output-format", android::base::StringPrintf("Format of the output. "
+ "Accepted values are '%s' and '%s'. When not set, defaults to '%s'.",
+ kOutputFormatProto, kOutputFormatBinary, kOutputFormatBinary), &output_format_);
+ AddOptionalSwitch("--enable-sparse-encoding",
+ "Enables encoding sparse entries using a binary search tree.\n"
+ "This decreases APK size at the cost of resource retrieval performance.",
+ &options_.use_sparse_entries);
+ AddOptionalSwitch("-v", "Enables verbose logging", &verbose_);
+ }
+
+ int Action(const std::vector<std::string>& args) override;
+
+ private:
+ const static char* kOutputFormatProto;
+ const static char* kOutputFormatBinary;
+
+ TableFlattenerOptions options_;
+ std::string output_path_;
+ Maybe<std::string> output_format_;
+ bool verbose_ = false;
+};
+
+}// namespace aapt
+
+#endif //AAPT2_CONVERT_H
diff --git a/tools/aapt2/cmd/Diff.cpp b/tools/aapt2/cmd/Diff.cpp
index 16c7bba..7875a2b 100644
--- a/tools/aapt2/cmd/Diff.cpp
+++ b/tools/aapt2/cmd/Diff.cpp
@@ -14,9 +14,10 @@
* limitations under the License.
*/
+#include "Diff.h"
+
#include "android-base/macros.h"
-#include "Flags.h"
#include "LoadedApk.h"
#include "ValueVisitor.h"
#include "process/IResourceTableConsumer.h"
@@ -348,23 +349,18 @@
VisitAllValuesInTable(table, &visitor);
}
-int Diff(const std::vector<StringPiece>& args) {
+int DiffCommand::Action(const std::vector<std::string>& args) {
DiffContext context;
- Flags flags;
- if (!flags.Parse("aapt2 diff", args, &std::cerr)) {
- return 1;
- }
-
- if (flags.GetArgs().size() != 2u) {
+ if (args.size() != 2u) {
std::cerr << "must have two apks as arguments.\n\n";
- flags.Usage("aapt2 diff", &std::cerr);
+ Usage(&std::cerr);
return 1;
}
IDiagnostics* diag = context.GetDiagnostics();
- std::unique_ptr<LoadedApk> apk_a = LoadedApk::LoadApkFromPath(flags.GetArgs()[0], diag);
- std::unique_ptr<LoadedApk> apk_b = LoadedApk::LoadApkFromPath(flags.GetArgs()[1], diag);
+ std::unique_ptr<LoadedApk> apk_a = LoadedApk::LoadApkFromPath(args[0], diag);
+ std::unique_ptr<LoadedApk> apk_b = LoadedApk::LoadApkFromPath(args[1], diag);
if (!apk_a || !apk_b) {
return 1;
}
diff --git a/tools/aapt2/cmd/Diff.h b/tools/aapt2/cmd/Diff.h
new file mode 100644
index 0000000..c388888
--- /dev/null
+++ b/tools/aapt2/cmd/Diff.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.
+ */
+
+#ifndef AAPT2_DIFF_H
+#define AAPT2_DIFF_H
+
+#include "Command.h"
+
+namespace aapt {
+
+class DiffCommand : public Command {
+ public:
+ explicit DiffCommand() : Command("diff") {
+ SetDescription("Prints the differences in resources of two apks.");
+ }
+
+ int Action(const std::vector<std::string>& args) override;
+};
+
+}// namespace aapt
+
+#endif //AAPT2_DIFF_H
diff --git a/tools/aapt2/cmd/Dump.cpp b/tools/aapt2/cmd/Dump.cpp
index fd133f3..717e757 100644
--- a/tools/aapt2/cmd/Dump.cpp
+++ b/tools/aapt2/cmd/Dump.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include "Dump.h"
+
#include <cinttypes>
#include <vector>
@@ -22,7 +24,6 @@
#include "Debug.h"
#include "Diagnostics.h"
-#include "Flags.h"
#include "format/Container.h"
#include "format/binary/BinaryResourceParser.h"
#include "format/proto/ProtoDeserialize.h"
@@ -38,13 +39,6 @@
namespace aapt {
-struct DumpOptions {
- DebugPrintTableOptions print_options;
-
- // The path to a file within an APK to dump.
- Maybe<std::string> file_to_dump_path;
-};
-
static const char* ResourceFileTypeToString(const ResourceFile::Type& type) {
switch (type) {
case ResourceFile::Type::kPng:
@@ -309,29 +303,13 @@
} // namespace
-// Entry point for dump command.
-int Dump(const std::vector<StringPiece>& args) {
- bool verbose = false;
- bool no_values = false;
- DumpOptions options;
- Flags flags = Flags()
- .OptionalSwitch("--no-values",
- "Suppresses output of values when displaying resource tables.",
- &no_values)
- .OptionalFlag("--file", "Dumps the specified file from the APK passed as arg.",
- &options.file_to_dump_path)
- .OptionalSwitch("-v", "increase verbosity of output", &verbose);
- if (!flags.Parse("aapt2 dump", args, &std::cerr)) {
- return 1;
- }
-
+int DumpCommand::Action(const std::vector<std::string>& args) {
DumpContext context;
- context.SetVerbose(verbose);
-
- options.print_options.show_sources = true;
- options.print_options.show_values = !no_values;
- for (const std::string& arg : flags.GetArgs()) {
- if (!TryDumpFile(&context, arg, options)) {
+ context.SetVerbose(verbose_);
+ options_.print_options.show_sources = true;
+ options_.print_options.show_values = !no_values_;
+ for (const std::string& arg : args) {
+ if (!TryDumpFile(&context, arg, options_)) {
return 1;
}
}
diff --git a/tools/aapt2/cmd/Dump.h b/tools/aapt2/cmd/Dump.h
new file mode 100644
index 0000000..4893c8b
--- /dev/null
+++ b/tools/aapt2/cmd/Dump.h
@@ -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.
+ */
+
+#ifndef AAPT2_DUMP_H
+#define AAPT2_DUMP_H
+
+#include "Command.h"
+#include "Debug.h"
+
+namespace aapt {
+
+struct DumpOptions {
+ DebugPrintTableOptions print_options;
+
+ // The path to a file within an APK to dump.
+ Maybe<std::string> file_to_dump_path;
+};
+
+class DumpCommand : public Command {
+ public:
+ DumpCommand() : Command("dump", "d") {
+ SetDescription("Prints resource and manifest information.");
+ AddOptionalSwitch("--no-values", "Suppresses output of values when displaying resource tables.",
+ &no_values_);
+ AddOptionalFlag("--file", "Dumps the specified file from the APK passed as arg.",
+ &options_.file_to_dump_path);
+ AddOptionalSwitch("-v", "increase verbosity of output", &verbose_);
+ }
+
+ int Action(const std::vector<std::string>& args) override;
+
+ private:
+ DumpOptions options_;
+
+ bool verbose_ = false;
+ bool no_values_ = false;
+};
+
+}// namespace aapt
+
+#endif //AAPT2_DUMP_H
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index 1a2da7f..1d508d9 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include "Link.h"
+
#include <sys/stat.h>
#include <cinttypes>
@@ -29,7 +31,6 @@
#include "AppInfo.h"
#include "Debug.h"
-#include "Flags.h"
#include "LoadedApk.h"
#include "Locale.h"
#include "NameMangler.h"
@@ -74,74 +75,6 @@
namespace aapt {
-enum class OutputFormat {
- kApk,
- kProto,
-};
-
-struct LinkOptions {
- std::string output_path;
- std::string manifest_path;
- std::vector<std::string> include_paths;
- std::vector<std::string> overlay_files;
- std::vector<std::string> assets_dirs;
- bool output_to_directory = false;
- bool auto_add_overlay = false;
- OutputFormat output_format = OutputFormat::kApk;
-
- // Java/Proguard options.
- Maybe<std::string> generate_java_class_path;
- Maybe<std::string> custom_java_package;
- std::set<std::string> extra_java_packages;
- Maybe<std::string> generate_text_symbols_path;
- Maybe<std::string> generate_proguard_rules_path;
- Maybe<std::string> generate_main_dex_proguard_rules_path;
- bool generate_conditional_proguard_rules = false;
- bool generate_non_final_ids = false;
- std::vector<std::string> javadoc_annotations;
- Maybe<std::string> private_symbols;
-
- // Optimizations/features.
- bool no_auto_version = false;
- bool no_version_vectors = false;
- bool no_version_transitions = false;
- bool no_resource_deduping = false;
- bool no_xml_namespaces = false;
- bool do_not_compress_anything = false;
- std::unordered_set<std::string> extensions_to_not_compress;
-
- // Static lib options.
- bool no_static_lib_packages = false;
- bool auto_namespace_static_lib = false;
-
- // AndroidManifest.xml massaging options.
- ManifestFixerOptions manifest_fixer_options;
-
- // Products to use/filter on.
- std::unordered_set<std::string> products;
-
- // Flattening options.
- TableFlattenerOptions table_flattener_options;
-
- // Split APK options.
- TableSplitterOptions table_splitter_options;
- std::vector<SplitConstraints> split_constraints;
- std::vector<std::string> split_paths;
-
- // Stable ID options.
- std::unordered_map<ResourceName, ResourceId> stable_id_map;
- Maybe<std::string> resource_id_map_path;
-
- // When 'true', allow reserved package IDs to be used for applications. Pre-O, the platform
- // treats negative resource IDs [those with a package ID of 0x80 or higher] as invalid.
- // In order to work around this limitation, we allow the use of traditionally reserved
- // resource IDs [those between 0x02 and 0x7E].
- bool allow_reserved_package_id = false;
-
- // Whether we should fail on definitions of a resource with conflicting visibility.
- bool strict_visibility = false;
-};
-
class LinkContext : public IAaptContext {
public:
LinkContext(IDiagnostics* diagnostics)
@@ -785,9 +718,9 @@
return table.getTableCookie(idx);
}
-class LinkCommand {
+class Linker {
public:
- LinkCommand(LinkContext* context, const LinkOptions& options)
+ Linker(LinkContext* context, const LinkOptions& options)
: options_(options),
context_(context),
final_table_(),
@@ -975,6 +908,18 @@
app_info.version_code = maybe_code.value();
}
+ if (xml::Attribute* version_code_major_attr =
+ manifest_el->FindAttribute(xml::kSchemaAndroid, "versionCodeMajor")) {
+ Maybe<uint32_t> maybe_code = ResourceUtils::ParseInt(version_code_major_attr->value);
+ if (!maybe_code) {
+ diag->Error(DiagMessage(xml_res->file.source.WithLine(manifest_el->line_number))
+ << "invalid android:versionCodeMajor '"
+ << version_code_major_attr->value << "'");
+ return {};
+ }
+ app_info.version_code_major = maybe_code.value();
+ }
+
if (xml::Attribute* revision_code_attr =
manifest_el->FindAttribute(xml::kSchemaAndroid, "revisionCode")) {
Maybe<uint32_t> maybe_code = ResourceUtils::ParseInt(revision_code_attr->value);
@@ -2040,197 +1985,12 @@
Maybe<std::string> included_feature_base_;
};
-int Link(const std::vector<StringPiece>& args, IDiagnostics* diagnostics) {
- LinkContext context(diagnostics);
- LinkOptions options;
- std::vector<std::string> overlay_arg_list;
- std::vector<std::string> extra_java_packages;
- Maybe<std::string> package_id;
- std::vector<std::string> configs;
- Maybe<std::string> preferred_density;
- Maybe<std::string> product_list;
- bool legacy_x_flag = false;
- bool require_localization = false;
- bool verbose = false;
- bool shared_lib = false;
- bool static_lib = false;
- bool proto_format = false;
- Maybe<std::string> stable_id_file_path;
- std::vector<std::string> split_args;
- Flags flags =
- Flags()
- .RequiredFlag("-o", "Output path.", &options.output_path)
- .RequiredFlag("--manifest", "Path to the Android manifest to build.",
- &options.manifest_path)
- .OptionalFlagList("-I", "Adds an Android APK to link against.", &options.include_paths)
- .OptionalFlagList("-A",
- "An assets directory to include in the APK. These are unprocessed.",
- &options.assets_dirs)
- .OptionalFlagList("-R",
- "Compilation unit to link, using `overlay` semantics.\n"
- "The last conflicting resource given takes precedence.",
- &overlay_arg_list)
- .OptionalFlag("--package-id",
- "Specify the package ID to use for this app. Must be greater or equal to\n"
- "0x7f and can't be used with --static-lib or --shared-lib.",
- &package_id)
- .OptionalFlag("--java", "Directory in which to generate R.java.",
- &options.generate_java_class_path)
- .OptionalFlag("--proguard", "Output file for generated Proguard rules.",
- &options.generate_proguard_rules_path)
- .OptionalFlag("--proguard-main-dex",
- "Output file for generated Proguard rules for the main dex.",
- &options.generate_main_dex_proguard_rules_path)
- .OptionalSwitch("--proguard-conditional-keep-rules",
- "Generate conditional Proguard keep rules.",
- &options.generate_conditional_proguard_rules)
- .OptionalSwitch("--no-auto-version",
- "Disables automatic style and layout SDK versioning.",
- &options.no_auto_version)
- .OptionalSwitch("--no-version-vectors",
- "Disables automatic versioning of vector drawables. Use this only\n"
- "when building with vector drawable support library.",
- &options.no_version_vectors)
- .OptionalSwitch("--no-version-transitions",
- "Disables automatic versioning of transition resources. Use this only\n"
- "when building with transition support library.",
- &options.no_version_transitions)
- .OptionalSwitch("--no-resource-deduping",
- "Disables automatic deduping of resources with\n"
- "identical values across compatible configurations.",
- &options.no_resource_deduping)
- .OptionalSwitch("--enable-sparse-encoding",
- "Enables encoding sparse entries using a binary search tree.\n"
- "This decreases APK size at the cost of resource retrieval performance.",
- &options.table_flattener_options.use_sparse_entries)
- .OptionalSwitch("-x", "Legacy flag that specifies to use the package identifier 0x01.",
- &legacy_x_flag)
- .OptionalSwitch("-z", "Require localization of strings marked 'suggested'.",
- &require_localization)
- .OptionalFlagList("-c",
- "Comma separated list of configurations to include. The default\n"
- "is all configurations.",
- &configs)
- .OptionalFlag("--preferred-density",
- "Selects the closest matching density and strips out all others.",
- &preferred_density)
- .OptionalFlag("--product", "Comma separated list of product names to keep", &product_list)
- .OptionalSwitch("--output-to-dir",
- "Outputs the APK contents to a directory specified by -o.",
- &options.output_to_directory)
- .OptionalSwitch("--no-xml-namespaces",
- "Removes XML namespace prefix and URI information from\n"
- "AndroidManifest.xml and XML binaries in res/*.",
- &options.no_xml_namespaces)
- .OptionalFlag("--min-sdk-version",
- "Default minimum SDK version to use for AndroidManifest.xml.",
- &options.manifest_fixer_options.min_sdk_version_default)
- .OptionalFlag("--target-sdk-version",
- "Default target SDK version to use for AndroidManifest.xml.",
- &options.manifest_fixer_options.target_sdk_version_default)
- .OptionalFlag("--version-code",
- "Version code (integer) to inject into the AndroidManifest.xml if none is\n"
- "present.",
- &options.manifest_fixer_options.version_code_default)
- .OptionalFlag("--version-name",
- "Version name to inject into the AndroidManifest.xml if none is present.",
- &options.manifest_fixer_options.version_name_default)
- .OptionalSwitch("--replace-version",
- "If --version-code and/or --version-name are specified, these\n"
- "values will replace any value already in the manifest. By\n"
- "default, nothing is changed if the manifest already defines\n"
- "these attributes.",
- &options.manifest_fixer_options.replace_version)
- .OptionalFlag("--compile-sdk-version-code",
- "Version code (integer) to inject into the AndroidManifest.xml if none is\n"
- "present.",
- &options.manifest_fixer_options.compile_sdk_version)
- .OptionalFlag("--compile-sdk-version-name",
- "Version name to inject into the AndroidManifest.xml if none is present.",
- &options.manifest_fixer_options.compile_sdk_version_codename)
- .OptionalSwitch("--shared-lib", "Generates a shared Android runtime library.",
- &shared_lib)
- .OptionalSwitch("--static-lib", "Generate a static Android library.", &static_lib)
- .OptionalSwitch("--proto-format",
- "Generates compiled resources in Protobuf format.\n"
- "Suitable as input to the bundle tool for generating an App Bundle.",
- &proto_format)
- .OptionalSwitch("--no-static-lib-packages",
- "Merge all library resources under the app's package.",
- &options.no_static_lib_packages)
- .OptionalSwitch("--auto-namespace-static-lib",
- "Automatically namespace resource references when building a static\n"
- "library.",
- &options.auto_namespace_static_lib)
- .OptionalSwitch("--non-final-ids",
- "Generates R.java without the final modifier. This is implied when\n"
- "--static-lib is specified.",
- &options.generate_non_final_ids)
- .OptionalFlag("--stable-ids", "File containing a list of name to ID mapping.",
- &stable_id_file_path)
- .OptionalFlag("--emit-ids",
- "Emit a file at the given path with a list of name to ID mappings,\n"
- "suitable for use with --stable-ids.",
- &options.resource_id_map_path)
- .OptionalFlag("--private-symbols",
- "Package name to use when generating R.java for private symbols.\n"
- "If not specified, public and private symbols will use the application's\n"
- "package name.",
- &options.private_symbols)
- .OptionalFlag("--custom-package", "Custom Java package under which to generate R.java.",
- &options.custom_java_package)
- .OptionalFlagList("--extra-packages",
- "Generate the same R.java but with different package names.",
- &extra_java_packages)
- .OptionalFlagList("--add-javadoc-annotation",
- "Adds a JavaDoc annotation to all generated Java classes.",
- &options.javadoc_annotations)
- .OptionalFlag("--output-text-symbols",
- "Generates a text file containing the resource symbols of the R class in\n"
- "the specified folder.",
- &options.generate_text_symbols_path)
- .OptionalSwitch("--allow-reserved-package-id",
- "Allows the use of a reserved package ID. This should on be used for\n"
- "packages with a pre-O min-sdk\n",
- &options.allow_reserved_package_id)
- .OptionalSwitch("--auto-add-overlay",
- "Allows the addition of new resources in overlays without\n"
- "<add-resource> tags.",
- &options.auto_add_overlay)
- .OptionalFlag("--rename-manifest-package", "Renames the package in AndroidManifest.xml.",
- &options.manifest_fixer_options.rename_manifest_package)
- .OptionalFlag("--rename-instrumentation-target-package",
- "Changes the name of the target package for instrumentation. Most useful\n"
- "when used in conjunction with --rename-manifest-package.",
- &options.manifest_fixer_options.rename_instrumentation_target_package)
- .OptionalFlagList("-0", "File extensions not to compress.",
- &options.extensions_to_not_compress)
- .OptionalSwitch("--no-compress", "Do not compress any resources.",
- &options.do_not_compress_anything)
- .OptionalSwitch("--warn-manifest-validation",
- "Treat manifest validation errors as warnings.",
- &options.manifest_fixer_options.warn_validation)
- .OptionalFlagList("--split",
- "Split resources matching a set of configs out to a Split APK.\n"
- "Syntax: path/to/output.apk:<config>[,<config>[...]].\n"
- "On Windows, use a semicolon ';' separator instead.",
- &split_args)
- .OptionalSwitch("-v", "Enables verbose logging.", &verbose)
- .OptionalSwitch("--debug-mode",
- "Inserts android:debuggable=\"true\" in to the application node of the\n"
- "manifest, making the application debuggable even on production devices.",
- &options.manifest_fixer_options.debug_mode)
- .OptionalSwitch("--strict-visibility",
- "Do not allow overlays with different visibility levels.",
- &options.strict_visibility);
-
- if (!flags.Parse("aapt2 link", args, &std::cerr)) {
- return 1;
- }
+int LinkCommand::Action(const std::vector<std::string>& args) {
+ LinkContext context(diag_);
// Expand all argument-files passed into the command line. These start with '@'.
std::vector<std::string> arg_list;
- for (const std::string& arg : flags.GetArgs()) {
+ for (const std::string& arg : args) {
if (util::StartsWith(arg, "@")) {
const std::string path = arg.substr(1, arg.size() - 1);
std::string error;
@@ -2244,27 +2004,27 @@
}
// Expand all argument-files passed to -R.
- for (const std::string& arg : overlay_arg_list) {
+ for (const std::string& arg : overlay_arg_list_) {
if (util::StartsWith(arg, "@")) {
const std::string path = arg.substr(1, arg.size() - 1);
std::string error;
- if (!file::AppendArgsFromFile(path, &options.overlay_files, &error)) {
+ if (!file::AppendArgsFromFile(path, &options_.overlay_files, &error)) {
context.GetDiagnostics()->Error(DiagMessage(path) << error);
return 1;
}
} else {
- options.overlay_files.push_back(arg);
+ options_.overlay_files.push_back(arg);
}
}
- if (verbose) {
- context.SetVerbose(verbose);
+ if (verbose_) {
+ context.SetVerbose(verbose_);
}
- if (int{shared_lib} + int{static_lib} + int{proto_format} > 1) {
+ if (int{shared_lib_} + int{static_lib_} + int{proto_format_} > 1) {
context.GetDiagnostics()->Error(
DiagMessage()
- << "only one of --shared-lib, --static-lib, or --proto_format can be defined");
+ << "only one of --shared-lib, --static-lib, or --proto_format can be defined");
return 1;
}
@@ -2272,18 +2032,18 @@
context.SetPackageType(PackageType::kApp);
context.SetPackageId(kAppPackageId);
- if (shared_lib) {
+ if (shared_lib_) {
context.SetPackageType(PackageType::kSharedLib);
context.SetPackageId(0x00);
- } else if (static_lib) {
+ } else if (static_lib_) {
context.SetPackageType(PackageType::kStaticLib);
- options.output_format = OutputFormat::kProto;
- } else if (proto_format) {
- options.output_format = OutputFormat::kProto;
+ options_.output_format = OutputFormat::kProto;
+ } else if (proto_format_) {
+ options_.output_format = OutputFormat::kProto;
}
- if (options.auto_namespace_static_lib) {
- if (!static_lib) {
+ if (options_.auto_namespace_static_lib) {
+ if (!static_lib_) {
context.GetDiagnostics()->Error(
DiagMessage() << "--auto-namespace-static-lib can only be used with --static-lib");
return 1;
@@ -2291,16 +2051,16 @@
context.SetAutoNamespace(true);
}
- if (package_id) {
+ if (package_id_) {
if (context.GetPackageType() != PackageType::kApp) {
context.GetDiagnostics()->Error(
DiagMessage() << "can't specify --package-id when not building a regular app");
return 1;
}
- const Maybe<uint32_t> maybe_package_id_int = ResourceUtils::ParseInt(package_id.value());
+ const Maybe<uint32_t> maybe_package_id_int = ResourceUtils::ParseInt(package_id_.value());
if (!maybe_package_id_int) {
- context.GetDiagnostics()->Error(DiagMessage() << "package ID '" << package_id.value()
+ context.GetDiagnostics()->Error(DiagMessage() << "package ID '" << package_id_.value()
<< "' is not a valid integer");
return 1;
}
@@ -2308,7 +2068,7 @@
const uint32_t package_id_int = maybe_package_id_int.value();
if (package_id_int > std::numeric_limits<uint8_t>::max()
|| package_id_int == kFrameworkPackageId
- || (!options.allow_reserved_package_id && package_id_int < kAppPackageId)) {
+ || (!options_.allow_reserved_package_id && package_id_int < kAppPackageId)) {
context.GetDiagnostics()->Error(
DiagMessage() << StringPrintf(
"invalid package ID 0x%02x. Must be in the range 0x7f-0xff.", package_id_int));
@@ -2318,71 +2078,71 @@
}
// Populate the set of extra packages for which to generate R.java.
- for (std::string& extra_package : extra_java_packages) {
+ for (std::string& extra_package : extra_java_packages_) {
// A given package can actually be a colon separated list of packages.
for (StringPiece package : util::Split(extra_package, ':')) {
- options.extra_java_packages.insert(package.to_string());
+ options_.extra_java_packages.insert(package.to_string());
}
}
- if (product_list) {
- for (StringPiece product : util::Tokenize(product_list.value(), ',')) {
+ if (product_list_) {
+ for (StringPiece product : util::Tokenize(product_list_.value(), ',')) {
if (product != "" && product != "default") {
- options.products.insert(product.to_string());
+ options_.products.insert(product.to_string());
}
}
}
std::unique_ptr<IConfigFilter> filter;
- if (!configs.empty()) {
- filter = ParseConfigFilterParameters(configs, context.GetDiagnostics());
+ if (!configs_.empty()) {
+ filter = ParseConfigFilterParameters(configs_, context.GetDiagnostics());
if (filter == nullptr) {
return 1;
}
- options.table_splitter_options.config_filter = filter.get();
+ options_.table_splitter_options.config_filter = filter.get();
}
- if (preferred_density) {
+ if (preferred_density_) {
Maybe<uint16_t> density =
- ParseTargetDensityParameter(preferred_density.value(), context.GetDiagnostics());
+ ParseTargetDensityParameter(preferred_density_.value(), context.GetDiagnostics());
if (!density) {
return 1;
}
- options.table_splitter_options.preferred_densities.push_back(density.value());
+ options_.table_splitter_options.preferred_densities.push_back(density.value());
}
// Parse the split parameters.
- for (const std::string& split_arg : split_args) {
- options.split_paths.push_back({});
- options.split_constraints.push_back({});
- if (!ParseSplitParameter(split_arg, context.GetDiagnostics(), &options.split_paths.back(),
- &options.split_constraints.back())) {
+ for (const std::string& split_arg : split_args_) {
+ options_.split_paths.push_back({});
+ options_.split_constraints.push_back({});
+ if (!ParseSplitParameter(split_arg, context.GetDiagnostics(), &options_.split_paths.back(),
+ &options_.split_constraints.back())) {
return 1;
}
}
- if (context.GetPackageType() != PackageType::kStaticLib && stable_id_file_path) {
- if (!LoadStableIdMap(context.GetDiagnostics(), stable_id_file_path.value(),
- &options.stable_id_map)) {
+ if (context.GetPackageType() != PackageType::kStaticLib && stable_id_file_path_) {
+ if (!LoadStableIdMap(context.GetDiagnostics(), stable_id_file_path_.value(),
+ &options_.stable_id_map)) {
return 1;
}
}
// Populate some default no-compress extensions that are already compressed.
- options.extensions_to_not_compress.insert(
+ options_.extensions_to_not_compress.insert(
{".jpg", ".jpeg", ".png", ".gif", ".wav", ".mp2", ".mp3", ".ogg",
- ".aac", ".mpg", ".mpeg", ".mid", ".midi", ".smf", ".jet", ".rtttl",
- ".imy", ".xmf", ".mp4", ".m4a", ".m4v", ".3gp", ".3gpp", ".3g2",
- ".3gpp2", ".amr", ".awb", ".wma", ".wmv", ".webm", ".mkv"});
+ ".aac", ".mpg", ".mpeg", ".mid", ".midi", ".smf", ".jet", ".rtttl",
+ ".imy", ".xmf", ".mp4", ".m4a", ".m4v", ".3gp", ".3gpp", ".3g2",
+ ".3gpp2", ".amr", ".awb", ".wma", ".wmv", ".webm", ".mkv"});
// Turn off auto versioning for static-libs.
if (context.GetPackageType() == PackageType::kStaticLib) {
- options.no_auto_version = true;
- options.no_version_vectors = true;
- options.no_version_transitions = true;
+ options_.no_auto_version = true;
+ options_.no_version_vectors = true;
+ options_.no_version_transitions = true;
}
- LinkCommand cmd(&context, options);
+ Linker cmd(&context, options_);
return cmd.Run(arg_list);
}
diff --git a/tools/aapt2/cmd/Link.h b/tools/aapt2/cmd/Link.h
new file mode 100644
index 0000000..434475e
--- /dev/null
+++ b/tools/aapt2/cmd/Link.h
@@ -0,0 +1,282 @@
+/*
+ * 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.
+ */
+
+#ifndef AAPT2_LINK_H
+#define AAPT2_LINK_H
+
+#include "Command.h"
+#include "Diagnostics.h"
+#include "Resource.h"
+#include "split/TableSplitter.h"
+#include "format/binary/TableFlattener.h"
+#include "link/ManifestFixer.h"
+
+namespace aapt {
+
+enum class OutputFormat {
+ kApk,
+ kProto,
+};
+
+struct LinkOptions {
+ std::string output_path;
+ std::string manifest_path;
+ std::vector<std::string> include_paths;
+ std::vector<std::string> overlay_files;
+ std::vector<std::string> assets_dirs;
+ bool output_to_directory = false;
+ bool auto_add_overlay = false;
+ OutputFormat output_format = OutputFormat::kApk;
+
+ // Java/Proguard options.
+ Maybe<std::string> generate_java_class_path;
+ Maybe<std::string> custom_java_package;
+ std::set<std::string> extra_java_packages;
+ Maybe<std::string> generate_text_symbols_path;
+ Maybe<std::string> generate_proguard_rules_path;
+ Maybe<std::string> generate_main_dex_proguard_rules_path;
+ bool generate_conditional_proguard_rules = false;
+ bool generate_non_final_ids = false;
+ std::vector<std::string> javadoc_annotations;
+ Maybe<std::string> private_symbols;
+
+ // Optimizations/features.
+ bool no_auto_version = false;
+ bool no_version_vectors = false;
+ bool no_version_transitions = false;
+ bool no_resource_deduping = false;
+ bool no_xml_namespaces = false;
+ bool do_not_compress_anything = false;
+ std::unordered_set<std::string> extensions_to_not_compress;
+
+ // Static lib options.
+ bool no_static_lib_packages = false;
+ bool auto_namespace_static_lib = false;
+
+ // AndroidManifest.xml massaging options.
+ ManifestFixerOptions manifest_fixer_options;
+
+ // Products to use/filter on.
+ std::unordered_set<std::string> products;
+
+ // Flattening options.
+ TableFlattenerOptions table_flattener_options;
+
+ // Split APK options.
+ TableSplitterOptions table_splitter_options;
+ std::vector<SplitConstraints> split_constraints;
+ std::vector<std::string> split_paths;
+
+ // Stable ID options.
+ std::unordered_map<ResourceName, ResourceId> stable_id_map;
+ Maybe<std::string> resource_id_map_path;
+
+ // When 'true', allow reserved package IDs to be used for applications. Pre-O, the platform
+ // treats negative resource IDs [those with a package ID of 0x80 or higher] as invalid.
+ // In order to work around this limitation, we allow the use of traditionally reserved
+ // resource IDs [those between 0x02 and 0x7E].
+ bool allow_reserved_package_id = false;
+
+ // Whether we should fail on definitions of a resource with conflicting visibility.
+ bool strict_visibility = false;
+};
+
+class LinkCommand : public Command {
+ public:
+ explicit LinkCommand(IDiagnostics* diag) : Command("link", "l"),
+ diag_(diag) {
+ SetDescription("Links resources into an apk.");
+ AddRequiredFlag("-o", "Output path.", &options_.output_path);
+ AddRequiredFlag("--manifest", "Path to the Android manifest to build.",
+ &options_.manifest_path);
+ AddOptionalFlagList("-I", "Adds an Android APK to link against.", &options_.include_paths);
+ AddOptionalFlagList("-A", "An assets directory to include in the APK. These are unprocessed.",
+ &options_.assets_dirs);
+ AddOptionalFlagList("-R", "Compilation unit to link, using `overlay` semantics.\n"
+ "The last conflicting resource given takes precedence.", &overlay_arg_list_);
+ AddOptionalFlag("--package-id",
+ "Specify the package ID to use for this app. Must be greater or equal to\n"
+ "0x7f and can't be used with --static-lib or --shared-lib.", &package_id_);
+ AddOptionalFlag("--java", "Directory in which to generate R.java.",
+ &options_.generate_java_class_path);
+ AddOptionalFlag("--proguard", "Output file for generated Proguard rules.",
+ &options_.generate_proguard_rules_path);
+ AddOptionalFlag("--proguard-main-dex",
+ "Output file for generated Proguard rules for the main dex.",
+ &options_.generate_main_dex_proguard_rules_path);
+ AddOptionalSwitch("--proguard-conditional-keep-rules",
+ "Generate conditional Proguard keep rules.",
+ &options_.generate_conditional_proguard_rules);
+ AddOptionalSwitch("--no-auto-version", "Disables automatic style and layout SDK versioning.",
+ &options_.no_auto_version);
+ AddOptionalSwitch("--no-version-vectors",
+ "Disables automatic versioning of vector drawables. Use this only\n"
+ "when building with vector drawable support library.",
+ &options_.no_version_vectors);
+ AddOptionalSwitch("--no-version-transitions",
+ "Disables automatic versioning of transition resources. Use this only\n"
+ "when building with transition support library.",
+ &options_.no_version_transitions);
+ AddOptionalSwitch("--no-resource-deduping", "Disables automatic deduping of resources with\n"
+ "identical values across compatible configurations.",
+ &options_.no_resource_deduping);
+ AddOptionalSwitch("--enable-sparse-encoding",
+ "This decreases APK size at the cost of resource retrieval performance.",
+ &options_.table_flattener_options.use_sparse_entries);
+ AddOptionalSwitch("-x", "Legacy flag that specifies to use the package identifier 0x01.",
+ &legacy_x_flag_);
+ AddOptionalSwitch("-z", "Require localization of strings marked 'suggested'.",
+ &require_localization_);
+ AddOptionalFlagList("-c",
+ "Comma separated list of configurations to include. The default\n"
+ "is all configurations.", &configs_);
+ AddOptionalFlag("--preferred-density",
+ "Selects the closest matching density and strips out all others.",
+ &preferred_density_);
+ AddOptionalFlag("--product", "Comma separated list of product names to keep", &product_list_);
+ AddOptionalSwitch("--output-to-dir", "Outputs the APK contents to a directory specified by -o.",
+ &options_.output_to_directory);
+ AddOptionalSwitch("--no-xml-namespaces", "Removes XML namespace prefix and URI information\n"
+ "from AndroidManifest.xml and XML binaries in res/*.",
+ &options_.no_xml_namespaces);
+ AddOptionalFlag("--min-sdk-version",
+ "Default minimum SDK version to use for AndroidManifest.xml.",
+ &options_.manifest_fixer_options.min_sdk_version_default);
+ AddOptionalFlag("--target-sdk-version",
+ "Default target SDK version to use for AndroidManifest.xml.",
+ &options_.manifest_fixer_options.target_sdk_version_default);
+ AddOptionalFlag("--version-code",
+ "Version code (integer) to inject into the AndroidManifest.xml if none is\n"
+ "present.",
+ &options_.manifest_fixer_options.version_code_default);
+ AddOptionalFlag("--version-name",
+ "Version name to inject into the AndroidManifest.xml if none is present.",
+ &options_.manifest_fixer_options.version_name_default);
+ AddOptionalSwitch("--replace-version",
+ "If --version-code and/or --version-name are specified, these\n"
+ "values will replace any value already in the manifest. By\n"
+ "default, nothing is changed if the manifest already defines\n"
+ "these attributes.",
+ &options_.manifest_fixer_options.replace_version);
+ AddOptionalFlag("--compile-sdk-version-code",
+ "Version code (integer) to inject into the AndroidManifest.xml if none is\n"
+ "present.",
+ &options_.manifest_fixer_options.compile_sdk_version);
+ AddOptionalFlag("--compile-sdk-version-name",
+ "Version name to inject into the AndroidManifest.xml if none is present.",
+ &options_.manifest_fixer_options.compile_sdk_version_codename);
+ AddOptionalSwitch("--shared-lib", "Generates a shared Android runtime library.",
+ &shared_lib_);
+ AddOptionalSwitch("--static-lib", "Generate a static Android library.", &static_lib_);
+ AddOptionalSwitch("--proto-format",
+ "Generates compiled resources in Protobuf format.\n"
+ "Suitable as input to the bundle tool for generating an App Bundle.",
+ &proto_format_);
+ AddOptionalSwitch("--no-static-lib-packages",
+ "Merge all library resources under the app's package.",
+ &options_.no_static_lib_packages);
+ AddOptionalSwitch("--auto-namespace-static-lib",
+ "Automatically namespace resource references when building a static\n"
+ "library.",
+ &options_.auto_namespace_static_lib);
+ AddOptionalSwitch("--non-final-ids",
+ "Generates R.java without the final modifier. This is implied when\n"
+ "--static-lib is specified.",
+ &options_.generate_non_final_ids);
+ AddOptionalFlag("--stable-ids", "File containing a list of name to ID mapping.",
+ &stable_id_file_path_);
+ AddOptionalFlag("--emit-ids",
+ "Emit a file at the given path with a list of name to ID mappings,\n"
+ "suitable for use with --stable-ids.",
+ &options_.resource_id_map_path);
+ AddOptionalFlag("--private-symbols",
+ "Package name to use when generating R.java for private symbols.\n"
+ "If not specified, public and private symbols will use the application's\n"
+ "package name.",
+ &options_.private_symbols);
+ AddOptionalFlag("--custom-package", "Custom Java package under which to generate R.java.",
+ &options_.custom_java_package);
+ AddOptionalFlagList("--extra-packages",
+ "Generate the same R.java but with different package names.",
+ &extra_java_packages_);
+ AddOptionalFlagList("--add-javadoc-annotation",
+ "Adds a JavaDoc annotation to all generated Java classes.",
+ &options_.javadoc_annotations);
+ AddOptionalFlag("--output-text-symbols",
+ "Generates a text file containing the resource symbols of the R class in\n"
+ "the specified folder.",
+ &options_.generate_text_symbols_path);
+ AddOptionalSwitch("--allow-reserved-package-id",
+ "Allows the use of a reserved package ID. This should on be used for\n"
+ "packages with a pre-O min-sdk\n",
+ &options_.allow_reserved_package_id);
+ AddOptionalSwitch("--auto-add-overlay",
+ "Allows the addition of new resources in overlays without\n"
+ "<add-resource> tags.",
+ &options_.auto_add_overlay);
+ AddOptionalFlag("--rename-manifest-package", "Renames the package in AndroidManifest.xml.",
+ &options_.manifest_fixer_options.rename_manifest_package);
+ AddOptionalFlag("--rename-instrumentation-target-package",
+ "Changes the name of the target package for instrumentation. Most useful\n"
+ "when used in conjunction with --rename-manifest-package.",
+ &options_.manifest_fixer_options.rename_instrumentation_target_package);
+ AddOptionalFlagList("-0", "File extensions not to compress.",
+ &options_.extensions_to_not_compress);
+ AddOptionalSwitch("--no-compress", "Do not compress any resources.",
+ &options_.do_not_compress_anything);
+ AddOptionalSwitch("--warn-manifest-validation",
+ "Treat manifest validation errors as warnings.",
+ &options_.manifest_fixer_options.warn_validation);
+ AddOptionalFlagList("--split",
+ "Split resources matching a set of configs out to a Split APK.\n"
+ "Syntax: path/to/output.apk:<config>[,<config>[...]].\n"
+ "On Windows, use a semicolon ';' separator instead.",
+ &split_args_);
+ AddOptionalSwitch("-v", "Enables verbose logging.", &verbose_);
+ AddOptionalSwitch("--debug-mode",
+ "Inserts android:debuggable=\"true\" in to the application node of the\n"
+ "manifest, making the application debuggable even on production devices.",
+ &options_.manifest_fixer_options.debug_mode);
+ AddOptionalSwitch("--strict-visibility",
+ "Do not allow overlays with different visibility levels.",
+ &options_.strict_visibility);
+ }
+
+ int Action(const std::vector<std::string>& args) override;
+
+ private:
+ IDiagnostics* diag_;
+ LinkOptions options_;
+
+ std::vector<std::string> overlay_arg_list_;
+ std::vector<std::string> extra_java_packages_;
+ Maybe<std::string> package_id_;
+ std::vector<std::string> configs_;
+ Maybe<std::string> preferred_density_;
+ Maybe<std::string> product_list_;
+ bool legacy_x_flag_ = false;
+ bool require_localization_ = false;
+ bool verbose_ = false;
+ bool shared_lib_ = false;
+ bool static_lib_ = false;
+ bool proto_format_ = false;
+ Maybe<std::string> stable_id_file_path_;
+ std::vector<std::string> split_args_;
+};
+
+}// namespace aapt
+
+#endif //AAPT2_LINK_H
\ No newline at end of file
diff --git a/tools/aapt2/cmd/Optimize.cpp b/tools/aapt2/cmd/Optimize.cpp
index 4afa8f0..b4cba8c 100644
--- a/tools/aapt2/cmd/Optimize.cpp
+++ b/tools/aapt2/cmd/Optimize.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include "Optimize.h"
+
#include <memory>
#include <vector>
@@ -24,7 +26,6 @@
#include "androidfw/StringPiece.h"
#include "Diagnostics.h"
-#include "Flags.h"
#include "LoadedApk.h"
#include "ResourceUtils.h"
#include "SdkConstants.h"
@@ -54,36 +55,6 @@
namespace aapt {
-struct OptimizeOptions {
- // Path to the output APK.
- Maybe<std::string> output_path;
- // Path to the output APK directory for splits.
- Maybe<std::string> output_dir;
-
- // Details of the app extracted from the AndroidManifest.xml
- AppInfo app_info;
-
- // Blacklist of unused resources that should be removed from the apk.
- std::unordered_set<ResourceName> resources_blacklist;
-
- // Split APK options.
- TableSplitterOptions table_splitter_options;
-
- // List of output split paths. These are in the same order as `split_constraints`.
- std::vector<std::string> split_paths;
-
- // List of SplitConstraints governing what resources go into each split. Ordered by `split_paths`.
- std::vector<SplitConstraints> split_constraints;
-
- TableFlattenerOptions table_flattener_options;
-
- Maybe<std::vector<OutputArtifact>> apk_artifacts;
-
- // Set of artifacts to keep when generating multi-APK splits. If the list is empty, all artifacts
- // are kept and will be written as output.
- std::unordered_set<std::string> kept_artifacts;
-};
-
class OptimizeContext : public IAaptContext {
public:
OptimizeContext() = default;
@@ -145,9 +116,9 @@
int sdk_version_ = 0;
};
-class OptimizeCommand {
+class Optimizer {
public:
- OptimizeCommand(OptimizeContext* context, const OptimizeOptions& options)
+ Optimizer(OptimizeContext* context, const OptimizeOptions& options)
: options_(options), context_(context) {
}
@@ -378,82 +349,24 @@
return true;
}
-int Optimize(const std::vector<StringPiece>& args) {
- OptimizeContext context;
- OptimizeOptions options;
- Maybe<std::string> config_path;
- Maybe<std::string> whitelist_path;
- Maybe<std::string> resources_config_path;
- Maybe<std::string> target_densities;
- std::vector<std::string> configs;
- std::vector<std::string> split_args;
- std::unordered_set<std::string> kept_artifacts;
- bool verbose = false;
- bool print_only = false;
- Flags flags =
- Flags()
- .OptionalFlag("-o", "Path to the output APK.", &options.output_path)
- .OptionalFlag("-d", "Path to the output directory (for splits).", &options.output_dir)
- .OptionalFlag("-x", "Path to XML configuration file.", &config_path)
- .OptionalSwitch("-p", "Print the multi APK artifacts and exit.", &print_only)
- .OptionalFlag(
- "--target-densities",
- "Comma separated list of the screen densities that the APK will be optimized for.\n"
- "All the resources that would be unused on devices of the given densities will be \n"
- "removed from the APK.",
- &target_densities)
- .OptionalFlag("--whitelist-path",
- "Path to the whitelist.cfg file containing whitelisted resources \n"
- "whose names should not be altered in final resource tables.",
- &whitelist_path)
- .OptionalFlag("--resources-config-path",
- "Path to the resources.cfg file containing the list of resources and \n"
- "directives to each resource. \n"
- "Format: type/resource_name#[directive][,directive]",
- &resources_config_path)
- .OptionalFlagList("-c",
- "Comma separated list of configurations to include. The default\n"
- "is all configurations.",
- &configs)
- .OptionalFlagList("--split",
- "Split resources matching a set of configs out to a "
- "Split APK.\nSyntax: path/to/output.apk;<config>[,<config>[...]].\n"
- "On Windows, use a semicolon ';' separator instead.",
- &split_args)
- .OptionalFlagList("--keep-artifacts",
- "Comma separated list of artifacts to keep. If none are specified,\n"
- "all artifacts will be kept.",
- &kept_artifacts)
- .OptionalSwitch("--enable-sparse-encoding",
- "Enables encoding sparse entries using a binary search tree.\n"
- "This decreases APK size at the cost of resource retrieval performance.",
- &options.table_flattener_options.use_sparse_entries)
- .OptionalSwitch("--enable-resource-obfuscation",
- "Enables obfuscation of key string pool to single value",
- &options.table_flattener_options.collapse_key_stringpool)
- .OptionalSwitch("-v", "Enables verbose logging", &verbose);
-
- if (!flags.Parse("aapt2 optimize", args, &std::cerr)) {
- return 1;
- }
-
- if (flags.GetArgs().size() != 1u) {
+int OptimizeCommand::Action(const std::vector<std::string>& args) {
+ if (args.size() != 1u) {
std::cerr << "must have one APK as argument.\n\n";
- flags.Usage("aapt2 optimize", &std::cerr);
+ Usage(&std::cerr);
return 1;
}
- const std::string& apk_path = flags.GetArgs()[0];
-
- context.SetVerbose(verbose);
+ const std::string& apk_path = args[0];
+ OptimizeContext context;
+ context.SetVerbose(verbose_);
IDiagnostics* diag = context.GetDiagnostics();
- if (config_path) {
- std::string& path = config_path.value();
+ if (config_path_) {
+ std::string& path = config_path_.value();
Maybe<ConfigurationParser> for_path = ConfigurationParser::ForPath(path);
if (for_path) {
- options.apk_artifacts = for_path.value().WithDiagnostics(diag).Parse(apk_path);
- if (!options.apk_artifacts) {
+ options_.apk_artifacts = for_path.value().WithDiagnostics(diag).Parse(apk_path);
+ if (!options_.apk_artifacts) {
diag->Error(DiagMessage() << "Failed to parse the output artifact list");
return 1;
}
@@ -463,28 +376,28 @@
return 1;
}
- if (print_only) {
- for (const OutputArtifact& artifact : options.apk_artifacts.value()) {
+ if (print_only_) {
+ for (const OutputArtifact& artifact : options_.apk_artifacts.value()) {
std::cout << artifact.name << std::endl;
}
return 0;
}
- if (!kept_artifacts.empty()) {
- for (const std::string& artifact_str : kept_artifacts) {
+ if (!kept_artifacts_.empty()) {
+ for (const std::string& artifact_str : kept_artifacts_) {
for (const StringPiece& artifact : util::Tokenize(artifact_str, ',')) {
- options.kept_artifacts.insert(artifact.to_string());
+ options_.kept_artifacts.insert(artifact.to_string());
}
}
}
// Since we know that we are going to process the APK (not just print targets), make sure we
// have somewhere to write them to.
- if (!options.output_dir) {
+ if (!options_.output_dir) {
diag->Error(DiagMessage() << "Output directory is required when using a configuration file");
return 1;
}
- } else if (print_only) {
+ } else if (print_only_) {
diag->Error(DiagMessage() << "Asked to print artifacts without providing a configurations");
return 1;
}
@@ -494,57 +407,57 @@
return 1;
}
- if (target_densities) {
+ if (target_densities_) {
// Parse the target screen densities.
- for (const StringPiece& config_str : util::Tokenize(target_densities.value(), ',')) {
+ for (const StringPiece& config_str : util::Tokenize(target_densities_.value(), ',')) {
Maybe<uint16_t> target_density = ParseTargetDensityParameter(config_str, diag);
if (!target_density) {
return 1;
}
- options.table_splitter_options.preferred_densities.push_back(target_density.value());
+ options_.table_splitter_options.preferred_densities.push_back(target_density.value());
}
}
std::unique_ptr<IConfigFilter> filter;
- if (!configs.empty()) {
- filter = ParseConfigFilterParameters(configs, diag);
+ if (!configs_.empty()) {
+ filter = ParseConfigFilterParameters(configs_, diag);
if (filter == nullptr) {
return 1;
}
- options.table_splitter_options.config_filter = filter.get();
+ options_.table_splitter_options.config_filter = filter.get();
}
// Parse the split parameters.
- for (const std::string& split_arg : split_args) {
- options.split_paths.emplace_back();
- options.split_constraints.emplace_back();
- if (!ParseSplitParameter(split_arg, diag, &options.split_paths.back(),
- &options.split_constraints.back())) {
+ for (const std::string& split_arg : split_args_) {
+ options_.split_paths.emplace_back();
+ options_.split_constraints.emplace_back();
+ if (!ParseSplitParameter(split_arg, diag, &options_.split_paths.back(),
+ &options_.split_constraints.back())) {
return 1;
}
}
- if (options.table_flattener_options.collapse_key_stringpool) {
- if (whitelist_path) {
- std::string& path = whitelist_path.value();
- if (!ExtractObfuscationWhitelistFromConfig(path, &context, &options)) {
+ if (options_.table_flattener_options.collapse_key_stringpool) {
+ if (whitelist_path_) {
+ std::string& path = whitelist_path_.value();
+ if (!ExtractObfuscationWhitelistFromConfig(path, &context, &options_)) {
return 1;
}
}
}
- if (resources_config_path) {
- std::string& path = resources_config_path.value();
- if (!ExtractConfig(path, &context, &options)) {
+ if (resources_config_path_) {
+ std::string& path = resources_config_path_.value();
+ if (!ExtractConfig(path, &context, &options_)) {
return 1;
}
}
- if (!ExtractAppDataFromManifest(&context, apk.get(), &options)) {
+ if (!ExtractAppDataFromManifest(&context, apk.get(), &options_)) {
return 1;
}
- OptimizeCommand cmd(&context, options);
+ Optimizer cmd(&context, options_);
return cmd.Run(std::move(apk));
}
diff --git a/tools/aapt2/cmd/Optimize.h b/tools/aapt2/cmd/Optimize.h
new file mode 100644
index 0000000..43bc216
--- /dev/null
+++ b/tools/aapt2/cmd/Optimize.h
@@ -0,0 +1,124 @@
+/*
+ * 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.
+ */
+
+#ifndef AAPT2_OPTIMIZE_H
+#define AAPT2_OPTIMIZE_H
+
+#include "AppInfo.h"
+#include "Command.h"
+#include "configuration/ConfigurationParser.h"
+#include "format/binary/TableFlattener.h"
+#include "split/TableSplitter.h"
+
+namespace aapt {
+
+struct OptimizeOptions {
+ friend class OptimizeCommand;
+
+ // Path to the output APK.
+ Maybe<std::string> output_path;
+ // Path to the output APK directory for splits.
+ Maybe<std::string> output_dir;
+
+ // Details of the app extracted from the AndroidManifest.xml
+ AppInfo app_info;
+
+ // Blacklist of unused resources that should be removed from the apk.
+ std::unordered_set<ResourceName> resources_blacklist;
+
+ // Split APK options.
+ TableSplitterOptions table_splitter_options;
+
+ // List of output split paths. These are in the same order as `split_constraints`.
+ std::vector<std::string> split_paths;
+
+ // List of SplitConstraints governing what resources go into each split. Ordered by `split_paths`.
+ std::vector<SplitConstraints> split_constraints;
+
+ TableFlattenerOptions table_flattener_options;
+
+ Maybe<std::vector<aapt::configuration::OutputArtifact>> apk_artifacts;
+
+ // Set of artifacts to keep when generating multi-APK splits. If the list is empty, all artifacts
+ // are kept and will be written as output.
+ std::unordered_set<std::string> kept_artifacts;
+};
+
+class OptimizeCommand : public Command {
+ public:
+ explicit OptimizeCommand() : Command("optimize") {
+ SetDescription("Preforms resource optimizations on an apk.");
+ AddOptionalFlag("-o", "Path to the output APK.", &options_.output_path);
+ AddOptionalFlag("-d", "Path to the output directory (for splits).", &options_.output_dir);
+ AddOptionalFlag("-x", "Path to XML configuration file.", &config_path_);
+ AddOptionalSwitch("-p", "Print the multi APK artifacts and exit.", &print_only_);
+ AddOptionalFlag(
+ "--target-densities",
+ "Comma separated list of the screen densities that the APK will be optimized for.\n"
+ "All the resources that would be unused on devices of the given densities will be \n"
+ "removed from the APK.",
+ &target_densities_);
+ AddOptionalFlag("--whitelist-path",
+ "Path to the whitelist.cfg file containing whitelisted resources \n"
+ "whose names should not be altered in final resource tables.",
+ &whitelist_path_);
+ AddOptionalFlag("--resources-config-path",
+ "Path to the resources.cfg file containing the list of resources and \n"
+ "directives to each resource. \n"
+ "Format: type/resource_name#[directive][,directive]",
+ &resources_config_path_);
+ AddOptionalFlagList("-c",
+ "Comma separated list of configurations to include. The default\n"
+ "is all configurations.",
+ &configs_);
+ AddOptionalFlagList("--split",
+ "Split resources matching a set of configs out to a "
+ "Split APK.\nSyntax: path/to/output.apk;<config>[,<config>[...]].\n"
+ "On Windows, use a semicolon ';' separator instead.",
+ &split_args_);
+ AddOptionalFlagList("--keep-artifacts",
+ "Comma separated list of artifacts to keep. If none are specified,\n"
+ "all artifacts will be kept.",
+ &kept_artifacts_);
+ AddOptionalSwitch("--enable-sparse-encoding",
+ "Enables encoding sparse entries using a binary search tree.\n"
+ "This decreases APK size at the cost of resource retrieval performance.",
+ &options_.table_flattener_options.use_sparse_entries);
+ AddOptionalSwitch("--enable-resource-obfuscation",
+ "Enables obfuscation of key string pool to single value",
+ &options_.table_flattener_options.collapse_key_stringpool);
+ AddOptionalSwitch("-v", "Enables verbose logging", &verbose_);
+ }
+
+ int Action(const std::vector<std::string>& args) override;
+
+ private:
+ OptimizeOptions options_;
+
+ Maybe<std::string> config_path_;
+ Maybe<std::string> whitelist_path_;
+ Maybe<std::string> resources_config_path_;
+ Maybe<std::string> target_densities_;
+ std::vector<std::string> configs_;
+ std::vector<std::string> split_args_;
+ std::unordered_set<std::string> kept_artifacts_;
+ bool print_only_ = false;
+ bool verbose_ = false;
+};
+
+}// namespace aapt
+
+#endif //AAPT2_OPTIMIZE_H
\ No newline at end of file
diff --git a/tools/aapt2/cmd/Util.cpp b/tools/aapt2/cmd/Util.cpp
index 4e77e9a..c6c82b0 100644
--- a/tools/aapt2/cmd/Util.cpp
+++ b/tools/aapt2/cmd/Util.cpp
@@ -29,6 +29,7 @@
#include "util/Util.h"
using ::android::StringPiece;
+using ::android::base::StringPrintf;
namespace aapt {
@@ -168,6 +169,7 @@
std::unique_ptr<xml::XmlResource> GenerateSplitManifest(const AppInfo& app_info,
const SplitConstraints& constraints) {
const ResourceId kVersionCode(0x0101021b);
+ const ResourceId kVersionCodeMajor(0x01010576);
const ResourceId kRevisionCode(0x010104d5);
const ResourceId kHasCode(0x0101000c);
@@ -184,6 +186,14 @@
util::make_unique<BinaryPrimitive>(android::Res_value::TYPE_INT_DEC, version_code)});
}
+ if (app_info.version_code_major) {
+ const uint32_t version_code_major = app_info.version_code_major.value();
+ manifest_el->attributes.push_back(xml::Attribute{
+ xml::kSchemaAndroid, "versionCodeMajor", std::to_string(version_code_major),
+ CreateAttributeWithId(kVersionCodeMajor),
+ util::make_unique<BinaryPrimitive>(android::Res_value::TYPE_INT_DEC, version_code_major)});
+ }
+
if (app_info.revision_code) {
const uint32_t revision_code = app_info.revision_code.value();
manifest_el->attributes.push_back(xml::Attribute{
@@ -355,6 +365,17 @@
app_info.version_code = maybe_code.value();
}
+ if (const xml::Attribute* version_code_major_attr =
+ manifest_el->FindAttribute(xml::kSchemaAndroid, "versionCodeMajor")) {
+ Maybe<uint32_t> maybe_code = ExtractCompiledInt(*version_code_major_attr, &error_msg);
+ if (!maybe_code) {
+ diag->Error(DiagMessage(xml_res.file.source.WithLine(manifest_el->line_number))
+ << "invalid android:versionCodeMajor: " << error_msg);
+ return {};
+ }
+ app_info.version_code_major = maybe_code.value();
+ }
+
if (const xml::Attribute* revision_code_attr =
manifest_el->FindAttribute(xml::kSchemaAndroid, "revisionCode")) {
Maybe<uint32_t> maybe_code = ExtractCompiledInt(*revision_code_attr, &error_msg);
@@ -391,4 +412,21 @@
return app_info;
}
+void SetLongVersionCode(xml::Element* manifest, uint64_t version) {
+ // Write the low bits of the version code to android:versionCode
+ auto version_code = manifest->FindOrCreateAttribute(xml::kSchemaAndroid, "versionCode");
+ version_code->value = StringPrintf("0x%08x", (uint32_t) (version & 0xffffffff));
+ version_code->compiled_value = ResourceUtils::TryParseInt(version_code->value);
+
+ auto version_high = (uint32_t) (version >> 32);
+ if (version_high != 0) {
+ // Write the high bits of the version code to android:versionCodeMajor
+ auto version_major = manifest->FindOrCreateAttribute(xml::kSchemaAndroid, "versionCodeMajor");
+ version_major->value = StringPrintf("0x%08x", version_high);
+ version_major->compiled_value = ResourceUtils::TryParseInt(version_major->value);
+ } else {
+ manifest->RemoveAttribute(xml::kSchemaAndroid, "versionCodeMajor");
+ }
+}
+
} // namespace aapt
diff --git a/tools/aapt2/cmd/Util.h b/tools/aapt2/cmd/Util.h
index fb8753e..cf1443e 100644
--- a/tools/aapt2/cmd/Util.h
+++ b/tools/aapt2/cmd/Util.h
@@ -67,6 +67,11 @@
// checks this at runtime.
std::string MakePackageSafeName(const std::string &name);
+// Sets the versionCode and versionCodeMajor attributes to the version code. Attempts to encode the
+// version code using the versionCode attribute only, and encodes using both versionCode and
+// versionCodeMajor if the version code requires more than 32 bits.
+void SetLongVersionCode(xml::Element* manifest, uint64_t version_code);
+
} // namespace aapt
#endif /* AAPT_SPLIT_UTIL_H */
diff --git a/tools/aapt2/cmd/Util_test.cpp b/tools/aapt2/cmd/Util_test.cpp
index 0c527f6..b9fb5b2 100644
--- a/tools/aapt2/cmd/Util_test.cpp
+++ b/tools/aapt2/cmd/Util_test.cpp
@@ -18,6 +18,7 @@
#include "AppInfo.h"
#include "split/TableSplitter.h"
+#include "test/Builders.h"
#include "test/Test.h"
namespace aapt {
@@ -36,4 +37,51 @@
EXPECT_EQ(root->FindAttribute("", "targetConfig")->value, "b+sr+Latn,en-rUS-land");
}
+TEST (UtilTest, LongVersionCodeDefined) {
+ auto doc = test::BuildXmlDom(R"(
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.aapt.test" android:versionCode="0x1" android:versionCodeMajor="0x1">
+ </manifest>)");
+ SetLongVersionCode(doc->root.get(), 42);
+
+ auto version_code = doc->root->FindAttribute(xml::kSchemaAndroid, "versionCode");
+ ASSERT_NE(version_code, nullptr);
+ EXPECT_EQ(version_code->value, "0x0000002a");
+
+ ASSERT_NE(version_code->compiled_value, nullptr);
+ auto compiled_version_code = ValueCast<BinaryPrimitive>(version_code->compiled_value.get());
+ ASSERT_NE(compiled_version_code, nullptr);
+ EXPECT_EQ(compiled_version_code->value.data, 42U);
+
+ auto version_code_major = doc->root->FindAttribute(xml::kSchemaAndroid, "versionCodeMajor");
+ EXPECT_EQ(version_code_major, nullptr);
+}
+
+TEST (UtilTest, LongVersionCodeUndefined) {
+ auto doc = test::BuildXmlDom(R"(
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.aapt.test">
+ </manifest>)");
+ SetLongVersionCode(doc->root.get(), 420000000000);
+
+ auto version_code = doc->root->FindAttribute(xml::kSchemaAndroid, "versionCode");
+ ASSERT_NE(version_code, nullptr);
+ EXPECT_EQ(version_code->value, "0xc9f36800");
+
+ ASSERT_NE(version_code->compiled_value, nullptr);
+ auto compiled_version_code = ValueCast<BinaryPrimitive>(version_code->compiled_value.get());
+ ASSERT_NE(compiled_version_code, nullptr);
+ EXPECT_EQ(compiled_version_code->value.data, 0xc9f36800);
+
+ auto version_code_major = doc->root->FindAttribute(xml::kSchemaAndroid, "versionCodeMajor");
+ ASSERT_NE(version_code_major, nullptr);
+ EXPECT_EQ(version_code_major->value, "0x00000061");
+
+ ASSERT_NE(version_code_major->compiled_value, nullptr);
+ auto compiled_version_code_major = ValueCast<BinaryPrimitive>(
+ version_code_major->compiled_value.get());
+ ASSERT_NE(compiled_version_code_major, nullptr);
+ EXPECT_EQ(compiled_version_code_major->value.data, 0x61);
+}
+
} // namespace aapt
diff --git a/tools/aapt2/java/JavaClassGenerator.cpp b/tools/aapt2/java/JavaClassGenerator.cpp
index db1561e..d1a70a7 100644
--- a/tools/aapt2/java/JavaClassGenerator.cpp
+++ b/tools/aapt2/java/JavaClassGenerator.cpp
@@ -256,9 +256,20 @@
styleable_attr.field_name =
TransformNestedAttr(attr.name.value(), array_field_name, package_name_to_generate);
+ Reference ref = attr;
+ if (attr.name.value().package.empty()) {
+
+ // If the resource does not have a package name, set the package to the unmangled package name
+ // of the styleable declaration because attributes without package names would have been
+ // declared in the same package as the styleable.
+ ref.name = ResourceName(package_name_to_generate, ref.name.value().type,
+ ref.name.value().entry);
+ }
+
// Look up the symbol so that we can write out in the comments what are possible legal values
// for this attribute.
- const SymbolTable::Symbol* symbol = context_->GetExternalSymbols()->FindByReference(attr);
+ const SymbolTable::Symbol* symbol = context_->GetExternalSymbols()->FindByReference(ref);
+
if (symbol && symbol->attribute) {
// Copy the symbol data structure because the returned instance can be destroyed.
styleable_attr.symbol = *symbol;
@@ -303,7 +314,7 @@
const ResourceName& attr_name = entry.attr_ref->name.value();
styleable_comment << "<tr><td><code>{@link #" << entry.field_name << " "
<< (!attr_name.package.empty() ? attr_name.package
- : context_->GetCompilationPackage())
+ : package_name_to_generate)
<< ":" << attr_name.entry << "}</code></td>";
// Only use the comment up until the first '.'. This is to stay compatible with
@@ -374,7 +385,7 @@
StringPiece package_name = attr_name.package;
if (package_name.empty()) {
- package_name = context_->GetCompilationPackage();
+ package_name = package_name_to_generate;
}
std::unique_ptr<IntMember> index_member =
diff --git a/tools/aapt2/java/JavaClassGenerator_test.cpp b/tools/aapt2/java/JavaClassGenerator_test.cpp
index 10a97d8..fa208be 100644
--- a/tools/aapt2/java/JavaClassGenerator_test.cpp
+++ b/tools/aapt2/java/JavaClassGenerator_test.cpp
@@ -107,6 +107,55 @@
EXPECT_THAT(output, Not(HasSubstr("com_foo$two")));
}
+TEST(JavaClassGeneratorTest, StyleableAttributesWithDifferentPackageName) {
+ std::unique_ptr<ResourceTable> table =
+ test::ResourceTableBuilder()
+ .SetPackageId("android", 0x01)
+ .SetPackageId("app", 0x7f)
+ .AddValue("app:attr/foo", ResourceId(0x7f010000),
+ test::AttributeBuilder().Build())
+ .AddValue("app:attr/bar", ResourceId(0x7f010001),
+ test::AttributeBuilder().Build())
+ .AddValue("android:attr/baz", ResourceId(0x01010000),
+ test::AttributeBuilder().Build())
+ .AddValue("app:styleable/MyStyleable", ResourceId(0x7f030000),
+ test::StyleableBuilder()
+ .AddItem("app:attr/foo", ResourceId(0x7f010000))
+ .AddItem("attr/bar", ResourceId(0x7f010001))
+ .AddItem("android:attr/baz", ResourceId(0x01010000))
+ .Build())
+ .Build();
+
+ std::unique_ptr<IAaptContext> context =
+ test::ContextBuilder()
+ .AddSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
+ .SetNameManglerPolicy(NameManglerPolicy{"custom"})
+ .SetCompilationPackage("custom")
+ .Build();
+ JavaClassGenerator generator(context.get(), table.get(), {});
+
+ std::string output;
+ StringOutputStream out(&output);
+ EXPECT_TRUE(generator.Generate("app", &out));
+ out.Flush();
+
+ EXPECT_THAT(output, Not(HasSubstr("public static final int baz=0x01010000;")));
+ EXPECT_THAT(output, HasSubstr("public static final int foo=0x7f010000;"));
+ EXPECT_THAT(output, HasSubstr("public static final int bar=0x7f010001;"));
+
+ EXPECT_THAT(output, HasSubstr("public static final int MyStyleable_android_baz=0;"));
+ EXPECT_THAT(output, HasSubstr("public static final int MyStyleable_foo=1;"));
+ EXPECT_THAT(output, HasSubstr("public static final int MyStyleable_bar=2;"));
+
+ EXPECT_THAT(output, HasSubstr("@link #MyStyleable_android_baz android:baz"));
+ EXPECT_THAT(output, HasSubstr("@link #MyStyleable_foo app:foo"));
+ EXPECT_THAT(output, HasSubstr("@link #MyStyleable_bar app:bar"));
+
+ EXPECT_THAT(output, HasSubstr("@link android.R.attr#baz"));
+ EXPECT_THAT(output, HasSubstr("@link app.R.attr#foo"));
+ EXPECT_THAT(output, HasSubstr("@link app.R.attr#bar"));
+}
+
TEST(JavaClassGeneratorTest, AttrPrivateIsWrittenAsAttr) {
std::unique_ptr<ResourceTable> table =
test::ResourceTableBuilder()
diff --git a/tools/aapt2/java/ProguardRules.cpp b/tools/aapt2/java/ProguardRules.cpp
index d03cdb3..d40795a 100644
--- a/tools/aapt2/java/ProguardRules.cpp
+++ b/tools/aapt2/java/ProguardRules.cpp
@@ -39,7 +39,11 @@
public:
using xml::Visitor::Visit;
- BaseVisitor(const ResourceFile& file, KeepSet* keep_set) : file_(file), keep_set_(keep_set) {
+ BaseVisitor(const ResourceFile& file, KeepSet* keep_set) : BaseVisitor(file, keep_set, "...") {
+ }
+
+ BaseVisitor(const ResourceFile& file, KeepSet* keep_set, const std::string& ctor_signature)
+ : file_(file), keep_set_(keep_set), ctor_signature_(ctor_signature) {
}
void Visit(xml::Element* node) override {
@@ -50,11 +54,11 @@
// This is a custom view, let's figure out the class name from this.
std::string package = maybe_package.value().package + "." + node->name;
if (util::IsJavaClassName(package)) {
- AddClass(node->line_number, package);
+ AddClass(node->line_number, package, ctor_signature_);
}
}
} else if (util::IsJavaClassName(node->name)) {
- AddClass(node->line_number, node->name);
+ AddClass(node->line_number, node->name, ctor_signature_);
}
for (const auto& child : node->children) {
@@ -74,9 +78,12 @@
protected:
ResourceFile file_;
KeepSet* keep_set_;
+ std::string ctor_signature_;
- virtual void AddClass(size_t line_number, const std::string& class_name) {
- keep_set_->AddConditionalClass({file_.name, file_.source.WithLine(line_number)}, class_name);
+ virtual void AddClass(size_t line_number, const std::string& class_name,
+ const std::string& ctor_signature) {
+ keep_set_->AddConditionalClass({file_.name, file_.source.WithLine(line_number)},
+ {class_name, ctor_signature});
}
void AddMethod(size_t line_number, const std::string& method_name,
@@ -102,31 +109,38 @@
class LayoutVisitor : public BaseVisitor {
public:
- LayoutVisitor(const ResourceFile& file, KeepSet* keep_set) : BaseVisitor(file, keep_set) {
+ LayoutVisitor(const ResourceFile& file, KeepSet* keep_set)
+ : BaseVisitor(file, keep_set, "android.content.Context, android.util.AttributeSet") {
}
void Visit(xml::Element* node) override {
- bool check_class = false;
- bool check_name = false;
+ bool is_view = false;
+ bool is_fragment = false;
if (node->namespace_uri.empty()) {
if (node->name == "view") {
- check_class = true;
+ is_view = true;
} else if (node->name == "fragment") {
- check_class = check_name = true;
+ is_fragment = true;
}
} else if (node->namespace_uri == xml::kSchemaAndroid) {
- check_name = node->name == "fragment";
+ is_fragment = node->name == "fragment";
}
for (const auto& attr : node->attributes) {
- if (check_class && attr.namespace_uri.empty() && attr.name == "class" &&
- util::IsJavaClassName(attr.value)) {
- AddClass(node->line_number, attr.value);
- } else if (check_name && attr.namespace_uri == xml::kSchemaAndroid &&
- attr.name == "name" && util::IsJavaClassName(attr.value)) {
- AddClass(node->line_number, attr.value);
- } else if (attr.namespace_uri == xml::kSchemaAndroid &&
- attr.name == "onClick") {
+ if (attr.namespace_uri.empty() && attr.name == "class") {
+ if (util::IsJavaClassName(attr.value)) {
+ if (is_view) {
+ AddClass(node->line_number, attr.value,
+ "android.content.Context, android.util.AttributeSet");
+ } else if (is_fragment) {
+ AddClass(node->line_number, attr.value, "");
+ }
+ }
+ } else if (attr.namespace_uri == xml::kSchemaAndroid && attr.name == "name") {
+ if (is_fragment && util::IsJavaClassName(attr.value)) {
+ AddClass(node->line_number, attr.value, "");
+ }
+ } else if (attr.namespace_uri == xml::kSchemaAndroid && attr.name == "onClick") {
AddMethod(node->line_number, attr.value, "android.view.View");
}
}
@@ -149,7 +163,7 @@
if (attr.namespace_uri == xml::kSchemaAndroid) {
if ((attr.name == "actionViewClass" || attr.name == "actionProviderClass") &&
util::IsJavaClassName(attr.value)) {
- AddClass(node->line_number, attr.value);
+ AddClass(node->line_number, attr.value, "android.content.Context");
} else if (attr.name == "onClick") {
AddMethod(node->line_number, attr.value, "android.view.MenuItem");
}
@@ -180,7 +194,7 @@
xml::Attribute* attr =
node->FindAttribute(xml::kSchemaAndroid, "fragment");
if (attr && util::IsJavaClassName(attr->value)) {
- AddClass(node->line_number, attr->value);
+ AddClass(node->line_number, attr->value, "");
}
}
@@ -202,7 +216,7 @@
if (attr != nullptr && !attr->value.empty()) {
std::string name = (attr->value[0] == '.') ? package_ + attr->value : attr->value;
if (util::IsJavaClassName(name)) {
- AddClass(node->line_number, name);
+ AddClass(node->line_number, name, "...");
}
}
@@ -225,7 +239,8 @@
if (check_class) {
xml::Attribute* attr = node->FindAttribute({}, "class");
if (attr && util::IsJavaClassName(attr->value)) {
- AddClass(node->line_number, attr->value);
+ AddClass(node->line_number, attr->value,
+ "android.content.Context, android.util.AttributeSet");
}
}
@@ -256,7 +271,14 @@
if (attr) {
Maybe<std::string> result = util::GetFullyQualifiedClassName(package_, attr->value);
if (result) {
- AddClass(node->line_number, result.value());
+ AddClass(node->line_number, result.value(), "");
+ }
+ }
+ attr = node->FindAttribute(xml::kSchemaAndroid, "appComponentFactory");
+ if (attr) {
+ Maybe<std::string> result = util::GetFullyQualifiedClassName(package_, attr->value);
+ if (result) {
+ AddClass(node->line_number, result.value(), "");
}
}
if (main_dex_only_) {
@@ -287,7 +309,7 @@
if (get_name) {
Maybe<std::string> result = util::GetFullyQualifiedClassName(package_, attr->value);
if (result) {
- AddClass(node->line_number, result.value());
+ AddClass(node->line_number, result.value(), "");
}
}
}
@@ -295,7 +317,8 @@
BaseVisitor::Visit(node);
}
- virtual void AddClass(size_t line_number, const std::string& class_name) override {
+ virtual void AddClass(size_t line_number, const std::string& class_name,
+ const std::string& ctor_signature) override {
keep_set_->AddManifestClass({file_.name, file_.source.WithLine(line_number)}, class_name);
}
@@ -383,13 +406,15 @@
printer.Print("-if class **.R$layout { int ")
.Print(JavaClassGenerator::TransformToFieldName(location.name.entry))
.Println("; }");
- printer.Print("-keep class ").Print(entry.first).Println(" { <init>(...); }");
+ printer.Print("-keep class ").Print(entry.first.name).Print(" { <init>(")
+ .Print(entry.first.signature).Println("); }");
}
} else {
for (const UsageLocation& location : entry.second) {
printer.Print("# Referenced at ").Println(location.source.to_string());
}
- printer.Print("-keep class ").Print(entry.first).Println(" { <init>(...); }");
+ printer.Print("-keep class ").Print(entry.first.name).Print(" { <init>(")
+ .Print(entry.first.signature).Println("); }");
}
printer.Println();
}
diff --git a/tools/aapt2/java/ProguardRules.h b/tools/aapt2/java/ProguardRules.h
index acaceac..01dad0b 100644
--- a/tools/aapt2/java/ProguardRules.h
+++ b/tools/aapt2/java/ProguardRules.h
@@ -56,8 +56,9 @@
manifest_class_set_[class_name].insert(file);
}
- inline void AddConditionalClass(const UsageLocation& file, const std::string& class_name) {
- conditional_class_set_[class_name].insert(file);
+ inline void AddConditionalClass(const UsageLocation& file,
+ const NameAndSignature& class_and_signature) {
+ conditional_class_set_[class_and_signature].insert(file);
}
inline void AddMethod(const UsageLocation& file, const NameAndSignature& name_and_signature) {
@@ -77,7 +78,7 @@
bool conditional_keep_rules_ = false;
std::map<std::string, std::set<UsageLocation>> manifest_class_set_;
std::map<NameAndSignature, std::set<UsageLocation>> method_set_;
- std::map<std::string, std::set<UsageLocation>> conditional_class_set_;
+ std::map<NameAndSignature, std::set<UsageLocation>> conditional_class_set_;
std::map<ResourceName, std::set<UsageLocation>> reference_set_;
};
diff --git a/tools/aapt2/java/ProguardRules_test.cpp b/tools/aapt2/java/ProguardRules_test.cpp
index b5e27e0..83c72d8 100644
--- a/tools/aapt2/java/ProguardRules_test.cpp
+++ b/tools/aapt2/java/ProguardRules_test.cpp
@@ -37,7 +37,11 @@
TEST(ProguardRulesTest, ManifestRuleDefaultConstructorOnly) {
std::unique_ptr<xml::XmlResource> manifest = test::BuildXmlDom(R"(
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
- <application android:backupAgent="com.foo.BarBackupAgent">
+ <application
+ android:appComponentFactory="com.foo.BarAppComponentFactory"
+ android:backupAgent="com.foo.BarBackupAgent"
+ android:name="com.foo.BarApplication"
+ >
<activity android:name="com.foo.BarActivity"/>
<service android:name="com.foo.BarService"/>
<receiver android:name="com.foo.BarReceiver"/>
@@ -51,7 +55,9 @@
std::string actual = GetKeepSetString(set);
+ EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarAppComponentFactory { <init>(); }"));
EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarBackupAgent { <init>(); }"));
+ EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarApplication { <init>(); }"));
EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarActivity { <init>(); }"));
EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarService { <init>(); }"));
EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarReceiver { <init>(); }"));
@@ -71,7 +77,7 @@
std::string actual = GetKeepSetString(set);
- EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(...); }"));
+ EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(); }"));
}
TEST(ProguardRulesTest, FragmentClassRuleIsEmitted) {
@@ -85,7 +91,7 @@
std::string actual = GetKeepSetString(set);
- EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(...); }"));
+ EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(); }"));
}
TEST(ProguardRulesTest, FragmentNameAndClassRulesAreEmitted) {
@@ -101,8 +107,8 @@
std::string actual = GetKeepSetString(set);
- EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(...); }"));
- EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Baz { <init>(...); }"));
+ EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(); }"));
+ EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Baz { <init>(); }"));
}
TEST(ProguardRulesTest, NavigationFragmentNameAndClassRulesAreEmitted) {
@@ -146,7 +152,8 @@
std::string actual = GetKeepSetString(set);
- EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(...); }"));
+ EXPECT_THAT(actual, HasSubstr(
+ "-keep class com.foo.Bar { <init>(android.content.Context, android.util.AttributeSet); }"));
}
TEST(ProguardRulesTest, IncludedLayoutRulesAreConditional) {
@@ -184,7 +191,8 @@
std::string actual = GetKeepSetString(set);
EXPECT_THAT(actual, HasSubstr("-if class **.R$layout"));
- EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(...); }"));
+ EXPECT_THAT(actual, HasSubstr(
+ "-keep class com.foo.Bar { <init>(android.content.Context, android.util.AttributeSet); }"));
EXPECT_THAT(actual, HasSubstr("int foo"));
EXPECT_THAT(actual, HasSubstr("int bar"));
}
@@ -203,7 +211,8 @@
std::string actual = GetKeepSetString(set);
- EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(...); }"));
+ EXPECT_THAT(actual, HasSubstr(
+ "-keep class com.foo.Bar { <init>(android.content.Context, android.util.AttributeSet); }"));
EXPECT_THAT(actual, HasSubstr("-if class **.R$layout"));
EXPECT_THAT(actual, HasSubstr("int foo"));
EXPECT_THAT(actual, HasSubstr("int bar"));
@@ -224,7 +233,8 @@
std::string actual = GetKeepSetString(set);
EXPECT_THAT(actual, Not(HasSubstr("-if")));
- EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(...); }"));
+ EXPECT_THAT(actual, HasSubstr(
+ "-keep class com.foo.Bar { <init>(android.content.Context, android.util.AttributeSet); }"));
}
TEST(ProguardRulesTest, ViewOnClickRuleIsEmitted) {
@@ -261,8 +271,8 @@
EXPECT_THAT(actual, HasSubstr(
"-keepclassmembers class * { *** on_click(android.view.MenuItem); }"));
- EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(...); }"));
- EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Baz { <init>(...); }"));
+ EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(android.content.Context); }"));
+ EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Baz { <init>(android.content.Context); }"));
EXPECT_THAT(actual, Not(HasSubstr("com.foo.Bat")));
}
@@ -279,7 +289,8 @@
std::string actual = GetKeepSetString(set);
- EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(...); }"));
+ EXPECT_THAT(actual, HasSubstr(
+ "-keep class com.foo.Bar { <init>(android.content.Context, android.util.AttributeSet); }"));
}
TEST(ProguardRulesTest, TransitionRulesAreEmitted) {
@@ -295,7 +306,8 @@
std::string actual = GetKeepSetString(set);
- EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(...); }"));
+ EXPECT_THAT(actual, HasSubstr(
+ "-keep class com.foo.Bar { <init>(android.content.Context, android.util.AttributeSet); }"));
}
} // namespace aapt
diff --git a/tools/aapt2/jni/aapt2_jni.cpp b/tools/aapt2/jni/aapt2_jni.cpp
index ad5ad4c..ba9646f 100644
--- a/tools/aapt2/jni/aapt2_jni.cpp
+++ b/tools/aapt2/jni/aapt2_jni.cpp
@@ -25,15 +25,12 @@
#include "ScopedUtfChars.h"
#include "Diagnostics.h"
+#include "cmd/Compile.h"
+#include "cmd/Link.h"
#include "util/Util.h"
using android::StringPiece;
-namespace aapt {
-extern int Compile(const std::vector<StringPiece>& args, IDiagnostics* iDiagnostics);
-extern int Link(const std::vector<StringPiece>& args, IDiagnostics* iDiagnostics);
-}
-
/*
* Converts a java List<String> into C++ vector<ScopedUtfChars>.
*/
@@ -126,7 +123,7 @@
list_to_utfchars(env, arguments_obj);
std::vector<StringPiece> compile_args = extract_pieces(compile_args_jni);
JniDiagnostics diagnostics(env, diagnostics_obj);
- return aapt::Compile(compile_args, &diagnostics);
+ return aapt::CompileCommand(&diagnostics).Execute(compile_args, &std::cerr);
}
JNIEXPORT jint JNICALL Java_com_android_tools_aapt2_Aapt2Jni_nativeLink(JNIEnv* env,
@@ -137,7 +134,7 @@
list_to_utfchars(env, arguments_obj);
std::vector<StringPiece> link_args = extract_pieces(link_args_jni);
JniDiagnostics diagnostics(env, diagnostics_obj);
- return aapt::Link(link_args, &diagnostics);
+ return aapt::LinkCommand(&diagnostics).Execute(link_args, &std::cerr);
}
JNIEXPORT void JNICALL Java_com_android_tools_aapt2_Aapt2Jni_ping(
diff --git a/tools/aapt2/optimize/MultiApkGenerator.cpp b/tools/aapt2/optimize/MultiApkGenerator.cpp
index 9cfd730..a931343 100644
--- a/tools/aapt2/optimize/MultiApkGenerator.cpp
+++ b/tools/aapt2/optimize/MultiApkGenerator.cpp
@@ -26,6 +26,7 @@
#include "ResourceUtils.h"
#include "ValueVisitor.h"
#include "configuration/ConfigurationParser.h"
+#include "cmd/Util.h"
#include "filter/AbiFilter.h"
#include "filter/Filter.h"
#include "format/Archive.h"
@@ -269,7 +270,7 @@
// Make sure the first element is <manifest> with package attribute.
xml::Element* manifest_el = manifest->root.get();
- if (manifest_el == nullptr) {
+ if (!manifest_el) {
return false;
}
@@ -278,21 +279,35 @@
return false;
}
- // Update the versionCode attribute.
- xml::Attribute* versionCode = manifest_el->FindAttribute(kSchemaAndroid, "versionCode");
- if (versionCode == nullptr) {
+ // Retrieve the versionCode attribute.
+ auto version_code = manifest_el->FindAttribute(kSchemaAndroid, "versionCode");
+ if (!version_code) {
diag->Error(DiagMessage(manifest->file.source) << "manifest must have a versionCode attribute");
return false;
}
- auto* compiled_version = ValueCast<BinaryPrimitive>(versionCode->compiled_value.get());
- if (compiled_version == nullptr) {
+ auto version_code_value = ValueCast<BinaryPrimitive>(version_code->compiled_value.get());
+ if (!version_code_value) {
diag->Error(DiagMessage(manifest->file.source) << "versionCode is invalid");
return false;
}
- int new_version = compiled_version->value.data + artifact.version;
- versionCode->compiled_value = ResourceUtils::TryParseInt(std::to_string(new_version));
+ // Retrieve the versionCodeMajor attribute.
+ auto version_code_major = manifest_el->FindAttribute(kSchemaAndroid, "versionCodeMajor");
+ BinaryPrimitive* version_code_major_value = nullptr;
+ if (version_code_major) {
+ version_code_major_value = ValueCast<BinaryPrimitive>(version_code_major->compiled_value.get());
+ if (!version_code_major_value) {
+ diag->Error(DiagMessage(manifest->file.source) << "versionCodeMajor is invalid");
+ return false;
+ }
+ }
+
+ // Calculate and set the updated version code
+ uint64_t major = (version_code_major_value)
+ ? ((uint64_t) version_code_major_value->value.data) << 32 : 0;
+ uint64_t new_version = (major | version_code_value->value.data) + artifact.version;
+ SetLongVersionCode(manifest_el, new_version);
// Check to see if the minSdkVersion needs to be updated.
if (artifact.android_sdk) {
diff --git a/tools/aapt2/util/Util.cpp b/tools/aapt2/util/Util.cpp
index d1c9ca1..9bef54e5 100644
--- a/tools/aapt2/util/Util.cpp
+++ b/tools/aapt2/util/Util.cpp
@@ -297,6 +297,53 @@
return true;
}
+std::string Utf8ToModifiedUtf8(const std::string& utf8) {
+ // Java uses Modified UTF-8 which only supports the 1, 2, and 3 byte formats of UTF-8. To encode
+ // 4 byte UTF-8 codepoints, Modified UTF-8 allows the use of surrogate pairs in the same format
+ // of CESU-8 surrogate pairs. Calculate the size of the utf8 string with all 4 byte UTF-8
+ // codepoints replaced with 2 3 byte surrogate pairs
+ size_t modified_size = 0;
+ const size_t size = utf8.size();
+ for (size_t i = 0; i < size; i++) {
+ if (((uint8_t) utf8[i] >> 4) == 0xF) {
+ modified_size += 6;
+ i += 3;
+ } else {
+ modified_size++;
+ }
+ }
+
+ // Early out if no 4 byte codepoints are found
+ if (size == modified_size) {
+ return utf8;
+ }
+
+ std::string output;
+ output.reserve(modified_size);
+ for (size_t i = 0; i < size; i++) {
+ if (((uint8_t) utf8[i] >> 4) == 0xF) {
+ auto codepoint = (char32_t) utf32_from_utf8_at(utf8.data(), size, i, nullptr);
+
+ // Calculate the high and low surrogates as UTF-16 would
+ char32_t high = ((codepoint - 0x10000) / 0x400) + 0xD800;
+ char32_t low = ((codepoint - 0x10000) % 0x400) + 0xDC00;
+
+ // Encode each surrogate in UTF-8
+ output.push_back((char) (0xE4 | ((high >> 12) & 0xF)));
+ output.push_back((char) (0x80 | ((high >> 6) & 0x3F)));
+ output.push_back((char) (0x80 | (high & 0x3F)));
+ output.push_back((char) (0xE4 | ((low >> 12) & 0xF)));
+ output.push_back((char) (0x80 | ((low >> 6) & 0x3F)));
+ output.push_back((char) (0x80 | (low & 0x3F)));
+ i += 3;
+ } else {
+ output.push_back(utf8[i]);
+ }
+ }
+
+ return output;
+}
+
std::u16string Utf8ToUtf16(const StringPiece& utf8) {
ssize_t utf16_length = utf8_to_utf16_length(
reinterpret_cast<const uint8_t*>(utf8.data()), utf8.length());
diff --git a/tools/aapt2/util/Util.h b/tools/aapt2/util/Util.h
index 0eb35d1..36b7333 100644
--- a/tools/aapt2/util/Util.h
+++ b/tools/aapt2/util/Util.h
@@ -197,6 +197,9 @@
return error_.empty();
}
+// Converts a UTF8 string into Modified UTF8
+std::string Utf8ToModifiedUtf8(const std::string& utf8);
+
// Converts a UTF8 string to a UTF16 string.
std::u16string Utf8ToUtf16(const android::StringPiece& utf8);
std::string Utf16ToUtf8(const android::StringPiece16& utf16);
diff --git a/tools/aapt2/xml/XmlPullParser.cpp b/tools/aapt2/xml/XmlPullParser.cpp
index 402e5a4..a023494 100644
--- a/tools/aapt2/xml/XmlPullParser.cpp
+++ b/tools/aapt2/xml/XmlPullParser.cpp
@@ -38,6 +38,7 @@
EndNamespaceHandler);
XML_SetCharacterDataHandler(parser_, CharacterDataHandler);
XML_SetCommentHandler(parser_, CommentDataHandler);
+ XML_SetCdataSectionHandler(parser_, StartCdataSectionHandler, EndCdataSectionHandler);
event_queue_.push(EventData{Event::kStartDocument, 0, depth_++});
}
@@ -287,6 +288,22 @@
parser->depth_, comment});
}
+void XMLCALL XmlPullParser::StartCdataSectionHandler(void* user_data) {
+ XmlPullParser* parser = reinterpret_cast<XmlPullParser*>(user_data);
+
+ parser->event_queue_.push(EventData{Event::kCdataStart,
+ XML_GetCurrentLineNumber(parser->parser_),
+ parser->depth_ });
+}
+
+void XMLCALL XmlPullParser::EndCdataSectionHandler(void* user_data) {
+ XmlPullParser* parser = reinterpret_cast<XmlPullParser*>(user_data);
+
+ parser->event_queue_.push(EventData{Event::kCdataEnd,
+ XML_GetCurrentLineNumber(parser->parser_),
+ parser->depth_ });
+}
+
Maybe<StringPiece> FindAttribute(const XmlPullParser* parser,
const StringPiece& name) {
auto iter = parser->FindAttribute("", name);
diff --git a/tools/aapt2/xml/XmlPullParser.h b/tools/aapt2/xml/XmlPullParser.h
index 63db66f..6ebaa28 100644
--- a/tools/aapt2/xml/XmlPullParser.h
+++ b/tools/aapt2/xml/XmlPullParser.h
@@ -52,6 +52,8 @@
kEndElement,
kText,
kComment,
+ kCdataStart,
+ kCdataEnd,
};
/**
@@ -159,6 +161,8 @@
static void XMLCALL EndElementHandler(void* user_data, const char* name);
static void XMLCALL EndNamespaceHandler(void* user_data, const char* prefix);
static void XMLCALL CommentDataHandler(void* user_data, const char* comment);
+ static void XMLCALL StartCdataSectionHandler(void* user_data);
+ static void XMLCALL EndCdataSectionHandler(void* user_data);
struct EventData {
Event event;
@@ -223,6 +227,10 @@
return out << "Text";
case XmlPullParser::Event::kComment:
return out << "Comment";
+ case XmlPullParser::Event::kCdataStart:
+ return out << "CdataStart";
+ case XmlPullParser::Event::kCdataEnd:
+ return out << "CdataEnd";
}
return out;
}
@@ -240,6 +248,8 @@
case Event::kText:
case Event::kComment:
case Event::kStartElement:
+ case Event::kCdataStart:
+ case Event::kCdataEnd:
return true;
default:
break;
diff --git a/tools/apilint/apilint.py b/tools/apilint/apilint.py
index 9db3f02..018e9c9 100644
--- a/tools/apilint/apilint.py
+++ b/tools/apilint/apilint.py
@@ -69,12 +69,19 @@
self.raw = raw.strip(" {;")
self.blame = blame
+ # drop generics for now; may need multiple passes
+ raw = re.sub("<[^<]+?>", "", raw)
+ raw = re.sub("<[^<]+?>", "", raw)
+
raw = raw.split()
self.split = list(raw)
for r in ["field", "volatile", "transient", "public", "protected", "static", "final", "deprecated"]:
while r in raw: raw.remove(r)
+ # ignore annotations for now
+ raw = [ r for r in raw if not r.startswith("@") ]
+
self.typ = raw[0]
self.name = raw[1].strip(";")
if len(raw) >= 4 and raw[2] == "=":
@@ -97,25 +104,39 @@
self.raw = raw.strip(" {;")
self.blame = blame
- # drop generics for now
- raw = re.sub("<.+?>", "", raw)
+ # drop generics for now; may need multiple passes
+ raw = re.sub("<[^<]+?>", "", raw)
+ raw = re.sub("<[^<]+?>", "", raw)
- raw = re.split("[\s(),;]+", raw)
+ # handle each clause differently
+ raw_prefix, raw_args, _, raw_throws = re.match(r"(.*?)\((.*?)\)( throws )?(.*?);$", raw).groups()
+
+ # parse prefixes
+ raw = re.split("[\s]+", raw_prefix)
for r in ["", ";"]:
while r in raw: raw.remove(r)
self.split = list(raw)
- for r in ["method", "public", "protected", "static", "final", "deprecated", "abstract", "default"]:
+ for r in ["method", "public", "protected", "static", "final", "deprecated", "abstract", "default", "operator"]:
while r in raw: raw.remove(r)
self.typ = raw[0]
self.name = raw[1]
+
+ # parse args
self.args = []
+ for arg in re.split(",\s*", raw_args):
+ arg = re.split("\s", arg)
+ # ignore annotations for now
+ arg = [ a for a in arg if not a.startswith("@") ]
+ if len(arg[0]) > 0:
+ self.args.append(arg[0])
+
+ # parse throws
self.throws = []
- target = self.args
- for r in raw[2:]:
- if r == "throws": target = self.throws
- else: target.append(r)
+ for throw in re.split(",\s*", raw_throws):
+ self.throws.append(throw)
+
self.ident = ident(self.raw)
def __hash__(self):
@@ -135,12 +156,18 @@
self.fields = []
self.methods = []
+ # drop generics for now; may need multiple passes
+ raw = re.sub("<[^<]+?>", "", raw)
+ raw = re.sub("<[^<]+?>", "", raw)
+
raw = raw.split()
self.split = list(raw)
if "class" in raw:
self.fullname = raw[raw.index("class")+1]
elif "interface" in raw:
self.fullname = raw[raw.index("interface")+1]
+ elif "@interface" in raw:
+ self.fullname = raw[raw.index("@interface")+1]
else:
raise ValueError("Funky class type %s" % (self.raw))
diff --git a/tools/fonts/fontchain_linter.py b/tools/fonts/fontchain_linter.py
index a8411aa..ce9becd 100755
--- a/tools/fonts/fontchain_linter.py
+++ b/tools/fonts/fontchain_linter.py
@@ -524,9 +524,12 @@
0xFE837: (ord('0'), COMBINING_KEYCAP),
}
+# This is used to define the emoji that should have the same glyph.
+# i.e. previously we had gender based Kiss (0x1F48F), which had the same glyph
+# with Kiss: Woman, Man (0x1F469, 0x200D, 0x2764, 0x200D, 0x1F48B, 0x200D, 0x1F468)
+# in that case a valid row would be:
+# (0x1F469, 0x200D, 0x2764, 0x200D, 0x1F48B, 0x200D, 0x1F468): 0x1F48F,
ZWJ_IDENTICALS = {
- # KISS
- (0x1F469, 0x200D, 0x2764, 0x200D, 0x1F48B, 0x200D, 0x1F468): 0x1F48F,
}
SAME_FLAG_MAPPINGS = [
diff --git a/tools/incident_section_gen/main.cpp b/tools/incident_section_gen/main.cpp
index 1f77719..639f980 100644
--- a/tools/incident_section_gen/main.cpp
+++ b/tools/incident_section_gen/main.cpp
@@ -110,9 +110,7 @@
N = descriptor->field_count();
for (int i=0; i<N; i++) {
const FieldDescriptor* field = descriptor->field(i);
- if (field->type() == FieldDescriptor::TYPE_MESSAGE) {
- sections[field->name()] = field;
- }
+ sections[field->name()] = field;
}
printf("IncidentSection const INCIDENT_SECTIONS[] = {\n");
@@ -404,7 +402,7 @@
for (int i=0; i<descriptor->field_count(); i++) {
const FieldDescriptor* field = descriptor->field(i);
- if (field->type() != FieldDescriptor::TYPE_MESSAGE) {
+ if (field->type() != FieldDescriptor::TYPE_MESSAGE && field->type() != FieldDescriptor::TYPE_STRING) {
continue;
}
const SectionFlags s = getSectionFlags(field);
@@ -456,13 +454,13 @@
const FieldDescriptor* field = fieldsInOrder[i];
const string fieldName = getFieldName(field);
const Destination fieldDest = getFieldDest(field);
- const string fieldMessageName = getMessageName(field->message_type(), fieldDest);
-
- skip[i] = true;
-
if (field->type() != FieldDescriptor::TYPE_MESSAGE) {
+ printPrivacy(fieldName, field, "NULL", fieldDest, "NULL");
continue;
}
+
+ skip[i] = true;
+ const string fieldMessageName = getMessageName(field->message_type(), fieldDest);
// generate privacy flags for each section.
if (generatePrivacyFlags(field->message_type(), fieldDest, variableNames, &parents)) {
printPrivacy(fieldName, field, fieldMessageName, fieldDest, "NULL");
diff --git a/vr/Android.bp b/vr/Android.bp
index b5904d6..775ec96 100644
--- a/vr/Android.bp
+++ b/vr/Android.bp
@@ -26,6 +26,7 @@
// Java platform library for vr stuff.
java_library {
name: "com.google.vr.platform",
+ installable: true,
owner: "google",
required: [
"libdvr_loader",
diff --git a/wifi/OWNERS b/wifi/OWNERS
index 0efa464..0601047 100644
--- a/wifi/OWNERS
+++ b/wifi/OWNERS
@@ -1,5 +1,6 @@
set noparent
etancohen@google.com
+mplass@google.com
+rpius@google.com
satk@google.com
-silberst@google.com
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 66ccc6c..af44b7e 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -147,6 +147,8 @@
boolean setWifiApConfiguration(in WifiConfiguration wifiConfig, String packageName);
+ void notifyUserOfApBandConversion(String packageName);
+
Messenger getWifiServiceMessenger(String packageName);
void enableTdls(String remoteIPAddress, boolean enable);
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 25f35d0..6963ed0 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -2170,6 +2170,21 @@
}
/**
+ * Method that triggers a notification to the user about a conversion to their saved AP config.
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
+ public void notifyUserOfApBandConversion() {
+ Log.d(TAG, "apBand was converted, notify the user");
+ try {
+ mService.notifyUserOfApBandConversion(mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Enable/Disable TDLS on a specific local route.
*
* <p>
diff --git a/wifi/java/android/net/wifi/hotspot2/omadm/PpsMoParser.java b/wifi/java/android/net/wifi/hotspot2/omadm/PpsMoParser.java
index f6183fa..984cf7d 100644
--- a/wifi/java/android/net/wifi/hotspot2/omadm/PpsMoParser.java
+++ b/wifi/java/android/net/wifi/hotspot2/omadm/PpsMoParser.java
@@ -136,7 +136,7 @@
private static final String NODE_UPDATE_IDENTIFIER = "UpdateIdentifier";
private static final String NODE_AAA_SERVER_TRUST_ROOT = "AAAServerTrustRoot";
private static final String NODE_SUBSCRIPTION_UPDATE = "SubscriptionUpdate";
- private static final String NODE_SUBSCRIPTION_PARAMETER = "SubscriptionParameter";
+ private static final String NODE_SUBSCRIPTION_PARAMETER = "SubscriptionParameters";
private static final String NODE_TYPE_OF_SUBSCRIPTION = "TypeOfSubscription";
private static final String NODE_USAGE_LIMITS = "UsageLimits";
private static final String NODE_DATA_LIMIT = "DataLimit";
diff --git a/wifi/tests/assets/pps/PerProviderSubscription.xml b/wifi/tests/assets/pps/PerProviderSubscription.xml
index 1fb8309..e4472ce 100644
--- a/wifi/tests/assets/pps/PerProviderSubscription.xml
+++ b/wifi/tests/assets/pps/PerProviderSubscription.xml
@@ -368,7 +368,7 @@
</Node>
</Node>
<Node>
- <NodeName>SubscriptionParameter</NodeName>
+ <NodeName>SubscriptionParameters</NodeName>
<Node>
<NodeName>CreationDate</NodeName>
<Value>2016-02-01T10:00:00Z</Value>