summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apct-tests/perftests/core/src/android/text/MeasuredTextMemoryUsageTest.java (renamed from apct-tests/perftests/core/src/android/text/PrecomputedTextMemoryUsageTest.java)56
-rw-r--r--apct-tests/perftests/core/src/android/text/MeasuredTextPerfTest.java (renamed from apct-tests/perftests/core/src/android/text/PrecomputedTextPerfTest.java)84
-rw-r--r--apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java108
-rw-r--r--api/current.txt61
-rw-r--r--cmds/statsd/src/metrics/CountMetricProducer.cpp3
-rw-r--r--cmds/statsd/src/metrics/DurationMetricProducer.cpp3
-rw-r--r--cmds/statsd/src/metrics/GaugeMetricProducer.cpp2
-rw-r--r--cmds/statsd/src/metrics/MetricProducer.h5
-rw-r--r--cmds/statsd/src/metrics/ValueMetricProducer.cpp2
-rw-r--r--cmds/statsd/src/stats_log_util.cpp11
-rw-r--r--cmds/statsd/src/stats_log_util.h6
-rw-r--r--cmds/statsd/tests/e2e/Attribution_e2e_test.cpp4
-rw-r--r--cmds/statsd/tests/e2e/DimensionInCondition_e2e_test.cpp10
-rw-r--r--cmds/statsd/tests/e2e/GaugeMetric_e2e_test.cpp4
-rw-r--r--cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp4
-rw-r--r--cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp4
-rw-r--r--cmds/statsd/tests/metrics/CountMetricProducer_test.cpp8
-rw-r--r--cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp9
-rw-r--r--cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp5
-rw-r--r--cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp10
-rw-r--r--core/java/android/app/ActivityOptions.java5
-rw-r--r--core/java/android/app/IActivityManager.aidl8
-rw-r--r--core/java/android/app/servertransaction/ActivityLifecycleItem.java5
-rw-r--r--core/java/android/app/servertransaction/DestroyActivityItem.java1
-rw-r--r--core/java/android/app/servertransaction/PauseActivityItem.java1
-rw-r--r--core/java/android/app/servertransaction/ResumeActivityItem.java1
-rw-r--r--core/java/android/app/servertransaction/StopActivityItem.java1
-rw-r--r--core/java/android/hardware/OWNERS7
-rw-r--r--core/java/android/hardware/camera2/OWNERS6
-rw-r--r--core/java/android/net/IpSecConfig.java19
-rw-r--r--core/java/android/net/IpSecTransform.java18
-rw-r--r--core/java/android/os/IUserManager.aidl1
-rw-r--r--core/java/android/os/UserManager.java12
-rw-r--r--core/java/android/provider/Settings.java31
-rw-r--r--core/java/android/security/keystore/OWNERS4
-rw-r--r--core/java/android/service/autofill/AutofillServiceInfo.java20
-rw-r--r--core/java/android/text/BoringLayout.java19
-rw-r--r--core/java/android/text/DynamicLayout.java2
-rw-r--r--core/java/android/text/Layout.java11
-rw-r--r--core/java/android/text/MeasuredText.java427
-rw-r--r--core/java/android/text/PrecomputedText.java412
-rw-r--r--core/java/android/text/StaticLayout.java84
-rw-r--r--core/java/android/text/TextLine.java42
-rw-r--r--core/java/android/util/LongArray.java17
-rw-r--r--core/java/android/view/RecordingCanvas.java8
-rw-r--r--core/java/android/view/RenderNodeAnimator.java2
-rw-r--r--core/java/android/view/accessibility/AccessibilityNodeInfo.java2
-rw-r--r--core/java/android/widget/TextView.java101
-rw-r--r--core/jni/OWNERS7
-rw-r--r--core/jni/android/graphics/Paint.cpp20
-rw-r--r--core/res/res/values/config.xml1
-rw-r--r--core/res/res/values/strings.xml2
-rw-r--r--core/res/res/values/symbols.xml1
-rw-r--r--core/tests/coretests/src/android/provider/SettingsBackupTest.java1
-rw-r--r--graphics/java/android/graphics/BaseCanvas.java8
-rw-r--r--graphics/java/android/graphics/Paint.java12
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/Utils.java37
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java103
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java322
-rw-r--r--packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java32
-rw-r--r--packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java66
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java12
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java3
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java13
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl5
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/NavigationBarCompat.java29
-rw-r--r--packages/SystemUI/src/com/android/systemui/OverviewProxyService.java24
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java46
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickScrubController.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java67
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java22
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java19
-rw-r--r--proto/src/metrics_constants.proto16
-rw-r--r--services/autofill/java/com/android/server/autofill/AutofillManagerService.java120
-rw-r--r--services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java26
-rw-r--r--services/core/java/com/android/server/InputMethodManagerService.java50
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java23
-rw-r--r--services/core/java/com/android/server/am/ActivityRecord.java8
-rw-r--r--services/core/java/com/android/server/am/ActivityStack.java24
-rw-r--r--services/core/java/com/android/server/am/ActivityStackSupervisor.java6
-rw-r--r--services/core/java/com/android/server/am/ActivityStartController.java14
-rw-r--r--services/core/java/com/android/server/am/ActivityStarter.java2
-rw-r--r--services/core/java/com/android/server/am/PendingRemoteAnimationRegistry.java86
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java102
-rw-r--r--services/core/java/com/android/server/locksettings/LockSettingsStrongAuth.java44
-rw-r--r--services/core/java/com/android/server/locksettings/recoverablekeystore/OWNERS4
-rw-r--r--services/core/java/com/android/server/net/NetworkPolicyManagerService.java8
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java17
-rw-r--r--services/core/java/com/android/server/pm/UserRestrictionsUtils.java10
-rw-r--r--services/tests/servicestests/src/com/android/server/am/PendingRemoteAnimationRegistryTest.java103
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/OWNERS4
-rw-r--r--tests/net/java/android/net/IpSecConfigTest.java43
-rw-r--r--tests/net/java/android/net/IpSecTransformTest.java61
-rw-r--r--tools/aapt2/Debug.cpp59
-rw-r--r--tools/aapt2/Debug.h2
-rw-r--r--tools/aapt2/LoadedApk.cpp4
-rw-r--r--tools/aapt2/ResourceUtils.cpp4
-rw-r--r--tools/aapt2/ResourceUtils.h3
-rw-r--r--tools/aapt2/cmd/Dump.cpp90
-rw-r--r--tools/aapt2/configuration/ConfigurationParser.cpp80
-rw-r--r--tools/aapt2/configuration/ConfigurationParser.internal.h34
-rw-r--r--tools/aapt2/configuration/ConfigurationParser_test.cpp129
-rw-r--r--tools/aapt2/configuration/aapt2.xsd5
-rw-r--r--tools/aapt2/configuration/example/config.xml24
-rw-r--r--tools/aapt2/optimize/MultiApkGenerator.cpp46
-rw-r--r--tools/aapt2/optimize/MultiApkGenerator.h5
109 files changed, 2194 insertions, 1620 deletions
diff --git a/apct-tests/perftests/core/src/android/text/PrecomputedTextMemoryUsageTest.java b/apct-tests/perftests/core/src/android/text/MeasuredTextMemoryUsageTest.java
index 73e17242ae78..fc6302ea9394 100644
--- a/apct-tests/perftests/core/src/android/text/PrecomputedTextMemoryUsageTest.java
+++ b/apct-tests/perftests/core/src/android/text/MeasuredTextMemoryUsageTest.java
@@ -45,7 +45,7 @@ import java.util.Random;
@LargeTest
@RunWith(AndroidJUnit4.class)
-public class PrecomputedTextMemoryUsageTest {
+public class MeasuredTextMemoryUsageTest {
private static final int WORD_LENGTH = 9; // Random word has 9 characters.
private static final boolean NO_STYLE_TEXT = false;
@@ -53,7 +53,7 @@ public class PrecomputedTextMemoryUsageTest {
private static int TRIAL_COUNT = 100;
- public PrecomputedTextMemoryUsageTest() {}
+ public MeasuredTextMemoryUsageTest() {}
private TextPerfUtils mTextUtil = new TextPerfUtils();
@@ -77,16 +77,13 @@ public class PrecomputedTextMemoryUsageTest {
@Test
public void testMemoryUsage_NoHyphenation() {
int[] memories = new int[TRIAL_COUNT];
- final PrecomputedText.Params param = new PrecomputedText.Params.Builder(PAINT)
+ // Report median of randomly generated MeasuredText.
+ for (int i = 0; i < TRIAL_COUNT; ++i) {
+ memories[i] = new MeasuredText.Builder(
+ mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT)
.setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
- .build();
-
- // Report median of randomly generated PrecomputedText.
- for (int i = 0; i < TRIAL_COUNT; ++i) {
- memories[i] = PrecomputedText.create(
- mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), param)
- .getMemoryUsage();
+ .build().getMemoryUsage();
}
reportMemoryUsage(median(memories), "MemoryUsage_NoHyphenation");
}
@@ -94,16 +91,13 @@ public class PrecomputedTextMemoryUsageTest {
@Test
public void testMemoryUsage_Hyphenation() {
int[] memories = new int[TRIAL_COUNT];
- final PrecomputedText.Params param = new PrecomputedText.Params.Builder(PAINT)
+ // Report median of randomly generated MeasuredText.
+ for (int i = 0; i < TRIAL_COUNT; ++i) {
+ memories[i] = new MeasuredText.Builder(
+ mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT)
.setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED)
.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
- .build();
-
- // Report median of randomly generated PrecomputedText.
- for (int i = 0; i < TRIAL_COUNT; ++i) {
- memories[i] = PrecomputedText.create(
- mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), param)
- .getMemoryUsage();
+ .build().getMemoryUsage();
}
reportMemoryUsage(median(memories), "MemoryUsage_Hyphenation");
}
@@ -111,16 +105,13 @@ public class PrecomputedTextMemoryUsageTest {
@Test
public void testMemoryUsage_NoHyphenation_WidthOnly() {
int[] memories = new int[TRIAL_COUNT];
- final PrecomputedText.Params param = new PrecomputedText.Params.Builder(PAINT)
+ // Report median of randomly generated MeasuredText.
+ for (int i = 0; i < TRIAL_COUNT; ++i) {
+ memories[i] = new MeasuredText.Builder(
+ mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT)
.setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
- .build();
-
- // Report median of randomly generated PrecomputedText.
- for (int i = 0; i < TRIAL_COUNT; ++i) {
- CharSequence cs = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
- memories[i] = PrecomputedText.createWidthOnly(cs, param, 0, cs.length())
- .getMemoryUsage();
+ .build(false /* width only */).getMemoryUsage();
}
reportMemoryUsage(median(memories), "MemoryUsage_NoHyphenation_WidthOnly");
}
@@ -128,16 +119,13 @@ public class PrecomputedTextMemoryUsageTest {
@Test
public void testMemoryUsage_Hyphenatation_WidthOnly() {
int[] memories = new int[TRIAL_COUNT];
- final PrecomputedText.Params param = new PrecomputedText.Params.Builder(PAINT)
+ // Report median of randomly generated MeasuredText.
+ for (int i = 0; i < TRIAL_COUNT; ++i) {
+ memories[i] = new MeasuredText.Builder(
+ mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT)
.setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED)
.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
- .build();
-
- // Report median of randomly generated PrecomputedText.
- for (int i = 0; i < TRIAL_COUNT; ++i) {
- CharSequence cs = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
- memories[i] = PrecomputedText.createWidthOnly(cs, param, 0, cs.length())
- .getMemoryUsage();
+ .build(false /* width only */).getMemoryUsage();
}
reportMemoryUsage(median(memories), "MemoryUsage_Hyphenation_WidthOnly");
}
diff --git a/apct-tests/perftests/core/src/android/text/PrecomputedTextPerfTest.java b/apct-tests/perftests/core/src/android/text/MeasuredTextPerfTest.java
index 1cd0ae13069b..98f2bd5e5736 100644
--- a/apct-tests/perftests/core/src/android/text/PrecomputedTextPerfTest.java
+++ b/apct-tests/perftests/core/src/android/text/MeasuredTextPerfTest.java
@@ -42,7 +42,7 @@ import java.util.Random;
@LargeTest
@RunWith(AndroidJUnit4.class)
-public class PrecomputedTextPerfTest {
+public class MeasuredTextPerfTest {
private static final int WORD_LENGTH = 9; // Random word has 9 characters.
private static final int WORDS_IN_LINE = 8; // Roughly, 8 words in a line.
private static final boolean NO_STYLE_TEXT = false;
@@ -51,7 +51,7 @@ public class PrecomputedTextPerfTest {
private static TextPaint PAINT = new TextPaint();
private static final int TEXT_WIDTH = WORDS_IN_LINE * WORD_LENGTH * (int) PAINT.getTextSize();
- public PrecomputedTextPerfTest() {}
+ public MeasuredTextPerfTest() {}
@Rule
public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
@@ -66,136 +66,120 @@ public class PrecomputedTextPerfTest {
@Test
public void testCreate_NoStyled_Hyphenation() {
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
- final PrecomputedText.Params param = new PrecomputedText.Params.Builder(PAINT)
- .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED)
- .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
- .build();
-
while (state.keepRunning()) {
state.pauseTiming();
final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
state.resumeTiming();
- PrecomputedText.create(text, param);
+ new MeasuredText.Builder(text, PAINT)
+ .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED)
+ .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
+ .build(true /* do full layout */);
}
}
@Test
public void testCreate_NoStyled_NoHyphenation() {
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
- final PrecomputedText.Params param = new PrecomputedText.Params.Builder(PAINT)
- .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
- .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
- .build();
-
while (state.keepRunning()) {
state.pauseTiming();
final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
state.resumeTiming();
- PrecomputedText.create(text, param);
+ new MeasuredText.Builder(text, PAINT)
+ .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
+ .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
+ .build(true /* do full layout */);
}
}
@Test
public void testCreate_NoStyled_Hyphenation_WidthOnly() {
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
- final PrecomputedText.Params param = new PrecomputedText.Params.Builder(PAINT)
- .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED)
- .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
- .build();
-
while (state.keepRunning()) {
state.pauseTiming();
final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
state.resumeTiming();
- PrecomputedText.create(text, param);
+ new MeasuredText.Builder(text, PAINT)
+ .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED)
+ .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
+ .build(false /* width only */);
}
}
@Test
public void testCreate_NoStyled_NoHyphenation_WidthOnly() {
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
- final PrecomputedText.Params param = new PrecomputedText.Params.Builder(PAINT)
- .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
- .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
- .build();
-
while (state.keepRunning()) {
state.pauseTiming();
final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
state.resumeTiming();
- PrecomputedText.create(text, param);
+ new MeasuredText.Builder(text, PAINT)
+ .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
+ .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
+ .build(false /* width only */);
}
}
@Test
public void testCreate_Styled_Hyphenation() {
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
- final PrecomputedText.Params param = new PrecomputedText.Params.Builder(PAINT)
- .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED)
- .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
- .build();
-
while (state.keepRunning()) {
state.pauseTiming();
final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT);
state.resumeTiming();
- PrecomputedText.create(text, param);
+ new MeasuredText.Builder(text, PAINT)
+ .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED)
+ .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
+ .build(true /* do full layout */);
}
}
@Test
public void testCreate_Styled_NoHyphenation() {
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
- final PrecomputedText.Params param = new PrecomputedText.Params.Builder(PAINT)
- .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
- .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
- .build();
-
while (state.keepRunning()) {
state.pauseTiming();
final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT);
state.resumeTiming();
- PrecomputedText.create(text, param);
+ new MeasuredText.Builder(text, PAINT)
+ .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
+ .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
+ .build(true /* do full layout */);
}
}
@Test
public void testCreate_Styled_Hyphenation_WidthOnly() {
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
- final PrecomputedText.Params param = new PrecomputedText.Params.Builder(PAINT)
- .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED)
- .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
- .build();
-
while (state.keepRunning()) {
state.pauseTiming();
final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT);
state.resumeTiming();
- PrecomputedText.create(text, param);
+ new MeasuredText.Builder(text, PAINT)
+ .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED)
+ .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
+ .build(false /* width only */);
}
}
@Test
public void testCreate_Styled_NoHyphenation_WidthOnly() {
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
- final PrecomputedText.Params param = new PrecomputedText.Params.Builder(PAINT)
- .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
- .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
- .build();
-
while (state.keepRunning()) {
state.pauseTiming();
final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT);
state.resumeTiming();
- PrecomputedText.create(text, param);
+ new MeasuredText.Builder(text, PAINT)
+ .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
+ .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
+ .build(false /* width only */);
}
}
}
diff --git a/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java b/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java
index 8823af1df350..231aaf2ca074 100644
--- a/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java
+++ b/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java
@@ -63,18 +63,6 @@ public class StaticLayoutPerfTest {
mTextUtil.resetRandom(0 /* seed */);
}
- private PrecomputedText makeMeasured(CharSequence text, TextPaint paint) {
- PrecomputedText.Params param = new PrecomputedText.Params.Builder(paint).build();
- return PrecomputedText.create(text, param);
- }
-
- private PrecomputedText makeMeasured(CharSequence text, TextPaint paint, int strategy,
- int frequency) {
- PrecomputedText.Params param = new PrecomputedText.Params.Builder(paint)
- .setHyphenationFrequency(frequency).setBreakStrategy(strategy).build();
- return PrecomputedText.create(text, param);
- }
-
@Test
public void testCreate_FixedText_NoStyle_Greedy_NoHyphenation() {
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
@@ -163,16 +151,18 @@ public class StaticLayoutPerfTest {
}
@Test
- public void testCreate_PrecomputedText_NoStyled_Greedy_NoHyphenation() {
+ public void testCreate_MeasuredText_NoStyled_Greedy_NoHyphenation() {
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
while (state.keepRunning()) {
state.pauseTiming();
- final PrecomputedText text = makeMeasured(
- mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT,
- Layout.BREAK_STRATEGY_SIMPLE, Layout.HYPHENATION_FREQUENCY_NONE);
+ final MeasuredText text = new MeasuredText.Builder(
+ mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT)
+ .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
+ .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
+ .build();
state.resumeTiming();
- StaticLayout.Builder.obtain(text, 0, text.getText().length(), PAINT, TEXT_WIDTH)
+ StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH)
.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
.setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
.build();
@@ -180,16 +170,18 @@ public class StaticLayoutPerfTest {
}
@Test
- public void testCreate_PrecomputedText_NoStyled_Greedy_Hyphenation() {
+ public void testCreate_MeasuredText_NoStyled_Greedy_Hyphenation() {
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
while (state.keepRunning()) {
state.pauseTiming();
- final PrecomputedText text = makeMeasured(
- mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT,
- Layout.BREAK_STRATEGY_SIMPLE, Layout.HYPHENATION_FREQUENCY_NORMAL);
+ final MeasuredText text = new MeasuredText.Builder(
+ mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT)
+ .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
+ .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
+ .build();
state.resumeTiming();
- StaticLayout.Builder.obtain(text, 0, text.getText().length(), PAINT, TEXT_WIDTH)
+ StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH)
.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
.setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
.build();
@@ -197,16 +189,18 @@ public class StaticLayoutPerfTest {
}
@Test
- public void testCreate_PrecomputedText_NoStyled_Balanced_NoHyphenation() {
+ public void testCreate_MeasuredText_NoStyled_Balanced_NoHyphenation() {
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
while (state.keepRunning()) {
state.pauseTiming();
- final PrecomputedText text = makeMeasured(
- mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT,
- Layout.BREAK_STRATEGY_BALANCED, Layout.HYPHENATION_FREQUENCY_NONE);
+ final MeasuredText text = new MeasuredText.Builder(
+ mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT)
+ .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED)
+ .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
+ .build();
state.resumeTiming();
- StaticLayout.Builder.obtain(text, 0, text.getText().length(), PAINT, TEXT_WIDTH)
+ StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH)
.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
.setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED)
.build();
@@ -214,16 +208,18 @@ public class StaticLayoutPerfTest {
}
@Test
- public void testCreate_PrecomputedText_NoStyled_Balanced_Hyphenation() {
+ public void testCreate_MeasuredText_NoStyled_Balanced_Hyphenation() {
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
while (state.keepRunning()) {
state.pauseTiming();
- final PrecomputedText text = makeMeasured(
- mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT,
- Layout.BREAK_STRATEGY_BALANCED, Layout.HYPHENATION_FREQUENCY_NORMAL);
+ final MeasuredText text = new MeasuredText.Builder(
+ mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT)
+ .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED)
+ .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
+ .build();
state.resumeTiming();
- StaticLayout.Builder.obtain(text, 0, text.getText().length(), PAINT, TEXT_WIDTH)
+ StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH)
.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
.setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED)
.build();
@@ -231,16 +227,18 @@ public class StaticLayoutPerfTest {
}
@Test
- public void testCreate_PrecomputedText_Styled_Greedy_NoHyphenation() {
+ public void testCreate_MeasuredText_Styled_Greedy_NoHyphenation() {
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
while (state.keepRunning()) {
state.pauseTiming();
- final PrecomputedText text = makeMeasured(
- mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT), PAINT,
- Layout.BREAK_STRATEGY_SIMPLE, Layout.HYPHENATION_FREQUENCY_NONE);
+ final MeasuredText text = new MeasuredText.Builder(
+ mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT), PAINT)
+ .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
+ .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
+ .build();
state.resumeTiming();
- StaticLayout.Builder.obtain(text, 0, text.getText().length(), PAINT, TEXT_WIDTH)
+ StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH)
.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
.setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
.build();
@@ -330,16 +328,15 @@ public class StaticLayoutPerfTest {
}
@Test
- public void testDraw_PrecomputedText_Styled() {
+ public void testDraw_MeasuredText_Styled() {
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
final RenderNode node = RenderNode.create("benchmark", null);
while (state.keepRunning()) {
state.pauseTiming();
- final PrecomputedText text = makeMeasured(
- mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT), PAINT);
+ final MeasuredText text = new MeasuredText.Builder(
+ mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT), PAINT).build();
final StaticLayout layout =
- StaticLayout.Builder.obtain(
- text, 0, text.getText().length(), PAINT, TEXT_WIDTH).build();
+ StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
final DisplayListCanvas c = node.start(1200, 200);
state.resumeTiming();
@@ -348,16 +345,15 @@ public class StaticLayoutPerfTest {
}
@Test
- public void testDraw_PrecomputedText_NoStyled() {
+ public void testDraw_MeasuredText_NoStyled() {
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
final RenderNode node = RenderNode.create("benchmark", null);
while (state.keepRunning()) {
state.pauseTiming();
- final PrecomputedText text = makeMeasured(
- mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT);
+ final MeasuredText text = new MeasuredText.Builder(
+ mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT).build();
final StaticLayout layout =
- StaticLayout.Builder.obtain(
- text, 0, text.getText().length(), PAINT, TEXT_WIDTH).build();
+ StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
final DisplayListCanvas c = node.start(1200, 200);
state.resumeTiming();
@@ -366,16 +362,15 @@ public class StaticLayoutPerfTest {
}
@Test
- public void testDraw_PrecomputedText_Styled_WithoutCache() {
+ public void testDraw_MeasuredText_Styled_WithoutCache() {
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
final RenderNode node = RenderNode.create("benchmark", null);
while (state.keepRunning()) {
state.pauseTiming();
- final PrecomputedText text = makeMeasured(
- mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT), PAINT);
+ final MeasuredText text = new MeasuredText.Builder(
+ mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT), PAINT).build();
final StaticLayout layout =
- StaticLayout.Builder.obtain(
- text, 0, text.getText().length(), PAINT, TEXT_WIDTH).build();
+ StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
final DisplayListCanvas c = node.start(1200, 200);
Canvas.freeTextLayoutCaches();
state.resumeTiming();
@@ -385,16 +380,15 @@ public class StaticLayoutPerfTest {
}
@Test
- public void testDraw_PrecomputedText_NoStyled_WithoutCache() {
+ public void testDraw_MeasuredText_NoStyled_WithoutCache() {
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
final RenderNode node = RenderNode.create("benchmark", null);
while (state.keepRunning()) {
state.pauseTiming();
- final PrecomputedText text = makeMeasured(
- mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT);
+ final MeasuredText text = new MeasuredText.Builder(
+ mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT).build();
final StaticLayout layout =
- StaticLayout.Builder.obtain(
- text, 0, text.getText().length(), PAINT, TEXT_WIDTH).build();
+ StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
final DisplayListCanvas c = node.start(1200, 200);
Canvas.freeTextLayoutCaches();
state.resumeTiming();
diff --git a/api/current.txt b/api/current.txt
index 7a9108e592a1..3963f3ac0c43 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -13839,7 +13839,6 @@ package android.graphics {
method public int breakText(java.lang.String, boolean, float, float[]);
method public void clearShadowLayer();
method public float descent();
- method public boolean equalsForTextMeasurement(android.graphics.Paint);
method public int getAlpha();
method public int getColor();
method public android.graphics.ColorFilter getColorFilter();
@@ -43229,6 +43228,36 @@ package android.text {
method public boolean isAllowed(char);
}
+ public class MeasuredText implements android.text.Spanned {
+ method public char charAt(int);
+ method public int getBreakStrategy();
+ method public int getEnd();
+ method public int getHyphenationFrequency();
+ method public android.text.TextPaint getPaint();
+ method public int getParagraphCount();
+ method public int getParagraphEnd(int);
+ method public int getParagraphStart(int);
+ method public int getSpanEnd(java.lang.Object);
+ method public int getSpanFlags(java.lang.Object);
+ method public int getSpanStart(java.lang.Object);
+ method public <T> T[] getSpans(int, int, java.lang.Class<T>);
+ method public int getStart();
+ method public java.lang.CharSequence getText();
+ method public android.text.TextDirectionHeuristic getTextDir();
+ method public int length();
+ method public int nextSpanTransition(int, int, java.lang.Class);
+ method public java.lang.CharSequence subSequence(int, int);
+ }
+
+ public static final class MeasuredText.Builder {
+ ctor public MeasuredText.Builder(java.lang.CharSequence, android.text.TextPaint);
+ method public android.text.MeasuredText build();
+ method public android.text.MeasuredText.Builder setBreakStrategy(int);
+ method public android.text.MeasuredText.Builder setHyphenationFrequency(int);
+ method public android.text.MeasuredText.Builder setRange(int, int);
+ method public android.text.MeasuredText.Builder setTextDirection(android.text.TextDirectionHeuristic);
+ }
+
public abstract interface NoCopySpan {
}
@@ -43240,31 +43269,6 @@ package android.text {
method public abstract int getSpanTypeId();
}
- public class PrecomputedText {
- method public static android.text.PrecomputedText create(java.lang.CharSequence, android.text.PrecomputedText.Params);
- method public int getParagraphCount();
- method public int getParagraphEnd(int);
- method public int getParagraphStart(int);
- method public android.text.PrecomputedText.Params getParams();
- method public java.lang.CharSequence getText();
- }
-
- public static class PrecomputedText.Params {
- method public int getBreakStrategy();
- method public int getHyphenationFrequency();
- method public android.text.TextDirectionHeuristic getTextDirection();
- method public android.text.TextPaint getTextPaint();
- method public boolean sameTextMetrics(android.text.PrecomputedText.Params);
- }
-
- public static class PrecomputedText.Params.Builder {
- ctor public PrecomputedText.Params.Builder(android.text.TextPaint);
- method public android.text.PrecomputedText.Params build();
- method public android.text.PrecomputedText.Params.Builder setBreakStrategy(int);
- method public android.text.PrecomputedText.Params.Builder setHyphenationFrequency(int);
- method public android.text.PrecomputedText.Params.Builder setTextDirection(android.text.TextDirectionHeuristic);
- }
-
public class Selection {
method public static boolean extendDown(android.text.Spannable, android.text.Layout);
method public static boolean extendLeft(android.text.Spannable, android.text.Layout);
@@ -43396,7 +43400,6 @@ package android.text {
public static final class StaticLayout.Builder {
method public android.text.StaticLayout build();
- method public static android.text.StaticLayout.Builder obtain(android.text.PrecomputedText, int, int, android.text.TextPaint, int);
method public static android.text.StaticLayout.Builder obtain(java.lang.CharSequence, int, int, android.text.TextPaint, int);
method public android.text.StaticLayout.Builder setAlignment(android.text.Layout.Alignment);
method public android.text.StaticLayout.Builder setBreakStrategy(int);
@@ -53648,7 +53651,6 @@ package android.widget {
method public final android.content.res.ColorStateList getTextColors();
method public java.util.Locale getTextLocale();
method public android.os.LocaleList getTextLocales();
- method public android.text.PrecomputedText.Params getTextMetricsParams();
method public float getTextScaleX();
method public float getTextSize();
method public int getTotalPaddingBottom();
@@ -53754,8 +53756,6 @@ package android.widget {
method public final void setMovementMethod(android.text.method.MovementMethod);
method public void setOnEditorActionListener(android.widget.TextView.OnEditorActionListener);
method public void setPaintFlags(int);
- method public void setPrecomputedTextAndParams(android.text.PrecomputedText);
- method public void setPrecomputedTextOrThrow(android.text.PrecomputedText);
method public void setPrivateImeOptions(java.lang.String);
method public void setRawInputType(int);
method public void setScroller(android.widget.Scroller);
@@ -53780,7 +53780,6 @@ package android.widget {
method public final void setTextKeepState(java.lang.CharSequence, android.widget.TextView.BufferType);
method public void setTextLocale(java.util.Locale);
method public void setTextLocales(android.os.LocaleList);
- method public void setTextMetricsParams(android.text.PrecomputedText.Params);
method public void setTextScaleX(float);
method public void setTextSize(float);
method public void setTextSize(int, float);
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp
index 178db1ae5e5f..af2e362368c3 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp
@@ -63,7 +63,8 @@ CountMetricProducer::CountMetricProducer(const ConfigKey& key, const CountMetric
: MetricProducer(metric.id(), key, startTimeNs, conditionIndex, wizard) {
// TODO: evaluate initial conditions. and set mConditionMet.
if (metric.has_bucket()) {
- mBucketSizeNs = TimeUnitToBucketSizeInMillis(metric.bucket()) * 1000000;
+ mBucketSizeNs =
+ TimeUnitToBucketSizeInMillisGuardrailed(key.GetUid(), metric.bucket()) * 1000000;
} else {
mBucketSizeNs = LLONG_MAX;
}
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
index 67d95dbd4cc5..3b7936dee497 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
@@ -72,7 +72,8 @@ DurationMetricProducer::DurationMetricProducer(const ConfigKey& key, const Durat
// 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 = TimeUnitToBucketSizeInMillis(metric.bucket()) * 1000000;
+ mBucketSizeNs =
+ TimeUnitToBucketSizeInMillisGuardrailed(key.GetUid(), metric.bucket()) * 1000000;
} else {
mBucketSizeNs = LLONG_MAX;
}
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index 8aa816938c0d..0daa506ba42d 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -69,7 +69,7 @@ GaugeMetricProducer::GaugeMetricProducer(const ConfigKey& key, const GaugeMetric
mCurrentSlicedBucketForAnomaly = std::make_shared<DimToValMap>();
int64_t bucketSizeMills = 0;
if (metric.has_bucket()) {
- bucketSizeMills = TimeUnitToBucketSizeInMillis(metric.bucket());
+ bucketSizeMills = TimeUnitToBucketSizeInMillisGuardrailed(key.GetUid(), metric.bucket());
} else {
bucketSizeMills = TimeUnitToBucketSizeInMillis(ONE_HOUR);
}
diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h
index e8f8299abd89..574c59f740bd 100644
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -137,6 +137,11 @@ public:
return mBucketSizeNs;
}
+ // Only needed for unit-testing to override guardrail.
+ void setBucketSize(int64_t bucketSize) {
+ mBucketSizeNs = bucketSize;
+ }
+
inline const int64_t& getMetricId() {
return mMetricId;
}
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index cbca884ae6f3..35fcdc48a08a 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -72,7 +72,7 @@ ValueMetricProducer::ValueMetricProducer(const ConfigKey& key, const ValueMetric
// TODO: valuemetric for pushed events may need unlimited bucket length
int64_t bucketSizeMills = 0;
if (metric.has_bucket()) {
- bucketSizeMills = TimeUnitToBucketSizeInMillis(metric.bucket());
+ bucketSizeMills = TimeUnitToBucketSizeInMillisGuardrailed(key.GetUid(), metric.bucket());
} else {
bucketSizeMills = TimeUnitToBucketSizeInMillis(ONE_HOUR);
}
diff --git a/cmds/statsd/src/stats_log_util.cpp b/cmds/statsd/src/stats_log_util.cpp
index f7b768f5f2b2..30eef4fef166 100644
--- a/cmds/statsd/src/stats_log_util.cpp
+++ b/cmds/statsd/src/stats_log_util.cpp
@@ -17,6 +17,7 @@
#include "stats_log_util.h"
#include <logd/LogEvent.h>
+#include <private/android_filesystem_config.h>
#include <utils/Log.h>
#include <set>
#include <stack>
@@ -216,6 +217,14 @@ void writeFieldValueTreeToStream(int tagId, const std::vector<FieldValue>& value
protoOutput->end(atomToken);
}
+int64_t TimeUnitToBucketSizeInMillisGuardrailed(int uid, TimeUnit unit) {
+ int64_t bucketSizeMillis = TimeUnitToBucketSizeInMillis(unit);
+ if (bucketSizeMillis > 1000 && bucketSizeMillis < 5 * 60 * 1000LL && uid != AID_SHELL) {
+ bucketSizeMillis = 5 * 60 * 1000LL;
+ }
+ return bucketSizeMillis;
+}
+
int64_t TimeUnitToBucketSizeInMillis(TimeUnit unit) {
switch (unit) {
case ONE_MINUTE:
@@ -283,4 +292,4 @@ int64_t getWallClockMillis() {
} // namespace statsd
} // namespace os
-} // namespace android \ No newline at end of file
+} // namespace android
diff --git a/cmds/statsd/src/stats_log_util.h b/cmds/statsd/src/stats_log_util.h
index 32fe0b8370b8..6a5123d8c844 100644
--- a/cmds/statsd/src/stats_log_util.h
+++ b/cmds/statsd/src/stats_log_util.h
@@ -32,6 +32,10 @@ void writeFieldValueTreeToStream(int tagId, const std::vector<FieldValue>& value
void writeDimensionToProto(const HashableDimensionKey& dimension,
util::ProtoOutputStream* protoOutput);
+// Convert the TimeUnit enum to the bucket size in millis with a guardrail on
+// bucket size.
+int64_t TimeUnitToBucketSizeInMillisGuardrailed(int uid, TimeUnit unit);
+
// Convert the TimeUnit enum to the bucket size in millis.
int64_t TimeUnitToBucketSizeInMillis(TimeUnit unit);
@@ -71,4 +75,4 @@ bool parseProtoOutputStream(util::ProtoOutputStream& protoOutput, T* message) {
} // namespace statsd
} // namespace os
-} // namespace android \ No newline at end of file
+} // namespace android
diff --git a/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp b/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp
index 93cd5875c52b..022800427b11 100644
--- a/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp
@@ -47,7 +47,7 @@ StatsdConfig CreateStatsdConfig() {
*countMetric->mutable_dimensions_in_what() =
CreateAttributionUidAndTagDimensions(
android::util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
- countMetric->set_bucket(ONE_MINUTE);
+ countMetric->set_bucket(FIVE_MINUTES);
return config;
}
@@ -206,4 +206,4 @@ GTEST_LOG_(INFO) << "This test does nothing.\n";
} // namespace statsd
} // namespace os
-} // namespace android \ No newline at end of file
+} // namespace android
diff --git a/cmds/statsd/tests/e2e/DimensionInCondition_e2e_test.cpp b/cmds/statsd/tests/e2e/DimensionInCondition_e2e_test.cpp
index 293f579ac848..4dffd13bef66 100644
--- a/cmds/statsd/tests/e2e/DimensionInCondition_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/DimensionInCondition_e2e_test.cpp
@@ -61,7 +61,7 @@ StatsdConfig CreateCountMetricWithNoLinkConfig() {
CreateDimensions(android::util::SCREEN_BRIGHTNESS_CHANGED, {1 /* level */});
*metric->mutable_dimensions_in_condition() = CreateAttributionUidDimensions(
android::util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
- metric->set_bucket(ONE_MINUTE);
+ metric->set_bucket(FIVE_MINUTES);
return config;
}
@@ -252,7 +252,7 @@ StatsdConfig CreateCountMetricWithLinkConfig() {
addPredicateToPredicateCombination(isSyncingPredicate, combinationPredicate);
auto metric = config.add_count_metric();
- metric->set_bucket(ONE_MINUTE);
+ metric->set_bucket(FIVE_MINUTES);
metric->set_id(StringToId("AppCrashMetric"));
metric->set_what(appCrashMatcher.id());
metric->set_condition(combinationPredicate->id());
@@ -441,7 +441,7 @@ StatsdConfig CreateDurationMetricConfigNoLink(DurationMetric::AggregationType ag
addPredicateToPredicateCombination(isSyncingPredicate, combinationPredicate);
auto metric = config.add_duration_metric();
- metric->set_bucket(ONE_MINUTE);
+ metric->set_bucket(FIVE_MINUTES);
metric->set_id(StringToId("BatterySaverModeDurationMetric"));
metric->set_what(inBatterySaverModePredicate.id());
metric->set_condition(combinationPredicate->id());
@@ -595,7 +595,7 @@ StatsdConfig CreateDurationMetricConfigWithLink(DurationMetric::AggregationType
addPredicateToPredicateCombination(isSyncingPredicate, combinationPredicate);
auto metric = config.add_duration_metric();
- metric->set_bucket(ONE_MINUTE);
+ metric->set_bucket(FIVE_MINUTES);
metric->set_id(StringToId("AppInBackgroundMetric"));
metric->set_what(isInBackgroundPredicate.id());
metric->set_condition(combinationPredicate->id());
@@ -730,4 +730,4 @@ GTEST_LOG_(INFO) << "This test does nothing.\n";
} // namespace statsd
} // namespace os
-} // namespace android \ No newline at end of file
+} // namespace android
diff --git a/cmds/statsd/tests/e2e/GaugeMetric_e2e_test.cpp b/cmds/statsd/tests/e2e/GaugeMetric_e2e_test.cpp
index 6aa7dd64393e..3843e0a3c67d 100644
--- a/cmds/statsd/tests/e2e/GaugeMetric_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/GaugeMetric_e2e_test.cpp
@@ -53,7 +53,7 @@ StatsdConfig CreateStatsdConfigForPushedEvent() {
fieldMatcher->add_child()->set_field(7); // activity_start_msec(int64)
*gaugeMetric->mutable_dimensions_in_what() =
CreateDimensions(android::util::APP_START_CHANGED, {1 /* uid field */ });
- gaugeMetric->set_bucket(ONE_MINUTE);
+ gaugeMetric->set_bucket(FIVE_MINUTES);
auto links = gaugeMetric->add_links();
links->set_condition(isInBackgroundPredicate.id());
@@ -198,4 +198,4 @@ GTEST_LOG_(INFO) << "This test does nothing.\n";
} // namespace statsd
} // namespace os
-} // namespace android \ No newline at end of file
+} // namespace android
diff --git a/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp b/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp
index d00518147bfe..1b51780a59bc 100644
--- a/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp
@@ -71,7 +71,7 @@ StatsdConfig CreateStatsdConfig() {
// The metric is dimensioning by uid only.
*countMetric->mutable_dimensions_in_what() =
CreateDimensions(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, {1});
- countMetric->set_bucket(ONE_MINUTE);
+ countMetric->set_bucket(FIVE_MINUTES);
// Links between crash atom and condition of app is in syncing.
auto links = countMetric->add_links();
@@ -337,4 +337,4 @@ GTEST_LOG_(INFO) << "This test does nothing.\n";
} // namespace statsd
} // namespace os
-} // namespace android \ No newline at end of file
+} // namespace android
diff --git a/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp b/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp
index 024fa3e23180..efdab9822984 100644
--- a/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp
@@ -56,7 +56,7 @@ StatsdConfig CreateStatsdConfig(DurationMetric::AggregationType aggregationType)
*durationMetric->mutable_dimensions_in_what() =
CreateAttributionUidDimensions(
android::util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
- durationMetric->set_bucket(ONE_MINUTE);
+ durationMetric->set_bucket(FIVE_MINUTES);
return config;
}
@@ -327,4 +327,4 @@ GTEST_LOG_(INFO) << "This test does nothing.\n";
} // namespace statsd
} // namespace os
-} // namespace android \ No newline at end of file
+} // namespace android
diff --git a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
index d9dbf1d7cadc..20ddbe9f0e38 100644
--- a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
@@ -56,6 +56,7 @@ TEST(CountMetricProducerTest, TestNonDimensionalEvents) {
CountMetricProducer countProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
bucketStartTimeNs);
+ countProducer.setBucketSize(60 * NS_PER_SEC);
// 2 events in bucket 1.
countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
@@ -118,6 +119,7 @@ TEST(CountMetricProducerTest, TestEventsWithNonSlicedCondition) {
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
CountMetricProducer countProducer(kConfigKey, metric, 1, wizard, bucketStartTimeNs);
+ countProducer.setBucketSize(60 * NS_PER_SEC);
countProducer.onConditionChanged(true, bucketStartTimeNs);
countProducer.onMatchedLogEvent(1 /*matcher index*/, event1);
@@ -179,6 +181,7 @@ TEST(CountMetricProducerTest, TestEventsWithSlicedCondition) {
CountMetricProducer countProducer(kConfigKey, metric, 1 /*condition tracker index*/, wizard,
bucketStartTimeNs);
+ countProducer.setBucketSize(60 * NS_PER_SEC);
countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
countProducer.flushIfNeededLocked(bucketStartTimeNs + 1);
@@ -217,6 +220,8 @@ TEST(CountMetricProducerTest, TestEventWithAppUpgrade) {
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
CountMetricProducer countProducer(kConfigKey, metric, -1 /* no condition */, wizard,
bucketStartTimeNs);
+ countProducer.setBucketSize(60 * NS_PER_SEC);
+
sp<AnomalyTracker> anomalyTracker = countProducer.addAnomalyTracker(alert);
EXPECT_TRUE(anomalyTracker != nullptr);
@@ -274,6 +279,7 @@ TEST(CountMetricProducerTest, TestEventWithAppUpgradeInNextBucket) {
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
CountMetricProducer countProducer(kConfigKey, metric, -1 /* no condition */, wizard,
bucketStartTimeNs);
+ countProducer.setBucketSize(60 * NS_PER_SEC);
// Bucket is flushed yet.
countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
@@ -329,6 +335,8 @@ TEST(CountMetricProducerTest, TestAnomalyDetectionUnSliced) {
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
CountMetricProducer countProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
bucketStartTimeNs);
+ countProducer.setBucketSize(60 * NS_PER_SEC);
+
sp<AnomalyTracker> anomalyTracker = countProducer.addAnomalyTracker(alert);
int tagId = 1;
diff --git a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp
index 57e2794c0eea..79695967a6dd 100644
--- a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp
@@ -59,6 +59,7 @@ TEST(DurationMetricTrackerTest, TestNoCondition) {
DurationMetricProducer durationProducer(
kConfigKey, metric, -1 /*no condition*/, 1 /* start index */, 2 /* stop index */,
3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs);
+ durationProducer.setBucketSize(60 * NS_PER_SEC);
durationProducer.onMatchedLogEvent(1 /* start index*/, event1);
durationProducer.onMatchedLogEvent(2 /* stop index*/, event2);
@@ -100,6 +101,8 @@ TEST(DurationMetricTrackerTest, TestNonSlicedCondition) {
DurationMetricProducer durationProducer(
kConfigKey, metric, 0 /* condition index */, 1 /* start index */, 2 /* stop index */,
3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs);
+ durationProducer.setBucketSize(60 * NS_PER_SEC);
+
EXPECT_FALSE(durationProducer.mCondition);
EXPECT_FALSE(durationProducer.isConditionSliced());
@@ -149,6 +152,7 @@ TEST(DurationMetricTrackerTest, TestSumDurationWithUpgrade) {
DurationMetricProducer durationProducer(
kConfigKey, metric, -1 /* no condition */, 1 /* start index */, 2 /* stop index */,
3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs);
+ durationProducer.setBucketSize(60 * NS_PER_SEC);
LogEvent start_event(tagId, startTimeNs);
start_event.init();
@@ -203,6 +207,7 @@ TEST(DurationMetricTrackerTest, TestSumDurationWithUpgradeInFollowingBucket) {
DurationMetricProducer durationProducer(
kConfigKey, metric, -1 /* no condition */, 1 /* start index */, 2 /* stop index */,
3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs);
+ durationProducer.setBucketSize(60 * NS_PER_SEC);
LogEvent start_event(tagId, startTimeNs);
start_event.init();
@@ -256,6 +261,8 @@ TEST(DurationMetricTrackerTest, TestSumDurationAnomalyWithUpgrade) {
DurationMetricProducer durationProducer(
kConfigKey, metric, -1 /* no condition */, 1 /* start index */, 2 /* stop index */,
3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs);
+ durationProducer.setBucketSize(60 * NS_PER_SEC);
+
sp<AnomalyTracker> anomalyTracker = durationProducer.addAnomalyTracker(alert);
EXPECT_TRUE(anomalyTracker != nullptr);
@@ -293,6 +300,7 @@ TEST(DurationMetricTrackerTest, TestMaxDurationWithUpgrade) {
DurationMetricProducer durationProducer(
kConfigKey, metric, -1 /* no condition */, 1 /* start index */, 2 /* stop index */,
3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs);
+ durationProducer.setBucketSize(60 * NS_PER_SEC);
LogEvent start_event(tagId, startTimeNs);
start_event.init();
@@ -340,6 +348,7 @@ TEST(DurationMetricTrackerTest, TestMaxDurationWithUpgradeInNextBucket) {
DurationMetricProducer durationProducer(
kConfigKey, metric, -1 /* no condition */, 1 /* start index */, 2 /* stop index */,
3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs);
+ durationProducer.setBucketSize(60 * NS_PER_SEC);
LogEvent start_event(tagId, startTimeNs);
start_event.init();
diff --git a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
index 8b4273bc7929..0eb8ce2603bd 100644
--- a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
@@ -67,6 +67,7 @@ TEST(GaugeMetricProducerTest, TestNoCondition) {
GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
tagId, bucketStartTimeNs, pullerManager);
+ gaugeProducer.setBucketSize(60 * NS_PER_SEC);
vector<shared_ptr<LogEvent>> allData;
allData.clear();
@@ -144,6 +145,7 @@ TEST(GaugeMetricProducerTest, TestPushedEventsWithUpgrade) {
GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
-1 /* -1 means no pulling */, bucketStartTimeNs,
pullerManager);
+ gaugeProducer.setBucketSize(60 * NS_PER_SEC);
sp<AnomalyTracker> anomalyTracker = gaugeProducer.addAnomalyTracker(alert);
EXPECT_TRUE(anomalyTracker != nullptr);
@@ -225,6 +227,7 @@ TEST(GaugeMetricProducerTest, TestPulledWithUpgrade) {
GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
tagId, bucketStartTimeNs, pullerManager);
+ gaugeProducer.setBucketSize(60 * NS_PER_SEC);
vector<shared_ptr<LogEvent>> allData;
shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 1);
@@ -292,6 +295,7 @@ TEST(GaugeMetricProducerTest, TestWithCondition) {
GaugeMetricProducer gaugeProducer(kConfigKey, metric, 1, wizard, tagId,
bucketStartTimeNs, pullerManager);
+ gaugeProducer.setBucketSize(60 * NS_PER_SEC);
gaugeProducer.onConditionChanged(true, bucketStartTimeNs + 8);
EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
@@ -350,6 +354,7 @@ TEST(GaugeMetricProducerTest, TestAnomalyDetection) {
gaugeFieldMatcher->add_child()->set_field(2);
GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
tagId, bucketStartTimeNs, pullerManager);
+ gaugeProducer.setBucketSize(60 * NS_PER_SEC);
Alert alert;
alert.set_id(101);
diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
index 6e66c6e8dcb6..ce4fa3278493 100644
--- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
@@ -66,6 +66,7 @@ TEST(ValueMetricProducerTest, TestNonDimensionalEvents) {
ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
tagId, bucketStartTimeNs, pullerManager);
+ valueProducer.setBucketSize(60 * NS_PER_SEC);
vector<shared_ptr<LogEvent>> allData;
allData.clear();
@@ -79,6 +80,8 @@ TEST(ValueMetricProducerTest, TestNonDimensionalEvents) {
// 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
EXPECT_EQ(true, curInterval.startUpdated);
EXPECT_EQ(0, curInterval.tainted);
@@ -162,7 +165,7 @@ TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition) {
ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, tagId, bucketStartTimeNs,
pullerManager);
-
+ valueProducer.setBucketSize(60 * NS_PER_SEC);
valueProducer.onConditionChanged(true, bucketStartTimeNs + 8);
// has one slice
@@ -215,6 +218,7 @@ TEST(ValueMetricProducerTest, TestPushedEventsWithUpgrade) {
make_shared<StrictMock<MockStatsPullerManager>>();
ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, -1, bucketStartTimeNs,
pullerManager);
+ valueProducer.setBucketSize(60 * NS_PER_SEC);
shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
event1->write(1);
@@ -269,6 +273,7 @@ TEST(ValueMetricProducerTest, TestPulledValueWithUpgrade) {
}));
ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, tagId, bucketStartTimeNs,
pullerManager);
+ valueProducer.setBucketSize(60 * NS_PER_SEC);
vector<shared_ptr<LogEvent>> allData;
allData.clear();
@@ -311,6 +316,7 @@ TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition) {
ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, -1, bucketStartTimeNs,
pullerManager);
+ valueProducer.setBucketSize(60 * NS_PER_SEC);
shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
event1->write(1);
@@ -357,6 +363,8 @@ TEST(ValueMetricProducerTest, TestAnomalyDetection) {
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
-1 /*not pulled*/, bucketStartTimeNs);
+ valueProducer.setBucketSize(60 * NS_PER_SEC);
+
sp<AnomalyTracker> anomalyTracker = valueProducer.addAnomalyTracker(alert);
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index fee58274a5fc..d5430f05d65b 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -1106,6 +1106,11 @@ public class ActivityOptions {
}
/** @hide */
+ public void setRemoteAnimationAdapter(RemoteAnimationAdapter remoteAnimationAdapter) {
+ mRemoteAnimationAdapter = remoteAnimationAdapter;
+ }
+
+ /** @hide */
public static ActivityOptions fromBundle(Bundle bOptions) {
return bOptions != null ? new ActivityOptions(bOptions) : null;
}
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 02be00268a45..60dccbc176d3 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -68,6 +68,7 @@ import android.os.WorkSource;
import android.service.voice.IVoiceInteractionSession;
import android.view.IRecentsAnimationRunner;
import android.view.RemoteAnimationDefinition;
+import android.view.RemoteAnimationAdapter;
import com.android.internal.app.IVoiceInteractor;
import com.android.internal.os.IResultReceiver;
import com.android.internal.policy.IKeyguardDismissCallback;
@@ -696,4 +697,11 @@ interface IActivityManager {
* Registers remote animations for a specific activity.
*/
void registerRemoteAnimations(in IBinder token, in RemoteAnimationDefinition definition);
+
+ /**
+ * Registers a remote animation to be run for all activity starts from a certain package during
+ * a short predefined amount of time.
+ */
+ void registerRemoteAnimationForNextActivityStart(in String packageName,
+ in RemoteAnimationAdapter adapter);
}
diff --git a/core/java/android/app/servertransaction/ActivityLifecycleItem.java b/core/java/android/app/servertransaction/ActivityLifecycleItem.java
index 9a50a009ce34..7f8c50cd4ce5 100644
--- a/core/java/android/app/servertransaction/ActivityLifecycleItem.java
+++ b/core/java/android/app/servertransaction/ActivityLifecycleItem.java
@@ -91,4 +91,9 @@ public abstract class ActivityLifecycleItem extends ClientTransactionItem {
pw.println(prefix + "target state:" + getTargetState());
pw.println(prefix + "description: " + mDescription);
}
+
+ @Override
+ public void recycle() {
+ setDescription(null);
+ }
}
diff --git a/core/java/android/app/servertransaction/DestroyActivityItem.java b/core/java/android/app/servertransaction/DestroyActivityItem.java
index 48a79f79dae1..0edcf1884f01 100644
--- a/core/java/android/app/servertransaction/DestroyActivityItem.java
+++ b/core/java/android/app/servertransaction/DestroyActivityItem.java
@@ -65,6 +65,7 @@ public class DestroyActivityItem extends ActivityLifecycleItem {
@Override
public void recycle() {
+ super.recycle();
mFinished = false;
mConfigChanges = 0;
ObjectPool.recycle(this);
diff --git a/core/java/android/app/servertransaction/PauseActivityItem.java b/core/java/android/app/servertransaction/PauseActivityItem.java
index 70a4755f99af..91e73cd5b1cd 100644
--- a/core/java/android/app/servertransaction/PauseActivityItem.java
+++ b/core/java/android/app/servertransaction/PauseActivityItem.java
@@ -102,6 +102,7 @@ public class PauseActivityItem extends ActivityLifecycleItem {
@Override
public void recycle() {
+ super.recycle();
mFinished = false;
mUserLeaving = false;
mConfigChanges = 0;
diff --git a/core/java/android/app/servertransaction/ResumeActivityItem.java b/core/java/android/app/servertransaction/ResumeActivityItem.java
index ed90f2cb1013..af2fb713e1bc 100644
--- a/core/java/android/app/servertransaction/ResumeActivityItem.java
+++ b/core/java/android/app/servertransaction/ResumeActivityItem.java
@@ -101,6 +101,7 @@ public class ResumeActivityItem extends ActivityLifecycleItem {
@Override
public void recycle() {
+ super.recycle();
mProcState = ActivityManager.PROCESS_STATE_UNKNOWN;
mUpdateProcState = false;
mIsForward = false;
diff --git a/core/java/android/app/servertransaction/StopActivityItem.java b/core/java/android/app/servertransaction/StopActivityItem.java
index b814d1ae1392..f955a903d649 100644
--- a/core/java/android/app/servertransaction/StopActivityItem.java
+++ b/core/java/android/app/servertransaction/StopActivityItem.java
@@ -72,6 +72,7 @@ public class StopActivityItem extends ActivityLifecycleItem {
@Override
public void recycle() {
+ super.recycle();
mShowWindow = false;
mConfigChanges = 0;
ObjectPool.recycle(this);
diff --git a/core/java/android/hardware/OWNERS b/core/java/android/hardware/OWNERS
new file mode 100644
index 000000000000..b8fea556e4c5
--- /dev/null
+++ b/core/java/android/hardware/OWNERS
@@ -0,0 +1,7 @@
+# Camera
+per-file *Camera* = cychen@google.com
+per-file *Camera* = epeev@google.com
+per-file *Camera* = etalvala@google.com
+per-file *Camera* = shuzhenwang@google.com
+per-file *Camera* = yinchiayeh@google.com
+per-file *Camera* = zhijunhe@google.com
diff --git a/core/java/android/hardware/camera2/OWNERS b/core/java/android/hardware/camera2/OWNERS
new file mode 100644
index 000000000000..18acfee14555
--- /dev/null
+++ b/core/java/android/hardware/camera2/OWNERS
@@ -0,0 +1,6 @@
+cychen@google.com
+epeev@google.com
+etalvala@google.com
+shuzhenwang@google.com
+yinchiayeh@google.com
+zhijunhe@google.com
diff --git a/core/java/android/net/IpSecConfig.java b/core/java/android/net/IpSecConfig.java
index 6a262e2c87ca..8599f47c6245 100644
--- a/core/java/android/net/IpSecConfig.java
+++ b/core/java/android/net/IpSecConfig.java
@@ -218,6 +218,25 @@ public final class IpSecConfig implements Parcelable {
@VisibleForTesting
public IpSecConfig() {}
+ /** Copy constructor */
+ @VisibleForTesting
+ public IpSecConfig(IpSecConfig c) {
+ mMode = c.mMode;
+ mSourceAddress = c.mSourceAddress;
+ mDestinationAddress = c.mDestinationAddress;
+ mNetwork = c.mNetwork;
+ mSpiResourceId = c.mSpiResourceId;
+ mEncryption = c.mEncryption;
+ mAuthentication = c.mAuthentication;
+ mAuthenticatedEncryption = c.mAuthenticatedEncryption;
+ mEncapType = c.mEncapType;
+ mEncapSocketResourceId = c.mEncapSocketResourceId;
+ mEncapRemotePort = c.mEncapRemotePort;
+ mNattKeepaliveInterval = c.mNattKeepaliveInterval;
+ mMarkValue = c.mMarkValue;
+ mMarkMask = c.mMarkMask;
+ }
+
private IpSecConfig(Parcel in) {
mMode = in.readInt();
mSourceAddress = in.readString();
diff --git a/core/java/android/net/IpSecTransform.java b/core/java/android/net/IpSecTransform.java
index 38759a9183f2..60e96f943401 100644
--- a/core/java/android/net/IpSecTransform.java
+++ b/core/java/android/net/IpSecTransform.java
@@ -84,9 +84,11 @@ public final class IpSecTransform implements AutoCloseable {
@Retention(RetentionPolicy.SOURCE)
public @interface EncapType {}
- private IpSecTransform(Context context, IpSecConfig config) {
+ /** @hide */
+ @VisibleForTesting
+ public IpSecTransform(Context context, IpSecConfig config) {
mContext = context;
- mConfig = config;
+ mConfig = new IpSecConfig(config);
mResourceId = INVALID_RESOURCE_ID;
}
@@ -143,6 +145,18 @@ public final class IpSecTransform implements AutoCloseable {
}
/**
+ * Equals method used for testing
+ *
+ * @hide
+ */
+ @VisibleForTesting
+ public static boolean equals(IpSecTransform lhs, IpSecTransform rhs) {
+ if (lhs == null || rhs == null) return (lhs == rhs);
+ return IpSecConfig.equals(lhs.getConfig(), rhs.getConfig())
+ && lhs.mResourceId == rhs.mResourceId;
+ }
+
+ /**
* Deactivate this {@code IpSecTransform} and free allocated resources.
*
* <p>Deactivating a transform while it is still applied to a socket will result in errors on
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index 65e9473380ce..5e23932c48cc 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -71,6 +71,7 @@ interface IUserManager {
Bundle getUserRestrictions(int userHandle);
boolean hasBaseUserRestriction(String restrictionKey, int userHandle);
boolean hasUserRestriction(in String restrictionKey, int userHandle);
+ boolean hasUserRestrictionOnAnyUser(in String restrictionKey);
void setUserRestriction(String key, boolean value, int userHandle);
void setApplicationRestrictions(in String packageName, in Bundle restrictions,
int userHandle);
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 7e7af1a12299..185620066454 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -1678,6 +1678,18 @@ public class UserManager {
}
/**
+ * @hide
+ * Returns whether any user on the device has the given user restriction set.
+ */
+ public boolean hasUserRestrictionOnAnyUser(String restrictionKey) {
+ try {
+ return mService.hasUserRestrictionOnAnyUser(restrictionKey);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Return the serial number for a user. This is a device-unique
* number assigned to that user; if the user is deleted and then a new
* user created, the new users will not be given the same serial number.
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 1223271ad4c7..1feb822ffc58 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -5443,32 +5443,6 @@ public final class Settings {
*/
public static final String ENABLED_INPUT_METHODS = "enabled_input_methods";
- private static final Validator ENABLED_INPUT_METHODS_VALIDATOR = new Validator() {
- @Override
- public boolean validate(String value) {
- if (value == null) {
- return false;
- }
- String[] inputMethods = value.split(":");
- boolean valid = true;
- for (String inputMethod : inputMethods) {
- if (inputMethod.length() == 0) {
- return false;
- }
- String[] subparts = inputMethod.split(";");
- for (String subpart : subparts) {
- // allow either a non negative integer or a ComponentName
- valid |= (NON_NEGATIVE_INTEGER_VALIDATOR.validate(subpart)
- || COMPONENT_NAME_VALIDATOR.validate(subpart));
- }
- if (!valid) {
- return false;
- }
- }
- return valid;
- }
- };
-
/**
* List of system input methods that are currently disabled. This is a string
* containing the IDs of all disabled input methods, each ID separated
@@ -7709,7 +7683,6 @@ public final class Settings {
ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE,
ENABLED_ACCESSIBILITY_SERVICES,
ENABLED_VR_LISTENERS,
- ENABLED_INPUT_METHODS,
TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES,
TOUCH_EXPLORATION_ENABLED,
ACCESSIBILITY_ENABLED,
@@ -7815,7 +7788,6 @@ public final class Settings {
VALIDATORS.put(ENABLED_ACCESSIBILITY_SERVICES,
ENABLED_ACCESSIBILITY_SERVICES_VALIDATOR);
VALIDATORS.put(ENABLED_VR_LISTENERS, ENABLED_VR_LISTENERS_VALIDATOR);
- VALIDATORS.put(ENABLED_INPUT_METHODS, ENABLED_INPUT_METHODS_VALIDATOR);
VALIDATORS.put(TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES,
TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES_VALIDATOR);
VALIDATORS.put(TOUCH_EXPLORATION_ENABLED, TOUCH_EXPLORATION_ENABLED_VALIDATOR);
@@ -11382,7 +11354,8 @@ public final class Settings {
"chained_battery_attribution_enabled";
/**
- * The packages whitelisted to be run in autofill compatibility mode.
+ * The packages whitelisted to be run in autofill compatibility mode. The list
+ * of packages is ":" colon delimited.
*
* @hide
*/
diff --git a/core/java/android/security/keystore/OWNERS b/core/java/android/security/keystore/OWNERS
new file mode 100644
index 000000000000..bb487fb52c9f
--- /dev/null
+++ b/core/java/android/security/keystore/OWNERS
@@ -0,0 +1,4 @@
+aseemk@google.com
+bozhu@google.com
+dementyev@google.com
+robertberry@google.com
diff --git a/core/java/android/service/autofill/AutofillServiceInfo.java b/core/java/android/service/autofill/AutofillServiceInfo.java
index f644887676a2..70c4ec07ee4e 100644
--- a/core/java/android/service/autofill/AutofillServiceInfo.java
+++ b/core/java/android/service/autofill/AutofillServiceInfo.java
@@ -45,7 +45,6 @@ import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
import java.io.PrintWriter;
-import java.util.Map;
/**
* {@link ServiceInfo} and meta-data about an {@link AutofillService}.
@@ -80,7 +79,7 @@ public final class AutofillServiceInfo {
private final String mSettingsActivity;
@Nullable
- private final Map<String, Pair<Long, String>> mCompatibilityPackages;
+ private final ArrayMap<String, Pair<Long, String>> mCompatibilityPackages;
public AutofillServiceInfo(Context context, ComponentName comp, int userHandle)
throws PackageManager.NameNotFoundException {
@@ -118,7 +117,7 @@ public final class AutofillServiceInfo {
}
String settingsActivity = null;
- Map<String, Pair<Long, String>> compatibilityPackages = null;
+ ArrayMap<String, Pair<Long, String>> compatibilityPackages = null;
try {
final Resources resources = context.getPackageManager().getResourcesForApplication(
@@ -154,10 +153,10 @@ public final class AutofillServiceInfo {
mCompatibilityPackages = compatibilityPackages;
}
- private Map<String, Pair<Long, String>> parseCompatibilityPackages(XmlPullParser parser,
+ private ArrayMap<String, Pair<Long, String>> parseCompatibilityPackages(XmlPullParser parser,
Resources resources)
throws IOException, XmlPullParserException {
- Map<String, Pair<Long, String>> compatibilityPackages = null;
+ ArrayMap<String, Pair<Long, String>> compatibilityPackages = null;
final int outerDepth = parser.getDepth();
int type;
@@ -229,15 +228,8 @@ public final class AutofillServiceInfo {
return mSettingsActivity;
}
- public boolean isCompatibilityModeRequested(String packageName, long versionCode) {
- if (mCompatibilityPackages == null) {
- return false;
- }
- final Pair<Long, String> pair = mCompatibilityPackages.get(packageName);
- if (pair == null) {
- return false;
- }
- return versionCode <= pair.first;
+ public ArrayMap<String, Pair<Long, String>> getCompatibilityPackages() {
+ return mCompatibilityPackages;
}
/**
diff --git a/core/java/android/text/BoringLayout.java b/core/java/android/text/BoringLayout.java
index dbe415773899..6fa5312be5cc 100644
--- a/core/java/android/text/BoringLayout.java
+++ b/core/java/android/text/BoringLayout.java
@@ -322,12 +322,6 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback
*/
public static Metrics isBoring(CharSequence text, TextPaint paint,
TextDirectionHeuristic textDir, Metrics metrics) {
- return isBoring(text, null /* precomputed */, paint, textDir, metrics);
- }
-
- /** @hide */
- public static Metrics isBoring(CharSequence text, PrecomputedText precomputed, TextPaint paint,
- TextDirectionHeuristic textDir, Metrics metrics) {
final int textLength = text.length();
if (hasAnyInterestingChars(text, textLength)) {
return null; // There are some interesting characters. Not boring.
@@ -350,17 +344,18 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback
fm.reset();
}
- if (precomputed != null) {
+ TextLine line = TextLine.obtain();
+ line.set(paint, text, 0, textLength, Layout.DIR_LEFT_TO_RIGHT,
+ Layout.DIRS_ALL_LEFT_TO_RIGHT, false, null);
+ if (text instanceof MeasuredText) {
+ MeasuredText mt = (MeasuredText) text;
// Reaching here means there is only one paragraph.
- MeasuredParagraph mp = precomputed.getMeasuredParagraph(0);
+ MeasuredParagraph mp = mt.getMeasuredParagraph(0);
fm.width = (int) Math.ceil(mp.getWidth(0, mp.getTextLength()));
} else {
- TextLine line = TextLine.obtain();
- line.set(paint, text, 0, textLength, Layout.DIR_LEFT_TO_RIGHT,
- Layout.DIRS_ALL_LEFT_TO_RIGHT, false, null);
fm.width = (int) Math.ceil(line.metrics(fm));
- TextLine.recycle(line);
}
+ TextLine.recycle(line);
return fm;
}
diff --git a/core/java/android/text/DynamicLayout.java b/core/java/android/text/DynamicLayout.java
index 10444f045d6f..18431cacbfaf 100644
--- a/core/java/android/text/DynamicLayout.java
+++ b/core/java/android/text/DynamicLayout.java
@@ -363,7 +363,7 @@ public class DynamicLayout extends Layout {
@JustificationMode int justificationMode,
@Nullable TextUtils.TruncateAt ellipsize,
@IntRange(from = 0) int ellipsizedWidth) {
- super(createEllipsizer(ellipsize, display), null /* precomputed */,
+ super(createEllipsizer(ellipsize, display),
paint, width, align, textDir, spacingmult, spacingadd);
final Builder b = Builder.obtain(base, paint, width)
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index d5d35904031c..aa97b2aba749 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -245,13 +245,6 @@ public abstract class Layout {
protected Layout(CharSequence text, TextPaint paint,
int width, Alignment align, TextDirectionHeuristic textDir,
float spacingMult, float spacingAdd) {
- this(text, null /* precomputed */, paint, width, align, textDir, spacingMult, spacingAdd);
- }
-
- /** @hide */
- protected Layout(CharSequence text, PrecomputedText precomputed, TextPaint paint,
- int width, Alignment align, TextDirectionHeuristic textDir,
- float spacingMult, float spacingAdd) {
if (width < 0)
throw new IllegalArgumentException("Layout: " + width + " < 0");
@@ -266,7 +259,6 @@ public abstract class Layout {
}
mText = text;
- mPrecomputed = precomputed;
mPaint = paint;
mWidth = width;
mAlignment = align;
@@ -570,7 +562,7 @@ public abstract class Layout {
// XXX: assumes there's nothing additional to be done
canvas.drawText(buf, start, end, x, lbaseline, paint);
} else {
- tl.set(paint, buf, mPrecomputed, start, end, dir, directions, hasTab, tabStops);
+ tl.set(paint, buf, start, end, dir, directions, hasTab, tabStops);
if (justify) {
tl.justify(right - left - indentWidth);
}
@@ -2272,7 +2264,6 @@ public abstract class Layout {
}
private CharSequence mText;
- private PrecomputedText mPrecomputed;
private TextPaint mPaint;
private TextPaint mWorkPaint = new TextPaint();
private int mWidth;
diff --git a/core/java/android/text/MeasuredText.java b/core/java/android/text/MeasuredText.java
new file mode 100644
index 000000000000..bb7a9e0b7906
--- /dev/null
+++ b/core/java/android/text/MeasuredText.java
@@ -0,0 +1,427 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.text;
+
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.util.IntArray;
+
+import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.Preconditions;
+
+import java.util.ArrayList;
+
+/**
+ * A text which has already been measured.
+ */
+public class MeasuredText implements Spanned {
+ private static final char LINE_FEED = '\n';
+
+ // The original text.
+ private final @NonNull CharSequence mText;
+
+ // The inclusive start offset of the measuring target.
+ private final @IntRange(from = 0) int mStart;
+
+ // The exclusive end offset of the measuring target.
+ private final @IntRange(from = 0) int mEnd;
+
+ // The TextPaint used for measurement.
+ private final @NonNull TextPaint mPaint;
+
+ // The requested text direction.
+ private final @NonNull TextDirectionHeuristic mTextDir;
+
+ // The measured paragraph texts.
+ private final @NonNull MeasuredParagraph[] mMeasuredParagraphs;
+
+ // The sorted paragraph end offsets.
+ private final @NonNull int[] mParagraphBreakPoints;
+
+ // The break strategy for this measured text.
+ private final @Layout.BreakStrategy int mBreakStrategy;
+
+ // The hyphenation frequency for this measured text.
+ private final @Layout.HyphenationFrequency int mHyphenationFrequency;
+
+ /**
+ * A Builder for MeasuredText
+ */
+ public static final class Builder {
+ // Mandatory parameters.
+ private final @NonNull CharSequence mText;
+ private final @NonNull TextPaint mPaint;
+
+ // Members to be updated by setters.
+ private @IntRange(from = 0) int mStart;
+ private @IntRange(from = 0) int mEnd;
+ private TextDirectionHeuristic mTextDir = TextDirectionHeuristics.FIRSTSTRONG_LTR;
+ private @Layout.BreakStrategy int mBreakStrategy = Layout.BREAK_STRATEGY_HIGH_QUALITY;
+ private @Layout.HyphenationFrequency int mHyphenationFrequency =
+ Layout.HYPHENATION_FREQUENCY_NORMAL;
+
+
+ /**
+ * Builder constructor
+ *
+ * @param text The text to be measured.
+ * @param paint The paint to be used for drawing.
+ */
+ public Builder(@NonNull CharSequence text, @NonNull TextPaint paint) {
+ Preconditions.checkNotNull(text);
+ Preconditions.checkNotNull(paint);
+
+ mText = text;
+ mPaint = paint;
+ mStart = 0;
+ mEnd = text.length();
+ }
+
+ /**
+ * Set the range of measuring target.
+ *
+ * @param start The measuring target start offset in the text.
+ * @param end The measuring target end offset in the text.
+ */
+ public @NonNull Builder setRange(@IntRange(from = 0) int start,
+ @IntRange(from = 0) int end) {
+ Preconditions.checkArgumentInRange(start, 0, mText.length(), "start");
+ Preconditions.checkArgumentInRange(end, 0, mText.length(), "end");
+ Preconditions.checkArgument(start <= end, "The range is reversed.");
+
+ mStart = start;
+ mEnd = end;
+ return this;
+ }
+
+ /**
+ * Set the text direction heuristic
+ *
+ * The default value is {@link TextDirectionHeuristics#FIRSTSTRONG_LTR}.
+ *
+ * @param textDir The text direction heuristic for resolving bidi behavior.
+ * @return this builder, useful for chaining.
+ */
+ public @NonNull Builder setTextDirection(@NonNull TextDirectionHeuristic textDir) {
+ Preconditions.checkNotNull(textDir);
+ mTextDir = textDir;
+ return this;
+ }
+
+ /**
+ * Set the break strategy
+ *
+ * The default value is {@link Layout#BREAK_STRATEGY_HIGH_QUALITY}.
+ *
+ * @param breakStrategy The break strategy.
+ * @return this builder, useful for chaining.
+ */
+ public @NonNull Builder setBreakStrategy(@Layout.BreakStrategy int breakStrategy) {
+ mBreakStrategy = breakStrategy;
+ return this;
+ }
+
+ /**
+ * Set the hyphenation frequency
+ *
+ * The default value is {@link Layout#HYPHENATION_FREQUENCY_NORMAL}.
+ *
+ * @param hyphenationFrequency The hyphenation frequency.
+ * @return this builder, useful for chaining.
+ */
+ public @NonNull Builder setHyphenationFrequency(
+ @Layout.HyphenationFrequency int hyphenationFrequency) {
+ mHyphenationFrequency = hyphenationFrequency;
+ return this;
+ }
+
+ /**
+ * Build the measured text
+ *
+ * @return the measured text.
+ */
+ public @NonNull MeasuredText build() {
+ return build(true /* build full layout result */);
+ }
+
+ /** @hide */
+ public @NonNull MeasuredText build(boolean computeLayout) {
+ final boolean needHyphenation = mBreakStrategy != Layout.BREAK_STRATEGY_SIMPLE
+ && mHyphenationFrequency != Layout.HYPHENATION_FREQUENCY_NONE;
+
+ final IntArray paragraphEnds = new IntArray();
+ final ArrayList<MeasuredParagraph> measuredTexts = new ArrayList<>();
+
+ int paraEnd = 0;
+ for (int paraStart = mStart; paraStart < mEnd; paraStart = paraEnd) {
+ paraEnd = TextUtils.indexOf(mText, LINE_FEED, paraStart, mEnd);
+ if (paraEnd < 0) {
+ // No LINE_FEED(U+000A) character found. Use end of the text as the paragraph
+ // end.
+ paraEnd = mEnd;
+ } else {
+ paraEnd++; // Includes LINE_FEED(U+000A) to the prev paragraph.
+ }
+
+ paragraphEnds.add(paraEnd);
+ measuredTexts.add(MeasuredParagraph.buildForStaticLayout(
+ mPaint, mText, paraStart, paraEnd, mTextDir, needHyphenation,
+ computeLayout, null /* no recycle */));
+ }
+
+ return new MeasuredText(mText, mStart, mEnd, mPaint, mTextDir, mBreakStrategy,
+ mHyphenationFrequency, measuredTexts.toArray(
+ new MeasuredParagraph[measuredTexts.size()]),
+ paragraphEnds.toArray());
+ }
+ };
+
+ // Use MeasuredText.Builder instead.
+ private MeasuredText(@NonNull CharSequence text,
+ @IntRange(from = 0) int start,
+ @IntRange(from = 0) int end,
+ @NonNull TextPaint paint,
+ @NonNull TextDirectionHeuristic textDir,
+ @Layout.BreakStrategy int breakStrategy,
+ @Layout.HyphenationFrequency int frequency,
+ @NonNull MeasuredParagraph[] measuredTexts,
+ @NonNull int[] paragraphBreakPoints) {
+ mText = text;
+ mStart = start;
+ mEnd = end;
+ // Copy the paint so that we can keep the reference of typeface in native layout result.
+ mPaint = new TextPaint(paint);
+ mMeasuredParagraphs = measuredTexts;
+ mParagraphBreakPoints = paragraphBreakPoints;
+ mTextDir = textDir;
+ mBreakStrategy = breakStrategy;
+ mHyphenationFrequency = frequency;
+ }
+
+ /**
+ * Return the underlying text.
+ */
+ public @NonNull CharSequence getText() {
+ return mText;
+ }
+
+ /**
+ * Returns the inclusive start offset of measured region.
+ */
+ public @IntRange(from = 0) int getStart() {
+ return mStart;
+ }
+
+ /**
+ * Returns the exclusive end offset of measured region.
+ */
+ public @IntRange(from = 0) int getEnd() {
+ return mEnd;
+ }
+
+ /**
+ * Returns the text direction associated with char sequence.
+ */
+ public @NonNull TextDirectionHeuristic getTextDir() {
+ return mTextDir;
+ }
+
+ /**
+ * Returns the paint used to measure this text.
+ */
+ public @NonNull TextPaint getPaint() {
+ return mPaint;
+ }
+
+ /**
+ * Returns the length of the paragraph of this text.
+ */
+ public @IntRange(from = 0) int getParagraphCount() {
+ return mParagraphBreakPoints.length;
+ }
+
+ /**
+ * Returns the paragraph start offset of the text.
+ */
+ public @IntRange(from = 0) int getParagraphStart(@IntRange(from = 0) int paraIndex) {
+ Preconditions.checkArgumentInRange(paraIndex, 0, getParagraphCount(), "paraIndex");
+ return paraIndex == 0 ? mStart : mParagraphBreakPoints[paraIndex - 1];
+ }
+
+ /**
+ * Returns the paragraph end offset of the text.
+ */
+ public @IntRange(from = 0) int getParagraphEnd(@IntRange(from = 0) int paraIndex) {
+ Preconditions.checkArgumentInRange(paraIndex, 0, getParagraphCount(), "paraIndex");
+ return mParagraphBreakPoints[paraIndex];
+ }
+
+ /** @hide */
+ public @NonNull MeasuredParagraph getMeasuredParagraph(@IntRange(from = 0) int paraIndex) {
+ return mMeasuredParagraphs[paraIndex];
+ }
+
+ /**
+ * Returns the break strategy for this text.
+ */
+ public @Layout.BreakStrategy int getBreakStrategy() {
+ return mBreakStrategy;
+ }
+
+ /**
+ * Returns the hyphenation frequency for this text.
+ */
+ public @Layout.HyphenationFrequency int getHyphenationFrequency() {
+ return mHyphenationFrequency;
+ }
+
+ /**
+ * Returns true if the given TextPaint gives the same result of text layout for this text.
+ * @hide
+ */
+ public boolean canUseMeasuredResult(@NonNull TextPaint paint) {
+ return mPaint.getTextSize() == paint.getTextSize()
+ && mPaint.getTextSkewX() == paint.getTextSkewX()
+ && mPaint.getTextScaleX() == paint.getTextScaleX()
+ && mPaint.getLetterSpacing() == paint.getLetterSpacing()
+ && mPaint.getWordSpacing() == paint.getWordSpacing()
+ && mPaint.getFlags() == paint.getFlags() // Maybe not all flag affects text layout.
+ && mPaint.getTextLocales() == paint.getTextLocales() // need to be equals?
+ && mPaint.getFontVariationSettings() == paint.getFontVariationSettings()
+ && mPaint.getTypeface() == paint.getTypeface()
+ && TextUtils.equals(mPaint.getFontFeatureSettings(), paint.getFontFeatureSettings());
+ }
+
+ /** @hide */
+ public int findParaIndex(@IntRange(from = 0) int pos) {
+ // TODO: Maybe good to remove paragraph concept from MeasuredText and add substring layout
+ // support to StaticLayout.
+ for (int i = 0; i < mParagraphBreakPoints.length; ++i) {
+ if (pos < mParagraphBreakPoints[i]) {
+ return i;
+ }
+ }
+ throw new IndexOutOfBoundsException(
+ "pos must be less than " + mParagraphBreakPoints[mParagraphBreakPoints.length - 1]
+ + ", gave " + pos);
+ }
+
+ /** @hide */
+ public float getWidth(@IntRange(from = 0) int start, @IntRange(from = 0) int end) {
+ final int paraIndex = findParaIndex(start);
+ final int paraStart = getParagraphStart(paraIndex);
+ final int paraEnd = getParagraphEnd(paraIndex);
+ if (start < paraStart || paraEnd < end) {
+ throw new RuntimeException("Cannot measured across the paragraph:"
+ + "para: (" + paraStart + ", " + paraEnd + "), "
+ + "request: (" + start + ", " + end + ")");
+ }
+ return getMeasuredParagraph(paraIndex).getWidth(start - paraStart, end - paraStart);
+ }
+
+ /**
+ * Returns the size of native MeasuredText memory usage
+ *
+ * Note that this may not be aculate. Must be used only for testing purposes.
+ * @hide
+ */
+ public int getMemoryUsage() {
+ int r = 0;
+ for (int i = 0; i < getParagraphCount(); ++i) {
+ r += getMeasuredParagraph(i).getMemoryUsage();
+ }
+ return r;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+ // Spanned overrides
+ //
+ // Just proxy for underlying mText if appropriate.
+
+ @Override
+ public <T> T[] getSpans(int start, int end, Class<T> type) {
+ if (mText instanceof Spanned) {
+ return ((Spanned) mText).getSpans(start, end, type);
+ } else {
+ return ArrayUtils.emptyArray(type);
+ }
+ }
+
+ @Override
+ public int getSpanStart(Object tag) {
+ if (mText instanceof Spanned) {
+ return ((Spanned) mText).getSpanStart(tag);
+ } else {
+ return -1;
+ }
+ }
+
+ @Override
+ public int getSpanEnd(Object tag) {
+ if (mText instanceof Spanned) {
+ return ((Spanned) mText).getSpanEnd(tag);
+ } else {
+ return -1;
+ }
+ }
+
+ @Override
+ public int getSpanFlags(Object tag) {
+ if (mText instanceof Spanned) {
+ return ((Spanned) mText).getSpanFlags(tag);
+ } else {
+ return 0;
+ }
+ }
+
+ @Override
+ public int nextSpanTransition(int start, int limit, Class type) {
+ if (mText instanceof Spanned) {
+ return ((Spanned) mText).nextSpanTransition(start, limit, type);
+ } else {
+ return mText.length();
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+ // CharSequence overrides.
+ //
+ // Just proxy for underlying mText.
+
+ @Override
+ public int length() {
+ return mText.length();
+ }
+
+ @Override
+ public char charAt(int index) {
+ // TODO: Should this be index + mStart ?
+ return mText.charAt(index);
+ }
+
+ @Override
+ public CharSequence subSequence(int start, int end) {
+ // TODO: return MeasuredText.
+ // TODO: Should this be index + mStart, end + mStart ?
+ return mText.subSequence(start, end);
+ }
+
+ @Override
+ public String toString() {
+ return mText.toString();
+ }
+}
diff --git a/core/java/android/text/PrecomputedText.java b/core/java/android/text/PrecomputedText.java
deleted file mode 100644
index 39fc2bd52db4..000000000000
--- a/core/java/android/text/PrecomputedText.java
+++ /dev/null
@@ -1,412 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.text;
-
-import android.annotation.IntRange;
-import android.annotation.NonNull;
-import android.util.IntArray;
-
-import com.android.internal.util.Preconditions;
-
-import java.util.ArrayList;
-
-/**
- * A text which has the character metrics data
- *
- * This text holds a part of the text layout result. You can accelerate
- * {@link android.widget.TextView} or {@link StaticLayout} by using this text.
- *
- * <pre>
- * Example of background measurement.
- * <code>
- * void asyncSetText(final TextView textView, final String expensiveLongString, Handler handler) {
- * final PrecomputedText.Params params = textView.getTextParams();
- * handler.post(() -> {
- * final PrecomputedText precomputedText
- * = PrecomputedText.create(expensiveLongString, params);
- * textView.post(() -> {
- * textView.setPrecomputedTextOrThrow(precomputedText);
- * });
- * });
- * }
- * </code>
- * </pre>
- *
- * Note that the {@link PrecomputedText} created from different parameters of the target
- * {@link android.widget.TextView} will be rejected internally and compute the text layout again
- * with the current {@link android.widget.TextView} parameters.
- */
-public class PrecomputedText {
- private static final char LINE_FEED = '\n';
-
- /**
- * The information required for building {@link PrecomputedText}.
- *
- * Contains information required for precomputing text measurement metadata, so it can be done
- * in isolation of a {@link android.widget.TextView} or {@link StaticLayout}, when final layout
- * constraints are not known.
- */
- public static class Params {
- // The TextPaint used for measurement.
- private final @NonNull TextPaint mPaint;
-
- // The requested text direction.
- private final @NonNull TextDirectionHeuristic mTextDir;
-
- // The break strategy for this measured text.
- private final @Layout.BreakStrategy int mBreakStrategy;
-
- // The hyphenation frequency for this measured text.
- private final @Layout.HyphenationFrequency int mHyphenationFrequency;
-
- /**
- * A builder for creating {@link Params}.
- */
- public static class Builder {
- // The TextPaint used for measurement.
- private final @NonNull TextPaint mPaint;
-
- // The requested text direction.
- private TextDirectionHeuristic mTextDir = TextDirectionHeuristics.FIRSTSTRONG_LTR;
-
- // The break strategy for this measured text.
- private @Layout.BreakStrategy int mBreakStrategy = Layout.BREAK_STRATEGY_HIGH_QUALITY;
-
- // The hyphenation frequency for this measured text.
- private @Layout.HyphenationFrequency int mHyphenationFrequency =
- Layout.HYPHENATION_FREQUENCY_NORMAL;
-
- /**
- * Builder constructor
- *
- * @param paint The paint to be used for drawing
- */
- public Builder(@NonNull TextPaint paint) {
- mPaint = paint;
- }
-
- /**
- * Set the line break strategy
- *
- * The default value is {@link Layout#BREAK_STRATEGY_HIGH_QUALITY}.
- *
- * @param strategy The break strategy
- * @return this builder, useful for chaining
- */
- public Builder setBreakStrategy(@Layout.BreakStrategy int strategy) {
- mBreakStrategy = strategy;
- return this;
- }
-
- /**
- * Set the hyphenation frequency
- *
- * The default value is {@link Layout#HYPHENATION_FREQUENCY_NORMAL}.
- *
- * @param frequency The hyphenation frequency
- * @return this builder, useful for chaining
- */
- public Builder setHyphenationFrequency(@Layout.HyphenationFrequency int frequency) {
- mHyphenationFrequency = frequency;
- return this;
- }
-
- /**
- * Set the text direction heuristic
- *
- * The default value is {@link TextDirectionHeuristics#FIRSTSTRONG_LTR}.
- *
- * @param textDir The text direction heuristic for resolving bidi behavior
- * @return this builder, useful for chaining
- */
- public Builder setTextDirection(@NonNull TextDirectionHeuristic textDir) {
- mTextDir = textDir;
- return this;
- }
-
- /**
- * Build the {@link Params}
- *
- * @return the layout parameter
- */
- public @NonNull Params build() {
- return new Params(mPaint, mTextDir, mBreakStrategy, mHyphenationFrequency);
- }
- }
-
- // Use Builder instead.
- /** @hide */
- public Params(@NonNull TextPaint paint, @NonNull TextDirectionHeuristic textDir,
- @Layout.BreakStrategy int strategy, @Layout.HyphenationFrequency int frequency) {
- mPaint = paint;
- mTextDir = textDir;
- mBreakStrategy = strategy;
- mHyphenationFrequency = frequency;
- }
-
- /**
- * Returns the {@link TextPaint} for this text
- *
- * @return A {@link TextPaint}
- */
- public @NonNull TextPaint getTextPaint() {
- return mPaint;
- }
-
- /**
- * Returns the {@link TextDirectionHeuristic} for this text
- *
- * @return A {@link TextDirectionHeuristic}
- */
- public @NonNull TextDirectionHeuristic getTextDirection() {
- return mTextDir;
- }
-
- /**
- * Returns the break strategy for this text
- *
- * @return A line break strategy
- */
- public @Layout.BreakStrategy int getBreakStrategy() {
- return mBreakStrategy;
- }
-
- /**
- * Returns the hyphenation frequency for this text
- *
- * @return A hyphenation frequency
- */
- public @Layout.HyphenationFrequency int getHyphenationFrequency() {
- return mHyphenationFrequency;
- }
-
- private boolean sameTextMetricsInternal(@NonNull TextPaint paint,
- @NonNull TextDirectionHeuristic textDir, @Layout.BreakStrategy int strategy,
- @Layout.HyphenationFrequency int frequency) {
- return mTextDir == textDir
- && mBreakStrategy == strategy
- && mHyphenationFrequency == frequency
- && mPaint.equalsForTextMeasurement(paint);
- }
-
- /**
- * Check if the same text layout.
- *
- * @return true if this and the given param result in the same text layout
- */
- public boolean sameTextMetrics(@NonNull Params param) {
- return sameTextMetricsInternal(param.mPaint, param.mTextDir, param.mBreakStrategy,
- param.mHyphenationFrequency);
- }
- };
-
- // The original text.
- private final @NonNull CharSequence mText;
-
- // The inclusive start offset of the measuring target.
- private final @IntRange(from = 0) int mStart;
-
- // The exclusive end offset of the measuring target.
- private final @IntRange(from = 0) int mEnd;
-
- private final @NonNull Params mParams;
-
- // The measured paragraph texts.
- private final @NonNull MeasuredParagraph[] mMeasuredParagraphs;
-
- // The sorted paragraph end offsets.
- private final @NonNull int[] mParagraphBreakPoints;
-
- /**
- * Create a new {@link PrecomputedText} which will pre-compute text measurement and glyph
- * positioning information.
- * <p>
- * This can be expensive, so computing this on a background thread before your text will be
- * presented can save work on the UI thread.
- * </p>
- *
- * @param text The text to be measured
- * @param param Parameters that define how text will be precomputed
- * @return A {@link PrecomputedText}
- */
- public static PrecomputedText create(@NonNull CharSequence text, @NonNull Params param) {
- return createInternal(text, param, 0, text.length(), true /* compute full Layout */);
- }
-
-
- /** @hide */
- public static PrecomputedText createWidthOnly(@NonNull CharSequence text, @NonNull Params param,
- @IntRange(from = 0) int start, @IntRange(from = 0) int end) {
- return createInternal(text, param, start, end, false /* compute width only */);
- }
-
- private static PrecomputedText createInternal(@NonNull CharSequence text, @NonNull Params param,
- @IntRange(from = 0) int start, @IntRange(from = 0) int end, boolean computeLayout) {
- Preconditions.checkNotNull(text);
- Preconditions.checkNotNull(param);
- final boolean needHyphenation = param.getBreakStrategy() != Layout.BREAK_STRATEGY_SIMPLE
- && param.getHyphenationFrequency() != Layout.HYPHENATION_FREQUENCY_NONE;
-
- final IntArray paragraphEnds = new IntArray();
- final ArrayList<MeasuredParagraph> measuredTexts = new ArrayList<>();
-
- int paraEnd = 0;
- for (int paraStart = start; paraStart < end; paraStart = paraEnd) {
- paraEnd = TextUtils.indexOf(text, LINE_FEED, paraStart, end);
- if (paraEnd < 0) {
- // No LINE_FEED(U+000A) character found. Use end of the text as the paragraph
- // end.
- paraEnd = end;
- } else {
- paraEnd++; // Includes LINE_FEED(U+000A) to the prev paragraph.
- }
-
- paragraphEnds.add(paraEnd);
- measuredTexts.add(MeasuredParagraph.buildForStaticLayout(
- param.getTextPaint(), text, paraStart, paraEnd, param.getTextDirection(),
- needHyphenation, computeLayout, null /* no recycle */));
- }
-
- return new PrecomputedText(text, start, end, param,
- measuredTexts.toArray(new MeasuredParagraph[measuredTexts.size()]),
- paragraphEnds.toArray());
- }
-
- // Use PrecomputedText.create instead.
- private PrecomputedText(@NonNull CharSequence text, @IntRange(from = 0) int start,
- @IntRange(from = 0) int end, @NonNull Params param,
- @NonNull MeasuredParagraph[] measuredTexts, @NonNull int[] paragraphBreakPoints) {
- mText = TextUtils.stringOrSpannedString(text);
- mStart = start;
- mEnd = end;
- mParams = param;
- mMeasuredParagraphs = measuredTexts;
- mParagraphBreakPoints = paragraphBreakPoints;
- }
-
- /**
- * Return the underlying text.
- */
- public @NonNull CharSequence getText() {
- return mText;
- }
-
- /**
- * Returns the inclusive start offset of measured region.
- * @hide
- */
- public @IntRange(from = 0) int getStart() {
- return mStart;
- }
-
- /**
- * Returns the exclusive end offset of measured region.
- * @hide
- */
- public @IntRange(from = 0) int getEnd() {
- return mEnd;
- }
-
- /**
- * Returns the layout parameters used to measure this text.
- */
- public @NonNull Params getParams() {
- return mParams;
- }
-
- /**
- * Returns the length of the paragraph of this text.
- */
- public @IntRange(from = 0) int getParagraphCount() {
- return mParagraphBreakPoints.length;
- }
-
- /**
- * Returns the paragraph start offset of the text.
- */
- public @IntRange(from = 0) int getParagraphStart(@IntRange(from = 0) int paraIndex) {
- Preconditions.checkArgumentInRange(paraIndex, 0, getParagraphCount(), "paraIndex");
- return paraIndex == 0 ? mStart : mParagraphBreakPoints[paraIndex - 1];
- }
-
- /**
- * Returns the paragraph end offset of the text.
- */
- public @IntRange(from = 0) int getParagraphEnd(@IntRange(from = 0) int paraIndex) {
- Preconditions.checkArgumentInRange(paraIndex, 0, getParagraphCount(), "paraIndex");
- return mParagraphBreakPoints[paraIndex];
- }
-
- /** @hide */
- public @NonNull MeasuredParagraph getMeasuredParagraph(@IntRange(from = 0) int paraIndex) {
- return mMeasuredParagraphs[paraIndex];
- }
-
- /**
- * Returns true if the given TextPaint gives the same result of text layout for this text.
- * @hide
- */
- 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.sameTextMetricsInternal(paint, textDir, strategy, frequency);
- }
-
- /** @hide */
- public int findParaIndex(@IntRange(from = 0) int pos) {
- // TODO: Maybe good to remove paragraph concept from PrecomputedText and add substring
- // layout support to StaticLayout.
- for (int i = 0; i < mParagraphBreakPoints.length; ++i) {
- if (pos < mParagraphBreakPoints[i]) {
- return i;
- }
- }
- throw new IndexOutOfBoundsException(
- "pos must be less than " + mParagraphBreakPoints[mParagraphBreakPoints.length - 1]
- + ", gave " + pos);
- }
-
- /** @hide */
- public float getWidth(@IntRange(from = 0) int start, @IntRange(from = 0) int end) {
- final int paraIndex = findParaIndex(start);
- final int paraStart = getParagraphStart(paraIndex);
- final int paraEnd = getParagraphEnd(paraIndex);
- if (start < paraStart || paraEnd < end) {
- throw new RuntimeException("Cannot measured across the paragraph:"
- + "para: (" + paraStart + ", " + paraEnd + "), "
- + "request: (" + start + ", " + end + ")");
- }
- return getMeasuredParagraph(paraIndex).getWidth(start - paraStart, end - paraStart);
- }
-
- /**
- * Returns the size of native PrecomputedText memory usage
- *
- * Note that this is not guaranteed to be accurate. Must be used only for testing purposes.
- * @hide
- */
- public int getMemoryUsage() {
- int r = 0;
- for (int i = 0; i < getParagraphCount(); ++i) {
- r += getMeasuredParagraph(i).getMemoryUsage();
- }
- return r;
- }
-}
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index 5869802e74e6..e62f4216f33a 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -78,23 +78,6 @@ public class StaticLayout extends Layout {
/**
* Obtain a builder for constructing StaticLayout objects.
*
- * @param source The precomputed text.
- * @param start The index of the start of the text
- * @param end The index + 1 of the end of the text
- * @param paint The base paint used for layout
- * @param width The width in pixels
- * @return a builder object used for constructing the StaticLayout
- */
- @NonNull
- public static Builder obtain(@NonNull PrecomputedText source, @IntRange(from = 0) int start,
- @IntRange(from = 0) int end, @NonNull TextPaint paint,
- @IntRange(from = 0) int width) {
- return obtain(source.getText(), source, start, end, paint, width);
- }
-
- /**
- * Obtain a builder for constructing StaticLayout objects.
- *
* @param source The text to be laid out, optionally with spans
* @param start The index of the start of the text
* @param end The index + 1 of the end of the text
@@ -106,12 +89,6 @@ public class StaticLayout extends Layout {
public static Builder obtain(@NonNull CharSequence source, @IntRange(from = 0) int start,
@IntRange(from = 0) int end, @NonNull TextPaint paint,
@IntRange(from = 0) int width) {
- return obtain(source, null, start, end, paint, width);
- }
-
- private static Builder obtain(@NonNull CharSequence source, @Nullable PrecomputedText text,
- @IntRange(from = 0) int start, @IntRange(from = 0) int end,
- @NonNull TextPaint paint, @IntRange(from = 0) int width) {
Builder b = sPool.acquire();
if (b == null) {
b = new Builder();
@@ -119,7 +96,6 @@ public class StaticLayout extends Layout {
// set default initial values
b.mText = source;
- b.mPrecomputed = text;
b.mStart = start;
b.mEnd = end;
b.mPaint = paint;
@@ -452,7 +428,6 @@ public class StaticLayout extends Layout {
}
private CharSequence mText;
- private PrecomputedText mPrecomputed;
private int mStart;
private int mEnd;
private TextPaint mPaint;
@@ -515,7 +490,7 @@ public class StaticLayout extends Layout {
float spacingmult, float spacingadd,
boolean includepad,
TextUtils.TruncateAt ellipsize, int ellipsizedWidth) {
- this(source, null /* precomputed */, bufstart, bufend, paint, outerwidth, align,
+ this(source, bufstart, bufend, paint, outerwidth, align,
TextDirectionHeuristics.FIRSTSTRONG_LTR,
spacingmult, spacingadd, includepad, ellipsize, ellipsizedWidth, Integer.MAX_VALUE);
}
@@ -525,16 +500,7 @@ public class StaticLayout extends Layout {
* @deprecated Use {@link Builder} instead.
*/
@Deprecated
- public StaticLayout(CharSequence source, int bufstart, int bufend, TextPaint paint,
- int outerwidth, Alignment align, TextDirectionHeuristic textDir,
- float spacingmult, float spacingadd, boolean includepad,
- TextUtils.TruncateAt ellipsize, int ellipsizedWidth, int maxLines) {
- this(source, null /* precomputed */, bufstart, bufend, paint, outerwidth, align, textDir,
- spacingmult, spacingadd, includepad, ellipsize, ellipsizedWidth, maxLines);
- }
-
- /** @hide */
- public StaticLayout(CharSequence source, PrecomputedText precomputed, int bufstart, int bufend,
+ public StaticLayout(CharSequence source, int bufstart, int bufend,
TextPaint paint, int outerwidth,
Alignment align, TextDirectionHeuristic textDir,
float spacingmult, float spacingadd,
@@ -545,7 +511,6 @@ public class StaticLayout extends Layout {
: (source instanceof Spanned)
? new SpannedEllipsizer(source)
: new Ellipsizer(source),
- (ellipsize == null) ? precomputed : null,
paint, outerwidth, align, textDir, spacingmult, spacingadd);
Builder b = Builder.obtain(source, bufstart, bufend, paint, outerwidth)
@@ -686,20 +651,43 @@ public class StaticLayout extends Layout {
b.mJustificationMode != Layout.JUSTIFICATION_MODE_NONE,
indents, mLeftPaddings, mRightPaddings);
- PrecomputedText measured = null;
- final Spanned spanned = (b.mText instanceof Spanned) ? (Spanned) b.mText : null;
- if (b.mPrecomputed != null) {
- if (b.mPrecomputed.canUseMeasuredResult(bufStart, bufEnd, textDir, paint,
- b.mBreakStrategy, b.mHyphenationFrequency)) {
- measured = b.mPrecomputed;
+ MeasuredText measured = null;
+ final Spanned spanned;
+ final boolean canUseMeasuredText;
+ if (source instanceof MeasuredText) {
+ measured = (MeasuredText) source;
+
+ if (bufStart != measured.getStart() || bufEnd != measured.getEnd()) {
+ // The buffer position has changed. Re-measure here.
+ canUseMeasuredText = false;
+ } else if (b.mBreakStrategy != measured.getBreakStrategy()
+ || b.mHyphenationFrequency != measured.getHyphenationFrequency()) {
+ // The computed hyphenation pieces may not be able to used. Re-measure it.
+ canUseMeasuredText = false;
+ } else {
+ // We can use measured information.
+ canUseMeasuredText = true;
}
- }
- if (measured == null) {
- final PrecomputedText.Params param = new PrecomputedText.Params(paint, textDir,
- b.mBreakStrategy, b.mHyphenationFrequency);
- measured = PrecomputedText.createWidthOnly(source, param, bufStart, bufEnd);
+ } else {
+ canUseMeasuredText = false;
}
+ if (!canUseMeasuredText) {
+ measured = new MeasuredText.Builder(source, paint)
+ .setRange(bufStart, bufEnd)
+ .setTextDirection(textDir)
+ .setBreakStrategy(b.mBreakStrategy)
+ .setHyphenationFrequency(b.mHyphenationFrequency)
+ .build(false /* full layout is not necessary for line breaking */);
+ spanned = (source instanceof Spanned) ? (Spanned) source : null;
+ } else {
+ final CharSequence original = measured.getText();
+ spanned = (original instanceof Spanned) ? (Spanned) original : null;
+ // Overwrite with the one when measured.
+ // TODO: Give an option for developer not to overwrite and measure again here?
+ textDir = measured.getTextDir();
+ paint = measured.getPaint();
+ }
try {
for (int paraIndex = 0; paraIndex < measured.getParagraphCount(); paraIndex++) {
diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java
index be5bb4d1809a..55367dcce47e 100644
--- a/core/java/android/text/TextLine.java
+++ b/core/java/android/text/TextLine.java
@@ -16,7 +16,6 @@
package android.text;
-import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.Canvas;
@@ -61,7 +60,7 @@ public class TextLine {
private char[] mChars;
private boolean mCharsValid;
private Spanned mSpanned;
- private PrecomputedText mComputed;
+ private MeasuredText mMeasured;
// Additional width of whitespace for justification. This value is per whitespace, thus
// the line width will increase by mAddedWidth x (number of stretchable whitespaces).
@@ -120,7 +119,7 @@ public class TextLine {
tl.mSpanned = null;
tl.mTabs = null;
tl.mChars = null;
- tl.mComputed = null;
+ tl.mMeasured = null;
tl.mMetricAffectingSpanSpanSet.recycle();
tl.mCharacterStyleSpanSet.recycle();
@@ -150,31 +149,10 @@ public class TextLine {
* @param tabStops the tabStops. Can be null.
*/
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
- public void set(TextPaint paint, CharSequence text, int start,
- int limit, int dir, Directions directions, boolean hasTabs, TabStops tabStops) {
- set(paint, text, null, start, limit, dir, directions, hasTabs, tabStops);
- }
-
- /**
- * Initializes a TextLine and prepares it for use.
- *
- * @param paint the base paint for the line
- * @param text the text, can be Styled
- * @param precomputed the precomputed text
- * @param start the start of the line relative to the text
- * @param limit the limit of the line relative to the text
- * @param dir the paragraph direction of this line
- * @param directions the directions information of this line
- * @param hasTabs true if the line might contain tabs
- * @param tabStops the tabStops.
- */
- public void set(@NonNull TextPaint paint, @NonNull CharSequence text,
- @Nullable PrecomputedText precomputed, @IntRange(from = 0) int start,
- @IntRange(from = 0) int limit, int dir, @NonNull Directions directions, boolean hasTabs,
- @Nullable TabStops tabStops) {
+ public void set(TextPaint paint, CharSequence text, int start, int limit, int dir,
+ Directions directions, boolean hasTabs, TabStops tabStops) {
mPaint = paint;
mText = text;
- mComputed = precomputed;
mStart = start;
mLen = limit - start;
mDir = dir;
@@ -192,6 +170,14 @@ public class TextLine {
hasReplacement = mReplacementSpanSpanSet.numberOfSpans > 0;
}
+ mMeasured = null;
+ if (text instanceof MeasuredText) {
+ MeasuredText mt = (MeasuredText) text;
+ if (mt.canUseMeasuredResult(paint)) {
+ mMeasured = mt;
+ }
+ }
+
mCharsValid = hasReplacement || hasTabs || directions != Layout.DIRS_ALL_LEFT_TO_RIGHT;
if (mCharsValid) {
@@ -760,12 +746,12 @@ public class TextLine {
return wp.getRunAdvance(mChars, start, end, contextStart, contextEnd, runIsRtl, offset);
} else {
final int delta = mStart;
- if (mComputed == null) {
+ if (mMeasured == null) {
// TODO: Enable measured getRunAdvance for ReplacementSpan and RTL text.
return wp.getRunAdvance(mText, delta + start, delta + end,
delta + contextStart, delta + contextEnd, runIsRtl, delta + offset);
} else {
- return mComputed.getWidth(start + delta, end + delta);
+ return mMeasured.getWidth(start + delta, end + delta);
}
}
}
diff --git a/core/java/android/util/LongArray.java b/core/java/android/util/LongArray.java
index 9b0489ca5c6e..f9454d962eb4 100644
--- a/core/java/android/util/LongArray.java
+++ b/core/java/android/util/LongArray.java
@@ -18,9 +18,11 @@ package android.util;
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 long primitives.
*
@@ -216,4 +218,17 @@ public class LongArray implements Cloneable {
throw new ArrayIndexOutOfBoundsException(mSize, index);
}
}
+
+ /**
+ * Test if each element of {@code a} equals corresponding element from {@code b}
+ */
+ public static boolean elementsEqual(LongArray a, LongArray b) {
+ if (a.mSize != b.mSize) return false;
+ for (int i = 0; i < a.mSize; i++) {
+ if (a.get(i) != b.get(i)) {
+ return false;
+ }
+ }
+ return true;
+ }
}
diff --git a/core/java/android/view/RecordingCanvas.java b/core/java/android/view/RecordingCanvas.java
index fc7d828de12e..fbb862be54ef 100644
--- a/core/java/android/view/RecordingCanvas.java
+++ b/core/java/android/view/RecordingCanvas.java
@@ -34,7 +34,7 @@ import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.TemporaryBuffer;
import android.text.GraphicsOperations;
-import android.text.PrecomputedText;
+import android.text.MeasuredText;
import android.text.SpannableString;
import android.text.SpannedString;
import android.text.TextUtils;
@@ -507,8 +507,8 @@ public class RecordingCanvas extends Canvas {
TextUtils.getChars(text, contextStart, contextEnd, buf, 0);
long measuredTextPtr = 0;
int measuredTextOffset = 0;
- if (text instanceof PrecomputedText) {
- PrecomputedText mt = (PrecomputedText) text;
+ if (text instanceof MeasuredText) {
+ MeasuredText mt = (MeasuredText) text;
int paraIndex = mt.findParaIndex(start);
if (end <= mt.getParagraphEnd(paraIndex)) {
// Only support if the target is in the same paragraph.
@@ -641,7 +641,7 @@ public class RecordingCanvas extends Canvas {
@FastNative
private static native void nDrawTextRun(long nativeCanvas, char[] text, int start, int count,
int contextStart, int contextCount, float x, float y, boolean isRtl, long nativePaint,
- long nativePrecomputedText, int measuredTextOffset);
+ long nativeMeasuredText, int measuredTextOffset);
@FastNative
private static native void nDrawTextOnPath(long nativeCanvas, char[] text, int index, int count,
diff --git a/core/java/android/view/RenderNodeAnimator.java b/core/java/android/view/RenderNodeAnimator.java
index c4a716011765..d26a2f643ed4 100644
--- a/core/java/android/view/RenderNodeAnimator.java
+++ b/core/java/android/view/RenderNodeAnimator.java
@@ -158,7 +158,7 @@ public class RenderNodeAnimator extends Animator {
}
private void applyInterpolator() {
- if (mInterpolator == null) return;
+ if (mInterpolator == null || mNativePtr == null) return;
long ni;
if (isNativeInterpolator(mInterpolator)) {
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 417a72530b72..5b1dd5c88cae 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -3203,7 +3203,7 @@ public class AccessibilityNodeInfo implements Parcelable {
fieldIndex++;
if (mConnectionId != DEFAULT.mConnectionId) nonDefaultFields |= bitAt(fieldIndex);
fieldIndex++;
- if (!Objects.equals(mChildNodeIds, DEFAULT.mChildNodeIds)) {
+ if (!LongArray.elementsEqual(mChildNodeIds, DEFAULT.mChildNodeIds)) {
nonDefaultFields |= bitAt(fieldIndex);
}
fieldIndex++;
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index f6e771a9605e..5710db3ce8e0 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -80,8 +80,8 @@ import android.text.GraphicsOperations;
import android.text.InputFilter;
import android.text.InputType;
import android.text.Layout;
+import android.text.MeasuredText;
import android.text.ParcelableSpan;
-import android.text.PrecomputedText;
import android.text.Selection;
import android.text.SpanWatcher;
import android.text.Spannable;
@@ -637,7 +637,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
private CharSequence mText;
private CharSequence mTransformed;
private BufferType mBufferType = BufferType.NORMAL;
- private PrecomputedText mPrecomputed;
private CharSequence mHint;
private Layout mHintLayout;
@@ -4086,80 +4085,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
/**
- * Gets the parameters for text layout precomputation, for use with {@link PrecomputedText}
- *
- * @return A current {@link PrecomputedText.Params}
- */
- public @NonNull PrecomputedText.Params getTextMetricsParams() {
- return new PrecomputedText.Params(new TextPaint(mTextPaint), getTextDirectionHeuristic(),
- mBreakStrategy, mHyphenationFrequency);
- }
-
- /**
- * Apply the text layout parameter.
- */
- public void setTextMetricsParams(@NonNull PrecomputedText.Params params) {
- mTextPaint.set(params.getTextPaint());
- mTextDir = params.getTextDirection();
- mBreakStrategy = params.getBreakStrategy();
- mHyphenationFrequency = params.getHyphenationFrequency();
- if (mLayout != null) {
- nullLayouts();
- requestLayout();
- invalidate();
- }
- }
-
- /**
- * Sets the precomputed text.
- *
- * If the parameters for the precomputed text is different from current text view parameters,
- * apply the parameteres to the text view too.
- *
- * @param text A precomputed text.
- */
- public void setPrecomputedTextAndParams(@NonNull PrecomputedText text) {
- Preconditions.checkNotNull(text);
- final PrecomputedText.Params params = text.getParams();
- if (!params.sameTextMetrics(getTextMetricsParams())) {
- setTextMetricsParams(params);
- }
- setText(text.getText());
- if (mTransformed != text.getText()) {
- // setText modified given text for some reasons, selection, transformation, etc.
- // Can't use computed result.
- return;
- } else {
- mPrecomputed = text;
- }
- }
-
- /**
- * Sets the precomputed text.
- *
- * If the parameters for the precomputed text is different from current text view parameters,
- * throws {@link IllegalArgumentException}.
- *
- * @param text A precomputed text.
- */
- public void setPrecomputedTextOrThrow(@NonNull PrecomputedText text) {
- Preconditions.checkNotNull(text);
- final PrecomputedText.Params params = text.getParams();
- if (!params.sameTextMetrics(getTextMetricsParams())) {
- throw new IllegalArgumentException(
- "The precomputed configuration is different from this TextView.");
- }
- setText(text.getText());
- if (mTransformed != text.getText()) {
- // setText modified given text for some reasons, selection, transformation, etc.
- // Can't use computed result.
- // TODO: Do we throw an exception here too?
- } else {
- mPrecomputed = text;
- }
- }
-
- /**
* Set justification mode. The default value is {@link Layout#JUSTIFICATION_MODE_NONE}. If the
* last line is too short for justification, the last line will be displayed with the
* alignment set by {@link android.view.View#setTextAlignment}.
@@ -5594,7 +5519,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
private void setText(CharSequence text, BufferType type,
boolean notifyBefore, int oldlen) {
- mPrecomputed = null;
mTextSetFromXmlOrResourceId = false;
if (text == null) {
text = "";
@@ -5653,7 +5577,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
if (imm != null) imm.restartInput(this);
} else if (type == BufferType.SPANNABLE || mMovement != null) {
text = mSpannableFactory.newSpannable(text);
- } else if (!(text instanceof CharWrapper)) {
+ } else if (!(text instanceof MeasuredText || text instanceof CharWrapper)) {
text = TextUtils.stringOrSpannedString(text);
}
@@ -8320,8 +8244,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
result = builder.build();
} else {
if (boring == UNKNOWN_BORING) {
- boring = BoringLayout.isBoring(mTransformed, mPrecomputed, mTextPaint, mTextDir,
- mBoring);
+ boring = BoringLayout.isBoring(mTransformed, mTextPaint, mTextDir, mBoring);
if (boring != null) {
mBoring = boring;
}
@@ -8359,15 +8282,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
}
if (result == null) {
- StaticLayout.Builder builder;
- if (mPrecomputed != null) {
- builder = StaticLayout.Builder.obtain(mPrecomputed, 0,
- mPrecomputed.getText().length(), mTextPaint, wantWidth);
- } else {
- builder = StaticLayout.Builder.obtain(mTransformed, 0, mTransformed.length(),
- mTextPaint, wantWidth);
- }
- builder.setAlignment(alignment)
+ StaticLayout.Builder builder = StaticLayout.Builder.obtain(mTransformed,
+ 0, mTransformed.length(), mTextPaint, wantWidth)
+ .setAlignment(alignment)
.setTextDirection(mTextDir)
.setLineSpacing(mSpacingAdd, mSpacingMult)
.setIncludePad(mIncludePad)
@@ -8494,8 +8411,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
if (des < 0) {
- boring = BoringLayout.isBoring(mTransformed, mPrecomputed, mTextPaint, mTextDir,
- mBoring);
+ boring = BoringLayout.isBoring(mTransformed, mTextPaint, mTextDir, mBoring);
if (boring != null) {
mBoring = boring;
}
@@ -11780,9 +11696,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
/**
- * Returns the current {@link TextDirectionHeuristic}
- *
- * @return A {@link TextDirectionHeuristic}.
* @hide
*/
protected TextDirectionHeuristic getTextDirectionHeuristic() {
diff --git a/core/jni/OWNERS b/core/jni/OWNERS
new file mode 100644
index 000000000000..e37452d98eec
--- /dev/null
+++ b/core/jni/OWNERS
@@ -0,0 +1,7 @@
+# Camera
+per-file *Camera*,*camera* = cychen@google.com
+per-file *Camera*,*camera* = epeev@google.com
+per-file *Camera*,*camera* = etalvala@google.com
+per-file *Camera*,*camera* = shuzhenwang@google.com
+per-file *Camera*,*camera* = yinchiayeh@google.com
+per-file *Camera*,*camera* = zhijunhe@google.com
diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp
index 2c05d0b976fc..482d028a67f3 100644
--- a/core/jni/android/graphics/Paint.cpp
+++ b/core/jni/android/graphics/Paint.cpp
@@ -1008,23 +1008,6 @@ namespace PaintGlue {
return paint->getLooper() && paint->getLooper()->asABlurShadow(nullptr);
}
- static jboolean equalsForTextMeasurement(jlong lPaint, jlong rPaint) {
- if (lPaint == rPaint) {
- return true;
- }
- Paint* leftPaint = reinterpret_cast<Paint*>(lPaint);
- Paint* rightPaint = reinterpret_cast<Paint*>(rPaint);
-
- const Typeface* leftTypeface = Typeface::resolveDefault(leftPaint->getAndroidTypeface());
- const Typeface* rightTypeface = Typeface::resolveDefault(rightPaint->getAndroidTypeface());
- minikin::MinikinPaint leftMinikinPaint
- = MinikinUtils::prepareMinikinPaint(leftPaint, leftTypeface);
- minikin::MinikinPaint rightMinikinPaint
- = MinikinUtils::prepareMinikinPaint(rightPaint, rightTypeface);
-
- return leftMinikinPaint == rightMinikinPaint;
- }
-
}; // namespace PaintGlue
static const JNINativeMethod methods[] = {
@@ -1124,8 +1107,7 @@ static const JNINativeMethod methods[] = {
{"nGetStrikeThruPosition","(J)F", (void*) PaintGlue::getStrikeThruPosition},
{"nGetStrikeThruThickness","(J)F", (void*) PaintGlue::getStrikeThruThickness},
{"nSetShadowLayer", "(JFFFI)V", (void*)PaintGlue::setShadowLayer},
- {"nHasShadowLayer", "(J)Z", (void*)PaintGlue::hasShadowLayer},
- {"nEqualsForTextMeasurement", "(JJ)Z", (void*)PaintGlue::equalsForTextMeasurement},
+ {"nHasShadowLayer", "(J)Z", (void*)PaintGlue::hasShadowLayer}
};
int register_android_graphics_Paint(JNIEnv* env) {
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 0218750d0f87..1f4425fef271 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3316,4 +3316,5 @@
<string-array name="config_wearActivityModeRadios">
<item>"wifi"</item>
</string-array>
+
</resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 0efb6f91fce0..59c742e5d499 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -547,7 +547,7 @@
<string name="global_action_voice_assist">Voice Assist</string>
<!-- label for item that locks the phone and enforces that it can't be unlocked without strong authentication. [CHAR LIMIT=15] -->
- <string name="global_action_lockdown">Enter lockdown</string>
+ <string name="global_action_lockdown">Lockdown</string>
<!-- Text to use when the number in a notification info is too large
(greater than status_bar_notification_info_maxnum, defined in
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 5a9dc7fa92be..b69ded80009c 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3262,4 +3262,5 @@
<java-symbol type="string" name="zen_upgrade_notification_title" />
<java-symbol type="string" name="zen_upgrade_notification_content" />
+
</resources>
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index dc8ed9efaa76..85cf9b6c3c91 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -495,6 +495,7 @@ public class SettingsBackupTest {
Settings.Secure.DOZE_ALWAYS_ON,
Settings.Secure.DOZE_PULSE_ON_LONG_PRESS,
Settings.Secure.EMERGENCY_ASSISTANCE_APPLICATION,
+ Settings.Secure.ENABLED_INPUT_METHODS, // Intentionally removed in P
Settings.Secure.ENABLED_NOTIFICATION_ASSISTANT,
Settings.Secure.ENABLED_NOTIFICATION_LISTENERS,
Settings.Secure.ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES,
diff --git a/graphics/java/android/graphics/BaseCanvas.java b/graphics/java/android/graphics/BaseCanvas.java
index 07df0454362c..eacb727099ed 100644
--- a/graphics/java/android/graphics/BaseCanvas.java
+++ b/graphics/java/android/graphics/BaseCanvas.java
@@ -22,7 +22,7 @@ import android.annotation.Nullable;
import android.annotation.Size;
import android.graphics.Canvas.VertexMode;
import android.text.GraphicsOperations;
-import android.text.PrecomputedText;
+import android.text.MeasuredText;
import android.text.SpannableString;
import android.text.SpannedString;
import android.text.TextUtils;
@@ -487,8 +487,8 @@ public abstract class BaseCanvas {
TextUtils.getChars(text, contextStart, contextEnd, buf, 0);
long measuredTextPtr = 0;
int measuredTextOffset = 0;
- if (text instanceof PrecomputedText) {
- PrecomputedText mt = (PrecomputedText) text;
+ if (text instanceof MeasuredText) {
+ MeasuredText mt = (MeasuredText) text;
int paraIndex = mt.findParaIndex(start);
if (end <= mt.getParagraphEnd(paraIndex)) {
// Only suppor the same paragraph.
@@ -647,7 +647,7 @@ public abstract class BaseCanvas {
private static native void nDrawTextRun(long nativeCanvas, char[] text, int start, int count,
int contextStart, int contextCount, float x, float y, boolean isRtl, long nativePaint,
- long nativePrecomputedText, int measuredTextOffset);
+ long nativeMeasuredText, int measuredTextOffset);
private static native void nDrawTextOnPath(long nativeCanvas, char[] text, int index, int count,
long nativePath, float hOffset, float vOffset, int bidiFlags, long nativePaint);
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index 42dac38affba..ed147e9e2ec7 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -2835,16 +2835,6 @@ public class Paint {
return result;
}
- /**
- * Returns true of the passed {@link Paint} will have the same effect on text measurement
- *
- * @param other A {@link Paint} object.
- * @return true if the other {@link Paint} has the same effect on text measurement.
- */
- public boolean equalsForTextMeasurement(@NonNull Paint other) {
- return nEqualsForTextMeasurement(mNativePaint, other.mNativePaint);
- }
-
// regular JNI
private static native long nGetNativeFinalizer();
private static native long nInit();
@@ -3012,6 +3002,4 @@ public class Paint {
private static native float nGetStrikeThruThickness(long paintPtr);
@CriticalNative
private static native void nSetTextSize(long paintPtr, float textSize);
- @CriticalNative
- private static native boolean nEqualsForTextMeasurement(long leftPaintPtr, long rightPaintPtr);
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java
index 1f67dfb568a6..9947dec14d48 100644
--- a/packages/SettingsLib/src/com/android/settingslib/Utils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java
@@ -1,6 +1,7 @@
package com.android.settingslib;
import android.annotation.ColorInt;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
@@ -17,10 +18,13 @@ import android.graphics.drawable.Drawable;
import android.location.LocationManager;
import android.net.ConnectivityManager;
import android.os.BatteryManager;
+import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
import android.print.PrintManager;
import android.provider.Settings;
+
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.UserIcons;
import com.android.settingslib.drawable.UserIconDrawable;
import com.android.settingslib.wrapper.LocationManagerWrapper;
@@ -30,6 +34,9 @@ public class Utils {
private static final String CURRENT_MODE_KEY = "CURRENT_MODE";
private static final String NEW_MODE_KEY = "NEW_MODE";
+ @VisibleForTesting
+ static final String STORAGE_MANAGER_SHOW_OPT_IN_PROPERTY =
+ "ro.storage_manager.show_opt_in";
private static Signature[] sSystemSignature;
private static String sPermissionControllerPackageName;
@@ -37,11 +44,11 @@ public class Utils {
private static String sSharedSystemSharedLibPackageName;
static final int[] WIFI_PIE = {
- com.android.internal.R.drawable.ic_wifi_signal_0,
- com.android.internal.R.drawable.ic_wifi_signal_1,
- com.android.internal.R.drawable.ic_wifi_signal_2,
- com.android.internal.R.drawable.ic_wifi_signal_3,
- com.android.internal.R.drawable.ic_wifi_signal_4
+ com.android.internal.R.drawable.ic_wifi_signal_0,
+ com.android.internal.R.drawable.ic_wifi_signal_1,
+ com.android.internal.R.drawable.ic_wifi_signal_2,
+ com.android.internal.R.drawable.ic_wifi_signal_3,
+ com.android.internal.R.drawable.ic_wifi_signal_4
};
public static void updateLocationEnabled(Context context, boolean enabled, int userId,
@@ -262,7 +269,7 @@ public class Utils {
*/
public static boolean isSystemPackage(Resources resources, PackageManager pm, PackageInfo pkg) {
if (sSystemSignature == null) {
- sSystemSignature = new Signature[]{ getSystemSignature(pm) };
+ sSystemSignature = new Signature[]{getSystemSignature(pm)};
}
if (sPermissionControllerPackageName == null) {
sPermissionControllerPackageName = pm.getPermissionControllerPackageName();
@@ -274,7 +281,7 @@ public class Utils {
sSharedSystemSharedLibPackageName = pm.getSharedSystemSharedLibraryPackageName();
}
return (sSystemSignature[0] != null
- && sSystemSignature[0].equals(getFirstSignature(pkg)))
+ && sSystemSignature[0].equals(getFirstSignature(pkg)))
|| pkg.packageName.equals(sPermissionControllerPackageName)
|| pkg.packageName.equals(sServicesSystemSharedLibPackageName)
|| pkg.packageName.equals(sSharedSystemSharedLibPackageName)
@@ -312,7 +319,6 @@ public class Utils {
* Returns the Wifi icon resource for a given RSSI level.
*
* @param level The number of bars to show (0-4)
- *
* @throws IllegalArgumentException if an invalid RSSI level is given.
*/
public static int getWifiIconResource(int level) {
@@ -342,4 +348,19 @@ public class Utils {
return !context.getSystemService(ConnectivityManager.class)
.isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
}
+
+ /** Returns if the automatic storage management feature is turned on or not. **/
+ public static boolean isStorageManagerEnabled(Context context) {
+ boolean isDefaultOn;
+ try {
+ // Turn off by default if the opt-in was shown.
+ isDefaultOn = !SystemProperties.getBoolean(STORAGE_MANAGER_SHOW_OPT_IN_PROPERTY, true);
+ } catch (Resources.NotFoundException e) {
+ isDefaultOn = false;
+ }
+ return Settings.Secure.getInt(context.getContentResolver(),
+ Settings.Secure.AUTOMATIC_STORAGE_MANAGER_ENABLED,
+ isDefaultOn ? 1 : 0)
+ != 0;
+ }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index f69944006a87..b380ac53ddad 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -17,6 +17,7 @@
package com.android.settingslib.wifi;
import android.annotation.IntDef;
+import android.annotation.MainThread;
import android.annotation.Nullable;
import android.app.AppGlobals;
import android.content.Context;
@@ -42,6 +43,8 @@ import android.net.wifi.WifiManager;
import android.net.wifi.WifiNetworkScoreCache;
import android.net.wifi.hotspot2.PasspointConfiguration;
import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
import android.os.Parcelable;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -57,6 +60,7 @@ import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import com.android.settingslib.R;
+import com.android.settingslib.utils.ThreadUtils;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -68,7 +72,14 @@ import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
-
+/**
+ * Represents a selectable Wifi Network for use in various wifi selection menus backed by
+ * {@link WifiTracker}.
+ *
+ * <p>An AccessPoint, which would be more fittingly named "WifiNetwork", is an aggregation of
+ * {@link ScanResult ScanResults} along with pertinent metadata (e.g. current connection info,
+ * network scores) required to successfully render the network to the user.
+ */
public class AccessPoint implements Comparable<AccessPoint> {
static final String TAG = "SettingsLib.AccessPoint";
@@ -288,11 +299,6 @@ public class AccessPoint implements Comparable<AccessPoint> {
mId = sLastId.incrementAndGet();
}
- AccessPoint(Context context, AccessPoint other) {
- mContext = context;
- copyFrom(other);
- }
-
AccessPoint(Context context, Collection<ScanResult> results) {
mContext = context;
@@ -346,33 +352,6 @@ public class AccessPoint implements Comparable<AccessPoint> {
}
/**
- * Copy accesspoint information. NOTE: We do not copy tag information because that is never
- * set on the internal copy.
- */
- void copyFrom(AccessPoint that) {
- this.ssid = that.ssid;
- this.bssid = that.bssid;
- this.security = that.security;
- this.mKey = that.mKey;
- this.networkId = that.networkId;
- this.pskType = that.pskType;
- this.mConfig = that.mConfig; //TODO: Watch out, this object is mutated.
- this.mRssi = that.mRssi;
- this.mInfo = that.mInfo;
- this.mNetworkInfo = that.mNetworkInfo;
- this.mScanResults.clear();
- this.mScanResults.addAll(that.mScanResults);
- this.mScoredNetworkCache.clear();
- this.mScoredNetworkCache.putAll(that.mScoredNetworkCache);
- this.mId = that.mId;
- this.mSpeed = that.mSpeed;
- this.mIsScoredNetworkMetered = that.mIsScoredNetworkMetered;
- this.mIsCarrierAp = that.mIsCarrierAp;
- this.mCarrierApEapType = that.mCarrierApEapType;
- this.mCarrierName = that.mCarrierName;
- }
-
- /**
* Returns a negative integer, zero, or a positive integer if this AccessPoint is less than,
* equal to, or greater than the other AccessPoint.
*
@@ -467,7 +446,7 @@ public class AccessPoint implements Comparable<AccessPoint> {
}
builder.append(",metered=").append(isMetered());
- if (WifiTracker.sVerboseLogging) {
+ if (isVerboseLoggingEnabled()) {
builder.append(",rssi=").append(mRssi);
builder.append(",scan cache size=").append(mScanResults.size());
}
@@ -546,7 +525,7 @@ public class AccessPoint implements Comparable<AccessPoint> {
mSpeed = generateAverageSpeedForSsid();
boolean changed = oldSpeed != mSpeed;
- if(WifiTracker.sVerboseLogging && changed) {
+ if(isVerboseLoggingEnabled() && changed) {
Log.i(TAG, String.format("%s: Set speed to %d", ssid, mSpeed));
}
return changed;
@@ -577,7 +556,7 @@ public class AccessPoint implements Comparable<AccessPoint> {
}
}
int speed = count == 0 ? Speed.NONE : totalSpeed / count;
- if (WifiTracker.sVerboseLogging) {
+ if (isVerboseLoggingEnabled()) {
Log.i(TAG, String.format("%s generated fallback speed is: %d", getSsidStr(), speed));
}
return roundToClosestSpeedEnum(speed);
@@ -913,7 +892,7 @@ public class AccessPoint implements Comparable<AccessPoint> {
}
}
- if (WifiTracker.sVerboseLogging) {
+ if (isVerboseLoggingEnabled()) {
summary.append(WifiUtils.buildLoggingSummary(this, config));
}
@@ -1070,12 +1049,12 @@ public class AccessPoint implements Comparable<AccessPoint> {
// Only update labels on visible rssi changes
updateSpeed();
if (mAccessPointListener != null) {
- mAccessPointListener.onLevelChanged(this);
+ ThreadUtils.postOnMainThread(() -> mAccessPointListener.onLevelChanged(this));
}
}
if (mAccessPointListener != null) {
- mAccessPointListener.onAccessPointChanged(this);
+ ThreadUtils.postOnMainThread(() -> mAccessPointListener.onAccessPointChanged(this));
}
if (!scanResults.isEmpty()) {
@@ -1123,10 +1102,10 @@ public class AccessPoint implements Comparable<AccessPoint> {
mNetworkInfo = null;
}
if (updated && mAccessPointListener != null) {
- mAccessPointListener.onAccessPointChanged(this);
+ ThreadUtils.postOnMainThread(() -> mAccessPointListener.onAccessPointChanged(this));
if (oldLevel != getLevel() /* current level */) {
- mAccessPointListener.onLevelChanged(this);
+ ThreadUtils.postOnMainThread(() -> mAccessPointListener.onLevelChanged(this));
}
}
@@ -1137,7 +1116,7 @@ public class AccessPoint implements Comparable<AccessPoint> {
mConfig = config;
networkId = config != null ? config.networkId : WifiConfiguration.INVALID_NETWORK_ID;
if (mAccessPointListener != null) {
- mAccessPointListener.onAccessPointChanged(this);
+ ThreadUtils.postOnMainThread(() -> mAccessPointListener.onAccessPointChanged(this));
}
}
@@ -1333,8 +1312,44 @@ public class AccessPoint implements Comparable<AccessPoint> {
return string;
}
+ /**
+ * Callbacks relaying changes to the AccessPoint representation.
+ *
+ * <p>All methods are invoked on the Main Thread.
+ */
public interface AccessPointListener {
- void onAccessPointChanged(AccessPoint accessPoint);
- void onLevelChanged(AccessPoint accessPoint);
+ /**
+ * Indicates a change to the externally visible state of the AccessPoint trigger by an
+ * update of ScanResults, saved configuration state, connection state, or score
+ * (labels/metered) state.
+ *
+ * <p>Clients should refresh their view of the AccessPoint to match the updated state when
+ * this is invoked. Overall this method is extraneous if clients are listening to
+ * {@link WifiTracker.WifiListener#onAccessPointsChanged()} callbacks.
+ *
+ * <p>Examples of changes include signal strength, connection state, speed label, and
+ * generally anything that would impact the summary string.
+ *
+ * @param accessPoint The accessPoint object the listener was registered on which has
+ * changed
+ */
+ @MainThread void onAccessPointChanged(AccessPoint accessPoint);
+
+ /**
+ * Indicates the "wifi pie signal level" has changed, retrieved via calls to
+ * {@link AccessPoint#getLevel()}.
+ *
+ * <p>This call is a subset of {@link #onAccessPointChanged(AccessPoint)} , hence is also
+ * extraneous if the client is already reacting to that or the
+ * {@link WifiTracker.WifiListener#onAccessPointsChanged()} callbacks.
+ *
+ * @param accessPoint The accessPoint object the listener was registered on whose level has
+ * changed
+ */
+ @MainThread void onLevelChanged(AccessPoint accessPoint);
+ }
+
+ private static boolean isVerboseLoggingEnabled() {
+ return WifiTracker.sVerboseLogging || Log.isLoggable(TAG, Log.VERBOSE);
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
index fac585e06306..ae544dd6dbe8 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
@@ -15,6 +15,7 @@
*/
package com.android.settingslib.wifi;
+import android.annotation.AnyThread;
import android.annotation.MainThread;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -48,8 +49,6 @@ import android.text.format.DateUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
-import android.util.SparseArray;
-import android.util.SparseIntArray;
import android.widget.Toast;
import com.android.settingslib.R;
@@ -58,6 +57,7 @@ import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnDestroy;
import com.android.settingslib.core.lifecycle.events.OnStart;
import com.android.settingslib.core.lifecycle.events.OnStop;
+import com.android.settingslib.utils.ThreadUtils;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -88,8 +88,17 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro
return Log.isLoggable(TAG, Log.DEBUG);
}
- /** verbose logging flag. this flag is set thru developer debugging options
- * and used so as to assist with in-the-field WiFi connectivity debugging */
+ private static boolean isVerboseLoggingEnabled() {
+ return WifiTracker.sVerboseLogging || Log.isLoggable(TAG, Log.VERBOSE);
+ }
+
+ /**
+ * Verbose logging flag set thru developer debugging options and used so as to assist with
+ * in-the-field WiFi connectivity debugging.
+ *
+ * <p>{@link #isVerboseLoggingEnabled()} should be read rather than referencing this value
+ * directly, to ensure adb TAG level verbose settings are respected.
+ */
public static boolean sVerboseLogging;
// TODO: Allow control of this?
@@ -104,7 +113,6 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro
private final NetworkRequest mNetworkRequest;
private final AtomicBoolean mConnected = new AtomicBoolean(false);
private final WifiListener mListener;
- @VisibleForTesting MainHandler mMainHandler;
@VisibleForTesting WorkHandler mWorkHandler;
private HandlerThread mWorkThread;
@@ -113,35 +121,17 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro
@GuardedBy("mLock")
private boolean mRegistered;
- /**
- * The externally visible access point list.
- *
- * Updated using main handler. Clone of this collection is returned from
- * {@link #getAccessPoints()}
- */
- private final List<AccessPoint> mAccessPoints = new ArrayList<>();
-
- /**
- * The internal list of access points, synchronized on itself.
- *
- * Never exposed outside this class.
- */
+ /** The list of AccessPoints, aggregated visible ScanResults with metadata. */
@GuardedBy("mLock")
private final List<AccessPoint> mInternalAccessPoints = new ArrayList<>();
/**
* Synchronization lock for managing concurrency between main and worker threads.
*
- * <p>This lock should be held for all background work.
- * TODO(b/37674366): Remove the worker thread so synchronization is no longer necessary.
+ * <p>This lock should be held for all modifications to {@link #mInternalAccessPoints}.
*/
private final Object mLock = new Object();
- //visible to both worker and main thread.
- @GuardedBy("mLock")
- private final AccessPointListenerAdapter mAccessPointListenerAdapter
- = new AccessPointListenerAdapter();
-
private final HashMap<String, Integer> mSeenBssids = new HashMap<>();
// TODO(sghuman): Change this to be keyed on AccessPoint.getKey
@@ -161,6 +151,12 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro
@VisibleForTesting
Scanner mScanner;
+ /**
+ * Tracks whether fresh scan results have been received since scanning start.
+ *
+ * <p>If this variable is false, we will not evict the scan result cache or invoke callbacks
+ * so that we do not update the UI with stale data / clear out existing UI elements prematurely.
+ */
@GuardedBy("mLock")
private boolean mStaleScanResults = true;
@@ -209,12 +205,11 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro
NetworkScoreManager networkScoreManager,
IntentFilter filter) {
mContext = context;
- mMainHandler = new MainHandler(Looper.getMainLooper());
mWifiManager = wifiManager;
mListener = new WifiListenerWrapper(wifiListener);
mConnectivityManager = connectivityManager;
- // check if verbose logging has been turned on or off
+ // check if verbose logging developer option has been turned on or off
sVerboseLogging = (mWifiManager.getVerboseLoggingLevel() > 0);
mFilter = filter;
@@ -226,6 +221,7 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro
mNetworkScoreManager = networkScoreManager;
+ // TODO(sghuman): Remove this and create less hacky solution for testing
final HandlerThread workThread = new HandlerThread(TAG
+ "{" + Integer.toHexString(System.identityHashCode(this)) + "}",
Process.THREAD_PRIORITY_BACKGROUND);
@@ -238,6 +234,8 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro
* @param workThread substitute Handler thread, for testing purposes only
*/
@VisibleForTesting
+ // TODO(sghuman): Remove this method, this needs to happen in a factory method and be passed in
+ // during construction
void setWorkThread(HandlerThread workThread) {
mWorkThread = workThread;
mWorkHandler = new WorkHandler(workThread.getLooper());
@@ -270,32 +268,29 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro
mLastNetworkInfo = mConnectivityManager.getNetworkInfo(mWifiManager.getCurrentNetwork());
final List<ScanResult> newScanResults = mWifiManager.getScanResults();
- if (sVerboseLogging) {
+ if (isVerboseLoggingEnabled()) {
Log.i(TAG, "Fetched scan results: " + newScanResults);
}
List<WifiConfiguration> configs = mWifiManager.getConfiguredNetworks();
mInternalAccessPoints.clear();
updateAccessPointsLocked(newScanResults, configs);
-
- // Synchronously copy access points
- mMainHandler.removeMessages(MainHandler.MSG_ACCESS_POINT_CHANGED);
- mMainHandler.handleMessage(
- Message.obtain(mMainHandler, MainHandler.MSG_ACCESS_POINT_CHANGED));
- if (sVerboseLogging) {
- Log.i(TAG, "force update - external access point list:\n" + mAccessPoints);
- }
}
}
/**
* Temporarily stop scanning for wifi networks.
+ *
+ * <p>Sets {@link #mStaleScanResults} to true.
*/
- public void pauseScanning() {
+ private void pauseScanning() {
if (mScanner != null) {
mScanner.pause();
mScanner = null;
}
+ synchronized (mLock) {
+ mStaleScanResults = true;
+ }
}
/**
@@ -387,11 +382,9 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro
mRegistered = false;
}
unregisterScoreCache();
- pauseScanning();
+ pauseScanning(); // and set mStaleScanResults
mWorkHandler.removePendingMessages();
- mMainHandler.removePendingMessages();
- mStaleScanResults = true;
}
}
@@ -409,12 +402,19 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro
}
/**
- * Gets the current list of access points. Should be called from main thread, otherwise
- * expect inconsistencies
+ * Gets the current list of access points.
+ *
+ * <p>This method is can be called on an abitrary thread by clients, but is normally called on
+ * the UI Thread by the rendering App.
*/
- @MainThread
+ @AnyThread
public List<AccessPoint> getAccessPoints() {
- return new ArrayList<>(mAccessPoints);
+ // TODO(sghuman): Investigate how to eliminate or reduce the need for locking now that we
+ // have transitioned to a single worker thread model.
+
+ synchronized (mLock) {
+ return new ArrayList<>(mInternalAccessPoints);
+ }
}
public WifiManager getManager() {
@@ -447,6 +447,8 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro
}
private void handleResume() {
+ // TODO(sghuman): Investigate removing this and replacing it with a cache eviction call
+ // instead.
mScanResultCache.clear();
mSeenBssids.clear();
}
@@ -509,7 +511,7 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro
private void updateAccessPoints() {
List<WifiConfiguration> configs = mWifiManager.getConfiguredNetworks();
final List<ScanResult> newScanResults = mWifiManager.getScanResults();
- if (sVerboseLogging) {
+ if (isVerboseLoggingEnabled()) {
Log.i(TAG, "Fetched scan results: " + newScanResults);
}
@@ -524,11 +526,15 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro
* Update the internal list of access points.
*
* <p>Do not call directly (except for forceUpdate), use {@link #updateAccessPoints()} which
- * respects {@link #mStaleScanResults}.
+ * acquires the lock first.
*/
@GuardedBy("mLock")
private void updateAccessPointsLocked(final List<ScanResult> newScanResults,
List<WifiConfiguration> configs) {
+ // TODO(sghuman): Reduce the synchronization time by only holding the lock when
+ // modifying lists exposed to operations on the MainThread (getAccessPoints, stopTracking,
+ // startTracking, etc).
+
WifiConfiguration connectionConfig = null;
if (mLastInfo != null) {
connectionConfig = getWifiConfigurationForNetworkId(
@@ -634,7 +640,7 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro
mInternalAccessPoints.clear();
mInternalAccessPoints.addAll(accessPoints);
- mMainHandler.sendEmptyMessage(MainHandler.MSG_ACCESS_POINT_CHANGED);
+ conditionallyNotifyListeners();
}
@VisibleForTesting
@@ -650,7 +656,6 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro
}
}
final AccessPoint accessPoint = new AccessPoint(mContext, scanResults);
- accessPoint.setListener(mAccessPointListenerAdapter);
return accessPoint;
}
@@ -661,16 +666,17 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro
if (cache.get(i).matches(config)) {
AccessPoint ret = cache.remove(i);
ret.loadConfig(config);
+
return ret;
}
}
final AccessPoint accessPoint = new AccessPoint(mContext, config);
- accessPoint.setListener(mAccessPointListenerAdapter);
return accessPoint;
}
private void updateNetworkInfo(NetworkInfo networkInfo) {
- /* sticky broadcasts can call this when wifi is disabled */
+
+ /* Sticky broadcasts can call this when wifi is disabled */
if (!mWifiManager.isWifiEnabled()) {
clearAccessPointsAndConditionallyUpdate();
return;
@@ -681,6 +687,10 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro
if (DBG()) {
Log.d(TAG, "mLastNetworkInfo set: " + mLastNetworkInfo);
}
+
+ if(networkInfo.isConnected() != mConnected.getAndSet(networkInfo.isConnected())) {
+ mListener.onConnectedChanged();
+ }
}
WifiConfiguration connectionConfig = null;
@@ -711,18 +721,25 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro
}
}
- if (reorder) Collections.sort(mInternalAccessPoints);
- if (updated) mMainHandler.sendEmptyMessage(MainHandler.MSG_ACCESS_POINT_CHANGED);
+ if (reorder) {
+ Collections.sort(mInternalAccessPoints);
+ }
+ if (updated) {
+ conditionallyNotifyListeners();
+ }
}
}
+ /**
+ * Clears the access point list and conditionally invokes
+ * {@link WifiListener#onAccessPointsChanged()} if required (i.e. the list was not already
+ * empty).
+ */
private void clearAccessPointsAndConditionallyUpdate() {
synchronized (mLock) {
if (!mInternalAccessPoints.isEmpty()) {
mInternalAccessPoints.clear();
- if (!mMainHandler.hasMessages(MainHandler.MSG_ACCESS_POINT_CHANGED)) {
- mMainHandler.sendEmptyMessage(MainHandler.MSG_ACCESS_POINT_CHANGED);
- }
+ mListener.onAccessPointsChanged();
}
}
}
@@ -745,27 +762,26 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro
}
if (updated) {
Collections.sort(mInternalAccessPoints);
- mMainHandler.sendEmptyMessage(MainHandler.MSG_ACCESS_POINT_CHANGED);
+ conditionallyNotifyListeners();
}
}
}
- private void updateWifiState(int state) {
- mWorkHandler.obtainMessage(WorkHandler.MSG_UPDATE_WIFI_STATE, state, 0).sendToTarget();
- if (!mWifiManager.isWifiEnabled()) {
- clearAccessPointsAndConditionallyUpdate();
- }
- }
-
@VisibleForTesting
final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
+ // No work should be performed in this Receiver, instead all operations should be passed
+ // off to the WorkHandler to avoid concurrent modification exceptions.
+
String action = intent.getAction();
if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) {
- updateWifiState(intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
- WifiManager.WIFI_STATE_UNKNOWN));
+ mWorkHandler.obtainMessage(
+ WorkHandler.MSG_UPDATE_WIFI_STATE,
+ intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
+ WifiManager.WIFI_STATE_UNKNOWN),
+ 0).sendToTarget();
} else if (WifiManager.SCAN_RESULTS_AVAILABLE_ACTION.equals(action)) {
mWorkHandler
.obtainMessage(
@@ -778,12 +794,6 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro
mWorkHandler.sendEmptyMessage(WorkHandler.MSG_UPDATE_ACCESS_POINTS);
} else if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(action)) {
NetworkInfo info = intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
-
- if(mConnected.get() != info.isConnected()) {
- mConnected.set(info.isConnected());
- mMainHandler.sendEmptyMessage(MainHandler.MSG_CONNECTED_CHANGED);
- }
-
mWorkHandler.obtainMessage(WorkHandler.MSG_UPDATE_NETWORK_INFO, info)
.sendToTarget();
mWorkHandler.sendEmptyMessage(WorkHandler.MSG_UPDATE_ACCESS_POINTS);
@@ -808,68 +818,8 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro
}
@VisibleForTesting
- final class MainHandler extends Handler {
- @VisibleForTesting static final int MSG_CONNECTED_CHANGED = 0;
- @VisibleForTesting static final int MSG_WIFI_STATE_CHANGED = 1;
- @VisibleForTesting static final int MSG_ACCESS_POINT_CHANGED = 2;
- private static final int MSG_RESUME_SCANNING = 3;
- private static final int MSG_PAUSE_SCANNING = 4;
-
- public MainHandler(Looper looper) {
- super(looper);
- }
-
- @Override
- public void handleMessage(Message msg) {
- if (mListener == null) {
- return;
- }
- switch (msg.what) {
- case MSG_CONNECTED_CHANGED:
- mListener.onConnectedChanged();
- break;
- case MSG_WIFI_STATE_CHANGED:
- mListener.onWifiStateChanged(msg.arg1);
- break;
- case MSG_ACCESS_POINT_CHANGED:
- // Only notify listeners of changes if we have fresh scan results, otherwise the
- // UI will be updated with stale results. We want to copy the APs regardless,
- // for instances where forceUpdate was invoked by the caller.
- if (mStaleScanResults) {
- copyAndNotifyListeners(false /*notifyListeners*/);
- } else {
- copyAndNotifyListeners(true /*notifyListeners*/);
- mListener.onAccessPointsChanged();
- }
- break;
- case MSG_RESUME_SCANNING:
- if (mScanner != null) {
- mScanner.resume();
- }
- break;
- case MSG_PAUSE_SCANNING:
- if (mScanner != null) {
- mScanner.pause();
- }
- synchronized (mLock) {
- mStaleScanResults = true;
- }
- break;
- }
- }
-
- void removePendingMessages() {
- removeMessages(MSG_ACCESS_POINT_CHANGED);
- removeMessages(MSG_CONNECTED_CHANGED);
- removeMessages(MSG_WIFI_STATE_CHANGED);
- removeMessages(MSG_PAUSE_SCANNING);
- removeMessages(MSG_RESUME_SCANNING);
- }
- }
-
- @VisibleForTesting
final class WorkHandler extends Handler {
- private static final int MSG_UPDATE_ACCESS_POINTS = 0;
+ @VisibleForTesting static final int MSG_UPDATE_ACCESS_POINTS = 0;
private static final int MSG_UPDATE_NETWORK_INFO = 1;
private static final int MSG_RESUME = 2;
private static final int MSG_UPDATE_WIFI_STATE = 3;
@@ -882,6 +832,8 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro
@Override
public void handleMessage(Message msg) {
+ // TODO(sghuman): Clean up synchronization to only be used when modifying collections
+ // exposed to the MainThread (through onStart, onStop, forceUpdate).
synchronized (mLock) {
processMessage(msg);
}
@@ -911,6 +863,7 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro
mScanner.resume();
}
} else {
+ clearAccessPointsAndConditionallyUpdate();
mLastInfo = null;
mLastNetworkInfo = null;
if (mScanner != null) {
@@ -920,8 +873,7 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro
mStaleScanResults = true;
}
}
- mMainHandler.obtainMessage(MainHandler.MSG_WIFI_STATE_CHANGED, msg.arg1, 0)
- .sendToTarget();
+ mListener.onWifiStateChanged(msg.arg1);
break;
}
}
@@ -1010,16 +962,26 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro
@Override
public void onWifiStateChanged(int state) {
+ if (isVerboseLoggingEnabled()) {
+ Log.i(TAG,
+ String.format("Invoking onWifiStateChanged callback with state %d", state));
+ }
mHandler.post(() -> mDelegatee.onWifiStateChanged(state));
}
@Override
public void onConnectedChanged() {
+ if (isVerboseLoggingEnabled()) {
+ Log.i(TAG, "Invoking onConnectedChanged callback");
+ }
mHandler.post(() -> mDelegatee.onConnectedChanged());
}
@Override
public void onAccessPointsChanged() {
+ if (isVerboseLoggingEnabled()) {
+ Log.i(TAG, "Invoking onAccessPointsChanged callback");
+ }
mHandler.post(() -> mDelegatee.onAccessPointsChanged());
}
}
@@ -1041,101 +1003,27 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro
void onWifiStateChanged(int state);
/**
- * Called when the connection state of wifi has changed and isConnected
- * should be called to get the updated state.
+ * Called when the connection state of wifi has changed and
+ * {@link WifiTracker#isConnected()} should be called to get the updated state.
*/
void onConnectedChanged();
/**
* Called to indicate the list of AccessPoints has been updated and
- * getAccessPoints should be called to get the latest information.
+ * {@link WifiTracker#getAccessPoints()} should be called to get the updated list.
*/
void onAccessPointsChanged();
}
/**
- * Helps capture notifications that were generated during AccessPoint modification. Used later
- * on by {@link #copyAndNotifyListeners(boolean)} to send notifications.
+ * Invokes {@link WifiListenerWrapper#onAccessPointsChanged()} if {@link #mStaleScanResults}
+ * is false.
*/
- private static class AccessPointListenerAdapter implements AccessPoint.AccessPointListener {
- static final int AP_CHANGED = 1;
- static final int LEVEL_CHANGED = 2;
-
- final SparseIntArray mPendingNotifications = new SparseIntArray();
-
- @Override
- public void onAccessPointChanged(AccessPoint accessPoint) {
- int type = mPendingNotifications.get(accessPoint.mId);
- mPendingNotifications.put(accessPoint.mId, type | AP_CHANGED);
- }
-
- @Override
- public void onLevelChanged(AccessPoint accessPoint) {
- int type = mPendingNotifications.get(accessPoint.mId);
- mPendingNotifications.put(accessPoint.mId, type | LEVEL_CHANGED);
- }
- }
-
- /**
- * Responsible for copying access points from {@link #mInternalAccessPoints} and notifying
- * accesspoint listeners.
- *
- * @param notifyListeners if true, accesspoint listeners are notified, otherwise notifications
- * dropped.
- */
- @MainThread
- private void copyAndNotifyListeners(boolean notifyListeners) {
- // Need to watch out for memory allocations on main thread.
- SparseArray<AccessPoint> oldAccessPoints = new SparseArray<>();
- SparseIntArray notificationMap = null;
- List<AccessPoint> updatedAccessPoints = new ArrayList<>();
-
- for (AccessPoint accessPoint : mAccessPoints) {
- oldAccessPoints.put(accessPoint.mId, accessPoint);
- }
-
- synchronized (mLock) {
- if (DBG()) {
- Log.d(TAG, "Starting to copy AP items on the MainHandler. Internal APs: "
- + mInternalAccessPoints);
- }
-
- if (notifyListeners) {
- notificationMap = mAccessPointListenerAdapter.mPendingNotifications.clone();
- }
-
- mAccessPointListenerAdapter.mPendingNotifications.clear();
-
- for (AccessPoint internalAccessPoint : mInternalAccessPoints) {
- AccessPoint accessPoint = oldAccessPoints.get(internalAccessPoint.mId);
- if (accessPoint == null) {
- accessPoint = new AccessPoint(mContext, internalAccessPoint);
- } else {
- accessPoint.copyFrom(internalAccessPoint);
- }
- updatedAccessPoints.add(accessPoint);
- }
+ private void conditionallyNotifyListeners() {
+ if (mStaleScanResults) {
+ return;
}
- mAccessPoints.clear();
- mAccessPoints.addAll(updatedAccessPoints);
-
- if (notificationMap != null && notificationMap.size() > 0) {
- for (AccessPoint accessPoint : updatedAccessPoints) {
- int notificationType = notificationMap.get(accessPoint.mId);
- AccessPoint.AccessPointListener listener = accessPoint.mAccessPointListener;
- if (notificationType == 0 || listener == null) {
- continue;
- }
-
- if ((notificationType & AccessPointListenerAdapter.AP_CHANGED) != 0) {
- listener.onAccessPointChanged(accessPoint);
- }
-
- if ((notificationType & AccessPointListenerAdapter.LEVEL_CHANGED) != 0) {
- listener.onLevelChanged(accessPoint);
- }
- }
- }
+ ThreadUtils.postOnMainThread(() -> mListener.onAccessPointsChanged());
}
}
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 144031108662..54c02a22e79f 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
@@ -113,38 +113,6 @@ public class AccessPointTest {
}
@Test
- public void testCopyAccessPoint_dataShouldMatch() {
- WifiConfiguration configuration = createWifiConfiguration();
- configuration.meteredHint = true;
-
- NetworkInfo networkInfo =
- new NetworkInfo(ConnectivityManager.TYPE_WIFI, 2, "WIFI", "WIFI_SUBTYPE");
- AccessPoint originalAccessPoint = new AccessPoint(mContext, configuration);
- WifiInfo wifiInfo = new WifiInfo();
- wifiInfo.setSSID(WifiSsid.createFromAsciiEncoded(configuration.SSID));
- wifiInfo.setBSSID(configuration.BSSID);
- originalAccessPoint.update(configuration, wifiInfo, networkInfo);
- AccessPoint copy = new AccessPoint(mContext, originalAccessPoint);
-
- assertThat(originalAccessPoint.getSsid().toString()).isEqualTo(copy.getSsid().toString());
- assertThat(originalAccessPoint.getBssid()).isEqualTo(copy.getBssid());
- assertThat(originalAccessPoint.getConfig()).isEqualTo(copy.getConfig());
- assertThat(originalAccessPoint.getSecurity()).isEqualTo(copy.getSecurity());
- assertThat(originalAccessPoint.isMetered()).isEqualTo(copy.isMetered());
- assertThat(originalAccessPoint.compareTo(copy) == 0).isTrue();
- }
-
- @Test
- public void testThatCopyAccessPoint_scanCacheShouldMatch() {
- AccessPoint original = createAccessPointWithScanResultCache();
- assertThat(original.getRssi()).isEqualTo(4);
- AccessPoint copy = new AccessPoint(mContext, createWifiConfiguration());
- assertThat(copy.getRssi()).isEqualTo(AccessPoint.UNREACHABLE_RSSI);
- copy.copyFrom(original);
- assertThat(original.getRssi()).isEqualTo(copy.getRssi());
- }
-
- @Test
public void testCompareTo_GivesActiveBeforeInactive() {
AccessPoint activeAp = new TestAccessPointBuilder(mContext).setActive(true).build();
AccessPoint inactiveAp = new TestAccessPointBuilder(mContext).setActive(false).build();
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
index 6be4936413b7..0c49bb66a40e 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
@@ -356,19 +356,14 @@ public class WifiTrackerTest {
private void waitForHandlersToProcessCurrentlyEnqueuedMessages(WifiTracker tracker)
throws InterruptedException {
+ // TODO(sghuman): This should no longer be necessary in a single work handler model
+
CountDownLatch workerLatch = new CountDownLatch(1);
tracker.mWorkHandler.post(() -> {
workerLatch.countDown();
});
assertTrue("Latch timed out while waiting for WorkerHandler",
workerLatch.await(LATCH_TIMEOUT, TimeUnit.MILLISECONDS));
-
- CountDownLatch mainLatch = new CountDownLatch(1);
- tracker.mMainHandler.post(() -> {
- mainLatch.countDown();
- });
- assertTrue("Latch timed out while waiting for MainHandler",
- mainLatch.await(LATCH_TIMEOUT, TimeUnit.MILLISECONDS));
}
private void switchToNetwork2(WifiTracker tracker) throws InterruptedException {
@@ -390,38 +385,6 @@ public class WifiTrackerTest {
}
@Test
- public void testAccessPointListenerSetWhenLookingUpUsingScanResults() {
- ScanResult scanResult = new ScanResult();
- scanResult.level = 123;
- scanResult.BSSID = "bssid-" + 111;
- scanResult.timestamp = SystemClock.elapsedRealtime() * 1000;
- scanResult.capabilities = "";
-
- WifiTracker tracker = new WifiTracker(
- InstrumentationRegistry.getTargetContext(), null, true, true);
-
- AccessPoint result = tracker.getCachedOrCreate(
- Collections.singletonList(scanResult), new ArrayList<AccessPoint>());
- assertTrue(result.mAccessPointListener != null);
- }
-
- @Test
- public void testAccessPointListenerSetWhenLookingUpUsingWifiConfiguration() {
- WifiConfiguration configuration = new WifiConfiguration();
- configuration.SSID = "test123";
- configuration.BSSID="bssid";
- configuration.networkId = 123;
- configuration.allowedKeyManagement = new BitSet();
- configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
-
- WifiTracker tracker = new WifiTracker(
- InstrumentationRegistry.getTargetContext(), null, true, true);
-
- AccessPoint result = tracker.getCachedOrCreate(configuration, new ArrayList<AccessPoint>());
- assertTrue(result.mAccessPointListener != null);
- }
-
- @Test
public void startAndStopTrackingShouldRegisterAndUnregisterScoreCache()
throws InterruptedException {
WifiTracker tracker = createMockedWifiTracker();
@@ -534,7 +497,6 @@ public class WifiTrackerTest {
waitForHandlersToProcessCurrentlyEnqueuedMessages(tracker);
}
- @FlakyTest
@Test
public void scoreCacheUpdateScoresShouldChangeSortOrder() throws InterruptedException {
WifiTracker tracker = createTrackerWithImmediateBroadcastsAndInjectInitialScanResults();
@@ -634,9 +596,9 @@ public class WifiTrackerTest {
public void scoresShouldBeRequestedForNewScanResultOnly() throws InterruptedException {
// Scores can be requested together or serially depending on how the scan results are
// processed.
- mRequestScoresLatch = new CountDownLatch(2);
+ mRequestScoresLatch = new CountDownLatch(1);
WifiTracker tracker = createTrackerWithImmediateBroadcastsAndInjectInitialScanResults();
- mRequestScoresLatch.await(LATCH_TIMEOUT, TimeUnit.MILLISECONDS);
+ assertTrue(mRequestScoresLatch.await(LATCH_TIMEOUT, TimeUnit.MILLISECONDS));
mRequestedKeys.clear();
String ssid = "ssid3";
@@ -770,7 +732,7 @@ public class WifiTrackerTest {
CountDownLatch ready = new CountDownLatch(1);
CountDownLatch latch = new CountDownLatch(1);
CountDownLatch lock = new CountDownLatch(1);
- tracker.mMainHandler.post(() -> {
+ tracker.mWorkHandler.post(() -> {
try {
ready.countDown();
lock.await();
@@ -781,12 +743,7 @@ public class WifiTrackerTest {
});
// Enqueue messages
- tracker.mMainHandler.sendEmptyMessage(
- WifiTracker.MainHandler.MSG_ACCESS_POINT_CHANGED);
- tracker.mMainHandler.sendEmptyMessage(
- WifiTracker.MainHandler.MSG_CONNECTED_CHANGED);
- tracker.mMainHandler.sendEmptyMessage(
- WifiTracker.MainHandler.MSG_WIFI_STATE_CHANGED);
+ tracker.mWorkHandler.sendEmptyMessage(WifiTracker.WorkHandler.MSG_UPDATE_ACCESS_POINTS);
try {
ready.await(); // Make sure we have entered the first message handler
@@ -800,12 +757,9 @@ public class WifiTrackerTest {
lock.countDown();
assertTrue("Latch timed out", latch.await(LATCH_TIMEOUT, TimeUnit.MILLISECONDS));
- assertThat(tracker.mMainHandler.hasMessages(
- WifiTracker.MainHandler.MSG_ACCESS_POINT_CHANGED)).isFalse();
- assertThat(tracker.mMainHandler.hasMessages(
- WifiTracker.MainHandler.MSG_CONNECTED_CHANGED)).isFalse();
- assertThat(tracker.mMainHandler.hasMessages(
- WifiTracker.MainHandler.MSG_WIFI_STATE_CHANGED)).isFalse();
+ assertThat(tracker.mWorkHandler.hasMessages(
+ WifiTracker.WorkHandler.MSG_UPDATE_ACCESS_POINTS)).isFalse();
+ waitForHandlersToProcessCurrentlyEnqueuedMessages(tracker);
verifyNoMoreInteractions(mockWifiListener);
}
@@ -862,7 +816,7 @@ public class WifiTrackerTest {
mAccessPointsChangedLatch = new CountDownLatch(1);
tracker.mReceiver.onReceive(mContext, new Intent(WifiManager.WIFI_STATE_CHANGED_ACTION));
- mAccessPointsChangedLatch.await(LATCH_TIMEOUT, TimeUnit.MILLISECONDS);
+ assertTrue(mAccessPointsChangedLatch.await(LATCH_TIMEOUT, TimeUnit.MILLISECONDS));
waitForHandlersToProcessCurrentlyEnqueuedMessages(tracker);
assertThat(tracker.getAccessPoints()).isEmpty();
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
index 12d3106cfe61..706d0c0f51e5 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
@@ -16,6 +16,9 @@
package com.android.settingslib;
import static android.Manifest.permission.WRITE_SECURE_SETTINGS;
+
+import static com.android.settingslib.Utils.STORAGE_MANAGER_SHOW_OPT_IN_PROPERTY;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Matchers.eq;
@@ -30,6 +33,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.location.LocationManager;
+import android.os.SystemProperties;
import android.os.UserHandle;
import android.provider.Settings;
import android.provider.Settings.Secure;
@@ -136,7 +140,7 @@ public class UtilsTest {
}
@Test
- public void testStorageManagerDaysToRetainUsesResources() {
+ public void testGetDefaultStorageManagerDaysToRetain_storageManagerDaysToRetainUsesResources() {
Resources resources = mock(Resources.class);
when(resources.getInteger(
eq(
@@ -149,6 +153,12 @@ public class UtilsTest {
assertThat(Utils.getDefaultStorageManagerDaysToRetain(resources)).isEqualTo(60);
}
+ @Test
+ public void testIsStorageManagerEnabled_UsesSystemProperties() {
+ SystemProperties.set(STORAGE_MANAGER_SHOW_OPT_IN_PROPERTY, "false");
+ assertThat(Utils.isStorageManagerEnabled(mContext)).isTrue();
+ }
+
private static ArgumentMatcher<Intent> actionMatches(String expected) {
return intent -> TextUtils.equals(expected, intent.getAction());
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
index 91957e1ff05c..ad422d8011d7 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
@@ -76,11 +76,10 @@ public class SettingsHelper {
*/
private static final ArraySet<String> sBroadcastOnRestore;
static {
- sBroadcastOnRestore = new ArraySet<String>(5);
+ sBroadcastOnRestore = new ArraySet<String>(4);
sBroadcastOnRestore.add(Settings.Secure.ENABLED_NOTIFICATION_LISTENERS);
sBroadcastOnRestore.add(Settings.Secure.ENABLED_VR_LISTENERS);
sBroadcastOnRestore.add(Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
- sBroadcastOnRestore.add(Settings.Secure.ENABLED_INPUT_METHODS);
sBroadcastOnRestore.add(Settings.Global.BLUETOOTH_ON);
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 85a579d2808d..87ea38202e2d 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -1591,6 +1591,7 @@ public class SettingsProvider extends ContentProvider {
private boolean isGlobalOrSecureSettingRestrictedForUser(String setting, int userId,
String value, int callingUid) {
String restriction;
+ boolean checkAllUser = false;
switch (setting) {
case Settings.Secure.LOCATION_MODE:
// Note LOCATION_MODE will be converted into LOCATION_PROVIDERS_ALLOWED
@@ -1656,6 +1657,12 @@ public class SettingsProvider extends ContentProvider {
restriction = UserManager.DISALLOW_AMBIENT_DISPLAY;
break;
+ case Global.LOCATION_GLOBAL_KILL_SWITCH:
+ if ("0".equals(value)) return false;
+ restriction = UserManager.DISALLOW_CONFIG_LOCATION;
+ checkAllUser = true;
+ break;
+
default:
if (setting != null && setting.startsWith(Settings.Global.DATA_ROAMING)) {
if ("0".equals(value)) return false;
@@ -1665,7 +1672,11 @@ public class SettingsProvider extends ContentProvider {
return false;
}
- return mUserManager.hasUserRestriction(restriction, UserHandle.of(userId));
+ if (checkAllUser) {
+ return mUserManager.hasUserRestrictionOnAnyUser(restriction);
+ } else {
+ return mUserManager.hasUserRestriction(restriction, UserHandle.of(userId));
+ }
}
private int resolveOwningUserIdForSecureSettingLocked(int userId, String setting) {
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
index 4cf817e02fff..b8319a8e8822 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
@@ -44,4 +44,9 @@ interface ISystemUiProxy {
* Specifies the text to be shown for onboarding the new swipe-up gesture to access recents.
*/
void setRecentsOnboardingText(CharSequence text);
+
+ /**
+ * Enables/disables launcher/overview interaction features {@link InteractionType}.
+ */
+ void setInteractionState(int flags);
}
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 f622d4a3338c..171918682099 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
@@ -28,4 +28,33 @@ public class NavigationBarCompat {
public static final int HIT_TARGET_NONE = 0;
public static final int HIT_TARGET_BACK = 1;
public static final int HIT_TARGET_HOME = 2;
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({FLAG_DISABLE_SWIPE_UP,
+ FLAG_DISABLE_QUICK_SCRUB,
+ FLAG_SHOW_OVERVIEW_BUTTON,
+ FLAG_HIDE_BACK_BUTTON
+ })
+ public @interface InteractionType {}
+
+ /**
+ * Interaction type: whether the gesture to swipe up from the navigation bar will trigger
+ * launcher to show overview
+ */
+
+ public static final int FLAG_DISABLE_SWIPE_UP = 0x1;
+ /**
+ * Interaction type: enable quick scrub and switch interaction on the home button
+ */
+ public static final int FLAG_DISABLE_QUICK_SCRUB = 0x2;
+
+ /**
+ * Interaction type: show/hide the overview button while this service is connected to launcher
+ */
+ public static final int FLAG_SHOW_OVERVIEW_BUTTON = 0x4;
+
+ /**
+ * Interaction type: show/hide the back button while this service is connected to launcher
+ */
+ public static final int FLAG_HIDE_BACK_BUTTON = 0x8;
}
diff --git a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
index d0128efe2c44..1185f45469df 100644
--- a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
@@ -47,6 +47,8 @@ import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
+import static com.android.systemui.shared.system.NavigationBarCompat.InteractionType;
+
/**
* Class to send information from overview to launcher with a binder.
*/
@@ -67,6 +69,7 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
private IOverviewProxy mOverviewProxy;
private int mConnectionBackoffAttempts;
private CharSequence mOnboardingText;
+ private @InteractionType int mInteractionFlags;
private ISystemUiProxy mSysUiProxy = new ISystemUiProxy.Stub() {
@@ -108,6 +111,22 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
public void setRecentsOnboardingText(CharSequence text) {
mOnboardingText = text;
}
+
+ public void setInteractionState(@InteractionType int flags) {
+ long token = Binder.clearCallingIdentity();
+ try {
+ if (mInteractionFlags != flags) {
+ mInteractionFlags = flags;
+ mHandler.post(() -> {
+ for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
+ mConnectionCallbacks.get(i).onInteractionFlagsChanged(flags);
+ }
+ });
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
};
private final BroadcastReceiver mLauncherAddedReceiver = new BroadcastReceiver() {
@@ -230,6 +249,10 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
return mOnboardingText;
}
+ public int getInteractionFlags() {
+ return mInteractionFlags;
+ }
+
private void disconnectFromLauncherService() {
if (mOverviewProxy != null) {
mOverviewProxy.asBinder().unlinkToDeath(mOverviewServiceDeathRcpt, 0);
@@ -263,5 +286,6 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
public interface OverviewProxyListener {
default void onConnectionChanged(boolean isConnected) {}
default void onRecentsAnimationStarted() {}
+ default void onInteractionFlagsChanged(@InteractionType int flags) {}
}
}
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 11d20b221051..ef44ad17e1c7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
@@ -77,11 +77,11 @@ public class ActivityLaunchAnimator {
mStatusBar = statusBar;
}
- public ActivityOptions getLaunchAnimation(
- ExpandableNotificationRow sourceNofitication) {
- AnimationRunner animationRunner = new AnimationRunner(sourceNofitication);
- return ActivityOptions.makeRemoteAnimation(
- new RemoteAnimationAdapter(animationRunner, 1000 /* Duration */, 0 /* delay */));
+ public RemoteAnimationAdapter getLaunchAnimation(
+ ExpandableNotificationRow sourceNotification) {
+ AnimationRunner animationRunner = new AnimationRunner(sourceNotification);
+ return new RemoteAnimationAdapter(animationRunner, ANIMATION_DURATION,
+ 0 /* statusBarTransitionDelay */);
}
public boolean isAnimationPending() {
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 72938c25b753..79c605e4ed23 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -19,6 +19,7 @@ import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SHOWN;
import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
import static android.app.StatusBarManager.windowStateToString;
+import static com.android.systemui.shared.system.NavigationBarCompat.InteractionType;
import static com.android.systemui.statusbar.phone.BarTransitions.MODE_SEMI_TRANSPARENT;
import static com.android.systemui.statusbar.phone.StatusBar.DEBUG_WINDOW_STATE;
import static com.android.systemui.statusbar.phone.StatusBar.dumpBarTransitions;
@@ -71,7 +72,6 @@ import android.view.WindowManagerGlobal;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener;
-import android.widget.Button;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
@@ -165,6 +165,11 @@ public class NavigationBarFragment extends Fragment implements Callbacks {
public void onRecentsAnimationStarted() {
mNavigationBarView.setRecentsAnimationStarted(true);
}
+
+ @Override
+ public void onInteractionFlagsChanged(@InteractionType int flags) {
+ mNavigationBarView.updateStates();
+ }
};
// ----- Fragment Lifecycle Callbacks -----
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 63f2cebb61cc..8970ea0c110b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
@@ -127,7 +127,7 @@ public class NavigationBarGestureHelper implements TunerService.Tunable, Gesture
private boolean proxyMotionEvents(MotionEvent event) {
final IOverviewProxy overviewProxy = mOverviewProxyService.getProxy();
- if (overviewProxy != null) {
+ if (overviewProxy != null && mNavigationBarView.isQuickStepSwipeUpEnabled()) {
mNavigationBarView.requestUnbufferedDispatch(event);
event.transform(mTransformGlobalMatrix);
try {
@@ -192,7 +192,7 @@ public class NavigationBarGestureHelper implements TunerService.Tunable, Gesture
}
public void onDraw(Canvas canvas) {
- if (mOverviewProxyService.getProxy() != null) {
+ if (mNavigationBarView.isQuickScrubEnabled()) {
mQuickScrubController.onDraw(canvas);
}
}
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 53dc814726a2..cd4eb236c892 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -40,6 +40,7 @@ import android.graphics.drawable.AnimatedVectorDrawable;
import android.os.Handler;
import android.os.Message;
import android.os.RemoteException;
+import android.os.SystemProperties;
import android.support.annotation.ColorInt;
import android.util.AttributeSet;
import android.util.Log;
@@ -76,6 +77,11 @@ import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.function.Consumer;
+import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_DISABLE_QUICK_SCRUB;
+import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_DISABLE_SWIPE_UP;
+import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_HIDE_BACK_BUTTON;
+import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_SHOW_OVERVIEW_BUTTON;
+
public class NavigationBarView extends FrameLayout implements PluginListener<NavGesture> {
final static boolean DEBUG = false;
final static String TAG = "StatusBar/NavBarView";
@@ -372,6 +378,19 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav
return getRecentsButton().getVisibility() == View.VISIBLE;
}
+ public boolean isQuickStepSwipeUpEnabled() {
+ return mOverviewProxyService.getProxy() != null
+ && ((mOverviewProxyService.getInteractionFlags()
+ & FLAG_DISABLE_SWIPE_UP) == 0);
+ }
+
+ public boolean isQuickScrubEnabled() {
+ return SystemProperties.getBoolean("persist.quickstep.scrub.enabled", true)
+ && mOverviewProxyService.getProxy() != null && !isRecentsButtonVisible()
+ && ((mOverviewProxyService.getInteractionFlags()
+ & FLAG_DISABLE_QUICK_SCRUB) == 0);
+ }
+
private void updateCarModeIcons(Context ctx) {
mBackCarModeIcon = getDrawable(ctx,
R.drawable.ic_sysbar_back_carmode, R.drawable.ic_sysbar_back_carmode);
@@ -391,20 +410,20 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav
}
if (oldConfig.densityDpi != newConfig.densityDpi
|| oldConfig.getLayoutDirection() != newConfig.getLayoutDirection()) {
- final boolean proxyAvailable = mOverviewProxyService.getProxy() != null;
- mBackIcon = proxyAvailable
+ final boolean quickStepEnabled = isQuickStepSwipeUpEnabled() || isQuickScrubEnabled();
+ mBackIcon = quickStepEnabled
? getDrawable(ctx, R.drawable.ic_sysbar_back_quick_step,
R.drawable.ic_sysbar_back_quick_step_dark)
: getDrawable(ctx, R.drawable.ic_sysbar_back, R.drawable.ic_sysbar_back_dark);
mBackLandIcon = mBackIcon;
- mBackAltIcon = proxyAvailable
+ mBackAltIcon = quickStepEnabled
? getDrawable(ctx, R.drawable.ic_sysbar_back_ime_quick_step,
R.drawable.ic_sysbar_back_ime_quick_step_dark)
: getDrawable(ctx, R.drawable.ic_sysbar_back_ime,
R.drawable.ic_sysbar_back_ime_dark);
mBackAltLandIcon = mBackAltIcon;
- mHomeDefaultIcon = proxyAvailable
+ mHomeDefaultIcon = quickStepEnabled
? getDrawable(ctx, R.drawable.ic_sysbar_home_quick_step,
R.drawable.ic_sysbar_home_quick_step_dark)
: getDrawable(ctx, R.drawable.ic_sysbar_home, R.drawable.ic_sysbar_home_dark);
@@ -555,9 +574,14 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav
disableBack = false;
disableRecent = false;
}
+
if (mOverviewProxyService.getProxy() != null) {
- // When overview is connected to the launcher service, disable the recents button
- disableRecent = true;
+ // When overview is connected to the launcher service, disable the recents button by
+ // default unless overwritten by interaction flags. Similar with the back button but
+ // shown by default.
+ final int flags = mOverviewProxyService.getInteractionFlags();
+ disableRecent |= (flags & FLAG_SHOW_OVERVIEW_BUTTON) == 0;
+ disableBack |= (flags & FLAG_HIDE_BACK_BUTTON) != 0;
}
ViewGroup navButtons = (ViewGroup) getCurrentView().findViewById(R.id.nav_buttons);
@@ -635,6 +659,11 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav
updateSlippery();
}
+ public void updateStates() {
+ updateSlippery();
+ setDisabledFlags(mDisabledFlags, true);
+ }
+
private void updateSlippery() {
setSlippery(mOverviewProxyService.getProxy() != null && mPanelView.isFullyExpanded());
}
@@ -794,9 +823,8 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav
}
public void onOverviewProxyConnectionChanged(boolean isConnected) {
- setSlippery(!isConnected);
- setDisabledFlags(mDisabledFlags, true);
- setUpSwipeUpOnboarding(isConnected);
+ updateStates();
+ setUpSwipeUpOnboarding(isQuickStepSwipeUpEnabled());
updateIcons(getContext(), Configuration.EMPTY, mConfiguration);
setNavigationIconHints(mNavigationIconHints, true);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickScrubController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickScrubController.java
index f4da0c38338d..0bf01b0cb949 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickScrubController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickScrubController.java
@@ -29,15 +29,12 @@ import android.graphics.Paint;
import android.graphics.Rect;
import android.os.Handler;
import android.os.RemoteException;
-import android.os.SystemProperties;
import android.util.Log;
import android.util.Slog;
-import android.view.Display;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
-import android.view.WindowManager;
import android.view.WindowManagerGlobal;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
@@ -138,8 +135,9 @@ public class QuickScrubController extends GestureDetector.SimpleOnGestureListene
new GestureDetector.SimpleOnGestureListener() {
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velX, float velY) {
- if (!isQuickScrubEnabled() || mQuickScrubActive || !mAllowQuickSwitch ||
- mNavigationBarView.getDownHitTarget() != HIT_TARGET_HOME) {
+ if (!mNavigationBarView.isQuickScrubEnabled() || mQuickScrubActive
+ || !mAllowQuickSwitch
+ || mNavigationBarView.getDownHitTarget() != HIT_TARGET_HOME) {
return false;
}
float velocityX = mIsRTL ? -velX : velX;
@@ -196,9 +194,8 @@ public class QuickScrubController extends GestureDetector.SimpleOnGestureListene
*/
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
- final IOverviewProxy overviewProxy = mOverviewEventSender.getProxy();
final ButtonDispatcher homeButton = mNavigationBarView.getHomeButton();
- if (overviewProxy == null) {
+ if (!mNavigationBarView.isQuickScrubEnabled()) {
homeButton.setDelayTouchFeedback(false);
return false;
}
@@ -228,7 +225,7 @@ public class QuickScrubController extends GestureDetector.SimpleOnGestureListene
int x = (int) event.getX();
int y = (int) event.getY();
mHomeButtonView = homeButton.getCurrentView();
- if (isQuickScrubEnabled()
+ if (mNavigationBarView.isQuickScrubEnabled()
&& mNavigationBarView.getDownHitTarget() == HIT_TARGET_HOME) {
mTouchDownX = x;
mTouchDownY = y;
@@ -296,7 +293,7 @@ public class QuickScrubController extends GestureDetector.SimpleOnGestureListene
: Utilities.clamp(offset - mDownOffset, 0, trackSize);
if (mQuickScrubActive) {
try {
- overviewProxy.onQuickScrubProgress(scrubFraction);
+ mOverviewEventSender.getProxy().onQuickScrubProgress(scrubFraction);
if (DEBUG_OVERVIEW_PROXY) {
Log.d(TAG_OPS, "Quick Scrub Progress:" + scrubFraction);
}
@@ -377,10 +374,6 @@ public class QuickScrubController extends GestureDetector.SimpleOnGestureListene
}
}
- boolean isQuickScrubEnabled() {
- return SystemProperties.getBoolean("persist.quickstep.scrub.enabled", true);
- }
-
private void startQuickScrub() {
if (!mQuickScrubActive) {
mQuickScrubActive = true;
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 94ebc1bac2b0..2b5085389804 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -138,8 +138,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener,
protected float mScrimBehindAlphaKeyguard = SCRIM_BEHIND_ALPHA_KEYGUARD;
protected float mScrimBehindAlphaUnlocking = SCRIM_BEHIND_ALPHA_UNLOCKING;
- // Assuming the shade is expanded during initialization
- private float mExpansionFraction = 1f;
+ private float mFraction;
private boolean mDarkenWhileDragging;
protected boolean mAnimateChange;
@@ -253,7 +252,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener,
mCurrentBehindTint = state.getBehindTint();
mCurrentInFrontAlpha = state.getFrontAlpha();
mCurrentBehindAlpha = state.getBehindAlpha();
- applyExpansionToAlpha();
// Cancel blanking transitions that were pending before we requested a new state
if (mPendingFrameCallback != null) {
@@ -365,50 +363,45 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener,
* @param fraction From 0 to 1 where 0 means collapse and 1 expanded.
*/
public void setPanelExpansion(float fraction) {
- if (mExpansionFraction != fraction) {
- mExpansionFraction = fraction;
-
- if (!(mState == ScrimState.UNLOCKED || mState == ScrimState.KEYGUARD)) {
- return;
- }
-
- applyExpansionToAlpha();
+ if (mFraction != fraction) {
+ mFraction = fraction;
+
+ if (mState == ScrimState.UNLOCKED) {
+ // Darken scrim as you pull down the shade when unlocked
+ float behindFraction = getInterpolatedFraction();
+ behindFraction = (float) Math.pow(behindFraction, 0.8f);
+ mCurrentBehindAlpha = behindFraction * mScrimBehindAlphaKeyguard;
+ mCurrentInFrontAlpha = 0;
+ } else if (mState == ScrimState.KEYGUARD) {
+ if (mUpdatePending) {
+ return;
+ }
- if (mUpdatePending) {
+ // Either darken of make the scrim transparent when you
+ // pull down the shade
+ float interpolatedFract = getInterpolatedFraction();
+ if (mDarkenWhileDragging) {
+ mCurrentBehindAlpha = MathUtils.lerp(mScrimBehindAlphaUnlocking,
+ mScrimBehindAlphaKeyguard, interpolatedFract);
+ mCurrentInFrontAlpha = (1f - interpolatedFract) * SCRIM_IN_FRONT_ALPHA_LOCKED;
+ } else {
+ mCurrentBehindAlpha = MathUtils.lerp(0 /* start */, mScrimBehindAlphaKeyguard,
+ interpolatedFract);
+ mCurrentInFrontAlpha = 0;
+ }
+ } else {
return;
}
if (mPinnedHeadsUpCount != 0) {
updateHeadsUpScrim(false);
}
+
updateScrim(false /* animate */, mScrimInFront, mCurrentInFrontAlpha);
updateScrim(false /* animate */, mScrimBehind, mCurrentBehindAlpha);
}
}
- private void applyExpansionToAlpha() {
- if (mState == ScrimState.UNLOCKED) {
- // Darken scrim as you pull down the shade when unlocked
- float behindFraction = getInterpolatedFraction();
- behindFraction = (float) Math.pow(behindFraction, 0.8f);
- mCurrentBehindAlpha = behindFraction * mScrimBehindAlphaKeyguard;
- mCurrentInFrontAlpha = 0;
- } else if (mState == ScrimState.KEYGUARD) {
- // Either darken of make the scrim transparent when you
- // pull down the shade
- float interpolatedFract = getInterpolatedFraction();
- if (mDarkenWhileDragging) {
- mCurrentBehindAlpha = MathUtils.lerp(mScrimBehindAlphaUnlocking,
- mScrimBehindAlphaKeyguard, interpolatedFract);
- mCurrentInFrontAlpha = (1f - interpolatedFract) * SCRIM_IN_FRONT_ALPHA_LOCKED;
- } else {
- mCurrentBehindAlpha = MathUtils.lerp(0 /* start */, mScrimBehindAlphaKeyguard,
- interpolatedFract);
- mCurrentInFrontAlpha = 0;
- }
- }
- }
-
/**
* Keyguard and shade scrim opacity varies according to how many notifications are visible.
* @param notificationCount Number of visible notifications.
@@ -504,7 +497,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener,
}
private float getInterpolatedFraction() {
- float frac = mExpansionFraction;
+ float frac = mFraction;
// let's start this 20% of the way down the screen
frac = frac * 1.2f - 0.2f;
if (frac <= 0) {
@@ -834,7 +827,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener,
} else {
alpha = 1.0f - mTopHeadsUpDragAmount;
}
- float expandFactor = (1.0f - mExpansionFraction);
+ float expandFactor = (1.0f - mFraction);
expandFactor = Math.max(expandFactor, 0.0f);
return alpha * expandFactor;
}
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 3b63d6c39146..24920cba21f5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -111,6 +111,7 @@ import android.view.IWindowManager;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
+import android.view.RemoteAnimationAdapter;
import android.view.ThreadedRenderer;
import android.view.View;
import android.view.ViewGroup;
@@ -2849,7 +2850,7 @@ public class StatusBar extends SystemUI implements DemoMode,
Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
int result = ActivityManager.START_CANCELED;
ActivityOptions options = new ActivityOptions(getActivityOptions(
- null /* sourceNotification */));
+ null /* remoteAnimation */));
options.setDisallowEnterPictureInPictureWhileLaunching(
disallowEnterPictureInPictureWhileLaunching);
if (intent == KeyguardBottomAreaView.INSECURE_CAMERA_INTENT) {
@@ -5001,11 +5002,15 @@ public class StatusBar extends SystemUI implements DemoMode,
fillInIntent = new Intent().putExtra(Notification.EXTRA_REMOTE_INPUT_DRAFT,
remoteInputText.toString());
}
+ RemoteAnimationAdapter adapter = mActivityLaunchAnimator.getLaunchAnimation(
+ row);
try {
+ ActivityManager.getService().registerRemoteAnimationForNextActivityStart(
+ intent.getCreatorPackage(), adapter);
launchResult = intent.sendAndReturnResult(mContext, 0, fillInIntent, null,
- null, null, getActivityOptions(row));
+ null, null, getActivityOptions(adapter));
mActivityLaunchAnimator.setLaunchResult(launchResult);
- } catch (PendingIntent.CanceledException e) {
+ } catch (RemoteException | PendingIntent.CanceledException e) {
// the stack trace isn't very helpful here.
// Just log the exception message.
Log.w(TAG, "Sending contentIntent failed: " + e);
@@ -5165,7 +5170,8 @@ public class StatusBar extends SystemUI implements DemoMode,
AsyncTask.execute(() -> {
int launchResult = TaskStackBuilder.create(mContext)
.addNextIntentWithParentStack(intent)
- .startActivities(getActivityOptions(row),
+ .startActivities(getActivityOptions(
+ mActivityLaunchAnimator.getLaunchAnimation(row)),
new UserHandle(UserHandle.getUserId(appUid)));
mActivityLaunchAnimator.setLaunchResult(launchResult);
if (shouldCollapse()) {
@@ -5300,7 +5306,7 @@ public class StatusBar extends SystemUI implements DemoMode,
}
try {
intent.send(null, 0, null, null, null, null, getActivityOptions(
- null /* sourceNotification */));
+ null /* animationAdapter */));
} catch (PendingIntent.CanceledException e) {
// the stack trace isn't very helpful here.
// Just log the exception message.
@@ -5328,10 +5334,10 @@ public class StatusBar extends SystemUI implements DemoMode,
return true;
}
- protected Bundle getActivityOptions(ExpandableNotificationRow sourceNotification) {
+ protected Bundle getActivityOptions(@Nullable RemoteAnimationAdapter animationAdapter) {
ActivityOptions options;
- if (sourceNotification != null) {
- options = mActivityLaunchAnimator.getLaunchAnimation(sourceNotification);
+ if (animationAdapter != null) {
+ options = ActivityOptions.makeRemoteAnimation(animationAdapter);
} else {
options = ActivityOptions.makeBasic();
}
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 47027935d8d7..43e16dbeaeed 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
@@ -180,7 +180,6 @@ public class ScrimControllerTest extends SysuiTestCase {
@Test
public void transitionToUnlocked() {
- mScrimController.setPanelExpansion(0f);
mScrimController.transitionTo(ScrimState.UNLOCKED);
mScrimController.finishAnimationsImmediately();
// Front scrim should be transparent
@@ -198,7 +197,6 @@ public class ScrimControllerTest extends SysuiTestCase {
public void transitionToUnlockedFromAod() {
// Simulate unlock with fingerprint
mScrimController.transitionTo(ScrimState.AOD);
- mScrimController.setPanelExpansion(0f);
mScrimController.finishAnimationsImmediately();
mScrimController.transitionTo(ScrimState.UNLOCKED);
// Immediately tinted after the transition starts
@@ -326,23 +324,6 @@ public class ScrimControllerTest extends SysuiTestCase {
verify(mAlarmManager).cancel(any(AlarmManager.OnAlarmListener.class));
}
- @Test
- public void testConservesExpansionOpacityAfterTransition() {
- mScrimController.transitionTo(ScrimState.UNLOCKED);
- mScrimController.setPanelExpansion(0.5f);
- mScrimController.finishAnimationsImmediately();
-
- final float expandedAlpha = mScrimBehind.getViewAlpha();
-
- mScrimController.transitionTo(ScrimState.BRIGHTNESS_MIRROR);
- mScrimController.finishAnimationsImmediately();
- mScrimController.transitionTo(ScrimState.UNLOCKED);
- mScrimController.finishAnimationsImmediately();
-
- Assert.assertEquals("Scrim expansion opacity wasn't conserved when transitioning back",
- expandedAlpha, mScrimBehind.getViewAlpha(), 0.01f);
- }
-
private void assertScrimTint(ScrimView scrimView, boolean tinted) {
final boolean viewIsTinted = scrimView.getTint() != Color.TRANSPARENT;
final String name = scrimView == mScrimInFront ? "front" : "back";
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index ae5e133fa02f..7eebf5a3f94a 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -5213,6 +5213,22 @@ message MetricsEvent {
// OS: P
ACTION_OUTPUT_CHOOSER_DISCONNECT = 1297;
+ // OPEN: TV Settings > Home theater control
+ // OS: P
+ SETTINGS_TV_HOME_THEATER_CONTROL_CATEGORY = 1298;
+
+ // OPEN: TV Settings > TV Inputs (Inputs & Devices)
+ // OS: P
+ SETTINGS_TV_INPUTS_CATEGORY = 1299;
+
+ // OPEN: TV Settings > Device
+ // OS: P
+ SETTINGS_TV_DEVICE_CATEGORY = 1300;
+
+ // OPEN: TV Settings > Network > Proxy settings
+ // OS: P
+ DIALOG_TV_NETWORK_PROXY = 1301;
+
// ---- End P Constants, all P constants go above this line ----
// Add new aosp constants above this line.
// END OF AOSP CONSTANTS
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index ebb5040a7073..4b3abeadaea1 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -24,7 +24,6 @@ import static com.android.server.autofill.Helper.sDebug;
import static com.android.server.autofill.Helper.sPartitionMaxCount;
import static com.android.server.autofill.Helper.sVerbose;
-import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
@@ -56,7 +55,12 @@ import android.os.UserManagerInternal;
import android.provider.Settings;
import android.service.autofill.FillEventHistory;
import android.service.autofill.UserData;
+import android.text.TextUtils;
+import android.text.TextUtils.SimpleStringSplitter;
+import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.LocalLog;
+import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
@@ -84,6 +88,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
+import java.util.Set;
/**
* Entry point service for autofill management.
@@ -98,6 +103,8 @@ public final class AutofillManagerService extends SystemService {
static final String RECEIVER_BUNDLE_EXTRA_SESSIONS = "sessions";
+ private static final char COMPAT_PACKAGE_DELIMITER = ':';
+
private final Context mContext;
private final AutoFillUI mUi;
@@ -123,6 +130,9 @@ public final class AutofillManagerService extends SystemService {
private final LocalLog mUiLatencyHistory = new LocalLog(20);
private final LocalLog mWtfHistory = new LocalLog(50);
+ private final AutofillCompatState mAutofillCompatState = new AutofillCompatState();
+ private final LocalService mLocalService = new LocalService();
+
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -271,7 +281,7 @@ public final class AutofillManagerService extends SystemService {
@Override
public void onStart() {
publishBinderService(AUTOFILL_MANAGER_SERVICE, new AutoFillManagerServiceStub());
- publishLocalService(AutofillManagerInternal.class, new LocalService());
+ publishLocalService(AutofillManagerInternal.class, mLocalService);
}
@Override
@@ -317,6 +327,11 @@ public final class AutofillManagerService extends SystemService {
mUiLatencyHistory, mWtfHistory, resolvedUserId, mUi,
mDisabledUsers.get(resolvedUserId));
mServicesCache.put(userId, service);
+ final ArrayMap<String, Pair<Long, String>> compatPackages =
+ service.getCompatibilityPackagesLocked();
+ if (compatPackages != null) {
+ addCompatibilityModeRequests(compatPackages, userId);
+ }
}
return service;
}
@@ -482,6 +497,7 @@ public final class AutofillManagerService extends SystemService {
if (service != null) {
mServicesCache.delete(userId);
service.destroyLocked();
+ mAutofillCompatState.removeCompatibilityModeRequests(userId);
}
}
@@ -498,18 +514,60 @@ public final class AutofillManagerService extends SystemService {
*/
@GuardedBy("mLock")
private void updateCachedServiceLocked(int userId, boolean disabled) {
- AutofillManagerServiceImpl service = getServiceForUserLocked(userId);
+ AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
if (service != null) {
service.destroySessionsLocked();
service.updateLocked(disabled);
if (!service.isEnabledLocked()) {
removeCachedServiceLocked(userId);
+ } else {
+ final ArrayMap<String, Pair<Long, String>> compatPackages =
+ service.getCompatibilityPackagesLocked();
+ if (compatPackages != null) {
+ addCompatibilityModeRequests(compatPackages, userId);
+ }
}
}
}
- private final class LocalService extends AutofillManagerInternal {
+ private void addCompatibilityModeRequests(
+ @NonNull ArrayMap<String, Pair<Long, String>> compatPackages, int userId) {
+ final Set<String> whiteListedPackages = Build.IS_ENG ? null
+ : getWhitelistedCompatModePackages();
+ final int compatPackageCount = compatPackages.size();
+ for (int i = 0; i < compatPackageCount; i++) {
+ final String packageName = compatPackages.keyAt(i);
+ if (!Build.IS_ENG && (whiteListedPackages == null
+ || !whiteListedPackages.contains(packageName))) {
+ Slog.w(TAG, "Ignoring not whitelisted compat package " + packageName);
+ continue;
+ }
+ final Long maxVersionCode = compatPackages.valueAt(i).first;
+ if (maxVersionCode != null) {
+ mAutofillCompatState.addCompatibilityModeRequest(packageName,
+ maxVersionCode, userId);
+ }
+ }
+ }
+ private @Nullable Set<String> getWhitelistedCompatModePackages() {
+ final String compatPackagesSetting = Settings.Global.getString(
+ mContext.getContentResolver(),
+ Settings.Global.AUTOFILL_COMPAT_ALLOWED_PACKAGES);
+ if (TextUtils.isEmpty(compatPackagesSetting)) {
+ return null;
+ }
+ final Set<String> compatPackages = new ArraySet<>();
+ final SimpleStringSplitter splitter = new SimpleStringSplitter(
+ COMPAT_PACKAGE_DELIMITER);
+ splitter.setString(compatPackagesSetting);
+ while (splitter.hasNext()) {
+ compatPackages.add(splitter.next());
+ }
+ return compatPackages;
+ }
+
+ private final class LocalService extends AutofillManagerInternal {
@Override
public void onBackKeyPressed() {
if (sDebug) Slog.d(TAG, "onBackKeyPressed()");
@@ -519,13 +577,59 @@ public final class AutofillManagerService extends SystemService {
@Override
public boolean isCompatibilityModeRequested(@NonNull String packageName,
long versionCode, @UserIdInt int userId) {
+ return mAutofillCompatState.isCompatibilityModeRequested(
+ packageName, versionCode, userId);
+ }
+ }
+
+ private static class AutofillCompatState {
+ private final Object mLock = new Object();
+
+ @GuardedBy("mLock")
+ private SparseArray<ArrayMap<String, Long>> mUserSpecs;
+
+ boolean isCompatibilityModeRequested(@NonNull String packageName,
+ long versionCode, @UserIdInt int userId) {
synchronized (mLock) {
- final AutofillManagerServiceImpl service = getServiceForUserLocked(userId);
- if (service != null) {
- return service.isCompatibilityModeRequestedLocked(packageName, versionCode);
+ if (mUserSpecs == null) {
+ return false;
+ }
+ final ArrayMap<String, Long> userSpec = mUserSpecs.get(userId);
+ if (userSpec == null) {
+ return false;
+ }
+ final Long maxVersionCode = userSpec.get(packageName);
+ if (maxVersionCode == null) {
+ return false;
+ }
+ return versionCode <= maxVersionCode;
+ }
+ }
+
+ void addCompatibilityModeRequest(@NonNull String packageName,
+ long versionCode, @UserIdInt int userId) {
+ synchronized (mLock) {
+ if (mUserSpecs == null) {
+ mUserSpecs = new SparseArray<>();
+ }
+ ArrayMap<String, Long> userSpec = mUserSpecs.get(userId);
+ if (userSpec == null) {
+ userSpec = new ArrayMap<>();
+ mUserSpecs.put(userId, userSpec);
+ }
+ userSpec.put(packageName, versionCode);
+ }
+ }
+
+ void removeCompatibilityModeRequests(@UserIdInt int userId) {
+ synchronized (mLock) {
+ if (mUserSpecs != null) {
+ mUserSpecs.remove(userId);
+ if (mUserSpecs.size() <= 0) {
+ mUserSpecs = null;
+ }
}
}
- return false;
}
}
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 75ae2dc0bb4b..31f293334c65 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -64,6 +64,7 @@ import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.DebugUtils;
import android.util.LocalLog;
+import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
import android.util.TimeUtils;
@@ -906,7 +907,10 @@ final class AutofillManagerServiceImpl {
pw.print(prefix); pw.print("Disabled: "); pw.println(mDisabled);
pw.print(prefix); pw.print("Field classification enabled: ");
pw.println(isFieldClassificationEnabledLocked());
- pw.print(prefix); pw.print("Compat pkgs: "); pw.println(getWhitelistedCompatModePackages());
+ final ArrayMap<String, Pair<Long, String>> compatPkgs = getCompatibilityPackagesLocked();
+ if (compatPkgs != null) {
+ pw.print(prefix); pw.print("Compat pkgs: "); pw.println(compatPkgs.keySet());
+ }
pw.print(prefix); pw.print("Setup complete: "); pw.println(mSetupComplete);
pw.print(prefix); pw.print("Last prune: "); pw.println(mLastPrune);
@@ -1030,23 +1034,11 @@ final class AutofillManagerServiceImpl {
}
@GuardedBy("mLock")
- boolean isCompatibilityModeRequestedLocked(@NonNull String packageName,
- long versionCode) {
- if (mInfo == null || !mInfo.isCompatibilityModeRequested(packageName, versionCode)) {
- return false;
+ @Nullable ArrayMap<String, Pair<Long, String>> getCompatibilityPackagesLocked() {
+ if (mInfo != null) {
+ return mInfo.getCompatibilityPackages();
}
- if (!Build.IS_ENG) {
- // TODO: Build a map and watch for settings changes (this is called on app start)
- final String whiteListedPackages = getWhitelistedCompatModePackages();
- return whiteListedPackages != null && whiteListedPackages.contains(packageName);
- }
- return true;
- }
-
- private String getWhitelistedCompatModePackages() {
- return Settings.Global.getString(
- mContext.getContentResolver(),
- Settings.Global.AUTOFILL_COMPAT_ALLOWED_PACKAGES);
+ return null;
}
private void sendStateToClients(boolean resetClient) {
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index bdeb23163e7e..7cd007bd0b8f 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -120,7 +120,6 @@ import android.service.vr.IVrManager;
import android.service.vr.IVrStateCallbacks;
import android.text.TextUtils;
import android.text.style.SuggestionSpan;
-import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.EventLog;
@@ -183,7 +182,6 @@ import java.util.concurrent.atomic.AtomicInteger;
public class InputMethodManagerService extends IInputMethodManager.Stub
implements ServiceConnection, Handler.Callback {
static final boolean DEBUG = false;
- static final boolean DEBUG_RESTORE = DEBUG || false;
static final String TAG = "InputMethodManagerService";
@Retention(SOURCE)
@@ -911,15 +909,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
|| Intent.ACTION_USER_REMOVED.equals(action)) {
updateCurrentProfileIds();
return;
- } else if (Intent.ACTION_SETTING_RESTORED.equals(action)) {
- final String name = intent.getStringExtra(Intent.EXTRA_SETTING_NAME);
- if (Settings.Secure.ENABLED_INPUT_METHODS.equals(name)) {
- final String prevValue = intent.getStringExtra(
- Intent.EXTRA_SETTING_PREVIOUS_VALUE);
- final String newValue = intent.getStringExtra(
- Intent.EXTRA_SETTING_NEW_VALUE);
- restoreEnabledInputMethods(mContext, prevValue, newValue);
- }
} else if (Intent.ACTION_LOCALE_CHANGED.equals(action)) {
onActionLocaleChanged();
} else if (ACTION_SHOW_INPUT_METHOD_PICKER.equals(action)) {
@@ -984,44 +973,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
}
- // Apply the results of a restore operation to the set of enabled IMEs. Note that this
- // does not attempt to validate on the fly with any installed device policy, so must only
- // be run in the context of initial device setup.
- //
- // TODO: Move this method to InputMethodUtils with adding unit tests.
- static void restoreEnabledInputMethods(Context context, String prevValue, String newValue) {
- if (DEBUG_RESTORE) {
- Slog.i(TAG, "Restoring enabled input methods:");
- Slog.i(TAG, "prev=" + prevValue);
- Slog.i(TAG, " new=" + newValue);
- }
- // 'new' is the just-restored state, 'prev' is what was in settings prior to the restore
- ArrayMap<String, ArraySet<String>> prevMap =
- InputMethodUtils.parseInputMethodsAndSubtypesString(prevValue);
- ArrayMap<String, ArraySet<String>> newMap =
- InputMethodUtils.parseInputMethodsAndSubtypesString(newValue);
-
- // Merge the restored ime+subtype enabled states into the live state
- for (ArrayMap.Entry<String, ArraySet<String>> entry : newMap.entrySet()) {
- final String imeId = entry.getKey();
- ArraySet<String> prevSubtypes = prevMap.get(imeId);
- if (prevSubtypes == null) {
- prevSubtypes = new ArraySet<>(2);
- prevMap.put(imeId, prevSubtypes);
- }
- prevSubtypes.addAll(entry.getValue());
- }
-
- final String mergedImesAndSubtypesString =
- InputMethodUtils.buildInputMethodsAndSubtypesString(prevMap);
- if (DEBUG_RESTORE) {
- Slog.i(TAG, "Merged IME string:");
- Slog.i(TAG, " " + mergedImesAndSubtypesString);
- }
- Settings.Secure.putString(context.getContentResolver(),
- Settings.Secure.ENABLED_INPUT_METHODS, mergedImesAndSubtypesString);
- }
-
final class MyPackageMonitor extends PackageMonitor {
/**
* Package names that are known to contain {@link InputMethodService}.
@@ -1577,7 +1528,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
broadcastFilter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
broadcastFilter.addAction(Intent.ACTION_USER_ADDED);
broadcastFilter.addAction(Intent.ACTION_USER_REMOVED);
- broadcastFilter.addAction(Intent.ACTION_SETTING_RESTORED);
broadcastFilter.addAction(Intent.ACTION_LOCALE_CHANGED);
broadcastFilter.addAction(ACTION_SHOW_INPUT_METHOD_PICKER);
mContext.registerReceiver(new ImmsBroadcastReceiver(), broadcastFilter);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index e8b78394f901..d488e6f819eb 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -380,6 +380,7 @@ import android.util.proto.ProtoUtils;
import android.view.Gravity;
import android.view.IRecentsAnimationRunner;
import android.view.LayoutInflater;
+import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationDefinition;
import android.view.View;
import android.view.WindowManager;
@@ -11362,9 +11363,6 @@ public class ActivityManagerService extends IActivityManager.Stub
throw new IllegalArgumentException("Invalid task, not in foreground");
}
- // When a task is locked, dismiss the pinned stack if it exists
- mStackSupervisor.removeStacksInWindowingModes(WINDOWING_MODE_PINNED);
-
// {@code isSystemCaller} is used to distinguish whether this request is initiated by the
// system or a specific app.
// * System-initiated requests will only start the pinned mode (screen pinning)
@@ -11374,6 +11372,9 @@ public class ActivityManagerService extends IActivityManager.Stub
final int callingUid = Binder.getCallingUid();
long ident = Binder.clearCallingIdentity();
try {
+ // When a task is locked, dismiss the pinned stack if it exists
+ mStackSupervisor.removeStacksInWindowingModes(WINDOWING_MODE_PINNED);
+
mLockTaskController.startLockTaskMode(task, isSystemCaller, callingUid);
} finally {
Binder.restoreCallingIdentity(ident);
@@ -26336,4 +26337,20 @@ public class ActivityManagerService extends IActivityManager.Stub
}
}
}
+
+ @Override
+ public void registerRemoteAnimationForNextActivityStart(String packageName,
+ RemoteAnimationAdapter adapter) throws RemoteException {
+ enforceCallingPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS,
+ "registerRemoteAnimationForNextActivityStart");
+ synchronized (this) {
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ mActivityStartController.registerRemoteAnimationForNextActivityStart(packageName,
+ adapter);
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 9838851a56cf..ddba349dbe86 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -380,8 +380,9 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
}
String getLifecycleDescription(String reason) {
- return "component:" + intent.getComponent().flattenToShortString() + ", state=" + state
- + ", reason=" + reason + ", time=" + System.currentTimeMillis();
+ return "name= " + this + ", component=" + intent.getComponent().flattenToShortString()
+ + ", package=" + packageName + ", state=" + state + ", reason=" + reason + ", time="
+ + System.currentTimeMillis();
}
void dump(PrintWriter pw, String prefix) {
@@ -2583,7 +2584,8 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
if (andResume) {
lifecycleItem = ResumeActivityItem.obtain(service.isNextTransitionForward());
} else {
- lifecycleItem = PauseActivityItem.obtain();
+ lifecycleItem = PauseActivityItem.obtain()
+ .setDescription(getLifecycleDescription("relaunchActivityLocked"));
}
final ClientTransaction transaction = ClientTransaction.obtain(app.thread, appToken);
transaction.addCallback(callbackItem);
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 055a1aa4bbca..812de88729de 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -1444,7 +1444,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
mService.mLifecycleManager.scheduleTransaction(prev.app.thread, prev.appToken,
PauseActivityItem.obtain(prev.finishing, userLeaving,
- prev.configChangeFlags, pauseImmediately));
+ prev.configChangeFlags, pauseImmediately).setDescription(
+ prev.getLifecycleDescription("startPausingLocked")));
} catch (Exception e) {
// Ignore exception, if process died other code will cleanup.
Slog.w(TAG, "Exception thrown during pause", e);
@@ -1524,7 +1525,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
if (r.finishing) {
if (DEBUG_PAUSE) Slog.v(TAG,
"Executing finish of failed to pause activity: " + r);
- finishCurrentActivityLocked(r, FINISH_AFTER_VISIBLE, false);
+ finishCurrentActivityLocked(r, FINISH_AFTER_VISIBLE, false,
+ "activityPausedLocked");
}
}
}
@@ -1541,7 +1543,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
prev.state = ActivityState.PAUSED;
if (prev.finishing) {
if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Executing finish of activity: " + prev);
- prev = finishCurrentActivityLocked(prev, FINISH_AFTER_VISIBLE, false);
+ prev = finishCurrentActivityLocked(prev, FINISH_AFTER_VISIBLE, false,
+ "completedPausedLocked");
} else if (prev.app != null) {
if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Enqueue pending stop if needed: " + prev
+ " wasStopping=" + wasStopping + " visible=" + prev.visible);
@@ -3673,8 +3676,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
final int finishMode = (r.visible || r.nowVisible) ? FINISH_AFTER_VISIBLE
: FINISH_AFTER_PAUSE;
- final boolean removedActivity = finishCurrentActivityLocked(r, finishMode, oomAdj)
- == null;
+ final boolean removedActivity = finishCurrentActivityLocked(r, finishMode, oomAdj,
+ "finishActivityLocked") == null;
// The following code is an optimization. When the last non-task overlay activity
// is removed from the task, we remove the entire task from the stack. However,
@@ -3715,7 +3718,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
static final int FINISH_AFTER_PAUSE = 1;
static final int FINISH_AFTER_VISIBLE = 2;
- final ActivityRecord finishCurrentActivityLocked(ActivityRecord r, int mode, boolean oomAdj) {
+ final ActivityRecord finishCurrentActivityLocked(ActivityRecord r, int mode, boolean oomAdj,
+ String reason) {
// First things first: if this activity is currently visible,
// and the resumed activity is not yet visible, then hold off on
// finishing until the resumed one becomes visible.
@@ -3758,7 +3762,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
|| prevState == STOPPED
|| prevState == ActivityState.INITIALIZING) {
r.makeFinishingLocked();
- boolean activityRemoved = destroyActivityLocked(r, true, "finish-imm");
+ boolean activityRemoved = destroyActivityLocked(r, true, "finish-imm:" + reason);
if (finishingActivityInNonFocusedStack) {
// Finishing activity that was in paused state and it was in not currently focused
@@ -3794,7 +3798,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
continue;
}
Slog.d(TAG, "finishAllActivitiesLocked: finishing " + r + " immediately");
- finishCurrentActivityLocked(r, FINISH_IMMEDIATELY, false);
+ finishCurrentActivityLocked(r, FINISH_IMMEDIATELY, false,
+ "finishAllActivitiesLocked");
}
}
if (noActivitiesInStack) {
@@ -4882,7 +4887,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
+ r.intent.getComponent().flattenToShortString());
// Force the destroy to skip right to removal.
r.app = null;
- finishCurrentActivityLocked(r, FINISH_IMMEDIATELY, false);
+ finishCurrentActivityLocked(r, FINISH_IMMEDIATELY, false,
+ "handleAppCrashedLocked");
}
}
}
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 4928e908ce44..5c30764e1e85 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -1453,7 +1453,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
lifecycleItem = ResumeActivityItem.obtain(mService.isNextTransitionForward())
.setDescription(r.getLifecycleDescription("realStartActivityLocked"));
} else {
- lifecycleItem = PauseActivityItem.obtain();
+ lifecycleItem = PauseActivityItem.obtain()
+ .setDescription(r.getLifecycleDescription("realStartActivityLocked"));
}
clientTransaction.setLifecycleStateRequest(lifecycleItem);
@@ -1955,7 +1956,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
final ActivityStack stack = r.getStack();
if (stack != null) {
if (r.finishing) {
- stack.finishCurrentActivityLocked(r, ActivityStack.FINISH_IMMEDIATELY, false);
+ stack.finishCurrentActivityLocked(r, ActivityStack.FINISH_IMMEDIATELY, false,
+ "activityIdleInternalLocked");
} else {
stack.stopActivityLocked(r);
}
diff --git a/services/core/java/com/android/server/am/ActivityStartController.java b/services/core/java/com/android/server/am/ActivityStartController.java
index da11f6861f54..868f90df5c1b 100644
--- a/services/core/java/com/android/server/am/ActivityStartController.java
+++ b/services/core/java/com/android/server/am/ActivityStartController.java
@@ -41,6 +41,7 @@ import android.os.Looper;
import android.os.Message;
import android.provider.Settings;
import android.util.Slog;
+import android.view.RemoteAnimationAdapter;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.am.ActivityStackSupervisor.PendingActivityLaunch;
@@ -85,6 +86,8 @@ public class ActivityStartController {
private final Handler mHandler;
+ private final PendingRemoteAnimationRegistry mPendingRemoteAnimationRegistry;
+
private final class StartHandler extends Handler {
public StartHandler(Looper looper) {
super(looper, null, true);
@@ -123,6 +126,8 @@ public class ActivityStartController {
mHandler = new StartHandler(mService.mHandlerThread.getLooper());
mFactory = factory;
mFactory.setController(this);
+ mPendingRemoteAnimationRegistry = new PendingRemoteAnimationRegistry(service,
+ service.mHandler);
}
/**
@@ -399,6 +404,15 @@ public class ActivityStartController {
return mPendingActivityLaunches.size() < pendingLaunches;
}
+ void registerRemoteAnimationForNextActivityStart(String packageName,
+ RemoteAnimationAdapter adapter) {
+ mPendingRemoteAnimationRegistry.addPendingAnimation(packageName, adapter);
+ }
+
+ PendingRemoteAnimationRegistry getPendingRemoteAnimationRegistry() {
+ return mPendingRemoteAnimationRegistry;
+ }
+
void dump(PrintWriter pw, String prefix, String dumpPackage) {
pw.print(prefix);
pw.print("mLastHomeActivityStartResult=");
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index 055b89b6a6f0..0dcefbfd26a4 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -708,6 +708,8 @@ class ActivityStarter {
ActivityOptions checkedOptions = options != null
? options.getOptions(intent, aInfo, callerApp, mSupervisor)
: null;
+ checkedOptions = mService.getActivityStartController().getPendingRemoteAnimationRegistry()
+ .overrideOptionsIfNeeded(callingPackage, checkedOptions);
if (mService.mController != null) {
try {
// The Intent we give to the watcher has the extra data
diff --git a/services/core/java/com/android/server/am/PendingRemoteAnimationRegistry.java b/services/core/java/com/android/server/am/PendingRemoteAnimationRegistry.java
new file mode 100644
index 000000000000..77713f57d017
--- /dev/null
+++ b/services/core/java/com/android/server/am/PendingRemoteAnimationRegistry.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 com.android.server.am;
+
+import android.annotation.Nullable;
+import android.app.ActivityOptions;
+import android.os.Handler;
+import android.util.ArrayMap;
+import android.view.RemoteAnimationAdapter;
+
+/**
+ * Registry to keep track of remote animations to be run for activity starts from a certain package.
+ *
+ * @see ActivityManagerService#registerRemoteAnimationForNextActivityStart
+ */
+class PendingRemoteAnimationRegistry {
+
+ private static final long TIMEOUT_MS = 3000;
+
+ private final ArrayMap<String, Entry> mEntries = new ArrayMap<>();
+ private final Handler mHandler;
+ private final ActivityManagerService mService;
+
+ PendingRemoteAnimationRegistry(ActivityManagerService service, Handler handler) {
+ mService = service;
+ mHandler = handler;
+ }
+
+ /**
+ * Adds a remote animation to be run for all activity starts originating from a certain package.
+ */
+ void addPendingAnimation(String packageName, RemoteAnimationAdapter adapter) {
+ mEntries.put(packageName, new Entry(packageName, adapter));
+ }
+
+ /**
+ * Overrides the activity options with a registered remote animation for a certain calling
+ * package if such a remote animation is registered.
+ */
+ ActivityOptions overrideOptionsIfNeeded(String callingPackage,
+ @Nullable ActivityOptions options) {
+ final Entry entry = mEntries.get(callingPackage);
+ if (entry == null) {
+ return options;
+ }
+ if (options == null) {
+ options = ActivityOptions.makeRemoteAnimation(entry.adapter);
+ } else {
+ options.setRemoteAnimationAdapter(entry.adapter);
+ }
+ mEntries.remove(callingPackage);
+ return options;
+ }
+
+ private class Entry {
+ final String packageName;
+ final RemoteAnimationAdapter adapter;
+
+ Entry(String packageName, RemoteAnimationAdapter adapter) {
+ this.packageName = packageName;
+ this.adapter = adapter;
+ mHandler.postDelayed(() -> {
+ synchronized (mService) {
+ final Entry entry = mEntries.get(packageName);
+ if (entry == this) {
+ mEntries.remove(packageName);
+ }
+ }
+ }, TIMEOUT_MS);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index c5424b7ddbcf..76e0d8984cb8 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -1028,14 +1028,16 @@ public class AudioService extends IAudioService.Stub
}
private void checkAllAliasStreamVolumes() {
- synchronized (VolumeStreamState.class) {
- int numStreamTypes = AudioSystem.getNumStreamTypes();
- for (int streamType = 0; streamType < numStreamTypes; streamType++) {
- mStreamStates[streamType]
- .setAllIndexes(mStreamStates[mStreamVolumeAlias[streamType]], TAG);
- // apply stream volume
- if (!mStreamStates[streamType].mIsMuted) {
- mStreamStates[streamType].applyAllVolumes();
+ synchronized (mSettingsLock) {
+ synchronized (VolumeStreamState.class) {
+ int numStreamTypes = AudioSystem.getNumStreamTypes();
+ for (int streamType = 0; streamType < numStreamTypes; streamType++) {
+ mStreamStates[streamType]
+ .setAllIndexes(mStreamStates[mStreamVolumeAlias[streamType]], TAG);
+ // apply stream volume
+ if (!mStreamStates[streamType].mIsMuted) {
+ mStreamStates[streamType].applyAllVolumes();
+ }
}
}
}
@@ -1141,13 +1143,16 @@ public class AudioService extends IAudioService.Stub
if (updateVolumes && mStreamStates != null) {
updateDefaultVolumes();
- mStreamStates[AudioSystem.STREAM_DTMF].setAllIndexes(mStreamStates[dtmfStreamAlias],
- caller);
-
- mStreamStates[AudioSystem.STREAM_ACCESSIBILITY].mVolumeIndexSettingName =
- System.VOLUME_SETTINGS_INT[a11yStreamAlias];
- mStreamStates[AudioSystem.STREAM_ACCESSIBILITY].setAllIndexes(
- mStreamStates[a11yStreamAlias], caller);
+ synchronized (mSettingsLock) {
+ synchronized (VolumeStreamState.class) {
+ mStreamStates[AudioSystem.STREAM_DTMF]
+ .setAllIndexes(mStreamStates[dtmfStreamAlias], caller);
+ mStreamStates[AudioSystem.STREAM_ACCESSIBILITY].mVolumeIndexSettingName =
+ System.VOLUME_SETTINGS_INT[a11yStreamAlias];
+ mStreamStates[AudioSystem.STREAM_ACCESSIBILITY].setAllIndexes(
+ mStreamStates[a11yStreamAlias], caller);
+ }
+ }
if (sIndependentA11yVolume) {
// restore the a11y values from the settings
mStreamStates[AudioSystem.STREAM_ACCESSIBILITY].readSettings();
@@ -4590,39 +4595,36 @@ public class AudioService extends IAudioService.Stub
* @param srcStream
* @param caller
*/
+ // must be sync'd on mSettingsLock before VolumeStreamState.class
+ @GuardedBy("VolumeStreamState.class")
public void setAllIndexes(VolumeStreamState srcStream, String caller) {
if (mStreamType == srcStream.mStreamType) {
return;
}
- synchronized (mSettingsLock) {
- synchronized (VolumeStreamState.class) {
- int srcStreamType = srcStream.getStreamType();
- // apply default device volume from source stream to all devices first in case
- // some devices are present in this stream state but not in source stream state
- int index = srcStream.getIndex(AudioSystem.DEVICE_OUT_DEFAULT);
- index = rescaleIndex(index, srcStreamType, mStreamType);
- for (int i = 0; i < mIndexMap.size(); i++) {
- mIndexMap.put(mIndexMap.keyAt(i), index);
- }
- // Now apply actual volume for devices in source stream state
- SparseIntArray srcMap = srcStream.mIndexMap;
- for (int i = 0; i < srcMap.size(); i++) {
- int device = srcMap.keyAt(i);
- index = srcMap.valueAt(i);
- index = rescaleIndex(index, srcStreamType, mStreamType);
-
- setIndex(index, device, caller);
- }
- }
+ int srcStreamType = srcStream.getStreamType();
+ // apply default device volume from source stream to all devices first in case
+ // some devices are present in this stream state but not in source stream state
+ int index = srcStream.getIndex(AudioSystem.DEVICE_OUT_DEFAULT);
+ index = rescaleIndex(index, srcStreamType, mStreamType);
+ for (int i = 0; i < mIndexMap.size(); i++) {
+ mIndexMap.put(mIndexMap.keyAt(i), index);
+ }
+ // Now apply actual volume for devices in source stream state
+ SparseIntArray srcMap = srcStream.mIndexMap;
+ for (int i = 0; i < srcMap.size(); i++) {
+ int device = srcMap.keyAt(i);
+ index = srcMap.valueAt(i);
+ index = rescaleIndex(index, srcStreamType, mStreamType);
+
+ setIndex(index, device, caller);
}
}
- @GuardedBy("mSettingsLock")
+ // must be sync'd on mSettingsLock before VolumeStreamState.class
+ @GuardedBy("VolumeStreamState.class")
public void setAllIndexesToMax() {
- synchronized (VolumeStreamState.class) {
- for (int i = 0; i < mIndexMap.size(); i++) {
- mIndexMap.put(mIndexMap.keyAt(i), mIndexMax);
- }
+ for (int i = 0; i < mIndexMap.size(); i++) {
+ mIndexMap.put(mIndexMap.keyAt(i), mIndexMax);
}
}
@@ -6201,15 +6203,17 @@ public class AudioService extends IAudioService.Stub
mCameraSoundForced = cameraSoundForced;
if (cameraSoundForcedChanged) {
if (!mIsSingleVolume) {
- VolumeStreamState s = mStreamStates[AudioSystem.STREAM_SYSTEM_ENFORCED];
- if (cameraSoundForced) {
- s.setAllIndexesToMax();
- mRingerModeAffectedStreams &=
- ~(1 << AudioSystem.STREAM_SYSTEM_ENFORCED);
- } else {
- s.setAllIndexes(mStreamStates[AudioSystem.STREAM_SYSTEM], TAG);
- mRingerModeAffectedStreams |=
- (1 << AudioSystem.STREAM_SYSTEM_ENFORCED);
+ synchronized (VolumeStreamState.class) {
+ VolumeStreamState s = mStreamStates[AudioSystem.STREAM_SYSTEM_ENFORCED];
+ if (cameraSoundForced) {
+ s.setAllIndexesToMax();
+ mRingerModeAffectedStreams &=
+ ~(1 << AudioSystem.STREAM_SYSTEM_ENFORCED);
+ } else {
+ s.setAllIndexes(mStreamStates[AudioSystem.STREAM_SYSTEM], TAG);
+ mRingerModeAffectedStreams |=
+ (1 << AudioSystem.STREAM_SYSTEM_ENFORCED);
+ }
}
// take new state into account for streams muted by ringer mode
setRingerModeInt(getRingerModeInternal(), false);
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsStrongAuth.java b/services/core/java/com/android/server/locksettings/LockSettingsStrongAuth.java
index c9c93293e2ee..c4f1f3d7369d 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsStrongAuth.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsStrongAuth.java
@@ -19,9 +19,6 @@ package com.android.server.locksettings;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_TIMEOUT;
-import com.android.internal.widget.LockPatternUtils;
-import com.android.internal.widget.LockPatternUtils.StrongAuthTracker;
-
import android.app.AlarmManager;
import android.app.AlarmManager.OnAlarmListener;
import android.app.admin.DevicePolicyManager;
@@ -29,10 +26,9 @@ import android.app.trust.IStrongAuthTracker;
import android.content.Context;
import android.content.pm.PackageManager;
import android.hardware.fingerprint.FingerprintManager;
-import android.os.Binder;
-import android.os.DeadObjectException;
import android.os.Handler;
import android.os.Message;
+import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
@@ -40,7 +36,7 @@ import android.util.ArrayMap;
import android.util.Slog;
import android.util.SparseIntArray;
-import java.util.ArrayList;
+import com.android.internal.widget.LockPatternUtils.StrongAuthTracker;
/**
* Keeps track of requests for strong authentication.
@@ -58,7 +54,7 @@ public class LockSettingsStrongAuth {
private static final String STRONG_AUTH_TIMEOUT_ALARM_TAG =
"LockSettingsStrongAuth.timeoutForUser";
- private final ArrayList<IStrongAuthTracker> mStrongAuthTrackers = new ArrayList<>();
+ private final RemoteCallbackList<IStrongAuthTracker> mTrackers = new RemoteCallbackList<>();
private final SparseIntArray mStrongAuthForUser = new SparseIntArray();
private final ArrayMap<Integer, StrongAuthTimeoutAlarmListener>
mStrongAuthTimeoutAlarmListenerForUser = new ArrayMap<>();
@@ -82,12 +78,7 @@ public class LockSettingsStrongAuth {
}
private void handleAddStrongAuthTracker(IStrongAuthTracker tracker) {
- for (int i = 0; i < mStrongAuthTrackers.size(); i++) {
- if (mStrongAuthTrackers.get(i).asBinder() == tracker.asBinder()) {
- return;
- }
- }
- mStrongAuthTrackers.add(tracker);
+ mTrackers.register(tracker);
for (int i = 0; i < mStrongAuthForUser.size(); i++) {
int key = mStrongAuthForUser.keyAt(i);
@@ -101,12 +92,7 @@ public class LockSettingsStrongAuth {
}
private void handleRemoveStrongAuthTracker(IStrongAuthTracker tracker) {
- for (int i = 0; i < mStrongAuthTrackers.size(); i++) {
- if (mStrongAuthTrackers.get(i).asBinder() == tracker.asBinder()) {
- mStrongAuthTrackers.remove(i);
- return;
- }
- }
+ mTrackers.unregister(tracker);
}
private void handleRequireStrongAuth(int strongAuthReason, int userId) {
@@ -157,16 +143,19 @@ public class LockSettingsStrongAuth {
}
private void notifyStrongAuthTrackers(int strongAuthReason, int userId) {
- for (int i = 0; i < mStrongAuthTrackers.size(); i++) {
- try {
- mStrongAuthTrackers.get(i).onStrongAuthRequiredChanged(strongAuthReason, userId);
- } catch (DeadObjectException e) {
- Slog.d(TAG, "Removing dead StrongAuthTracker.");
- mStrongAuthTrackers.remove(i);
+ int i = mTrackers.beginBroadcast();
+ try {
+ while (i > 0) {
i--;
- } catch (RemoteException e) {
- Slog.e(TAG, "Exception while notifying StrongAuthTracker.", e);
+ try {
+ mTrackers.getBroadcastItem(i).onStrongAuthRequiredChanged(
+ strongAuthReason, userId);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Exception while notifying StrongAuthTracker.", e);
+ }
}
+ } finally {
+ mTrackers.finishBroadcast();
}
}
@@ -243,4 +232,5 @@ public class LockSettingsStrongAuth {
}
}
};
+
}
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/OWNERS b/services/core/java/com/android/server/locksettings/recoverablekeystore/OWNERS
new file mode 100644
index 000000000000..bb487fb52c9f
--- /dev/null
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/OWNERS
@@ -0,0 +1,4 @@
+aseemk@google.com
+bozhu@google.com
+dementyev@google.com
+robertberry@google.com
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index f34662909a85..e2b2d46eb4a6 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -145,6 +145,7 @@ import android.net.TrafficStats;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiManager;
import android.os.Binder;
+import android.os.Build;
import android.os.Environment;
import android.os.Handler;
import android.os.HandlerThread;
@@ -2759,6 +2760,13 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
return;
}
+ // Fourth check: is caller a testing app on a debug build?
+ final boolean enableDebug = Build.IS_USERDEBUG || Build.IS_ENG;
+ if (enableDebug && callingPackage
+ .equals(SystemProperties.get("fw.sub_plan_owner." + subId, null))) {
+ return;
+ }
+
// Final check: does the caller hold a permission?
mContext.enforceCallingOrSelfPermission(MANAGE_SUBSCRIPTION_PLANS, TAG);
}
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index a0577b15736b..0eeaf661635d 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -1486,6 +1486,23 @@ public class UserManagerService extends IUserManager.Stub {
return restrictions != null && restrictions.getBoolean(restrictionKey);
}
+ /** @return if any user has the given restriction. */
+ @Override
+ public boolean hasUserRestrictionOnAnyUser(String restrictionKey) {
+ if (!UserRestrictionsUtils.isValidRestriction(restrictionKey)) {
+ return false;
+ }
+ final List<UserInfo> users = getUsers(/* excludeDying= */ true);
+ for (int i = 0; i < users.size(); i++) {
+ final int userId = users.get(i).id;
+ Bundle restrictions = getEffectiveUserRestrictions(userId);
+ if (restrictions != null && restrictions.getBoolean(restrictionKey)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
/**
* @hide
*
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index 23185d7fb5ab..41570c48c474 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -33,6 +33,7 @@ import android.os.UserHandle;
import android.os.UserManager;
import android.os.UserManagerInternal;
import android.provider.Settings;
+import android.provider.Settings.Global;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.util.Log;
@@ -581,6 +582,15 @@ public class UserRestrictionsUtils {
Settings.Secure.DOZE_PULSE_ON_DOUBLE_TAP, "0");
}
break;
+ case UserManager.DISALLOW_CONFIG_LOCATION:
+ // When DISALLOW_CONFIG_LOCATION is set on any user, we undo the global
+ // kill switch.
+ if (newValue) {
+ android.provider.Settings.Global.putString(
+ context.getContentResolver(),
+ Global.LOCATION_GLOBAL_KILL_SWITCH, "0");
+ }
+ break;
}
} finally {
Binder.restoreCallingIdentity(id);
diff --git a/services/tests/servicestests/src/com/android/server/am/PendingRemoteAnimationRegistryTest.java b/services/tests/servicestests/src/com/android/server/am/PendingRemoteAnimationRegistryTest.java
new file mode 100644
index 000000000000..2baf9952cb9e
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/am/PendingRemoteAnimationRegistryTest.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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 org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import android.annotation.Nullable;
+import android.app.ActivityOptions;
+import android.os.Handler;
+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.ArrayMap;
+import android.view.RemoteAnimationAdapter;
+
+import com.android.server.testutils.OffsettableClock;
+import com.android.server.testutils.TestHandler;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * atest PendingRemoteAnimationRegistryTest
+ */
+@SmallTest
+@Presubmit
+@FlakyTest
+@RunWith(AndroidJUnit4.class)
+public class PendingRemoteAnimationRegistryTest extends ActivityTestsBase {
+
+ @Mock RemoteAnimationAdapter mAdapter;
+ private PendingRemoteAnimationRegistry mRegistry;
+ private final OffsettableClock mClock = new OffsettableClock.Stopped();
+ private TestHandler mHandler;
+ private ActivityManagerService mService;
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+ MockitoAnnotations.initMocks(this);
+ mService = createActivityManagerService();
+ mService.mHandlerThread.getThreadHandler().runWithScissors(() -> {
+ mHandler = new TestHandler(null, mClock);
+ }, 0);
+ mRegistry = new PendingRemoteAnimationRegistry(mService, mHandler);
+ }
+
+ @Test
+ public void testOverrideActivityOptions() {
+ mRegistry.addPendingAnimation("com.android.test", mAdapter);
+ ActivityOptions opts = ActivityOptions.makeBasic();
+ opts = mRegistry.overrideOptionsIfNeeded("com.android.test", opts);
+ assertEquals(mAdapter, opts.getRemoteAnimationAdapter());
+ }
+
+ @Test
+ public void testOverrideActivityOptions_null() {
+ mRegistry.addPendingAnimation("com.android.test", mAdapter);
+ final ActivityOptions opts = mRegistry.overrideOptionsIfNeeded("com.android.test", null);
+ assertNotNull(opts);
+ assertEquals(mAdapter, opts.getRemoteAnimationAdapter());
+ }
+
+ @Test
+ public void testTimeout() {
+ mRegistry.addPendingAnimation("com.android.test", mAdapter);
+ mClock.fastForward(5000);
+ mHandler.timeAdvance();
+ assertNull(mRegistry.overrideOptionsIfNeeded("com.android.test", null));
+ }
+
+ @Test
+ public void testTimeout_overridenEntry() {
+ mRegistry.addPendingAnimation("com.android.test", mAdapter);
+ mClock.fastForward(2500);
+ mHandler.timeAdvance();
+ mRegistry.addPendingAnimation("com.android.test", mAdapter);
+ mClock.fastForward(1000);
+ mHandler.timeAdvance();
+ final ActivityOptions opts = mRegistry.overrideOptionsIfNeeded("com.android.test", null);
+ assertEquals(mAdapter, opts.getRemoteAnimationAdapter());
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/OWNERS b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/OWNERS
new file mode 100644
index 000000000000..bb487fb52c9f
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/OWNERS
@@ -0,0 +1,4 @@
+aseemk@google.com
+bozhu@google.com
+dementyev@google.com
+robertberry@google.com
diff --git a/tests/net/java/android/net/IpSecConfigTest.java b/tests/net/java/android/net/IpSecConfigTest.java
index f6c5532363e8..f186ee55d2c7 100644
--- a/tests/net/java/android/net/IpSecConfigTest.java
+++ b/tests/net/java/android/net/IpSecConfigTest.java
@@ -17,6 +17,7 @@
package android.net;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
@@ -48,18 +49,12 @@ public class IpSecConfigTest {
assertEquals(IpSecManager.INVALID_RESOURCE_ID, c.getSpiResourceId());
}
- @Test
- public void testParcelUnparcel() throws Exception {
- assertParcelingIsLossless(new IpSecConfig());
-
+ private IpSecConfig getSampleConfig() {
IpSecConfig c = new IpSecConfig();
c.setMode(IpSecTransform.MODE_TUNNEL);
c.setSourceAddress("0.0.0.0");
c.setDestinationAddress("1.2.3.4");
- c.setEncapType(android.system.OsConstants.UDP_ENCAP_ESPINUDP);
- c.setEncapSocketResourceId(7);
- c.setEncapRemotePort(22);
- c.setNattKeepaliveInterval(42);
+ c.setSpiResourceId(1984);
c.setEncryption(
new IpSecAlgorithm(
IpSecAlgorithm.CRYPT_AES_CBC,
@@ -68,7 +63,37 @@ public class IpSecConfigTest {
new IpSecAlgorithm(
IpSecAlgorithm.AUTH_HMAC_MD5,
new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 0}));
- c.setSpiResourceId(1984);
+ c.setAuthenticatedEncryption(
+ new IpSecAlgorithm(
+ IpSecAlgorithm.AUTH_CRYPT_AES_GCM,
+ new byte[] {
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 0, 1, 2, 3, 4
+ },
+ 128));
+ c.setEncapType(android.system.OsConstants.UDP_ENCAP_ESPINUDP);
+ c.setEncapSocketResourceId(7);
+ c.setEncapRemotePort(22);
+ c.setNattKeepaliveInterval(42);
+ c.setMarkValue(12);
+ c.setMarkMask(23);
+
+ return c;
+ }
+
+ @Test
+ public void testCopyConstructor() {
+ IpSecConfig original = getSampleConfig();
+ IpSecConfig copy = new IpSecConfig(original);
+
+ assertTrue(IpSecConfig.equals(original, copy));
+ assertFalse(original == copy);
+ }
+
+ @Test
+ public void testParcelUnparcel() throws Exception {
+ assertParcelingIsLossless(new IpSecConfig());
+
+ IpSecConfig c = getSampleConfig();
assertParcelingIsLossless(c);
}
diff --git a/tests/net/java/android/net/IpSecTransformTest.java b/tests/net/java/android/net/IpSecTransformTest.java
new file mode 100644
index 000000000000..b4342df58549
--- /dev/null
+++ b/tests/net/java/android/net/IpSecTransformTest.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import static org.junit.Assert.assertFalse;
+
+import android.support.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Unit tests for {@link IpSecTransform}. */
+@SmallTest
+@RunWith(JUnit4.class)
+public class IpSecTransformTest {
+
+ @Test
+ public void testCreateTransformCopiesConfig() {
+ // Create a config with a few parameters to make sure it's not empty
+ IpSecConfig config = new IpSecConfig();
+ config.setSourceAddress("0.0.0.0");
+ config.setDestinationAddress("1.2.3.4");
+ config.setSpiResourceId(1984);
+
+ IpSecTransform preModification = new IpSecTransform(null, config);
+
+ config.setSpiResourceId(1985);
+ IpSecTransform postModification = new IpSecTransform(null, config);
+
+ assertFalse(IpSecTransform.equals(preModification, postModification));
+ }
+
+ @Test
+ public void testCreateTransformsWithSameConfigEqual() {
+ // Create a config with a few parameters to make sure it's not empty
+ IpSecConfig config = new IpSecConfig();
+ config.setSourceAddress("0.0.0.0");
+ config.setDestinationAddress("1.2.3.4");
+ config.setSpiResourceId(1984);
+
+ IpSecTransform config1 = new IpSecTransform(null, config);
+ IpSecTransform config2 = new IpSecTransform(null, config);
+
+ assertFalse(IpSecTransform.equals(config1, config2));
+ }
+}
diff --git a/tools/aapt2/Debug.cpp b/tools/aapt2/Debug.cpp
index 5831875680ac..249557af921e 100644
--- a/tools/aapt2/Debug.cpp
+++ b/tools/aapt2/Debug.cpp
@@ -414,59 +414,70 @@ class XmlPrinter : public xml::ConstVisitor {
public:
using xml::ConstVisitor::Visit;
- void Visit(const xml::Element* el) override {
- const size_t previous_size = prefix_.size();
+ XmlPrinter(Printer* printer) : printer_(printer) {
+ }
+ void Visit(const xml::Element* el) override {
for (const xml::NamespaceDecl& decl : el->namespace_decls) {
- std::cerr << prefix_ << "N: " << decl.prefix << "=" << decl.uri
- << " (line=" << decl.line_number << ")\n";
- prefix_ += " ";
+ printer_->Println(StringPrintf("N: %s=%s (line=%zu)", decl.prefix.c_str(), decl.uri.c_str(),
+ decl.line_number));
+ printer_->Indent();
}
- std::cerr << prefix_ << "E: ";
+ printer_->Print("E: ");
if (!el->namespace_uri.empty()) {
- std::cerr << el->namespace_uri << ":";
+ printer_->Print(el->namespace_uri);
+ printer_->Print(":");
}
- std::cerr << el->name << " (line=" << el->line_number << ")\n";
+ printer_->Println(StringPrintf("%s (line=%zu)", el->name.c_str(), el->line_number));
+ printer_->Indent();
for (const xml::Attribute& attr : el->attributes) {
- std::cerr << prefix_ << " A: ";
+ printer_->Print("A: ");
if (!attr.namespace_uri.empty()) {
- std::cerr << attr.namespace_uri << ":";
+ printer_->Print(attr.namespace_uri);
+ printer_->Print(":");
}
- std::cerr << attr.name;
+ printer_->Print(attr.name);
if (attr.compiled_attribute) {
- std::cerr << "(" << attr.compiled_attribute.value().id.value_or_default(ResourceId(0x0))
- << ")";
+ printer_->Print("(");
+ printer_->Print(
+ attr.compiled_attribute.value().id.value_or_default(ResourceId(0)).to_string());
+ printer_->Print(")");
}
- std::cerr << "=";
+ printer_->Print("=");
if (attr.compiled_value != nullptr) {
- std::cerr << *attr.compiled_value;
+ attr.compiled_value->PrettyPrint(printer_);
} else {
- std::cerr << attr.value;
+ printer_->Print(attr.value);
}
- std::cerr << "\n";
+ printer_->Println();
}
- prefix_ += " ";
+ printer_->Indent();
xml::ConstVisitor::Visit(el);
- prefix_.resize(previous_size);
+ printer_->Undent();
+ printer_->Undent();
+
+ for (size_t i = 0; i < el->namespace_decls.size(); i++) {
+ printer_->Undent();
+ }
}
void Visit(const xml::Text* text) override {
- std::cerr << prefix_ << "T: '" << text->text << "'\n";
+ printer_->Println(StringPrintf("T: '%s'", text->text.c_str()));
}
private:
- std::string prefix_;
+ Printer* printer_;
};
} // namespace
-void Debug::DumpXml(const xml::XmlResource& doc) {
- XmlPrinter printer;
- doc.root->Accept(&printer);
+void Debug::DumpXml(const xml::XmlResource& doc, Printer* printer) {
+ XmlPrinter xml_visitor(printer);
+ doc.root->Accept(&xml_visitor);
}
} // namespace aapt
diff --git a/tools/aapt2/Debug.h b/tools/aapt2/Debug.h
index 6209a04c3c2d..382707e1d4cd 100644
--- a/tools/aapt2/Debug.h
+++ b/tools/aapt2/Debug.h
@@ -37,7 +37,7 @@ struct Debug {
text::Printer* printer);
static void PrintStyleGraph(ResourceTable* table, const ResourceName& target_style);
static void DumpHex(const void* data, size_t len);
- static void DumpXml(const xml::XmlResource& doc);
+ static void DumpXml(const xml::XmlResource& doc, text::Printer* printer);
};
} // namespace aapt
diff --git a/tools/aapt2/LoadedApk.cpp b/tools/aapt2/LoadedApk.cpp
index 20a9f417228c..ac28227b8778 100644
--- a/tools/aapt2/LoadedApk.cpp
+++ b/tools/aapt2/LoadedApk.cpp
@@ -222,7 +222,9 @@ bool LoadedApk::WriteToArchive(IAaptContext* context, ResourceTable* split_table
} else if (manifest != nullptr && path == "AndroidManifest.xml") {
BigBuffer buffer(8192);
- XmlFlattener xml_flattener(&buffer, {});
+ XmlFlattenerOptions xml_flattener_options;
+ xml_flattener_options.use_utf16 = true;
+ XmlFlattener xml_flattener(&buffer, xml_flattener_options);
if (!xml_flattener.Consume(context, manifest)) {
context->GetDiagnostics()->Error(DiagMessage(path) << "flattening failed");
return false;
diff --git a/tools/aapt2/ResourceUtils.cpp b/tools/aapt2/ResourceUtils.cpp
index 02ac86c94b46..628466d0a281 100644
--- a/tools/aapt2/ResourceUtils.cpp
+++ b/tools/aapt2/ResourceUtils.cpp
@@ -520,6 +520,10 @@ std::unique_ptr<BinaryPrimitive> TryParseInt(const StringPiece& str) {
return util::make_unique<BinaryPrimitive>(value);
}
+std::unique_ptr<BinaryPrimitive> MakeInt(uint32_t val) {
+ return util::make_unique<BinaryPrimitive>(android::Res_value::TYPE_INT_DEC, val);
+}
+
std::unique_ptr<BinaryPrimitive> TryParseFloat(const StringPiece& str) {
std::u16string str16 = util::Utf8ToUtf16(util::TrimWhitespace(str));
android::Res_value value;
diff --git a/tools/aapt2/ResourceUtils.h b/tools/aapt2/ResourceUtils.h
index 36f6c2bda8f8..f83d49ee5591 100644
--- a/tools/aapt2/ResourceUtils.h
+++ b/tools/aapt2/ResourceUtils.h
@@ -165,6 +165,9 @@ std::unique_ptr<BinaryPrimitive> MakeBool(bool val);
*/
std::unique_ptr<BinaryPrimitive> TryParseInt(const android::StringPiece& str);
+// Returns an integer BinaryPrimitive.
+std::unique_ptr<BinaryPrimitive> MakeInt(uint32_t value);
+
/*
* Returns a BinaryPrimitve object representing a floating point number
* (float, dimension, etc) if the string was parsed as one.
diff --git a/tools/aapt2/cmd/Dump.cpp b/tools/aapt2/cmd/Dump.cpp
index 3d2fb556cf59..8e7e5e59bc31 100644
--- a/tools/aapt2/cmd/Dump.cpp
+++ b/tools/aapt2/cmd/Dump.cpp
@@ -38,6 +38,13 @@ using ::android::base::StringPrintf;
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:
@@ -69,8 +76,52 @@ static void DumpCompiledFile(const ResourceFile& file, const Source& source, off
printer->Println(StringPrintf("Data: offset=%" PRIi64 " length=%zd", offset, len));
}
+static bool DumpXmlFile(IAaptContext* context, io::IFile* file, bool proto,
+ text::Printer* printer) {
+ std::unique_ptr<xml::XmlResource> doc;
+ if (proto) {
+ std::unique_ptr<io::InputStream> in = file->OpenInputStream();
+ if (in == nullptr) {
+ context->GetDiagnostics()->Error(DiagMessage() << "failed to open file");
+ return false;
+ }
+
+ io::ZeroCopyInputAdaptor adaptor(in.get());
+ pb::XmlNode pb_node;
+ if (!pb_node.ParseFromZeroCopyStream(&adaptor)) {
+ context->GetDiagnostics()->Error(DiagMessage() << "failed to parse file as proto XML");
+ return false;
+ }
+
+ std::string err;
+ doc = DeserializeXmlResourceFromPb(pb_node, &err);
+ if (doc == nullptr) {
+ context->GetDiagnostics()->Error(DiagMessage() << "failed to deserialize proto XML");
+ return false;
+ }
+ printer->Println("Proto XML");
+ } else {
+ std::unique_ptr<io::IData> data = file->OpenAsData();
+ if (data == nullptr) {
+ context->GetDiagnostics()->Error(DiagMessage() << "failed to open file");
+ return false;
+ }
+
+ std::string err;
+ doc = xml::Inflate(data->data(), data->size(), &err);
+ if (doc == nullptr) {
+ context->GetDiagnostics()->Error(DiagMessage() << "failed to parse file as binary XML");
+ return false;
+ }
+ printer->Println("Binary XML");
+ }
+
+ Debug::DumpXml(*doc, printer);
+ return true;
+}
+
static bool TryDumpFile(IAaptContext* context, const std::string& file_path,
- const DebugPrintTableOptions& print_options) {
+ const DumpOptions& options) {
// Use a smaller buffer so that there is less latency for dumping to stdout.
constexpr size_t kStdOutBufferSize = 1024u;
io::FileOutputStream fout(STDOUT_FILENO, kStdOutBufferSize);
@@ -80,7 +131,10 @@ static bool TryDumpFile(IAaptContext* context, const std::string& file_path,
std::unique_ptr<io::ZipFileCollection> zip = io::ZipFileCollection::Create(file_path, &err);
if (zip) {
ResourceTable table;
+ bool proto = false;
if (io::IFile* file = zip->FindFile("resources.pb")) {
+ proto = true;
+
std::unique_ptr<io::IData> data = file->OpenAsData();
if (data == nullptr) {
context->GetDiagnostics()->Error(DiagMessage(file_path) << "failed to open resources.pb");
@@ -98,8 +152,6 @@ static bool TryDumpFile(IAaptContext* context, const std::string& file_path,
<< "failed to parse table: " << err);
return false;
}
-
- printer.Println("Proto APK");
} else if (io::IFile* file = zip->FindFile("resources.arsc")) {
std::unique_ptr<io::IData> data = file->OpenAsData();
if (!data) {
@@ -112,12 +164,26 @@ static bool TryDumpFile(IAaptContext* context, const std::string& file_path,
if (!parser.Parse()) {
return false;
}
+ }
- printer.Println("Binary APK");
+ if (!options.file_to_dump_path) {
+ if (proto) {
+ printer.Println("Proto APK");
+ } else {
+ printer.Println("Binary APK");
+ }
+ Debug::PrintTable(table, options.print_options, &printer);
+ return true;
}
- Debug::PrintTable(table, print_options, &printer);
- return true;
+ io::IFile* file = zip->FindFile(options.file_to_dump_path.value());
+ if (file == nullptr) {
+ context->GetDiagnostics()->Error(DiagMessage(file_path)
+ << "file '" << options.file_to_dump_path.value()
+ << "' not found in APK");
+ return false;
+ }
+ return DumpXmlFile(context, file, proto, &printer);
}
err.clear();
@@ -159,7 +225,7 @@ static bool TryDumpFile(IAaptContext* context, const std::string& file_path,
}
printer.Indent();
- Debug::PrintTable(table, print_options, &printer);
+ Debug::PrintTable(table, options.print_options, &printer);
printer.Undent();
} else if (entry->Type() == ContainerEntryType::kResFile) {
printer.Println("kResFile");
@@ -243,10 +309,13 @@ class DumpContext : public IAaptContext {
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;
@@ -255,11 +324,10 @@ int Dump(const std::vector<StringPiece>& args) {
DumpContext context;
context.SetVerbose(verbose);
- DebugPrintTableOptions dump_table_options;
- dump_table_options.show_sources = true;
- dump_table_options.show_values = !no_values;
+ options.print_options.show_sources = true;
+ options.print_options.show_values = !no_values;
for (const std::string& arg : flags.GetArgs()) {
- if (!TryDumpFile(&context, arg, dump_table_options)) {
+ if (!TryDumpFile(&context, arg, options)) {
return 1;
}
}
diff --git a/tools/aapt2/configuration/ConfigurationParser.cpp b/tools/aapt2/configuration/ConfigurationParser.cpp
index eabeb47fccec..902334b98d00 100644
--- a/tools/aapt2/configuration/ConfigurationParser.cpp
+++ b/tools/aapt2/configuration/ConfigurationParser.cpp
@@ -20,6 +20,7 @@
#include <functional>
#include <map>
#include <memory>
+#include <string>
#include <utility>
#include "android-base/file.h"
@@ -93,6 +94,7 @@ class NoopDiagnostics : public IDiagnostics {
};
NoopDiagnostics noop_;
+/** Returns the value of the label attribute for a given element. */
std::string GetLabel(const Element* element, IDiagnostics* diag) {
std::string label;
for (const auto& attr : element->attributes) {
@@ -108,6 +110,18 @@ std::string GetLabel(const Element* element, IDiagnostics* diag) {
return label;
}
+/** Returns the value of the version-code-order attribute for a given element. */
+Maybe<int32_t> GetVersionCodeOrder(const Element* element, IDiagnostics* diag) {
+ const xml::Attribute* version = element->FindAttribute("", "version-code-order");
+ if (version == nullptr) {
+ std::string label = GetLabel(element, diag);
+ diag->Error(DiagMessage() << "No version-code-order found for element '" << element->name
+ << "' with label '" << label << "'");
+ return {};
+ }
+ return std::stoi(version->value);
+}
+
/** XML node visitor that removes all of the namespace URIs from the node and all children. */
class NamespaceVisitor : public xml::Visitor {
public:
@@ -437,26 +451,37 @@ Maybe<std::vector<OutputArtifact>> ConfigurationParser::Parse(
// Convert from a parsed configuration to a list of artifacts for processing.
const std::string& apk_name = file::GetFilename(apk_path).to_string();
std::vector<OutputArtifact> output_artifacts;
- bool has_errors = false;
PostProcessingConfiguration& config = maybe_config.value();
- config.SortArtifacts();
+ bool valid = true;
int version = 1;
+
for (const ConfiguredArtifact& artifact : config.artifacts) {
Maybe<OutputArtifact> output_artifact = ToOutputArtifact(artifact, apk_name, config, diag_);
if (!output_artifact) {
// Defer return an error condition so that all errors are reported.
- has_errors = true;
+ valid = false;
} else {
output_artifact.value().version = version++;
output_artifacts.push_back(std::move(output_artifact.value()));
}
}
- if (has_errors) {
+ if (!config.ValidateVersionCodeOrdering(diag_)) {
+ diag_->Error(DiagMessage() << "could not validate post processing configuration");
+ valid = false;
+ }
+
+ if (valid) {
+ // Sorting artifacts requires that all references are valid as it uses them to determine order.
+ config.SortArtifacts();
+ }
+
+ if (!valid) {
return {};
}
+
return {output_artifacts};
}
@@ -509,8 +534,15 @@ bool AbiGroupTagHandler(PostProcessingConfiguration* config, Element* root_eleme
return false;
}
- auto& group = GetOrCreateGroup(label, &config->abi_groups);
bool valid = true;
+ OrderedEntry<Abi>& entry = config->abi_groups[label];
+ Maybe<int32_t> order = GetVersionCodeOrder(root_element, diag);
+ if (!order) {
+ valid = false;
+ } else {
+ entry.order = order.value();
+ }
+ auto& group = entry.entry;
// Special case for empty abi-group tag. Label will be used as the ABI.
if (root_element->GetChildElements().empty()) {
@@ -519,7 +551,7 @@ bool AbiGroupTagHandler(PostProcessingConfiguration* config, Element* root_eleme
return false;
}
group.push_back(abi->second);
- return true;
+ return valid;
}
for (auto* child : root_element->GetChildElements()) {
@@ -553,8 +585,15 @@ bool ScreenDensityGroupTagHandler(PostProcessingConfiguration* config, Element*
return false;
}
- auto& group = GetOrCreateGroup(label, &config->screen_density_groups);
bool valid = true;
+ OrderedEntry<ConfigDescription>& entry = config->screen_density_groups[label];
+ Maybe<int32_t> order = GetVersionCodeOrder(root_element, diag);
+ if (!order) {
+ valid = false;
+ } else {
+ entry.order = order.value();
+ }
+ auto& group = entry.entry;
// Special case for empty screen-density-group tag. Label will be used as the screen density.
if (root_element->GetChildElements().empty()) {
@@ -613,8 +652,15 @@ bool LocaleGroupTagHandler(PostProcessingConfiguration* config, Element* root_el
return false;
}
- auto& group = GetOrCreateGroup(label, &config->locale_groups);
bool valid = true;
+ OrderedEntry<ConfigDescription>& entry = config->locale_groups[label];
+ Maybe<int32_t> order = GetVersionCodeOrder(root_element, diag);
+ if (!order) {
+ valid = false;
+ } else {
+ entry.order = order.value();
+ }
+ auto& group = entry.entry;
// Special case to auto insert a locale for an empty group. Label will be used for locale.
if (root_element->GetChildElements().empty()) {
@@ -728,8 +774,15 @@ bool GlTextureGroupTagHandler(PostProcessingConfiguration* config, Element* root
return false;
}
- auto& group = GetOrCreateGroup(label, &config->gl_texture_groups);
bool valid = true;
+ OrderedEntry<GlTexture>& entry = config->gl_texture_groups[label];
+ Maybe<int32_t> order = GetVersionCodeOrder(root_element, diag);
+ if (!order) {
+ valid = false;
+ } else {
+ entry.order = order.value();
+ }
+ auto& group = entry.entry;
GlTexture result;
for (auto* child : root_element->GetChildElements()) {
@@ -771,8 +824,15 @@ bool DeviceFeatureGroupTagHandler(PostProcessingConfiguration* config, Element*
return false;
}
- auto& group = GetOrCreateGroup(label, &config->device_feature_groups);
bool valid = true;
+ OrderedEntry<DeviceFeature>& entry = config->device_feature_groups[label];
+ Maybe<int32_t> order = GetVersionCodeOrder(root_element, diag);
+ if (!order) {
+ valid = false;
+ } else {
+ entry.order = order.value();
+ }
+ auto& group = entry.entry;
for (auto* child : root_element->GetChildElements()) {
if (child->name != "supports-feature") {
diff --git a/tools/aapt2/configuration/ConfigurationParser.internal.h b/tools/aapt2/configuration/ConfigurationParser.internal.h
index a583057427e6..f071a69fc9e3 100644
--- a/tools/aapt2/configuration/ConfigurationParser.internal.h
+++ b/tools/aapt2/configuration/ConfigurationParser.internal.h
@@ -33,18 +33,31 @@ namespace configuration {
template <typename T>
struct OrderedEntry {
- size_t order;
+ int32_t order;
std::vector<T> entry;
};
-/** A mapping of group labels to group of configuration items. */
-template <class T>
-using Group = std::unordered_map<std::string, OrderedEntry<T>>;
-
/** A mapping of group label to a single configuration item. */
template <class T>
using Entry = std::unordered_map<std::string, T>;
+/** A mapping of group labels to group of configuration items. */
+template <class T>
+using Group = Entry<OrderedEntry<T>>;
+
+template<typename T>
+bool IsGroupValid(const Group<T>& group, const std::string& name, IDiagnostics* diag) {
+ std::set<int32_t> orders;
+ for (const auto& p : group) {
+ orders.insert(p.second.order);
+ }
+ bool valid = orders.size() == group.size();
+ if (!valid) {
+ diag->Error(DiagMessage() << name << " have overlapping version-code-order attributes");
+ }
+ return valid;
+}
+
/** Retrieves an entry from the provided Group, creating a new instance if one does not exist. */
template <typename T>
std::vector<T>& GetOrCreateGroup(std::string label, Group<T>* group) {
@@ -93,7 +106,7 @@ class ComparisonChain {
private:
template <typename T>
- inline size_t GetGroupOrder(const Group<T>& groups, const Maybe<std::string>& label) {
+ inline size_t GetGroupOrder(const Entry<T>& groups, const Maybe<std::string>& label) {
if (!label) {
return std::numeric_limits<size_t>::max();
}
@@ -141,6 +154,15 @@ struct PostProcessingConfiguration {
Group<GlTexture> gl_texture_groups;
Entry<AndroidSdk> android_sdks;
+ bool ValidateVersionCodeOrdering(IDiagnostics* diag) {
+ bool valid = IsGroupValid(abi_groups, "abi-groups", diag);
+ valid &= IsGroupValid(screen_density_groups, "screen-density-groups", diag);
+ valid &= IsGroupValid(locale_groups, "locale-groups", diag);
+ valid &= IsGroupValid(device_feature_groups, "device-feature-groups", diag);
+ valid &= IsGroupValid(gl_texture_groups, "gl-texture-groups", diag);
+ return valid;
+ }
+
/**
* Sorts the configured artifacts based on the ordering of the groups in the configuration file.
* The only exception to this rule is Android SDK versions. Larger SDK versions will have a larger
diff --git a/tools/aapt2/configuration/ConfigurationParser_test.cpp b/tools/aapt2/configuration/ConfigurationParser_test.cpp
index 0329846a5bb5..febbb2ed11a0 100644
--- a/tools/aapt2/configuration/ConfigurationParser_test.cpp
+++ b/tools/aapt2/configuration/ConfigurationParser_test.cpp
@@ -82,22 +82,22 @@ using ::testing::StrEq;
constexpr const char* kValidConfig = R"(<?xml version="1.0" encoding="utf-8" ?>
<post-process xmlns="http://schemas.android.com/tools/aapt">
<abi-groups>
- <abi-group label="arm">
- <abi>armeabi-v7a</abi>
- <abi>arm64-v8a</abi>
- </abi-group>
- <abi-group label="other">
+ <abi-group label="other" version-code-order="2">
<abi>x86</abi>
<abi>mips</abi>
</abi-group>
+ <abi-group label="arm" version-code-order="1">
+ <abi>armeabi-v7a</abi>
+ <abi>arm64-v8a</abi>
+ </abi-group>
</abi-groups>
<screen-density-groups>
- <screen-density-group label="large">
+ <screen-density-group label="large" version-code-order="2">
<screen-density>xhdpi</screen-density>
<screen-density>xxhdpi</screen-density>
<screen-density>xxxhdpi</screen-density>
</screen-density-group>
- <screen-density-group label="alldpi">
+ <screen-density-group label="alldpi" version-code-order="1">
<screen-density>ldpi</screen-density>
<screen-density>mdpi</screen-density>
<screen-density>hdpi</screen-density>
@@ -107,17 +107,20 @@ constexpr const char* kValidConfig = R"(<?xml version="1.0" encoding="utf-8" ?>
</screen-density-group>
</screen-density-groups>
<locale-groups>
- <locale-group label="europe">
+ <locale-group label="europe" version-code-order="1">
<locale>en</locale>
<locale>es</locale>
<locale>fr</locale>
<locale>de</locale>
</locale-group>
- <locale-group label="north-america">
+ <locale-group label="north-america" version-code-order="2">
<locale>en</locale>
<locale>es-rMX</locale>
<locale>fr-rCA</locale>
</locale-group>
+ <locale-group label="all" version-code-order="-1">
+ <locale />
+ </locale-group>
</locale-groups>
<android-sdks>
<android-sdk
@@ -131,14 +134,14 @@ constexpr const char* kValidConfig = R"(<?xml version="1.0" encoding="utf-8" ?>
</android-sdk>
</android-sdks>
<gl-texture-groups>
- <gl-texture-group label="dxt1">
+ <gl-texture-group label="dxt1" version-code-order="2">
<gl-texture name="GL_EXT_texture_compression_dxt1">
<texture-path>assets/dxt1/*</texture-path>
</gl-texture>
</gl-texture-group>
</gl-texture-groups>
<device-feature-groups>
- <device-feature-group label="low-latency">
+ <device-feature-group label="low-latency" version-code-order="2">
<supports-feature>android.hardware.audio.low_latency</supports-feature>
</device-feature-group>
</device-feature-groups>
@@ -188,19 +191,22 @@ TEST_F(ConfigurationParserTest, ExtractConfiguration) {
auto& arm = config.abi_groups["arm"];
auto& other = config.abi_groups["other"];
- EXPECT_EQ(arm.order, 1ul);
- EXPECT_EQ(other.order, 2ul);
+ EXPECT_EQ(arm.order, 1);
+ EXPECT_EQ(other.order, 2);
auto& large = config.screen_density_groups["large"];
auto& alldpi = config.screen_density_groups["alldpi"];
- EXPECT_EQ(large.order, 1ul);
- EXPECT_EQ(alldpi.order, 2ul);
+ EXPECT_EQ(large.order, 2);
+ EXPECT_EQ(alldpi.order, 1);
auto& north_america = config.locale_groups["north-america"];
auto& europe = config.locale_groups["europe"];
+ auto& all = config.locale_groups["all"];
// Checked in reverse to make sure access order does not matter.
- EXPECT_EQ(north_america.order, 2ul);
- EXPECT_EQ(europe.order, 1ul);
+ EXPECT_EQ(north_america.order, 2);
+ EXPECT_EQ(europe.order, 1);
+ EXPECT_EQ(all.order, -1);
+ EXPECT_EQ(3ul, config.locale_groups.size());
}
TEST_F(ConfigurationParserTest, ValidateFile) {
@@ -392,7 +398,7 @@ TEST_F(ConfigurationParserTest, ArtifactFormatAction) {
TEST_F(ConfigurationParserTest, AbiGroupAction) {
static constexpr const char* xml = R"xml(
- <abi-group label="arm">
+ <abi-group label="arm" version-code-order="2">
<!-- First comment. -->
<abi>
armeabi-v7a
@@ -415,7 +421,8 @@ TEST_F(ConfigurationParserTest, AbiGroupAction) {
}
TEST_F(ConfigurationParserTest, AbiGroupAction_EmptyGroup) {
- static constexpr const char* xml = R"xml(<abi-group label="arm64-v8a"/>)xml";
+ static constexpr const char* xml =
+ R"xml(<abi-group label="arm64-v8a" version-code-order="3"/>)xml";
auto doc = test::BuildXmlDom(xml);
@@ -426,12 +433,23 @@ TEST_F(ConfigurationParserTest, AbiGroupAction_EmptyGroup) {
EXPECT_THAT(config.abi_groups, SizeIs(1ul));
ASSERT_EQ(1u, config.abi_groups.count("arm64-v8a"));
- auto& out = config.abi_groups["arm64-v8a"].entry;
- ASSERT_THAT(out, ElementsAre(Abi::kArm64V8a));
+ auto& out = config.abi_groups["arm64-v8a"];
+ ASSERT_THAT(out.entry, ElementsAre(Abi::kArm64V8a));
+ EXPECT_EQ(3, out.order);
+}
+
+TEST_F(ConfigurationParserTest, AbiGroupAction_EmptyGroup_NoOrder) {
+ static constexpr const char* xml = R"xml(<abi-group label="arm64-v8a"/>)xml";
+
+ auto doc = test::BuildXmlDom(xml);
+
+ PostProcessingConfiguration config;
+ bool ok = AbiGroupTagHandler(&config, NodeCast<Element>(doc.get()->root.get()), &diag_);
+ ASSERT_FALSE(ok);
}
TEST_F(ConfigurationParserTest, AbiGroupAction_InvalidEmptyGroup) {
- static constexpr const char* xml = R"xml(<abi-group label="arm"/>)xml";
+ static constexpr const char* xml = R"xml(<abi-group label="arm" order="2"/>)xml";
auto doc = test::BuildXmlDom(xml);
@@ -442,7 +460,7 @@ TEST_F(ConfigurationParserTest, AbiGroupAction_InvalidEmptyGroup) {
TEST_F(ConfigurationParserTest, ScreenDensityGroupAction) {
static constexpr const char* xml = R"xml(
- <screen-density-group label="large">
+ <screen-density-group label="large" version-code-order="2">
<screen-density>xhdpi</screen-density>
<screen-density>
xxhdpi
@@ -471,7 +489,8 @@ TEST_F(ConfigurationParserTest, ScreenDensityGroupAction) {
}
TEST_F(ConfigurationParserTest, ScreenDensityGroupAction_EmtpyGroup) {
- static constexpr const char* xml = R"xml(<screen-density-group label="xhdpi"/>)xml";
+ static constexpr const char* xml =
+ R"xml(<screen-density-group label="xhdpi" version-code-order="4"/>)xml";
auto doc = test::BuildXmlDom(xml);
@@ -485,8 +504,19 @@ TEST_F(ConfigurationParserTest, ScreenDensityGroupAction_EmtpyGroup) {
ConfigDescription xhdpi;
xhdpi.density = ResTable_config::DENSITY_XHIGH;
- auto& out = config.screen_density_groups["xhdpi"].entry;
- ASSERT_THAT(out, ElementsAre(xhdpi));
+ auto& out = config.screen_density_groups["xhdpi"];
+ EXPECT_THAT(out.entry, ElementsAre(xhdpi));
+ EXPECT_EQ(4, out.order);
+}
+
+TEST_F(ConfigurationParserTest, ScreenDensityGroupAction_EmtpyGroup_NoVersion) {
+ static constexpr const char* xml = R"xml(<screen-density-group label="xhdpi"/>)xml";
+
+ auto doc = test::BuildXmlDom(xml);
+
+ PostProcessingConfiguration config;
+ bool ok = ScreenDensityGroupTagHandler(&config, NodeCast<Element>(doc.get()->root.get()), &diag_);
+ ASSERT_FALSE(ok);
}
TEST_F(ConfigurationParserTest, ScreenDensityGroupAction_InvalidEmtpyGroup) {
@@ -501,7 +531,7 @@ TEST_F(ConfigurationParserTest, ScreenDensityGroupAction_InvalidEmtpyGroup) {
TEST_F(ConfigurationParserTest, LocaleGroupAction) {
static constexpr const char* xml = R"xml(
- <locale-group label="europe">
+ <locale-group label="europe" version-code-order="2">
<locale>en</locale>
<locale>es</locale>
<locale>fr</locale>
@@ -528,7 +558,7 @@ TEST_F(ConfigurationParserTest, LocaleGroupAction) {
}
TEST_F(ConfigurationParserTest, LocaleGroupAction_EmtpyGroup) {
- static constexpr const char* xml = R"xml(<locale-group label="en"/>)xml";
+ static constexpr const char* xml = R"xml(<locale-group label="en" version-code-order="6"/>)xml";
auto doc = test::BuildXmlDom(xml);
@@ -539,11 +569,22 @@ TEST_F(ConfigurationParserTest, LocaleGroupAction_EmtpyGroup) {
ASSERT_EQ(1ul, config.locale_groups.size());
ASSERT_EQ(1u, config.locale_groups.count("en"));
- const auto& out = config.locale_groups["en"].entry;
+ const auto& out = config.locale_groups["en"];
ConfigDescription en = test::ParseConfigOrDie("en");
- ASSERT_THAT(out, ElementsAre(en));
+ EXPECT_THAT(out.entry, ElementsAre(en));
+ EXPECT_EQ(6, out.order);
+}
+
+TEST_F(ConfigurationParserTest, LocaleGroupAction_EmtpyGroup_NoOrder) {
+ static constexpr const char* xml = R"xml(<locale-group label="en"/>)xml";
+
+ auto doc = test::BuildXmlDom(xml);
+
+ PostProcessingConfiguration config;
+ bool ok = LocaleGroupTagHandler(&config, NodeCast<Element>(doc.get()->root.get()), &diag_);
+ ASSERT_FALSE(ok);
}
TEST_F(ConfigurationParserTest, LocaleGroupAction_InvalidEmtpyGroup) {
@@ -695,7 +736,7 @@ TEST_F(ConfigurationParserTest, AndroidSdkGroupAction_NonNumeric) {
TEST_F(ConfigurationParserTest, GlTextureGroupAction) {
static constexpr const char* xml = R"xml(
- <gl-texture-group label="dxt1">
+ <gl-texture-group label="dxt1" version-code-order="2">
<gl-texture name="GL_EXT_texture_compression_dxt1">
<texture-path>assets/dxt1/main/*</texture-path>
<texture-path>
@@ -726,7 +767,7 @@ TEST_F(ConfigurationParserTest, GlTextureGroupAction) {
TEST_F(ConfigurationParserTest, DeviceFeatureGroupAction) {
static constexpr const char* xml = R"xml(
- <device-feature-group label="low-latency">
+ <device-feature-group label="low-latency" version-code-order="2">
<supports-feature>android.hardware.audio.low_latency</supports-feature>
<supports-feature>
android.hardware.audio.pro
@@ -749,6 +790,30 @@ TEST_F(ConfigurationParserTest, DeviceFeatureGroupAction) {
ASSERT_THAT(out, ElementsAre(low_latency, pro));
}
+TEST_F(ConfigurationParserTest, Group_Valid) {
+ Group<int32_t> group;
+ group["item1"].order = 1;
+ group["item2"].order = 2;
+ group["item3"].order = 3;
+ group["item4"].order = 4;
+ group["item5"].order = 5;
+ group["item6"].order = 6;
+
+ EXPECT_TRUE(IsGroupValid(group, "test", &diag_));
+}
+
+TEST_F(ConfigurationParserTest, Group_OverlappingOrder) {
+ Group<int32_t> group;
+ group["item1"].order = 1;
+ group["item2"].order = 2;
+ group["item3"].order = 3;
+ group["item4"].order = 2;
+ group["item5"].order = 5;
+ group["item6"].order = 1;
+
+ EXPECT_FALSE(IsGroupValid(group, "test", &diag_));
+}
+
// Artifact name parser test cases.
TEST(ArtifactTest, Simple) {
diff --git a/tools/aapt2/configuration/aapt2.xsd b/tools/aapt2/configuration/aapt2.xsd
index fb2f49bae7b7..a28e28be48f4 100644
--- a/tools/aapt2/configuration/aapt2.xsd
+++ b/tools/aapt2/configuration/aapt2.xsd
@@ -81,6 +81,7 @@
<xsd:element name="gl-texture" type="gl-texture" maxOccurs="unbounded"/>
</xsd:sequence>
<xsd:attribute name="label" type="xsd:string"/>
+ <xsd:attribute name="version-code-order" type="xsd:unsignedInt" use="required"/>
</xsd:complexType>
<xsd:complexType name="gl-texture">
@@ -95,6 +96,7 @@
<xsd:element name="supports-feature" type="xsd:string" maxOccurs="unbounded"/>
</xsd:sequence>
<xsd:attribute name="label" type="xsd:string"/>
+ <xsd:attribute name="version-code-order" type="xsd:unsignedInt" use="required"/>
</xsd:complexType>
<xsd:complexType name="abi-group">
@@ -102,6 +104,7 @@
<xsd:element name="abi" type="abi-name" maxOccurs="unbounded"/>
</xsd:sequence>
<xsd:attribute name="label" type="xsd:string"/>
+ <xsd:attribute name="version-code-order" type="xsd:unsignedInt" use="required"/>
</xsd:complexType>
<xsd:simpleType name="abi-name">
@@ -122,6 +125,7 @@
<xsd:element name="screen-density" type="screen-density" maxOccurs="unbounded"/>
</xsd:sequence>
<xsd:attribute name="label" type="xsd:string"/>
+ <xsd:attribute name="version-code-order" type="xsd:unsignedInt" use="required"/>
</xsd:complexType>
<xsd:simpleType name="screen-density">
@@ -158,6 +162,7 @@
<xsd:element name="locale" type="locale" maxOccurs="unbounded"/>
</xsd:sequence>
<xsd:attribute name="label" type="xsd:string"/>
+ <xsd:attribute name="version-code-order" type="xsd:unsignedInt" use="required"/>
</xsd:complexType>
<xsd:complexType name="locale">
diff --git a/tools/aapt2/configuration/example/config.xml b/tools/aapt2/configuration/example/config.xml
index d8aba09e836d..e6db2a0f461b 100644
--- a/tools/aapt2/configuration/example/config.xml
+++ b/tools/aapt2/configuration/example/config.xml
@@ -36,28 +36,28 @@
</android-sdks>
<abi-groups>
- <abi-group label="arm">
+ <abi-group label="arm" version-code-order="1">
<abi>armeabi-v7a</abi>
<abi>arm64-v8a</abi>
</abi-group>
- <abi-group label="other">
+ <abi-group label="other" version-code-order="2">
<abi>x86</abi>
<abi>mips</abi>
</abi-group>
</abi-groups>
<screen-density-groups>
- <screen-density-group label="large">
+ <screen-density-group label="alldpi" version-code-order="1">
+ <screen-density>ldpi</screen-density>
+ <screen-density>mdpi</screen-density>
+ <screen-density>hdpi</screen-density>
<screen-density>xhdpi</screen-density>
<screen-density>xxhdpi</screen-density>
<screen-density>xxxhdpi</screen-density>
</screen-density-group>
- <screen-density-group label="alldpi">
- <screen-density>ldpi</screen-density>
- <screen-density>mdpi</screen-density>
- <screen-density>hdpi</screen-density>
+ <screen-density-group label="large" version-code-order="2">
<screen-density>xhdpi</screen-density>
<screen-density>xxhdpi</screen-density>
<screen-density>xxxhdpi</screen-density>
@@ -65,26 +65,26 @@
</screen-density-groups>
<locale-groups>
- <locale-group label="europe">
+ <locale-group label="europe" version-code-order="1">
<locale lang="en"/>
<locale lang="es"/>
<locale lang="fr"/>
<locale lang="de" compressed="true"/>
</locale-group>
- <locale-group label="north-america">
+ <locale-group label="north-america" version-code-order="2">
<locale lang="en"/>
<locale lang="es" region="MX"/>
<locale lang="fr" region="CA" compressed="true"/>
</locale-group>
- <locale-group label="all">
+ <locale-group label="all" version-code-order="0">
<locale compressed="true"/>
</locale-group>
</locale-groups>
<gl-texture-groups>
- <gl-texture-group label="dxt1">
+ <gl-texture-group label="dxt1" version-code-order="1">
<gl-texture name="GL_EXT_texture_compression_dxt1">
<texture-path>assets/dxt1/*</texture-path>
</gl-texture>
@@ -92,7 +92,7 @@
</gl-texture-groups>
<device-feature-groups>
- <device-feature-group label="low-latency">
+ <device-feature-group label="low-latency" version-code-order="1">
<supports-feature>android.hardware.audio.low_latency</supports-feature>
</device-feature-group>
</device-feature-groups>
diff --git a/tools/aapt2/optimize/MultiApkGenerator.cpp b/tools/aapt2/optimize/MultiApkGenerator.cpp
index 991faadcafc4..588b3316e6fa 100644
--- a/tools/aapt2/optimize/MultiApkGenerator.cpp
+++ b/tools/aapt2/optimize/MultiApkGenerator.cpp
@@ -322,22 +322,56 @@ bool MultiApkGenerator::UpdateManifest(const OutputArtifact& artifact,
std::unique_ptr<xml::Element> new_screens_el = util::make_unique<xml::Element>();
new_screens_el->name = "compatible-screens";
screens_el = new_screens_el.get();
- manifest_el->InsertChild(0, std::move(new_screens_el));
+ manifest_el->AppendChild(std::move(new_screens_el));
} else {
// clear out the old element.
screens_el->GetChildElements().clear();
}
for (const auto& density : artifact.screen_densities) {
- std::unique_ptr<xml::Element> screen_el = util::make_unique<xml::Element>();
- screen_el->name = "screen";
- const char* density_str = density.toString().string();
- screen_el->attributes.push_back(xml::Attribute{kSchemaAndroid, "screenDensity", density_str});
- screens_el->AppendChild(std::move(screen_el));
+ AddScreens(density, screens_el);
}
}
return true;
}
+/**
+ * Adds a screen element with both screenSize and screenDensity set. Since we only know the density
+ * we add it for all screen sizes.
+ *
+ * This requires the resource IDs for the attributes from the framework library. Since these IDs are
+ * a part of the public API (and in public.xml) we hard code the values.
+ *
+ * The excert from the framework is as follows:
+ * <public type="attr" name="screenSize" id="0x010102ca" />
+ * <public type="attr" name="screenDensity" id="0x010102cb" />
+ */
+void MultiApkGenerator::AddScreens(const ConfigDescription& config, xml::Element* parent) {
+ // Hard coded integer representation of the supported screen sizes:
+ // small = 200
+ // normal = 300
+ // large = 400
+ // xlarge = 500
+ constexpr const uint32_t kScreenSizes[4] = {200, 300, 400, 500,};
+ constexpr const uint32_t kScreenSizeResourceId = 0x010102ca;
+ constexpr const uint32_t kScreenDensityResourceId = 0x010102cb;
+
+ for (uint32_t screen_size : kScreenSizes) {
+ std::unique_ptr<xml::Element> screen = util::make_unique<xml::Element>();
+ screen->name = "screen";
+
+ xml::Attribute* size = screen->FindOrCreateAttribute(kSchemaAndroid, "screenSize");
+ size->compiled_attribute = xml::AaptAttribute(Attribute(), {kScreenSizeResourceId});
+ size->compiled_value = ResourceUtils::MakeInt(screen_size);
+
+ xml::Attribute* density = screen->FindOrCreateAttribute(kSchemaAndroid, "screenDensity");
+ density->compiled_attribute = xml::AaptAttribute(Attribute(), {kScreenDensityResourceId});
+ density->compiled_value = ResourceUtils::MakeInt(config.density);
+
+
+ parent->AppendChild(std::move(screen));
+ }
+}
+
} // namespace aapt
diff --git a/tools/aapt2/optimize/MultiApkGenerator.h b/tools/aapt2/optimize/MultiApkGenerator.h
index 19f64cc0e588..c8588791662a 100644
--- a/tools/aapt2/optimize/MultiApkGenerator.h
+++ b/tools/aapt2/optimize/MultiApkGenerator.h
@@ -63,6 +63,11 @@ class MultiApkGenerator {
bool UpdateManifest(const configuration::OutputArtifact& artifact,
std::unique_ptr<xml::XmlResource>* updated_manifest, IDiagnostics* diag);
+ /**
+ * Adds the <screen> elements to the parent node for the provided density configuration.
+ */
+ void AddScreens(const ConfigDescription& config, xml::Element* parent);
+
LoadedApk* apk_;
IAaptContext* context_;
};