Merge "Correctly update rtl state in navbar once language changes"
diff --git a/apct-tests/perftests/core/src/android/text/PrecomputedTextMemoryUsageTest.java b/apct-tests/perftests/core/src/android/text/MeasuredTextMemoryUsageTest.java
similarity index 73%
rename from apct-tests/perftests/core/src/android/text/PrecomputedTextMemoryUsageTest.java
rename to apct-tests/perftests/core/src/android/text/MeasuredTextMemoryUsageTest.java
index 73e1724..fc6302e 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 @@
@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 @@
private static int TRIAL_COUNT = 100;
- public PrecomputedTextMemoryUsageTest() {}
+ public MeasuredTextMemoryUsageTest() {}
private TextPerfUtils mTextUtil = new TextPerfUtils();
@@ -77,16 +77,13 @@
@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 @@
@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 @@
@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 @@
@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
similarity index 66%
rename from apct-tests/perftests/core/src/android/text/PrecomputedTextPerfTest.java
rename to apct-tests/perftests/core/src/android/text/MeasuredTextPerfTest.java
index 1cd0ae1..98f2bd5 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 @@
@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 @@
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 @@
@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 8823af1..231aaf2 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 @@
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 @@
}
@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 @@
}
@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 @@
}
@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 @@
}
@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 @@
}
@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 @@
}
@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 @@
}
@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 @@
}
@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 @@
}
@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 53f503a..df18f10 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -13839,7 +13839,6 @@
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();
@@ -26757,7 +26756,7 @@
field public static final deprecated java.lang.String ACTION_BACKGROUND_DATA_SETTING_CHANGED = "android.net.conn.BACKGROUND_DATA_SETTING_CHANGED";
field public static final java.lang.String ACTION_CAPTIVE_PORTAL_SIGN_IN = "android.net.conn.CAPTIVE_PORTAL";
field public static final java.lang.String ACTION_RESTRICT_BACKGROUND_CHANGED = "android.net.conn.RESTRICT_BACKGROUND_CHANGED";
- field public static final java.lang.String CONNECTIVITY_ACTION = "android.net.conn.CONNECTIVITY_CHANGE";
+ field public static final deprecated java.lang.String CONNECTIVITY_ACTION = "android.net.conn.CONNECTIVITY_CHANGE";
field public static final deprecated int DEFAULT_NETWORK_PREFERENCE = 1; // 0x1
field public static final java.lang.String EXTRA_CAPTIVE_PORTAL = "android.net.extra.CAPTIVE_PORTAL";
field public static final java.lang.String EXTRA_CAPTIVE_PORTAL_URL = "android.net.extra.CAPTIVE_PORTAL_URL";
@@ -27033,6 +27032,7 @@
field public static final int NET_CAPABILITY_CBS = 5; // 0x5
field public static final int NET_CAPABILITY_DUN = 2; // 0x2
field public static final int NET_CAPABILITY_EIMS = 10; // 0xa
+ field public static final int NET_CAPABILITY_FOREGROUND = 19; // 0x13
field public static final int NET_CAPABILITY_FOTA = 3; // 0x3
field public static final int NET_CAPABILITY_IA = 7; // 0x7
field public static final int NET_CAPABILITY_IMS = 4; // 0x4
@@ -27041,6 +27041,7 @@
field public static final int NET_CAPABILITY_NOT_METERED = 11; // 0xb
field public static final int NET_CAPABILITY_NOT_RESTRICTED = 13; // 0xd
field public static final int NET_CAPABILITY_NOT_ROAMING = 18; // 0x12
+ field public static final int NET_CAPABILITY_NOT_SUSPENDED = 21; // 0x15
field public static final int NET_CAPABILITY_NOT_VPN = 15; // 0xf
field public static final int NET_CAPABILITY_RCS = 8; // 0x8
field public static final int NET_CAPABILITY_SUPL = 1; // 0x1
@@ -28560,6 +28561,7 @@
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.net.wifi.rtt.RangingResult> CREATOR;
field public static final int STATUS_FAIL = 1; // 0x1
+ field public static final int STATUS_RESPONDER_DOES_NOT_SUPPORT_IEEE80211MC = 2; // 0x2
field public static final int STATUS_SUCCESS = 0; // 0x0
}
@@ -38685,14 +38687,14 @@
}
public final class DateTransformation implements android.os.Parcelable android.service.autofill.Transformation {
- ctor public DateTransformation(android.view.autofill.AutofillId, java.text.DateFormat);
+ ctor public DateTransformation(android.view.autofill.AutofillId, android.icu.text.DateFormat);
method public int describeContents();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.service.autofill.DateTransformation> CREATOR;
}
public final class DateValueSanitizer implements android.os.Parcelable android.service.autofill.Sanitizer {
- ctor public DateValueSanitizer(java.text.DateFormat);
+ ctor public DateValueSanitizer(android.icu.text.DateFormat);
method public int describeContents();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.service.autofill.DateValueSanitizer> CREATOR;
@@ -38703,7 +38705,7 @@
}
public static final class FieldClassification.Match {
- method public java.lang.String getRemoteId();
+ method public java.lang.String getCategoryId();
method public float getScore();
}
@@ -38868,6 +38870,7 @@
method public int describeContents();
method public java.lang.String getFieldClassificationAlgorithm();
method public java.lang.String getId();
+ method public static int getMaxCategoryCount();
method public static int getMaxFieldClassificationIdsSize();
method public static int getMaxUserDataSize();
method public static int getMaxValueLength();
@@ -43229,6 +43232,36 @@
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 +43273,6 @@
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 +43404,6 @@
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 +53655,6 @@
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 +53760,6 @@
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 +53784,6 @@
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/api/system-current.txt b/api/system-current.txt
index 5afb455..8001ee3 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -4164,6 +4164,8 @@
method public static void resetToDefaults(android.content.ContentResolver, java.lang.String);
field public static final java.lang.String AUTOFILL_COMPAT_ALLOWED_PACKAGES = "autofill_compat_allowed_packages";
field public static final java.lang.String DEFAULT_SM_DP_PLUS = "default_sm_dp_plus";
+ field public static final java.lang.String INSTALL_CARRIER_APP_NOTIFICATION_PERSISTENT = "install_carrier_app_notification_persistent";
+ field public static final java.lang.String INSTALL_CARRIER_APP_NOTIFICATION_SLEEP_MILLIS = "install_carrier_app_notification_sleep_millis";
field public static final java.lang.String OTA_DISABLE_AUTOMATIC_UPDATE = "ota_disable_automatic_update";
field public static final java.lang.String THEATER_MODE_ON = "theater_mode_on";
field public static final java.lang.String WEBVIEW_MULTIPROCESS = "webview_multiprocess";
@@ -4175,6 +4177,7 @@
method public static boolean putString(android.content.ContentResolver, java.lang.String, java.lang.String, java.lang.String, boolean);
method public static void resetToDefaults(android.content.ContentResolver, java.lang.String);
field public static final java.lang.String AUTOFILL_FEATURE_FIELD_CLASSIFICATION = "autofill_field_classification";
+ field public static final java.lang.String AUTOFILL_USER_DATA_MAX_CATEGORY_COUNT = "autofill_user_data_max_category_count";
field public static final java.lang.String AUTOFILL_USER_DATA_MAX_FIELD_CLASSIFICATION_IDS_SIZE = "autofill_user_data_max_field_classification_size";
field public static final java.lang.String AUTOFILL_USER_DATA_MAX_USER_DATA_SIZE = "autofill_user_data_max_user_data_size";
field public static final java.lang.String AUTOFILL_USER_DATA_MAX_VALUE_LENGTH = "autofill_user_data_max_value_length";
diff --git a/api/test-current.txt b/api/test-current.txt
index eda6296..9bfc105 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -541,6 +541,7 @@
field public static final java.lang.String ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED = "accessibility_display_magnification_enabled";
field public static final java.lang.String AUTOFILL_FEATURE_FIELD_CLASSIFICATION = "autofill_field_classification";
field public static final java.lang.String AUTOFILL_SERVICE = "autofill_service";
+ field public static final java.lang.String AUTOFILL_USER_DATA_MAX_CATEGORY_COUNT = "autofill_user_data_max_category_count";
field public static final java.lang.String AUTOFILL_USER_DATA_MAX_FIELD_CLASSIFICATION_IDS_SIZE = "autofill_user_data_max_field_classification_size";
field public static final java.lang.String AUTOFILL_USER_DATA_MAX_USER_DATA_SIZE = "autofill_user_data_max_user_data_size";
field public static final java.lang.String AUTOFILL_USER_DATA_MAX_VALUE_LENGTH = "autofill_user_data_max_value_length";
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index 813335a..c04e61b 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -172,6 +172,8 @@
} else if (opt.equals("--no_window_animation")
|| opt.equals("--no-window-animation")) {
instrument.noWindowAnimation = true;
+ } else if (opt.equals("--no-hidden-api-checks")) {
+ instrument.disableHiddenApiChecks = true;
} else if (opt.equals("--user")) {
instrument.userId = parseUserArg(nextArgRequired());
} else if (opt.equals("--abi")) {
diff --git a/cmds/am/src/com/android/commands/am/Instrument.java b/cmds/am/src/com/android/commands/am/Instrument.java
index d79b1a6..0dade0b 100644
--- a/cmds/am/src/com/android/commands/am/Instrument.java
+++ b/cmds/am/src/com/android/commands/am/Instrument.java
@@ -73,12 +73,17 @@
boolean protoFile = false; // write proto to a file
String logPath = null;
public boolean noWindowAnimation = false;
+ public boolean disableHiddenApiChecks = false;
public String abi = null;
public int userId = UserHandle.USER_CURRENT;
public Bundle args = new Bundle();
// Required
public String componentNameArg;
+ // Disable hidden API checks for the newly started instrumentation.
+ // Must be kept in sync with ActivityManagerService.
+ private static final int INSTRUMENTATION_FLAG_DISABLE_HIDDEN_API_CHECKS = 1 << 0;
+
/**
* Construct the instrument command runner.
*/
@@ -475,7 +480,8 @@
}
// Start the instrumentation
- if (!mAm.startInstrumentation(cn, profileFile, 0, args, watcher, connection, userId,
+ int flags = disableHiddenApiChecks ? INSTRUMENTATION_FLAG_DISABLE_HIDDEN_API_CHECKS : 0;
+ if (!mAm.startInstrumentation(cn, profileFile, flags, args, watcher, connection, userId,
abi)) {
throw new AndroidException("INSTRUMENTATION_FAILED: " + cn.flattenToString());
}
diff --git a/cmds/incident_helper/OWNERS b/cmds/incident_helper/OWNERS
new file mode 100644
index 0000000..1a68a32
--- /dev/null
+++ b/cmds/incident_helper/OWNERS
@@ -0,0 +1,2 @@
+jinyithu@google.com
+kwekua@google.com
diff --git a/cmds/incident_helper/src/parsers/CpuFreqParser.cpp b/cmds/incident_helper/src/parsers/CpuFreqParser.cpp
index 02f1ce7..fde17bd 100644
--- a/cmds/incident_helper/src/parsers/CpuFreqParser.cpp
+++ b/cmds/incident_helper/src/parsers/CpuFreqParser.cpp
@@ -62,15 +62,15 @@
ProtoOutputStream proto;
long jiffyHz = sysconf(_SC_CLK_TCK);
- proto.write(CpuFreq::JIFFY_HZ, (int)jiffyHz);
+ proto.write(CpuFreqProto::JIFFY_HZ, (int)jiffyHz);
for (int i=0; i<numCpus; i++) {
- long long token = proto.start(CpuFreq::CPU_FREQS);
- proto.write(CpuFreqStats::CPU_NAME, header[i+1]);
+ long long token = proto.start(CpuFreqProto::CPU_FREQS);
+ proto.write(CpuFreqProto::Stats::CPU_NAME, header[i+1]);
for (vector<pair<int, long long>>::iterator it = cpucores[i].begin(); it != cpucores[i].end(); it++) {
- long long stateToken = proto.start(CpuFreqStats::TIMES);
- proto.write(CpuFreqStats::TimeInState::STATE_KHZ, it->first);
- proto.write(CpuFreqStats::TimeInState::TIME_JIFFY, it->second);
+ long long stateToken = proto.start(CpuFreqProto::Stats::TIMES);
+ proto.write(CpuFreqProto::Stats::TimeInState::STATE_KHZ, it->first);
+ proto.write(CpuFreqProto::Stats::TimeInState::TIME_JIFFY, it->second);
proto.end(stateToken);
}
proto.end(token);
diff --git a/cmds/incident_helper/src/parsers/CpuInfoParser.cpp b/cmds/incident_helper/src/parsers/CpuInfoParser.cpp
index d73de54..b2b431c 100644
--- a/cmds/incident_helper/src/parsers/CpuInfoParser.cpp
+++ b/cmds/incident_helper/src/parsers/CpuInfoParser.cpp
@@ -54,11 +54,11 @@
bool nextToUsage = false;
ProtoOutputStream proto;
- Table table(CpuInfo::Task::_FIELD_NAMES, CpuInfo::Task::_FIELD_IDS, CpuInfo::Task::_FIELD_COUNT);
- table.addEnumTypeMap("s", CpuInfo::Task::_ENUM_STATUS_NAMES,
- CpuInfo::Task::_ENUM_STATUS_VALUES, CpuInfo::Task::_ENUM_STATUS_COUNT);
- table.addEnumTypeMap("pcy", CpuInfo::Task::_ENUM_POLICY_NAMES,
- CpuInfo::Task::_ENUM_POLICY_VALUES, CpuInfo::Task::_ENUM_POLICY_COUNT);
+ Table table(CpuInfoProto::Task::_FIELD_NAMES, CpuInfoProto::Task::_FIELD_IDS, CpuInfoProto::Task::_FIELD_COUNT);
+ table.addEnumTypeMap("s", CpuInfoProto::Task::_ENUM_STATUS_NAMES,
+ CpuInfoProto::Task::_ENUM_STATUS_VALUES, CpuInfoProto::Task::_ENUM_STATUS_COUNT);
+ table.addEnumTypeMap("pcy", CpuInfoProto::Task::_ENUM_POLICY_NAMES,
+ CpuInfoProto::Task::_ENUM_POLICY_VALUES, CpuInfoProto::Task::_ENUM_POLICY_COUNT);
// parse line by line
while (reader.readLine(&line)) {
@@ -67,33 +67,33 @@
nline++;
if (stripPrefix(&line, "Tasks:")) {
- writeSuffixLine(&proto, CpuInfo::TASK_STATS, line, COMMA_DELIMITER,
- CpuInfo::TaskStats::_FIELD_COUNT,
- CpuInfo::TaskStats::_FIELD_NAMES,
- CpuInfo::TaskStats::_FIELD_IDS);
+ writeSuffixLine(&proto, CpuInfoProto::TASK_STATS, line, COMMA_DELIMITER,
+ CpuInfoProto::TaskStats::_FIELD_COUNT,
+ CpuInfoProto::TaskStats::_FIELD_NAMES,
+ CpuInfoProto::TaskStats::_FIELD_IDS);
continue;
}
if (stripPrefix(&line, "Mem:")) {
- writeSuffixLine(&proto, CpuInfo::MEM, line, COMMA_DELIMITER,
- CpuInfo::MemStats::_FIELD_COUNT,
- CpuInfo::MemStats::_FIELD_NAMES,
- CpuInfo::MemStats::_FIELD_IDS);
+ writeSuffixLine(&proto, CpuInfoProto::MEM, line, COMMA_DELIMITER,
+ CpuInfoProto::MemStats::_FIELD_COUNT,
+ CpuInfoProto::MemStats::_FIELD_NAMES,
+ CpuInfoProto::MemStats::_FIELD_IDS);
continue;
}
if (stripPrefix(&line, "Swap:")) {
- writeSuffixLine(&proto, CpuInfo::SWAP, line, COMMA_DELIMITER,
- CpuInfo::MemStats::_FIELD_COUNT,
- CpuInfo::MemStats::_FIELD_NAMES,
- CpuInfo::MemStats::_FIELD_IDS);
+ writeSuffixLine(&proto, CpuInfoProto::SWAP, line, COMMA_DELIMITER,
+ CpuInfoProto::MemStats::_FIELD_COUNT,
+ CpuInfoProto::MemStats::_FIELD_NAMES,
+ CpuInfoProto::MemStats::_FIELD_IDS);
nextToSwap = true;
continue;
}
if (nextToSwap) {
- writeSuffixLine(&proto, CpuInfo::CPU_USAGE, line, DEFAULT_WHITESPACE,
- CpuInfo::CpuUsage::_FIELD_COUNT,
- CpuInfo::CpuUsage::_FIELD_NAMES,
- CpuInfo::CpuUsage::_FIELD_IDS);
+ writeSuffixLine(&proto, CpuInfoProto::CPU_USAGE, line, DEFAULT_WHITESPACE,
+ CpuInfoProto::CpuUsage::_FIELD_COUNT,
+ CpuInfoProto::CpuUsage::_FIELD_NAMES,
+ CpuInfoProto::CpuUsage::_FIELD_IDS);
nextToUsage = true;
nextToSwap = false;
continue;
@@ -138,7 +138,7 @@
continue;
}
- long long token = proto.start(CpuInfo::TASKS);
+ long long token = proto.start(CpuInfoProto::TASKS);
for (int i=0; i<(int)record.size(); i++) {
if (!table.insertField(&proto, header[i], record[i])) {
fprintf(stderr, "[%s]Line %d fails to insert field %s with value %s\n",
diff --git a/cmds/incident_helper/src/parsers/KernelWakesParser.cpp b/cmds/incident_helper/src/parsers/KernelWakesParser.cpp
index cae51ab..28816ea 100644
--- a/cmds/incident_helper/src/parsers/KernelWakesParser.cpp
+++ b/cmds/incident_helper/src/parsers/KernelWakesParser.cpp
@@ -33,7 +33,9 @@
int nline = 0;
ProtoOutputStream proto;
- Table table(WakeupSourceProto::_FIELD_NAMES, WakeupSourceProto::_FIELD_IDS, WakeupSourceProto::_FIELD_COUNT);
+ Table table(KernelWakeSourcesProto::WakeupSource::_FIELD_NAMES,
+ KernelWakeSourcesProto::WakeupSource::_FIELD_IDS,
+ KernelWakeSourcesProto::WakeupSource::_FIELD_COUNT);
// parse line by line
while (reader.readLine(&line)) {
@@ -57,7 +59,7 @@
continue;
}
- long long token = proto.start(KernelWakeSources::WAKEUP_SOURCES);
+ long long token = proto.start(KernelWakeSourcesProto::WAKEUP_SOURCES);
for (int i=0; i<(int)record.size(); i++) {
if (!table.insertField(&proto, header[i], record[i])) {
fprintf(stderr, "[%s]Line %d has bad value %s of %s\n",
diff --git a/cmds/incident_helper/src/parsers/PageTypeInfoParser.cpp b/cmds/incident_helper/src/parsers/PageTypeInfoParser.cpp
index f1b93ff..45a0e7b 100644
--- a/cmds/incident_helper/src/parsers/PageTypeInfoParser.cpp
+++ b/cmds/incident_helper/src/parsers/PageTypeInfoParser.cpp
@@ -33,7 +33,9 @@
header_t blockHeader;
ProtoOutputStream proto;
- Table table(BlockProto::_FIELD_NAMES, BlockProto::_FIELD_IDS, BlockProto::_FIELD_COUNT);
+ Table table(PageTypeInfoProto::Block::_FIELD_NAMES,
+ PageTypeInfoProto::Block::_FIELD_IDS,
+ PageTypeInfoProto::Block::_FIELD_COUNT);
while (reader.readLine(&line)) {
if (line.empty()) {
@@ -44,11 +46,11 @@
if (stripPrefix(&line, "Page block order:")) {
pageBlockOrder = toInt(line);
- proto.write(PageTypeInfo::PAGE_BLOCK_ORDER, pageBlockOrder);
+ proto.write(PageTypeInfoProto::PAGE_BLOCK_ORDER, pageBlockOrder);
continue;
}
if (stripPrefix(&line, "Pages per block:")) {
- proto.write(PageTypeInfo::PAGES_PER_BLOCK, toInt(line));
+ proto.write(PageTypeInfoProto::PAGES_PER_BLOCK, toInt(line));
continue;
}
if (stripPrefix(&line, "Free pages count per migrate type at order")) {
@@ -62,14 +64,14 @@
record_t record = parseRecord(line, COMMA_DELIMITER);
if (migrateTypeSession && record.size() == 3) {
- long long token = proto.start(PageTypeInfo::MIGRATE_TYPES);
+ long long token = proto.start(PageTypeInfoProto::MIGRATE_TYPES);
// expect part 0 starts with "Node"
if (stripPrefix(&record[0], "Node")) {
- proto.write(MigrateTypeProto::NODE, toInt(record[0]));
+ proto.write(PageTypeInfoProto::MigrateType::NODE, toInt(record[0]));
} else return BAD_VALUE;
// expect part 1 starts with "zone"
if (stripPrefix(&record[1], "zone")) {
- proto.write(MigrateTypeProto::ZONE, record[1]);
+ proto.write(PageTypeInfoProto::MigrateType::ZONE, record[1]);
} else return BAD_VALUE;
// expect part 2 starts with "type"
if (stripPrefix(&record[2], "type")) {
@@ -83,22 +85,22 @@
int pageCountsSize = pageBlockOrder + 2;
if ((int)pageCounts.size() != pageCountsSize) return BAD_VALUE;
- proto.write(MigrateTypeProto::TYPE, pageCounts[0]);
+ proto.write(PageTypeInfoProto::MigrateType::TYPE, pageCounts[0]);
for (auto i=1; i<pageCountsSize; i++) {
- proto.write(MigrateTypeProto::FREE_PAGES_COUNT, toInt(pageCounts[i]));
+ proto.write(PageTypeInfoProto::MigrateType::FREE_PAGES_COUNT, toInt(pageCounts[i]));
}
} else return BAD_VALUE;
proto.end(token);
} else if (!blockHeader.empty() && record.size() == 2) {
- long long token = proto.start(PageTypeInfo::BLOCKS);
+ long long token = proto.start(PageTypeInfoProto::BLOCKS);
if (stripPrefix(&record[0], "Node")) {
- proto.write(BlockProto::NODE, toInt(record[0]));
+ proto.write(PageTypeInfoProto::Block::NODE, toInt(record[0]));
} else return BAD_VALUE;
if (stripPrefix(&record[1], "zone")) {
record_t blockCounts = parseRecord(record[1]);
- proto.write(BlockProto::ZONE, blockCounts[0]);
+ proto.write(PageTypeInfoProto::Block::ZONE, blockCounts[0]);
for (size_t i=0; i<blockHeader.size(); i++) {
if (!table.insertField(&proto, blockHeader[i], blockCounts[i+1])) {
diff --git a/cmds/incident_helper/src/parsers/ProcrankParser.cpp b/cmds/incident_helper/src/parsers/ProcrankParser.cpp
index a4eb0fd..c1c458e 100644
--- a/cmds/incident_helper/src/parsers/ProcrankParser.cpp
+++ b/cmds/incident_helper/src/parsers/ProcrankParser.cpp
@@ -33,7 +33,7 @@
int nline = 0;
ProtoOutputStream proto;
- Table table(ProcessProto::_FIELD_NAMES, ProcessProto::_FIELD_IDS, ProcessProto::_FIELD_COUNT);
+ Table table(ProcrankProto::Process::_FIELD_NAMES, ProcrankProto::Process::_FIELD_IDS, ProcrankProto::Process::_FIELD_COUNT);
string zram, ram, total;
// parse line by line
@@ -66,7 +66,7 @@
continue;
}
- long long token = proto.start(Procrank::PROCESSES);
+ long long token = proto.start(ProcrankProto::PROCESSES);
for (int i=0; i<(int)record.size(); i++) {
if (!table.insertField(&proto, header[i], record[i])) {
fprintf(stderr, "[%s]Line %d has bad value %s of %s\n",
@@ -77,23 +77,23 @@
}
// add summary
- long long token = proto.start(Procrank::SUMMARY);
+ long long token = proto.start(ProcrankProto::SUMMARY);
if (!total.empty()) {
record = parseRecord(total);
- long long token = proto.start(SummaryProto::TOTAL);
+ long long token = proto.start(ProcrankProto::Summary::TOTAL);
for (int i=(int)record.size(); i>0; i--) {
table.insertField(&proto, header[header.size() - i].c_str(), record[record.size() - i].c_str());
}
proto.end(token);
}
if (!zram.empty()) {
- long long token = proto.start(SummaryProto::ZRAM);
- proto.write(ZramProto::RAW_TEXT, zram);
+ long long token = proto.start(ProcrankProto::Summary::ZRAM);
+ proto.write(ProcrankProto::Summary::Zram::RAW_TEXT, zram);
proto.end(token);
}
if (!ram.empty()) {
- long long token = proto.start(SummaryProto::RAM);
- proto.write(RamProto::RAW_TEXT, ram);
+ long long token = proto.start(ProcrankProto::Summary::RAM);
+ proto.write(ProcrankProto::Summary::Ram::RAW_TEXT, ram);
proto.end(token);
}
proto.end(token);
diff --git a/cmds/incident_helper/src/parsers/PsParser.cpp b/cmds/incident_helper/src/parsers/PsParser.cpp
index e9014ca..420775f 100644
--- a/cmds/incident_helper/src/parsers/PsParser.cpp
+++ b/cmds/incident_helper/src/parsers/PsParser.cpp
@@ -33,12 +33,12 @@
int diff = 0;
ProtoOutputStream proto;
- Table table(PsDumpProto::Process::_FIELD_NAMES, PsDumpProto::Process::_FIELD_IDS, PsDumpProto::Process::_FIELD_COUNT);
+ Table table(PsProto::Process::_FIELD_NAMES, PsProto::Process::_FIELD_IDS, PsProto::Process::_FIELD_COUNT);
const char* pcyNames[] = { "fg", "bg", "ta" };
- const int pcyValues[] = {PsDumpProto::Process::POLICY_FG, PsDumpProto::Process::POLICY_BG, PsDumpProto::Process::POLICY_TA};
+ const int pcyValues[] = {PsProto::Process::POLICY_FG, PsProto::Process::POLICY_BG, PsProto::Process::POLICY_TA};
table.addEnumTypeMap("pcy", pcyNames, pcyValues, 3);
const char* sNames[] = { "D", "R", "S", "T", "t", "X", "Z" };
- const int sValues[] = {PsDumpProto::Process::STATE_D, PsDumpProto::Process::STATE_R, PsDumpProto::Process::STATE_S, PsDumpProto::Process::STATE_T, PsDumpProto::Process::STATE_TRACING, PsDumpProto::Process::STATE_X, PsDumpProto::Process::STATE_Z};
+ const int sValues[] = {PsProto::Process::STATE_D, PsProto::Process::STATE_R, PsProto::Process::STATE_S, PsProto::Process::STATE_T, PsProto::Process::STATE_TRACING, PsProto::Process::STATE_X, PsProto::Process::STATE_Z};
table.addEnumTypeMap("s", sNames, sValues, 7);
// Parse line by line
@@ -71,7 +71,7 @@
continue;
}
- long long token = proto.start(PsDumpProto::PROCESSES);
+ long long token = proto.start(PsProto::PROCESSES);
for (int i=0; i<(int)record.size(); i++) {
if (!table.insertField(&proto, header[i], record[i])) {
fprintf(stderr, "[%s]Line %d has bad value %s of %s\n",
diff --git a/cmds/incident_helper/tests/CpuFreqParser_test.cpp b/cmds/incident_helper/tests/CpuFreqParser_test.cpp
index 82deee4..0839a7e 100644
--- a/cmds/incident_helper/tests/CpuFreqParser_test.cpp
+++ b/cmds/incident_helper/tests/CpuFreqParser_test.cpp
@@ -52,14 +52,14 @@
TEST_F(CpuFreqParserTest, Success) {
const string testFile = kTestDataPath + "cpufreq.txt";
CpuFreqParser parser;
- CpuFreq expected;
+ CpuFreqProto expected;
long jiffyHz = sysconf(_SC_CLK_TCK);
expected.set_jiffy_hz(jiffyHz);
- CpuFreqStats::TimeInState* state;
+ CpuFreqProto::Stats::TimeInState* state;
- CpuFreqStats* cpu0 = expected.add_cpu_freqs();
+ CpuFreqProto::Stats* cpu0 = expected.add_cpu_freqs();
cpu0->set_cpu_name("cpu0");
state = cpu0->add_times();
state->set_state_khz(307200);
@@ -71,7 +71,7 @@
state->set_state_khz(768000);
state->set_time_jiffy(22652);
- CpuFreqStats* cpu1 = expected.add_cpu_freqs();
+ CpuFreqProto::Stats* cpu1 = expected.add_cpu_freqs();
cpu1->set_cpu_name("cpu1");
state = cpu1->add_times();
state->set_state_khz(307200);
@@ -83,7 +83,7 @@
state->set_state_khz(768000);
state->set_time_jiffy(22652);
- CpuFreqStats* cpu2 = expected.add_cpu_freqs();
+ CpuFreqProto::Stats* cpu2 = expected.add_cpu_freqs();
cpu2->set_cpu_name("cpu2");
state = cpu2->add_times();
state->set_state_khz(307200);
@@ -98,7 +98,7 @@
state->set_state_khz(825600);
state->set_time_jiffy(13173);
- CpuFreqStats* cpu3 = expected.add_cpu_freqs();
+ CpuFreqProto::Stats* cpu3 = expected.add_cpu_freqs();
cpu3->set_cpu_name("cpu3");
state = cpu3->add_times();
state->set_state_khz(307200);
diff --git a/cmds/incident_helper/tests/CpuInfoParser_test.cpp b/cmds/incident_helper/tests/CpuInfoParser_test.cpp
index 8dce53e..2e50d63 100644
--- a/cmds/incident_helper/tests/CpuInfoParser_test.cpp
+++ b/cmds/incident_helper/tests/CpuInfoParser_test.cpp
@@ -52,28 +52,28 @@
TEST_F(CpuInfoParserTest, Success) {
const string testFile = kTestDataPath + "cpuinfo.txt";
CpuInfoParser parser;
- CpuInfo expected;
+ CpuInfoProto expected;
- CpuInfo::TaskStats* taskStats = expected.mutable_task_stats();
+ CpuInfoProto::TaskStats* taskStats = expected.mutable_task_stats();
taskStats->set_total(2038);
taskStats->set_running(1);
taskStats->set_sleeping(2033);
taskStats->set_stopped(0);
taskStats->set_zombie(0);
- CpuInfo::MemStats* mem = expected.mutable_mem();
+ CpuInfoProto::MemStats* mem = expected.mutable_mem();
mem->set_total(3842668);
mem->set_used(3761936);
mem->set_free(80732);
mem->set_buffers(220188);
- CpuInfo::MemStats* swap = expected.mutable_swap();
+ CpuInfoProto::MemStats* swap = expected.mutable_swap();
swap->set_total(524284);
swap->set_used(25892);
swap->set_free(498392);
swap->set_cached(1316952);
- CpuInfo::CpuUsage* usage = expected.mutable_cpu_usage();
+ CpuInfoProto::CpuUsage* usage = expected.mutable_cpu_usage();
usage->set_cpu(400);
usage->set_user(17);
usage->set_nice(0);
@@ -85,59 +85,59 @@
usage->set_host(0);
// This is a special line which is able to be parsed by the CpuInfoParser
- CpuInfo::Task* task1 = expected.add_tasks();
+ CpuInfoProto::Task* task1 = expected.add_tasks();
task1->set_pid(29438);
task1->set_tid(29438);
task1->set_user("rootabcdefghij");
task1->set_pr("20");
task1->set_ni(0);
task1->set_cpu(57.9);
- task1->set_s(CpuInfo::Task::STATUS_R);
+ task1->set_s(CpuInfoProto::Task::STATUS_R);
task1->set_virt("14M");
task1->set_res("3.8M");
- task1->set_pcy(CpuInfo::Task::POLICY_UNKNOWN);
+ task1->set_pcy(CpuInfoProto::Task::POLICY_UNKNOWN);
task1->set_cmd("top test");
task1->set_name("top");
- CpuInfo::Task* task2 = expected.add_tasks();
+ CpuInfoProto::Task* task2 = expected.add_tasks();
task2->set_pid(916);
task2->set_tid(916);
task2->set_user("system");
task2->set_pr("18");
task2->set_ni(-2);
task2->set_cpu(1.4);
- task2->set_s(CpuInfo::Task::STATUS_S);
+ task2->set_s(CpuInfoProto::Task::STATUS_S);
task2->set_virt("4.6G");
task2->set_res("404M");
- task2->set_pcy(CpuInfo::Task::POLICY_fg);
+ task2->set_pcy(CpuInfoProto::Task::POLICY_fg);
task2->set_cmd("system_server");
task2->set_name("system_server");
- CpuInfo::Task* task3 = expected.add_tasks();
+ CpuInfoProto::Task* task3 = expected.add_tasks();
task3->set_pid(28);
task3->set_tid(28);
task3->set_user("root");
task3->set_pr("-2");
task3->set_ni(0);
task3->set_cpu(1.4);
- task3->set_s(CpuInfo::Task::STATUS_S);
+ task3->set_s(CpuInfoProto::Task::STATUS_S);
task3->set_virt("0");
task3->set_res("0");
- task3->set_pcy(CpuInfo::Task::POLICY_bg);
+ task3->set_pcy(CpuInfoProto::Task::POLICY_bg);
task3->set_cmd("rcuc/3");
task3->set_name("[rcuc/3]");
- CpuInfo::Task* task4 = expected.add_tasks();
+ CpuInfoProto::Task* task4 = expected.add_tasks();
task4->set_pid(27);
task4->set_tid(27);
task4->set_user("root");
task4->set_pr("RT");
task4->set_ni(0);
task4->set_cpu(1.4);
- task4->set_s(CpuInfo::Task::STATUS_S);
+ task4->set_s(CpuInfoProto::Task::STATUS_S);
task4->set_virt("0");
task4->set_res("0");
- task4->set_pcy(CpuInfo::Task::POLICY_ta);
+ task4->set_pcy(CpuInfoProto::Task::POLICY_ta);
task4->set_cmd("migration/3");
task4->set_name("[migration/3]");
diff --git a/cmds/incident_helper/tests/KernelWakesParser_test.cpp b/cmds/incident_helper/tests/KernelWakesParser_test.cpp
index a98c62b..f92d813 100644
--- a/cmds/incident_helper/tests/KernelWakesParser_test.cpp
+++ b/cmds/incident_helper/tests/KernelWakesParser_test.cpp
@@ -52,14 +52,14 @@
TEST_F(KernelWakesParserTest, Short) {
const string testFile = kTestDataPath + "kernel_wakeups_short.txt";
KernelWakesParser parser;
- KernelWakeSources expected;
+ KernelWakeSourcesProto expected;
- WakeupSourceProto* record1 = expected.add_wakeup_sources();
+ KernelWakeSourcesProto::WakeupSource* record1 = expected.add_wakeup_sources();
record1->set_name("ab");
record1->set_active_count(8);
record1->set_last_change(123456123456LL);
- WakeupSourceProto* record2 = expected.add_wakeup_sources();
+ KernelWakeSourcesProto::WakeupSource* record2 = expected.add_wakeup_sources();
record2->set_name("df");
record2->set_active_count(143);
record2->set_last_change(0LL);
@@ -76,9 +76,9 @@
TEST_F(KernelWakesParserTest, Normal) {
const string testFile = kTestDataPath + "kernel_wakeups.txt";
KernelWakesParser parser;
- KernelWakeSources expected;
+ KernelWakeSourcesProto expected;
- WakeupSourceProto* record1 = expected.add_wakeup_sources();
+ KernelWakeSourcesProto::WakeupSource* record1 = expected.add_wakeup_sources();
record1->set_name("ipc000000ab_ATFWD-daemon");
record1->set_active_count(8);
record1->set_event_count(8);
@@ -90,7 +90,7 @@
record1->set_last_change(131348LL);
record1->set_prevent_suspend_time(0LL);
- WakeupSourceProto* record2 = expected.add_wakeup_sources();
+ KernelWakeSourcesProto::WakeupSource* record2 = expected.add_wakeup_sources();
record2->set_name("ipc000000aa_ATFWD-daemon");
record2->set_active_count(143);
record2->set_event_count(143);
diff --git a/cmds/incident_helper/tests/PageTypeInfoParser_test.cpp b/cmds/incident_helper/tests/PageTypeInfoParser_test.cpp
index a9e6e816..9bad7be 100644
--- a/cmds/incident_helper/tests/PageTypeInfoParser_test.cpp
+++ b/cmds/incident_helper/tests/PageTypeInfoParser_test.cpp
@@ -52,12 +52,12 @@
TEST_F(PageTypeInfoParserTest, Success) {
const string testFile = kTestDataPath + "pagetypeinfo.txt";
PageTypeInfoParser parser;
- PageTypeInfo expected;
+ PageTypeInfoProto expected;
expected.set_page_block_order(10);
expected.set_pages_per_block(1024);
- MigrateTypeProto* mt1 = expected.add_migrate_types();
+ PageTypeInfoProto::MigrateType* mt1 = expected.add_migrate_types();
mt1->set_node(0);
mt1->set_zone("DMA");
mt1->set_type("Unmovable");
@@ -66,7 +66,7 @@
mt1->add_free_pages_count(arr1[i]);
}
- MigrateTypeProto* mt2 = expected.add_migrate_types();
+ PageTypeInfoProto::MigrateType* mt2 = expected.add_migrate_types();
mt2->set_node(0);
mt2->set_zone("Normal");
mt2->set_type("Reclaimable");
@@ -75,7 +75,7 @@
mt2->add_free_pages_count(arr2[i]);
}
- BlockProto* block1 = expected.add_blocks();
+ PageTypeInfoProto::Block* block1 = expected.add_blocks();
block1->set_node(0);
block1->set_zone("DMA");
block1->set_unmovable(74);
@@ -86,7 +86,7 @@
block1->set_isolate(0);
- BlockProto* block2 = expected.add_blocks();
+ PageTypeInfoProto::Block* block2 = expected.add_blocks();
block2->set_node(0);
block2->set_zone("Normal");
block2->set_unmovable(70);
diff --git a/cmds/incident_helper/tests/ProcrankParser_test.cpp b/cmds/incident_helper/tests/ProcrankParser_test.cpp
index 76b25d7..0b567ae 100644
--- a/cmds/incident_helper/tests/ProcrankParser_test.cpp
+++ b/cmds/incident_helper/tests/ProcrankParser_test.cpp
@@ -52,9 +52,9 @@
TEST_F(ProcrankParserTest, HasSwapInfo) {
const string testFile = kTestDataPath + "procrank.txt";
ProcrankParser parser;
- Procrank expected;
+ ProcrankProto expected;
- ProcessProto* process1 = expected.add_processes();
+ ProcrankProto::Process* process1 = expected.add_processes();
process1->set_pid(1119);
process1->set_vss(2607640);
process1->set_rss(339564);
@@ -66,7 +66,7 @@
process1->set_zswap(10);
process1->set_cmdline("system_server");
- ProcessProto* process2 = expected.add_processes();
+ ProcrankProto::Process* process2 = expected.add_processes();
process2->set_pid(649);
process2->set_vss(11016);
process2->set_rss(1448);
@@ -78,7 +78,7 @@
process2->set_zswap(75);
process2->set_cmdline("/vendor/bin/qseecomd");
- ProcessProto* total = expected.mutable_summary()->mutable_total();
+ ProcrankProto::Process* total = expected.mutable_summary()->mutable_total();
total->set_pss(1201993);
total->set_uss(935300);
total->set_swap(88164);
@@ -104,9 +104,9 @@
TEST_F(ProcrankParserTest, NoSwapInfo) {
const string testFile = kTestDataPath + "procrank_short.txt";
ProcrankParser parser;
- Procrank expected;
+ ProcrankProto expected;
- ProcessProto* process1 = expected.add_processes();
+ ProcrankProto::Process* process1 = expected.add_processes();
process1->set_pid(1119);
process1->set_vss(2607640);
process1->set_rss(339564);
@@ -114,7 +114,7 @@
process1->set_uss(114216);
process1->set_cmdline("system_server");
- ProcessProto* process2 = expected.add_processes();
+ ProcrankProto::Process* process2 = expected.add_processes();
process2->set_pid(649);
process2->set_vss(11016);
process2->set_rss(1448);
@@ -122,7 +122,7 @@
process2->set_uss(48);
process2->set_cmdline("/vendor/bin/qseecomd");
- ProcessProto* total = expected.mutable_summary()->mutable_total();
+ ProcrankProto::Process* total = expected.mutable_summary()->mutable_total();
total->set_pss(1201993);
total->set_uss(935300);
total->set_cmdline("TOTAL");
diff --git a/cmds/incident_helper/tests/PsParser_test.cpp b/cmds/incident_helper/tests/PsParser_test.cpp
index 1f03a7f..114d634 100644
--- a/cmds/incident_helper/tests/PsParser_test.cpp
+++ b/cmds/incident_helper/tests/PsParser_test.cpp
@@ -52,10 +52,10 @@
TEST_F(PsParserTest, Normal) {
const string testFile = kTestDataPath + "ps.txt";
PsParser parser;
- PsDumpProto expected;
- PsDumpProto got;
+ PsProto expected;
+ PsProto got;
- PsDumpProto::Process* record1 = expected.add_processes();
+ PsProto::Process* record1 = expected.add_processes();
record1->set_label("u:r:init:s0");
record1->set_user("root");
record1->set_pid(1);
@@ -65,16 +65,16 @@
record1->set_rss(2636);
record1->set_wchan("SyS_epoll_wait");
record1->set_addr("0");
- record1->set_s(PsDumpProto_Process_ProcessStateCode_STATE_S);
+ record1->set_s(PsProto_Process_ProcessStateCode_STATE_S);
record1->set_pri(19);
record1->set_ni(0);
record1->set_rtprio("-");
- record1->set_sch(PsDumpProto_Process_SchedulingPolicy_SCH_NORMAL);
- record1->set_pcy(PsDumpProto::Process::POLICY_FG);
+ record1->set_sch(PsProto_Process_SchedulingPolicy_SCH_NORMAL);
+ record1->set_pcy(PsProto::Process::POLICY_FG);
record1->set_time("00:00:01");
record1->set_cmd("init");
- PsDumpProto::Process* record2 = expected.add_processes();
+ PsProto::Process* record2 = expected.add_processes();
record2->set_label("u:r:kernel:s0");
record2->set_user("root");
record2->set_pid(2);
@@ -84,16 +84,16 @@
record2->set_rss(0);
record2->set_wchan("kthreadd");
record2->set_addr("0");
- record2->set_s(PsDumpProto_Process_ProcessStateCode_STATE_S);
+ record2->set_s(PsProto_Process_ProcessStateCode_STATE_S);
record2->set_pri(19);
record2->set_ni(0);
record2->set_rtprio("-");
- record2->set_sch(PsDumpProto_Process_SchedulingPolicy_SCH_NORMAL);
- record2->set_pcy(PsDumpProto::Process::POLICY_FG);
+ record2->set_sch(PsProto_Process_SchedulingPolicy_SCH_NORMAL);
+ record2->set_pcy(PsProto::Process::POLICY_FG);
record2->set_time("00:00:00");
record2->set_cmd("kthreadd");
- PsDumpProto::Process* record3 = expected.add_processes();
+ PsProto::Process* record3 = expected.add_processes();
record3->set_label("u:r:surfaceflinger:s0");
record3->set_user("system");
record3->set_pid(499);
@@ -103,16 +103,16 @@
record3->set_rss(22024);
record3->set_wchan("futex_wait_queue_me");
record3->set_addr("0");
- record3->set_s(PsDumpProto_Process_ProcessStateCode_STATE_S);
+ record3->set_s(PsProto_Process_ProcessStateCode_STATE_S);
record3->set_pri(42);
record3->set_ni(-9);
record3->set_rtprio("2");
- record3->set_sch(PsDumpProto_Process_SchedulingPolicy_SCH_FIFO);
- record3->set_pcy(PsDumpProto::Process::POLICY_FG);
+ record3->set_sch(PsProto_Process_SchedulingPolicy_SCH_FIFO);
+ record3->set_pcy(PsProto::Process::POLICY_FG);
record3->set_time("00:00:00");
record3->set_cmd("EventThread");
- PsDumpProto::Process* record4 = expected.add_processes();
+ PsProto::Process* record4 = expected.add_processes();
record4->set_label("u:r:hal_gnss_default:s0");
record4->set_user("gps");
record4->set_pid(670);
@@ -122,16 +122,16 @@
record4->set_rss(7272);
record4->set_wchan("poll_schedule_timeout");
record4->set_addr("0");
- record4->set_s(PsDumpProto_Process_ProcessStateCode_STATE_S);
+ record4->set_s(PsProto_Process_ProcessStateCode_STATE_S);
record4->set_pri(19);
record4->set_ni(0);
record4->set_rtprio("-");
- record4->set_sch(PsDumpProto_Process_SchedulingPolicy_SCH_NORMAL);
- record4->set_pcy(PsDumpProto::Process::POLICY_FG);
+ record4->set_sch(PsProto_Process_SchedulingPolicy_SCH_NORMAL);
+ record4->set_pcy(PsProto::Process::POLICY_FG);
record4->set_time("00:00:00");
record4->set_cmd("Loc_hal_worker");
- PsDumpProto::Process* record5 = expected.add_processes();
+ PsProto::Process* record5 = expected.add_processes();
record5->set_label("u:r:platform_app:s0:c512,c768");
record5->set_user("u0_a48");
record5->set_pid(1660);
@@ -141,16 +141,16 @@
record5->set_rss(138328);
record5->set_wchan("binder_thread_read");
record5->set_addr("0");
- record5->set_s(PsDumpProto_Process_ProcessStateCode_STATE_S);
+ record5->set_s(PsProto_Process_ProcessStateCode_STATE_S);
record5->set_pri(35);
record5->set_ni(-16);
record5->set_rtprio("-");
- record5->set_sch(PsDumpProto_Process_SchedulingPolicy_SCH_NORMAL);
- record5->set_pcy(PsDumpProto::Process::POLICY_TA);
+ record5->set_sch(PsProto_Process_SchedulingPolicy_SCH_NORMAL);
+ record5->set_pcy(PsProto::Process::POLICY_TA);
record5->set_time("00:00:00");
record5->set_cmd("HwBinder:1660_1");
- PsDumpProto::Process* record6 = expected.add_processes();
+ PsProto::Process* record6 = expected.add_processes();
record6->set_label("u:r:perfd:s0");
record6->set_user("root");
record6->set_pid(1939);
@@ -160,16 +160,16 @@
record6->set_rss(2088);
record6->set_wchan("__skb_recv_datagram");
record6->set_addr("7b9782fd14");
- record6->set_s(PsDumpProto_Process_ProcessStateCode_STATE_S);
+ record6->set_s(PsProto_Process_ProcessStateCode_STATE_S);
record6->set_pri(19);
record6->set_ni(0);
record6->set_rtprio("-");
- record6->set_sch(PsDumpProto_Process_SchedulingPolicy_SCH_NORMAL);
- record6->set_pcy(PsDumpProto::Process::POLICY_UNKNOWN);
+ record6->set_sch(PsProto_Process_SchedulingPolicy_SCH_NORMAL);
+ record6->set_pcy(PsProto::Process::POLICY_UNKNOWN);
record6->set_time("00:00:00");
record6->set_cmd("perfd");
- PsDumpProto::Process* record7 = expected.add_processes();
+ PsProto::Process* record7 = expected.add_processes();
record7->set_label("u:r:perfd:s0");
record7->set_user("root");
record7->set_pid(1939);
@@ -179,16 +179,16 @@
record7->set_rss(2088);
record7->set_wchan("do_sigtimedwait");
record7->set_addr("7b9782ff6c");
- record7->set_s(PsDumpProto_Process_ProcessStateCode_STATE_S);
+ record7->set_s(PsProto_Process_ProcessStateCode_STATE_S);
record7->set_pri(19);
record7->set_ni(0);
record7->set_rtprio("-");
- record7->set_sch(PsDumpProto_Process_SchedulingPolicy_SCH_NORMAL);
- record7->set_pcy(PsDumpProto::Process::POLICY_UNKNOWN);
+ record7->set_sch(PsProto_Process_SchedulingPolicy_SCH_NORMAL);
+ record7->set_pcy(PsProto::Process::POLICY_UNKNOWN);
record7->set_time("00:00:00");
record7->set_cmd("POSIX timer 0");
- PsDumpProto::Process* record8 = expected.add_processes();
+ PsProto::Process* record8 = expected.add_processes();
record8->set_label("u:r:shell:s0");
record8->set_user("shell");
record8->set_pid(2645);
@@ -198,12 +198,12 @@
record8->set_rss(2972);
record8->set_wchan("0");
record8->set_addr("7f67a2f8b4");
- record8->set_s(PsDumpProto_Process_ProcessStateCode_STATE_R);
+ record8->set_s(PsProto_Process_ProcessStateCode_STATE_R);
record8->set_pri(19);
record8->set_ni(0);
record8->set_rtprio("-");
- record8->set_sch(PsDumpProto_Process_SchedulingPolicy_SCH_NORMAL);
- record8->set_pcy(PsDumpProto::Process::POLICY_FG);
+ record8->set_sch(PsProto_Process_SchedulingPolicy_SCH_NORMAL);
+ record8->set_pcy(PsProto::Process::POLICY_FG);
record8->set_time("00:00:00");
record8->set_cmd("ps");
@@ -221,8 +221,8 @@
} else {
int n = got.processes_size();
for (int i = 0; i < n; i++) {
- PsDumpProto::Process g = got.processes(i);
- PsDumpProto::Process e = expected.processes(i);
+ PsProto::Process g = got.processes(i);
+ PsProto::Process e = expected.processes(i);
if (g.label() != e.label()) {
fprintf(stderr, "prcs[%d]: Invalid label. Got %s, want %s\n", i, g.label().c_str(), e.label().c_str());
diff --git a/cmds/incidentd/Android.mk b/cmds/incidentd/Android.mk
index 2b00d9e..23cd2af 100644
--- a/cmds/incidentd/Android.mk
+++ b/cmds/incidentd/Android.mk
@@ -56,6 +56,7 @@
libcutils \
libincident \
liblog \
+ libprotobuf-cpp-lite \
libprotoutil \
libselinux \
libservices \
diff --git a/cmds/incidentd/OWNERS b/cmds/incidentd/OWNERS
new file mode 100644
index 0000000..1a68a32
--- /dev/null
+++ b/cmds/incidentd/OWNERS
@@ -0,0 +1,2 @@
+jinyithu@google.com
+kwekua@google.com
diff --git a/cmds/incidentd/src/Privacy.cpp b/cmds/incidentd/src/Privacy.cpp
index 3f0e331..c5078f0 100644
--- a/cmds/incidentd/src/Privacy.cpp
+++ b/cmds/incidentd/src/Privacy.cpp
@@ -73,11 +73,6 @@
case android::os::DEST_LOCAL:
return PrivacySpec(dest);
default:
- return PrivacySpec();
+ return PrivacySpec(android::os::DEST_AUTOMATIC);
}
}
-
-PrivacySpec PrivacySpec::get_default_dropbox_spec()
-{
- return PrivacySpec(android::os::DEST_AUTOMATIC);
-}
diff --git a/cmds/incidentd/src/Privacy.h b/cmds/incidentd/src/Privacy.h
index 4f3db67..ce1b8e9 100644
--- a/cmds/incidentd/src/Privacy.h
+++ b/cmds/incidentd/src/Privacy.h
@@ -75,7 +75,6 @@
// Constructs spec using static methods below.
static PrivacySpec new_spec(int dest);
- static PrivacySpec get_default_dropbox_spec();
private:
PrivacySpec(uint8_t dest) : dest(dest) {}
};
diff --git a/cmds/incidentd/src/Reporter.cpp b/cmds/incidentd/src/Reporter.cpp
index b9f479b..06baeba 100644
--- a/cmds/incidentd/src/Reporter.cpp
+++ b/cmds/incidentd/src/Reporter.cpp
@@ -18,6 +18,7 @@
#include "Reporter.h"
+#include "Privacy.h"
#include "report_directory.h"
#include "section_list.h"
@@ -65,7 +66,9 @@
:mRequests(),
mSections(),
mMainFd(-1),
- mMainDest(-1)
+ mMainDest(-1),
+ mMetadata(),
+ mSectionStats()
{
}
@@ -79,18 +82,32 @@
{
mRequests.push_back(request);
mSections.merge(request->args);
+ mMetadata.set_request_size(mMetadata.request_size() + 1);
}
void
ReportRequestSet::setMainFd(int fd)
{
mMainFd = fd;
+ mMetadata.set_use_dropbox(fd > 0);
}
void
ReportRequestSet::setMainDest(int dest)
{
mMainDest = dest;
+ PrivacySpec spec = PrivacySpec::new_spec(dest);
+ switch (spec.dest) {
+ case android::os::DEST_AUTOMATIC:
+ mMetadata.set_dest(IncidentMetadata_Destination_AUTOMATIC);
+ break;
+ case android::os::DEST_EXPLICIT:
+ mMetadata.set_dest(IncidentMetadata_Destination_EXPLICIT);
+ break;
+ case android::os::DEST_LOCAL:
+ mMetadata.set_dest(IncidentMetadata_Destination_LOCAL);
+ break;
+ }
}
bool
@@ -98,6 +115,16 @@
return mSections.containsSection(id);
}
+IncidentMetadata::SectionStats*
+ReportRequestSet::sectionStats(int id) {
+ if (mSectionStats.find(id) == mSectionStats.end()) {
+ auto stats = mMetadata.add_sections();
+ stats->set_id(id);
+ mSectionStats[id] = stats;
+ }
+ return mSectionStats[id];
+}
+
// ================================================================================
Reporter::Reporter() : Reporter(INCIDENT_DIRECTORY) { isTest = false; };
@@ -128,12 +155,12 @@
Reporter::run_report_status_t
Reporter::runReport()
{
-
status_t err = NO_ERROR;
bool needMainFd = false;
int mainFd = -1;
int mainDest = -1;
HeaderSection headers;
+ MetadataSection metadataSection;
// See if we need the main file
for (ReportRequestSet::iterator it=batch.begin(); it!=batch.end(); it++) {
@@ -182,7 +209,7 @@
const int id = (*section)->id;
if (this->batch.containsSection(id)) {
ALOGD("Taking incident report section %d '%s'", id, (*section)->name.string());
- // Notify listener of starting
+ // Notify listener of starting.
for (ReportRequestSet::iterator it=batch.begin(); it!=batch.end(); it++) {
if ((*it)->listener != NULL && (*it)->args.containsSection(id)) {
(*it)->listener->onReportSectionStatus(id,
@@ -191,14 +218,20 @@
}
// Execute - go get the data and write it into the file descriptors.
+ auto stats = batch.sectionStats(id);
+ int64_t startTime = uptimeMillis();
err = (*section)->Execute(&batch);
+ int64_t endTime = uptimeMillis();
+
+ stats->set_success(err == NO_ERROR);
+ stats->set_exec_duration_ms(endTime - startTime);
if (err != NO_ERROR) {
ALOGW("Incident section %s (%d) failed: %s. Stopping report.",
(*section)->name.string(), id, strerror(-err));
goto DONE;
}
- // Notify listener of starting
+ // Notify listener of ending.
for (ReportRequestSet::iterator it=batch.begin(); it!=batch.end(); it++) {
if ((*it)->listener != NULL && (*it)->args.containsSection(id)) {
(*it)->listener->onReportSectionStatus(id,
@@ -210,6 +243,9 @@
}
DONE:
+ // Reports the metdadata when taking the incident report.
+ if (!isTest) metadataSection.Execute(&batch);
+
// Close the file.
if (mainFd >= 0) {
close(mainFd);
diff --git a/cmds/incidentd/src/Reporter.h b/cmds/incidentd/src/Reporter.h
index f30ecf0..6058068 100644
--- a/cmds/incidentd/src/Reporter.h
+++ b/cmds/incidentd/src/Reporter.h
@@ -17,9 +17,12 @@
#ifndef REPORTER_H
#define REPORTER_H
+#include "frameworks/base/libs/incident/proto/android/os/metadata.pb.h"
+
#include <android/os/IIncidentReportStatusListener.h>
#include <android/os/IncidentReportArgs.h>
+#include <map>
#include <string>
#include <vector>
@@ -61,13 +64,20 @@
iterator end() { return mRequests.end(); }
int mainFd() { return mMainFd; }
- bool containsSection(int id);
int mainDest() { return mMainDest; }
+ IncidentMetadata& metadata() { return mMetadata; }
+
+ bool containsSection(int id);
+ IncidentMetadata::SectionStats* sectionStats(int id);
+
private:
vector<sp<ReportRequest>> mRequests;
IncidentReportArgs mSections;
int mMainFd;
int mMainDest;
+
+ IncidentMetadata mMetadata;
+ map<int, IncidentMetadata::SectionStats*> mSectionStats;
};
// ================================================================================
@@ -81,8 +91,8 @@
ReportRequestSet batch;
- Reporter();
- Reporter(const char* directory);
+ Reporter(); // PROD must use this constructor.
+ Reporter(const char* directory); // For testing purpose only.
virtual ~Reporter();
// Run the report as described in the batch and args parameters.
diff --git a/cmds/incidentd/src/Section.cpp b/cmds/incidentd/src/Section.cpp
index faeab87..3c76298 100644
--- a/cmds/incidentd/src/Section.cpp
+++ b/cmds/incidentd/src/Section.cpp
@@ -45,6 +45,7 @@
// special section ids
const int FIELD_ID_INCIDENT_HEADER = 1;
+const int FIELD_ID_INCIDENT_METADATA = 2;
// incident section parameters
const int WAIT_MAX = 5;
@@ -149,6 +150,12 @@
EncodedBuffer::iterator data = buffer.data();
PrivacyBuffer privacyBuffer(get_privacy_of_section(id), data);
int writeable = 0;
+ auto stats = requests->sectionStats(id);
+
+ stats->set_dump_size_bytes(data.size());
+ stats->set_dump_duration_ms(buffer.durationMs());
+ stats->set_timed_out(buffer.timedOut());
+ stats->set_is_truncated(buffer.truncated());
// The streaming ones, group requests by spec in order to save unnecessary strip operations
map<PrivacySpec, vector<sp<ReportRequest>>> requestsBySpec;
@@ -182,9 +189,7 @@
// The dropbox file
if (requests->mainFd() >= 0) {
- PrivacySpec spec = requests->mainDest() < 0 ?
- PrivacySpec::get_default_dropbox_spec() :
- PrivacySpec::new_spec(requests->mainDest());
+ PrivacySpec spec = PrivacySpec::new_spec(requests->mainDest());
err = privacyBuffer.strip(spec);
if (err != NO_ERROR) return err; // the buffer data is corrupted.
if (privacyBuffer.size() == 0) goto DONE;
@@ -196,6 +201,7 @@
writeable++;
ALOGD("Section %d flushed %zu bytes to dropbox %d with spec %d", id,
privacyBuffer.size(), requests->mainFd(), spec.dest);
+ stats->set_report_size_bytes(privacyBuffer.size());
}
DONE:
@@ -236,7 +242,7 @@
// So the idea is only requests with negative fd are written to dropbox file.
int fd = request->fd >= 0 ? request->fd : requests->mainFd();
- write_section_header(fd, FIELD_ID_INCIDENT_HEADER, buf->size());
+ write_section_header(fd, id, buf->size());
write_all(fd, (uint8_t const*)buf->data(), buf->size());
// If there was an error now, there will be an error later and we will remove
// it from the list then.
@@ -244,7 +250,35 @@
}
return NO_ERROR;
}
+// ================================================================================
+MetadataSection::MetadataSection()
+ :Section(FIELD_ID_INCIDENT_METADATA, 0)
+{
+}
+MetadataSection::~MetadataSection()
+{
+}
+
+status_t
+MetadataSection::Execute(ReportRequestSet* requests) const
+{
+ std::string metadataBuf;
+ requests->metadata().SerializeToString(&metadataBuf);
+ for (ReportRequestSet::iterator it=requests->begin(); it!=requests->end(); it++) {
+ const sp<ReportRequest> request = *it;
+ if (metadataBuf.empty() || request->fd < 0 || request->err != NO_ERROR) {
+ continue;
+ }
+ write_section_header(request->fd, id, metadataBuf.size());
+ write_all(request->fd, (uint8_t const*)metadataBuf.data(), metadataBuf.size());
+ }
+ if (requests->mainFd() >= 0 && !metadataBuf.empty()) {
+ write_section_header(requests->mainFd(), id, metadataBuf.size());
+ write_all(requests->mainFd(), (uint8_t const*)metadataBuf.data(), metadataBuf.size());
+ }
+ return NO_ERROR;
+}
// ================================================================================
FileSection::FileSection(int id, const char* filename, const int64_t timeoutMs)
:Section(id, timeoutMs),
diff --git a/cmds/incidentd/src/Section.h b/cmds/incidentd/src/Section.h
index d440ee9..80cc033 100644
--- a/cmds/incidentd/src/Section.h
+++ b/cmds/incidentd/src/Section.h
@@ -59,6 +59,18 @@
};
/**
+ * Section that generates incident metadata.
+ */
+class MetadataSection : public Section
+{
+public:
+ MetadataSection();
+ virtual ~MetadataSection();
+
+ virtual status_t Execute(ReportRequestSet* requests) const;
+};
+
+/**
* Section that reads in a file.
*/
class FileSection : public Section
diff --git a/cmds/incidentd/tests/Reporter_test.cpp b/cmds/incidentd/tests/Reporter_test.cpp
index c494bd6..a1e3c34 100644
--- a/cmds/incidentd/tests/Reporter_test.cpp
+++ b/cmds/incidentd/tests/Reporter_test.cpp
@@ -180,3 +180,18 @@
ASSERT_EQ((int)results.size(), 1);
EXPECT_EQ(results[0], "\n\x2" "\b\f\n\x6" "\x12\x4" "abcd");
}
+
+TEST_F(ReporterTest, ReportMetadata) {
+ IncidentReportArgs args;
+ args.addSection(1);
+ args.setDest(android::os::DEST_EXPLICIT);
+ sp<ReportRequest> r = new ReportRequest(args, l, -1);
+ reporter->batch.add(r);
+
+ ASSERT_EQ(Reporter::REPORT_FINISHED, reporter->runReport());
+ auto metadata = reporter->batch.metadata();
+ EXPECT_EQ(IncidentMetadata_Destination_EXPLICIT, metadata.dest());
+ EXPECT_EQ(1, metadata.request_size());
+ EXPECT_TRUE(metadata.use_dropbox());
+ EXPECT_EQ(0, metadata.sections_size());
+}
\ No newline at end of file
diff --git a/cmds/incidentd/tests/Section_test.cpp b/cmds/incidentd/tests/Section_test.cpp
index 2cfd7df..5752a67 100644
--- a/cmds/incidentd/tests/Section_test.cpp
+++ b/cmds/incidentd/tests/Section_test.cpp
@@ -18,6 +18,7 @@
#include <android-base/file.h>
#include <android-base/test_utils.h>
+#include <android/os/IncidentReportArgs.h>
#include <frameworks/base/libs/incident/proto/android/os/header.pb.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
@@ -89,6 +90,18 @@
EXPECT_THAT(content, StrEq("\n\x05\x12\x03pup"));
}
+TEST(SectionTest, MetadataSection) {
+ MetadataSection ms;
+ ReportRequestSet requests;
+
+ requests.setMainFd(STDOUT_FILENO);
+ requests.sectionStats(1)->set_success(true);
+
+ CaptureStdout();
+ ASSERT_EQ(NO_ERROR, ms.Execute(&requests));
+ EXPECT_THAT(GetCapturedStdout(), StrEq("\x12\b\x18\x1\"\x4\b\x1\x10\x1"));
+}
+
TEST(SectionTest, FileSection) {
TemporaryFile tf;
FileSection fs(REVERSE_PARSER, tf.path);
@@ -243,8 +256,9 @@
IncidentReportArgs args1, args2, args3;
args1.setAll(true);
- args1.setDest(0); // LOCAL
- args2.setAll(true); // default to explicit
+ args1.setDest(android::os::DEST_LOCAL);
+ args2.setAll(true);
+ args2.setDest(android::os::DEST_EXPLICIT);
sp<SimpleListener> l = new SimpleListener();
requests.add(new ReportRequest(args1, l, output1.fd));
requests.add(new ReportRequest(args2, l, output2.fd));
@@ -283,10 +297,12 @@
ASSERT_TRUE(WriteStringToFile(VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3, input.path));
- IncidentReportArgs args1, args2, args3, args4;
+ IncidentReportArgs args1, args2, args3;
args1.setAll(true);
+ args1.setDest(android::os::DEST_EXPLICIT);
args2.setAll(true);
- args4.setAll(true);
+ args2.setDest(android::os::DEST_EXPLICIT);
+ args3.setAll(true);
sp<SimpleListener> l = new SimpleListener();
requests.add(new ReportRequest(args1, l, output1.fd));
requests.add(new ReportRequest(args2, l, output2.fd));
@@ -307,7 +323,8 @@
EXPECT_TRUE(ReadFileToString(output2.path, &content));
EXPECT_THAT(content, StrEq(string("\x02") + c + expect));
- // because args3 doesn't set section, so it should receive nothing
+ // output3 has only auto field
+ c = (char) STRING_FIELD_2.size();
EXPECT_TRUE(ReadFileToString(output3.path, &content));
- EXPECT_THAT(content, StrEq(""));
+ EXPECT_THAT(content, StrEq(string("\x02") + c + STRING_FIELD_2));
}
\ No newline at end of file
diff --git a/cmds/statsd/OWNERS b/cmds/statsd/OWNERS
new file mode 100644
index 0000000..362d411
--- /dev/null
+++ b/cmds/statsd/OWNERS
@@ -0,0 +1,6 @@
+bookatz@google.com
+jinyithu@google.com
+kwekua@google.com
+stlafon@google.com
+yaochen@google.com
+yanglu@google.com
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index 0a4e412..791fb14 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -571,8 +571,8 @@
good = true;
} else {
fprintf(out,
- "Selecting a UID for writing Appbreadcrumb can only be dumped for other UIDs on eng"
- " or userdebug builds.\n");
+ "Selecting a UID for writing AppBreadcrumb can only be done for other UIDs "
+ "on eng or userdebug builds.\n");
}
}
if (good) {
diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h
index fd65869..9690de7 100644
--- a/cmds/statsd/src/StatsService.h
+++ b/cmds/statsd/src/StatsService.h
@@ -193,8 +193,8 @@
status_t cmd_write_data_to_disk(FILE* out);
/**
- * Write an AppBreadcrumbReported event to the StatsLog buffer, as though StatsLog.write
- * (APP_BREADCRUMB_REPORTED).
+ * Write an AppBreadcrumbReported event to the StatsLog buffer, as if calling
+ * StatsLog.write(APP_BREADCRUMB_REPORTED).
*/
status_t cmd_log_app_breadcrumb(FILE* out, const Vector<String8>& args);
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 1224504..85e209b 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -1471,7 +1471,7 @@
*/
message CpuActiveTime {
optional uint64 uid = 1;
- optional uint32 cluster_number =2;
+ optional uint32 cluster_number = 2;
optional uint64 idx = 3;
optional uint64 time_millis = 4;
}
diff --git a/cmds/statsd/src/guardrail/StatsdStats.h b/cmds/statsd/src/guardrail/StatsdStats.h
index f254327..7baa5e5 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.h
+++ b/cmds/statsd/src/guardrail/StatsdStats.h
@@ -42,6 +42,7 @@
const static int kDimensionKeySizeHardLimit = 500;
const static int kMaxConfigCount = 10;
+ const static int kMaxAlertCountPerConfig = 100;
const static int kMaxConditionCountPerConfig = 200;
const static int kMaxMetricCountPerConfig = 300;
const static int kMaxMatcherCountPerConfig = 500;
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp
index 178db1a..af2e362 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp
@@ -63,7 +63,8 @@
: 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 67d95db..3b7936d 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
@@ -72,7 +72,8 @@
// 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 8aa8169..0daa506 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -69,7 +69,7 @@
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 e8f8299..8663e5e 100644
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -123,6 +123,7 @@
return byteSizeLocked();
}
+ /* If alert is valid, adds an AnomalyTracker and returns it. If invalid, returns nullptr. */
virtual sp<AnomalyTracker> addAnomalyTracker(const Alert &alert) {
std::lock_guard<std::mutex> lock(mMutex);
sp<AnomalyTracker> anomalyTracker = new AnomalyTracker(alert, mConfigKey);
@@ -137,6 +138,11 @@
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/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp
index 66e1aeb..e75b710 100644
--- a/cmds/statsd/src/metrics/MetricsManager.cpp
+++ b/cmds/statsd/src/metrics/MetricsManager.cpp
@@ -92,8 +92,10 @@
ALOGE("This config is too big! Reject!");
mConfigValid = false;
}
-
- // TODO: add alert size.
+ if (mAllAnomalyTrackers.size() > StatsdStats::kMaxAlertCountPerConfig) {
+ ALOGE("This config has too many alerts! Reject!");
+ mConfigValid = false;
+ }
// no matter whether this config is valid, log it in the stats.
StatsdStats::getInstance().noteConfigReceived(key, mAllMetricProducers.size(),
mAllConditionTrackers.size(),
@@ -188,8 +190,9 @@
return;
}
- if (event.GetTagId() == android::util::APP_BREADCRUMB_REPORTED) { // Check that app hook fields are valid.
- // TODO: Find a way to make these checks easier to maintain if the app hooks get changed.
+ if (event.GetTagId() == android::util::APP_BREADCRUMB_REPORTED) {
+ // Check that app breadcrumb reported fields are valid.
+ // TODO: Find a way to make these checks easier to maintain.
status_t err = NO_ERROR;
// Uid is 3rd from last field and must match the caller's uid,
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index cbca884..35fcdc4 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -72,7 +72,7 @@
// 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/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp
index 769f46d..71e5c33 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.cpp
+++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp
@@ -513,10 +513,12 @@
const int metricIndex = itr->second;
sp<MetricProducer> metric = allMetricProducers[metricIndex];
sp<AnomalyTracker> anomalyTracker = metric->addAnomalyTracker(alert);
- if (anomalyTracker != nullptr) {
- anomalyTrackerMap.insert(std::make_pair(alert.id(), allAnomalyTrackers.size()));
- allAnomalyTrackers.push_back(anomalyTracker);
+ if (anomalyTracker == nullptr) {
+ // The ALOGW for this invalid alert was already displayed in addAnomalyTracker().
+ return false;
}
+ anomalyTrackerMap.insert(std::make_pair(alert.id(), allAnomalyTrackers.size()));
+ allAnomalyTrackers.push_back(anomalyTracker);
}
for (int i = 0; i < config.subscription_size(); ++i) {
const Subscription& subscription = config.subscription(i);
diff --git a/cmds/statsd/src/stats_log_util.cpp b/cmds/statsd/src/stats_log_util.cpp
index f7b768f..30eef4f 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 @@
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 @@
} // 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 32fe0b8..6a5123d 100644
--- a/cmds/statsd/src/stats_log_util.h
+++ b/cmds/statsd/src/stats_log_util.h
@@ -32,6 +32,10 @@
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 @@
} // 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 93cd587..0228004 100644
--- a/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp
@@ -47,7 +47,7 @@
*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 @@
} // 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 293f579..4dffd13 100644
--- a/cmds/statsd/tests/e2e/DimensionInCondition_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/DimensionInCondition_e2e_test.cpp
@@ -61,7 +61,7 @@
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 @@
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 @@
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 @@
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 @@
} // 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 6aa7dd6..3843e0a 100644
--- a/cmds/statsd/tests/e2e/GaugeMetric_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/GaugeMetric_e2e_test.cpp
@@ -53,7 +53,7 @@
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 @@
} // 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 d005181..1b51780 100644
--- a/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp
@@ -71,7 +71,7 @@
// 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 @@
} // 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 024fa3e..efdab98 100644
--- a/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp
@@ -56,7 +56,7 @@
*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 @@
} // 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 d9dbf1d..20ddbe9 100644
--- a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
@@ -56,6 +56,7 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 57e2794..7969596 100644
--- a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp
@@ -59,6 +59,7 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 8b4273b..0eb8ce2 100644
--- a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
@@ -67,6 +67,7 @@
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 @@
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 @@
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 @@
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 @@
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 6e66c6e..ce4fa32 100644
--- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
@@ -66,6 +66,7 @@
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 @@
// 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 @@
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 @@
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 @@
}));
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 @@
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 @@
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/Activity.java b/core/java/android/app/Activity.java
index 3283759..6c3d248 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -7162,8 +7162,8 @@
String appName = getApplicationInfo().loadLabel(getPackageManager())
.toString();
- String warning = "Detected problems with API compatiblity\n"
- + "(please consult log for detail)";
+ String warning = "Detected problems with API compatibility\n"
+ + "(visit g.co/dev/appcompat for more info)";
if (isAppDebuggable) {
new AlertDialog.Builder(this)
.setTitle(appName)
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index ae47a68..03faeee 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -44,6 +44,7 @@
import android.graphics.Matrix;
import android.graphics.Point;
import android.graphics.Rect;
+import android.net.Uri;
import android.os.BatteryStats;
import android.os.Binder;
import android.os.Build;
@@ -2750,6 +2751,30 @@
}
/**
+ * Updates (grants or revokes) a persitable URI permission.
+ *
+ * @param uri URI to be granted or revoked.
+ * @param prefix if {@code false}, permission apply to this specific URI; if {@code true}, it
+ * applies to all URIs that are prefixed by this URI.
+ * @param packageName target package.
+ * @param grant if {@code true} a new permission will be granted, otherwise an existing
+ * permission will be revoked.
+ *
+ * @return whether or not the requested succeeded.
+ *
+ * @hide
+ */
+ public boolean updatePersistableUriPermission(Uri uri, boolean prefix, String packageName,
+ boolean grant) {
+ try {
+ return getService().updatePersistableUriPermission(uri, prefix, packageName, grant,
+ UserHandle.myUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Information you can retrieve about any processes that are in an error condition.
*/
public static class ProcessErrorStateInfo implements Parcelable {
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index fee5827..d5430f0 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -1106,6 +1106,11 @@
}
/** @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 02be002..f6e5f37 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -68,6 +68,7 @@
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;
@@ -423,6 +424,8 @@
void restart();
void performIdleMaintenance();
void takePersistableUriPermission(in Uri uri, int modeFlags, int userId);
+ boolean updatePersistableUriPermission(in Uri uri, boolean prefix, String packageName,
+ boolean grant, int userId);
void releasePersistableUriPermission(in Uri uri, int modeFlags, int userId);
ParceledListSlice getPersistedUriPermissions(in String packageName, boolean incoming);
void appNotRespondingViaProvider(in IBinder connection);
@@ -696,4 +699,11 @@
* 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/admin/SecurityLog.java b/core/java/android/app/admin/SecurityLog.java
index 69ec26c..faaa004 100644
--- a/core/java/android/app/admin/SecurityLog.java
+++ b/core/java/android/app/admin/SecurityLog.java
@@ -319,6 +319,7 @@
* {@link SecurityEvent#getData()}:
* <li> [0] admin package name ({@code String}),
* <li> [1] admin user ID ({@code Integer}).
+ * <li> [2] target user ID ({@code Integer})
*/
public static final int TAG_REMOTE_LOCK = SecurityLogTags.SECURITY_REMOTE_LOCK;
diff --git a/core/java/android/app/backup/OWNERS b/core/java/android/app/backup/OWNERS
new file mode 100644
index 0000000..1c9a43a
--- /dev/null
+++ b/core/java/android/app/backup/OWNERS
@@ -0,0 +1,7 @@
+artikz@google.com
+brufino@google.com
+bryanmawhinney@google.com
+ctate@google.com
+jorlow@google.com
+mkarpinski@google.com
+
diff --git a/core/java/android/app/servertransaction/ActivityLifecycleItem.java b/core/java/android/app/servertransaction/ActivityLifecycleItem.java
index 9a50a00..7f8c50c 100644
--- a/core/java/android/app/servertransaction/ActivityLifecycleItem.java
+++ b/core/java/android/app/servertransaction/ActivityLifecycleItem.java
@@ -91,4 +91,9 @@
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 48a79f7..0edcf18 100644
--- a/core/java/android/app/servertransaction/DestroyActivityItem.java
+++ b/core/java/android/app/servertransaction/DestroyActivityItem.java
@@ -65,6 +65,7 @@
@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 70a4755..91e73cd 100644
--- a/core/java/android/app/servertransaction/PauseActivityItem.java
+++ b/core/java/android/app/servertransaction/PauseActivityItem.java
@@ -102,6 +102,7 @@
@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 ed90f2c..af2fb71 100644
--- a/core/java/android/app/servertransaction/ResumeActivityItem.java
+++ b/core/java/android/app/servertransaction/ResumeActivityItem.java
@@ -101,6 +101,7 @@
@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 b814d1a..f955a90 100644
--- a/core/java/android/app/servertransaction/StopActivityItem.java
+++ b/core/java/android/app/servertransaction/StopActivityItem.java
@@ -72,6 +72,7 @@
@Override
public void recycle() {
+ super.recycle();
mShowWindow = false;
mConfigChanges = 0;
ObjectPool.recycle(this);
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index bc7823b..1dc7549 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -2307,6 +2307,9 @@
} else if (profile == BluetoothProfile.HID_DEVICE) {
BluetoothHidDevice hidDevice = new BluetoothHidDevice(context, listener);
return true;
+ } else if (profile == BluetoothProfile.HEARING_AID) {
+ BluetoothHearingAid hearingAid = new BluetoothHearingAid(context, listener);
+ return true;
} else {
return false;
}
@@ -2389,6 +2392,9 @@
BluetoothHidDevice hidDevice = (BluetoothHidDevice) proxy;
hidDevice.close();
break;
+ case BluetoothProfile.HEARING_AID:
+ BluetoothHearingAid hearingAid = (BluetoothHearingAid) proxy;
+ hearingAid.close();
}
}
diff --git a/core/java/android/bluetooth/BluetoothHearingAid.java b/core/java/android/bluetooth/BluetoothHearingAid.java
new file mode 100644
index 0000000..647e0d0
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothHearingAid.java
@@ -0,0 +1,693 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.bluetooth;
+
+import android.Manifest;
+import android.annotation.RequiresPermission;
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+/**
+ * This class provides the public APIs to control the Bluetooth Hearing Aid
+ * profile.
+ *
+ * <p>BluetoothHearingAid is a proxy object for controlling the Bluetooth Hearing Aid
+ * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get
+ * the BluetoothHearingAid proxy object.
+ *
+ * <p> Each method is protected with its appropriate permission.
+ * @hide
+ */
+public final class BluetoothHearingAid implements BluetoothProfile {
+ private static final String TAG = "BluetoothHearingAid";
+ private static final boolean DBG = false;
+ private static final boolean VDBG = false;
+
+ /**
+ * Intent used to broadcast the change in connection state of the Hearing Aid
+ * profile.
+ *
+ * <p>This intent will have 3 extras:
+ * <ul>
+ * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
+ * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.</li>
+ * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
+ * </ul>
+ *
+ * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
+ * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
+ * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
+ *
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
+ * receive.
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_CONNECTION_STATE_CHANGED =
+ "android.bluetooth.hearingaid.profile.action.CONNECTION_STATE_CHANGED";
+
+ /**
+ * Intent used to broadcast the change in the Playing state of the Hearing Aid
+ * profile.
+ *
+ * <p>This intent will have 3 extras:
+ * <ul>
+ * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
+ * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile. </li>
+ * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
+ * </ul>
+ *
+ * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
+ * {@link #STATE_PLAYING}, {@link #STATE_NOT_PLAYING},
+ *
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
+ * receive.
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_PLAYING_STATE_CHANGED =
+ "android.bluetooth.hearingaid.profile.action.PLAYING_STATE_CHANGED";
+
+ /**
+ * Intent used to broadcast the selection of a connected device as active.
+ *
+ * <p>This intent will have one extra:
+ * <ul>
+ * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. It can
+ * be null if no device is active. </li>
+ * </ul>
+ *
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
+ * receive.
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_ACTIVE_DEVICE_CHANGED =
+ "android.bluetooth.hearingaid.profile.action.ACTIVE_DEVICE_CHANGED";
+
+ /**
+ * Hearing Aid device is streaming music. This state can be one of
+ * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of
+ * {@link #ACTION_PLAYING_STATE_CHANGED} intent.
+ */
+ public static final int STATE_PLAYING = 10;
+
+ /**
+ * Hearing Aid device is NOT streaming music. This state can be one of
+ * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of
+ * {@link #ACTION_PLAYING_STATE_CHANGED} intent.
+ */
+ public static final int STATE_NOT_PLAYING = 11;
+
+ /** This device represents Left Hearing Aid. */
+ public static final int SIDE_LEFT = IBluetoothHearingAid.SIDE_LEFT;
+
+ /** This device represents Right Hearing Aid. */
+ public static final int SIDE_RIGHT = IBluetoothHearingAid.SIDE_RIGHT;
+
+ /** This device is Monaural. */
+ public static final int MODE_MONAURAL = IBluetoothHearingAid.MODE_MONAURAL;
+
+ /** This device is Binaural (should receive only left or right audio). */
+ public static final int MODE_BINAURAL = IBluetoothHearingAid.MODE_BINAURAL;
+
+ /** Can't read ClientID for this device */
+ public static final long HI_SYNC_ID_INVALID = IBluetoothHearingAid.HI_SYNC_ID_INVALID;
+
+ private Context mContext;
+ private ServiceListener mServiceListener;
+ private final ReentrantReadWriteLock mServiceLock = new ReentrantReadWriteLock();
+ @GuardedBy("mServiceLock")
+ private IBluetoothHearingAid mService;
+ private BluetoothAdapter mAdapter;
+
+ private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
+ new IBluetoothStateChangeCallback.Stub() {
+ public void onBluetoothStateChange(boolean up) {
+ if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
+ if (!up) {
+ if (VDBG) Log.d(TAG, "Unbinding service...");
+ try {
+ mServiceLock.writeLock().lock();
+ mService = null;
+ mContext.unbindService(mConnection);
+ } catch (Exception re) {
+ Log.e(TAG, "", re);
+ } finally {
+ mServiceLock.writeLock().unlock();
+ }
+ } else {
+ try {
+ mServiceLock.readLock().lock();
+ if (mService == null) {
+ if (VDBG) Log.d(TAG, "Binding service...");
+ doBind();
+ }
+ } catch (Exception re) {
+ Log.e(TAG, "", re);
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
+ }
+ }
+ };
+
+ /**
+ * Create a BluetoothHearingAid proxy object for interacting with the local
+ * Bluetooth Hearing Aid service.
+ */
+ /*package*/ BluetoothHearingAid(Context context, ServiceListener l) {
+ mContext = context;
+ mServiceListener = l;
+ mAdapter = BluetoothAdapter.getDefaultAdapter();
+ IBluetoothManager mgr = mAdapter.getBluetoothManager();
+ if (mgr != null) {
+ try {
+ mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
+ } catch (RemoteException e) {
+ Log.e(TAG, "", e);
+ }
+ }
+
+ doBind();
+ }
+
+ void doBind() {
+ Intent intent = new Intent(IBluetoothHearingAid.class.getName());
+ ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
+ intent.setComponent(comp);
+ if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
+ android.os.Process.myUserHandle())) {
+ Log.e(TAG, "Could not bind to Bluetooth Hearing Aid Service with " + intent);
+ return;
+ }
+ }
+
+ /*package*/ void close() {
+ mServiceListener = null;
+ IBluetoothManager mgr = mAdapter.getBluetoothManager();
+ if (mgr != null) {
+ try {
+ mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
+ } catch (Exception e) {
+ Log.e(TAG, "", e);
+ }
+ }
+
+ try {
+ mServiceLock.writeLock().lock();
+ if (mService != null) {
+ mService = null;
+ mContext.unbindService(mConnection);
+ }
+ } catch (Exception re) {
+ Log.e(TAG, "", re);
+ } finally {
+ mServiceLock.writeLock().unlock();
+ }
+ }
+
+ @Override
+ public void finalize() {
+ // The empty finalize needs to be kept or the
+ // cts signature tests would fail.
+ }
+
+ /**
+ * Initiate connection to a profile of the remote bluetooth device.
+ *
+ * <p> This API returns false in scenarios like the profile on the
+ * device is already connected or Bluetooth is not turned on.
+ * When this API returns true, it is guaranteed that
+ * connection state intent for the profile will be broadcasted with
+ * the state. Users can get the connection state of the profile
+ * from this intent.
+ *
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
+ * permission.
+ *
+ * @param device Remote Bluetooth Device
+ * @return false on immediate error, true otherwise
+ * @hide
+ */
+ public boolean connect(BluetoothDevice device) {
+ if (DBG) log("connect(" + device + ")");
+ try {
+ mServiceLock.readLock().lock();
+ if (mService != null && isEnabled() && isValidDevice(device)) {
+ return mService.connect(device);
+ }
+ if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ return false;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+ return false;
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
+ }
+
+ /**
+ * Initiate disconnection from a profile
+ *
+ * <p> This API will return false in scenarios like the profile on the
+ * Bluetooth device is not in connected state etc. When this API returns,
+ * true, it is guaranteed that the connection state change
+ * intent will be broadcasted with the state. Users can get the
+ * disconnection state of the profile from this intent.
+ *
+ * <p> If the disconnection is initiated by a remote device, the state
+ * will transition from {@link #STATE_CONNECTED} to
+ * {@link #STATE_DISCONNECTED}. If the disconnect is initiated by the
+ * host (local) device the state will transition from
+ * {@link #STATE_CONNECTED} to state {@link #STATE_DISCONNECTING} to
+ * state {@link #STATE_DISCONNECTED}. The transition to
+ * {@link #STATE_DISCONNECTING} can be used to distinguish between the
+ * two scenarios.
+ *
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
+ * permission.
+ *
+ * @param device Remote Bluetooth Device
+ * @return false on immediate error, true otherwise
+ * @hide
+ */
+ public boolean disconnect(BluetoothDevice device) {
+ if (DBG) log("disconnect(" + device + ")");
+ try {
+ mServiceLock.readLock().lock();
+ if (mService != null && isEnabled() && isValidDevice(device)) {
+ return mService.disconnect(device);
+ }
+ if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ return false;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+ return false;
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public List<BluetoothDevice> getConnectedDevices() {
+ if (VDBG) log("getConnectedDevices()");
+ try {
+ mServiceLock.readLock().lock();
+ if (mService != null && isEnabled()) {
+ return mService.getConnectedDevices();
+ }
+ if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ return new ArrayList<BluetoothDevice>();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+ return new ArrayList<BluetoothDevice>();
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
+ if (VDBG) log("getDevicesMatchingStates()");
+ try {
+ mServiceLock.readLock().lock();
+ if (mService != null && isEnabled()) {
+ return mService.getDevicesMatchingConnectionStates(states);
+ }
+ if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ return new ArrayList<BluetoothDevice>();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+ return new ArrayList<BluetoothDevice>();
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int getConnectionState(BluetoothDevice device) {
+ if (VDBG) log("getState(" + device + ")");
+ try {
+ mServiceLock.readLock().lock();
+ if (mService != null && isEnabled()
+ && isValidDevice(device)) {
+ return mService.getConnectionState(device);
+ }
+ if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ return BluetoothProfile.STATE_DISCONNECTED;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+ return BluetoothProfile.STATE_DISCONNECTED;
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
+ }
+
+ /**
+ * Set priority of the profile
+ *
+ * <p> The device should already be paired.
+ * Priority can be one of {@link #PRIORITY_ON} orgetBluetoothManager
+ * {@link #PRIORITY_OFF},
+ *
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
+ * permission.
+ *
+ * @param device Paired bluetooth device
+ * @param priority
+ * @return true if priority is set, false on error
+ * @hide
+ */
+ public boolean setPriority(BluetoothDevice device, int priority) {
+ if (DBG) log("setPriority(" + device + ", " + priority + ")");
+ try {
+ mServiceLock.readLock().lock();
+ if (mService != null && isEnabled()
+ && isValidDevice(device)) {
+ if (priority != BluetoothProfile.PRIORITY_OFF
+ && priority != BluetoothProfile.PRIORITY_ON) {
+ return false;
+ }
+ return mService.setPriority(device, priority);
+ }
+ if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ return false;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+ return false;
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
+ }
+
+ /**
+ * Get the priority of the profile.
+ *
+ * <p> The priority can be any of:
+ * {@link #PRIORITY_AUTO_CONNECT}, {@link #PRIORITY_OFF},
+ * {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED}
+ *
+ * @param device Bluetooth device
+ * @return priority of the device
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.BLUETOOTH)
+ public int getPriority(BluetoothDevice device) {
+ if (VDBG) log("getPriority(" + device + ")");
+ try {
+ mServiceLock.readLock().lock();
+ if (mService != null && isEnabled()
+ && isValidDevice(device)) {
+ return mService.getPriority(device);
+ }
+ if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ return BluetoothProfile.PRIORITY_OFF;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+ return BluetoothProfile.PRIORITY_OFF;
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
+ }
+
+ /**
+ * Helper for converting a state to a string.
+ *
+ * For debug use only - strings are not internationalized.
+ *
+ * @hide
+ */
+ public static String stateToString(int state) {
+ switch (state) {
+ case STATE_DISCONNECTED:
+ return "disconnected";
+ case STATE_CONNECTING:
+ return "connecting";
+ case STATE_CONNECTED:
+ return "connected";
+ case STATE_DISCONNECTING:
+ return "disconnecting";
+ case STATE_PLAYING:
+ return "playing";
+ case STATE_NOT_PLAYING:
+ return "not playing";
+ default:
+ return "<unknown state " + state + ">";
+ }
+ }
+
+ /**
+ * Get the volume of the device.
+ *
+ * <p> The volume is between -128 dB (mute) to 0 dB.
+ *
+ * @return volume of the hearing aid device.
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.BLUETOOTH)
+ public int getVolume() {
+ if (VDBG) {
+ log("getVolume()");
+ }
+ try {
+ mServiceLock.readLock().lock();
+ if (mService != null && isEnabled()) {
+ return mService.getVolume();
+ }
+ if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ return 0;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+ return 0;
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
+ }
+
+ /**
+ * Tells remote device to adjust volume. Uses the following values:
+ * <ul>
+ * <li>{@link AudioManager#ADJUST_LOWER}</li>
+ * <li>{@link AudioManager#ADJUST_RAISE}</li>
+ * <li>{@link AudioManager#ADJUST_MUTE}</li>
+ * <li>{@link AudioManager#ADJUST_UNMUTE}</li>
+ * </ul>
+ *
+ * @param direction One of the supported adjust values.
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.BLUETOOTH)
+ public void adjustVolume(int direction) {
+ if (DBG) log("adjustVolume(" + direction + ")");
+
+ try {
+ mServiceLock.readLock().lock();
+
+ if (mService == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ return;
+ }
+
+ if (!isEnabled()) return;
+
+ mService.adjustVolume(direction);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
+ }
+
+ /**
+ * Tells remote device to set an absolute volume.
+ *
+ * @param volume Absolute volume to be set on remote
+ * @hide
+ */
+ public void setVolume(int volume) {
+ if (DBG) Log.d(TAG, "setVolume(" + volume + ")");
+
+ try {
+ mServiceLock.readLock().lock();
+ if (mService == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ return;
+ }
+
+ if (!isEnabled()) return;
+
+ mService.setVolume(volume);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
+ }
+
+ /**
+ * Get the CustomerId of the device.
+ *
+ * @param device Bluetooth device
+ * @return the CustomerId of the device
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.BLUETOOTH)
+ public long getHiSyncId(BluetoothDevice device) {
+ if (VDBG) {
+ log("getCustomerId(" + device + ")");
+ }
+ try {
+ mServiceLock.readLock().lock();
+ if (mService == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ return HI_SYNC_ID_INVALID;
+ }
+
+ if (!isEnabled() || !isValidDevice(device)) return HI_SYNC_ID_INVALID;
+
+ return mService.getHiSyncId(device);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+ return HI_SYNC_ID_INVALID;
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
+ }
+
+ /**
+ * Get the side of the device.
+ *
+ * @param device Bluetooth device.
+ * @return SIDE_LEFT or SIDE_RIGHT
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.BLUETOOTH)
+ public int getDeviceSide(BluetoothDevice device) {
+ if (VDBG) {
+ log("getDeviceSide(" + device + ")");
+ }
+ try {
+ mServiceLock.readLock().lock();
+ if (mService != null && isEnabled()
+ && isValidDevice(device)) {
+ return mService.getDeviceSide(device);
+ }
+ if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ return SIDE_LEFT;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+ return SIDE_LEFT;
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
+ }
+
+ /**
+ * Get the mode of the device.
+ *
+ * @param device Bluetooth device
+ * @return MODE_MONAURAL or MODE_BINAURAL
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.BLUETOOTH)
+ public int getDeviceMode(BluetoothDevice device) {
+ if (VDBG) {
+ log("getDeviceMode(" + device + ")");
+ }
+ try {
+ mServiceLock.readLock().lock();
+ if (mService != null && isEnabled()
+ && isValidDevice(device)) {
+ return mService.getDeviceMode(device);
+ }
+ if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ return MODE_MONAURAL;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+ return MODE_MONAURAL;
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
+ }
+
+ private final ServiceConnection mConnection = new ServiceConnection() {
+ public void onServiceConnected(ComponentName className, IBinder service) {
+ if (DBG) Log.d(TAG, "Proxy object connected");
+ try {
+ mServiceLock.writeLock().lock();
+ mService = IBluetoothHearingAid.Stub.asInterface(Binder.allowBlocking(service));
+ } finally {
+ mServiceLock.writeLock().unlock();
+ }
+
+ if (mServiceListener != null) {
+ mServiceListener.onServiceConnected(BluetoothProfile.HEARING_AID,
+ BluetoothHearingAid.this);
+ }
+ }
+
+ public void onServiceDisconnected(ComponentName className) {
+ if (DBG) Log.d(TAG, "Proxy object disconnected");
+ try {
+ mServiceLock.writeLock().lock();
+ mService = null;
+ } finally {
+ mServiceLock.writeLock().unlock();
+ }
+ if (mServiceListener != null) {
+ mServiceListener.onServiceDisconnected(BluetoothProfile.HEARING_AID);
+ }
+ }
+ };
+
+ private boolean isEnabled() {
+ if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true;
+ return false;
+ }
+
+ private boolean isValidDevice(BluetoothDevice device) {
+ if (device == null) return false;
+
+ if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true;
+ return false;
+ }
+
+ private static void log(String msg) {
+ Log.d(TAG, msg);
+ }
+}
diff --git a/core/java/android/bluetooth/BluetoothProfile.java b/core/java/android/bluetooth/BluetoothProfile.java
index 0e2263f..656188f 100644
--- a/core/java/android/bluetooth/BluetoothProfile.java
+++ b/core/java/android/bluetooth/BluetoothProfile.java
@@ -165,12 +165,19 @@
public static final int OPP = 20;
/**
+ * Hearing Aid Device
+ *
+ * @hide
+ */
+ int HEARING_AID = 21;
+
+ /**
* Max profile ID. This value should be updated whenever a new profile is added to match
* the largest value assigned to a profile.
*
* @hide
*/
- public static final int MAX_PROFILE_ID = 20;
+ int MAX_PROFILE_ID = 21;
/**
* Default priority for devices that we try to auto-connect to and
diff --git a/core/java/android/bluetooth/BluetoothUuid.java b/core/java/android/bluetooth/BluetoothUuid.java
index 76cb3f5..0a0d214 100644
--- a/core/java/android/bluetooth/BluetoothUuid.java
+++ b/core/java/android/bluetooth/BluetoothUuid.java
@@ -79,6 +79,9 @@
ParcelUuid.fromString("00001132-0000-1000-8000-00805F9B34FB");
public static final ParcelUuid SAP =
ParcelUuid.fromString("0000112D-0000-1000-8000-00805F9B34FB");
+ /* TODO: b/69623109 update this value. It will change to 16bit UUID!! */
+ public static final ParcelUuid HearingAid =
+ ParcelUuid.fromString("7312C48F-22CC-497F-85FD-A0616A3B9E05");
public static final ParcelUuid BASE_UUID =
ParcelUuid.fromString("00000000-0000-1000-8000-00805F9B34FB");
diff --git a/core/java/android/hardware/OWNERS b/core/java/android/hardware/OWNERS
new file mode 100644
index 0000000..b8fea55
--- /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 0000000..18acfee
--- /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/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 166342d..7dde2ed 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -112,8 +112,14 @@
* <p/>
* For a disconnect event, the boolean extra EXTRA_NO_CONNECTIVITY
* is set to {@code true} if there are no connected networks at all.
+ *
+ * @deprecated apps should use the more versatile {@link #requestNetwork},
+ * {@link #registerNetworkCallback} or {@link #registerDefaultNetworkCallback}
+ * functions instead for faster and more detailed updates about the network
+ * changes they care about.
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ @Deprecated
public static final String CONNECTIVITY_ACTION = "android.net.conn.CONNECTIVITY_CHANGE";
/**
@@ -2685,6 +2691,32 @@
* satisfying the request changes.
*
* @param network The {@link Network} of the satisfying network.
+ * @param networkCapabilities The {@link NetworkCapabilities} of the satisfying network.
+ * @param linkProperties The {@link LinkProperties} of the satisfying network.
+ * @hide
+ */
+ public void onAvailable(Network network, NetworkCapabilities networkCapabilities,
+ LinkProperties linkProperties) {
+ // Internally only this method is called when a new network is available, and
+ // it calls the callback in the same way and order that older versions used
+ // to call so as not to change the behavior.
+ onAvailable(network);
+ if (!networkCapabilities.hasCapability(
+ NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED)) {
+ onNetworkSuspended(network);
+ }
+ onCapabilitiesChanged(network, networkCapabilities);
+ onLinkPropertiesChanged(network, linkProperties);
+ }
+
+ /**
+ * Called when the framework connects and has declared a new network ready for use.
+ * This callback may be called more than once if the {@link Network} that is
+ * satisfying the request changes. This will always immediately be followed by a
+ * call to {@link #onCapabilitiesChanged(Network, NetworkCapabilities)} then by a
+ * call to {@link #onLinkPropertiesChanged(Network, LinkProperties)}.
+ *
+ * @param network The {@link Network} of the satisfying network.
*/
public void onAvailable(Network network) {}
@@ -2727,7 +2759,8 @@
* changes capabilities but still satisfies the stated need.
*
* @param network The {@link Network} whose capabilities have changed.
- * @param networkCapabilities The new {@link android.net.NetworkCapabilities} for this network.
+ * @param networkCapabilities The new {@link android.net.NetworkCapabilities} for this
+ * network.
*/
public void onCapabilitiesChanged(Network network,
NetworkCapabilities networkCapabilities) {}
@@ -2743,7 +2776,7 @@
/**
* Called when the network the framework connected to for this request
- * goes into {@link NetworkInfo.DetailedState.SUSPENDED}.
+ * goes into {@link NetworkInfo.State#SUSPENDED}.
* This generally means that while the TCP connections are still live,
* temporarily network data fails to transfer. Specifically this is used
* on cellular networks to mask temporary outages when driving through
@@ -2754,9 +2787,8 @@
/**
* Called when the network the framework connected to for this request
- * returns from a {@link NetworkInfo.DetailedState.SUSPENDED} state.
- * This should always be preceeded by a matching {@code onNetworkSuspended}
- * call.
+ * returns from a {@link NetworkInfo.State#SUSPENDED} state. This should always be
+ * preceded by a matching {@link NetworkCallback#onNetworkSuspended} call.
* @hide
*/
public void onNetworkResumed(Network network) {}
@@ -2865,7 +2897,9 @@
break;
}
case CALLBACK_AVAILABLE: {
- callback.onAvailable(network);
+ NetworkCapabilities cap = getObject(message, NetworkCapabilities.class);
+ LinkProperties lp = getObject(message, LinkProperties.class);
+ callback.onAvailable(network, cap, lp);
break;
}
case CALLBACK_LOSING: {
diff --git a/core/java/android/net/IpSecConfig.java b/core/java/android/net/IpSecConfig.java
index 6a262e2..8599f47 100644
--- a/core/java/android/net/IpSecConfig.java
+++ b/core/java/android/net/IpSecConfig.java
@@ -218,6 +218,25 @@
@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 38759a9..60e96f9 100644
--- a/core/java/android/net/IpSecTransform.java
+++ b/core/java/android/net/IpSecTransform.java
@@ -84,9 +84,11 @@
@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 @@
}
/**
+ * 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/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index 8e05cfa..bae373d 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -116,6 +116,7 @@
NET_CAPABILITY_NOT_ROAMING,
NET_CAPABILITY_FOREGROUND,
NET_CAPABILITY_NOT_CONGESTED,
+ NET_CAPABILITY_NOT_SUSPENDED,
})
public @interface NetCapability { }
@@ -239,7 +240,6 @@
/**
* Indicates that this network is available for use by apps, and not a network that is being
* kept up in the background to facilitate fast network switching.
- * @hide
*/
public static final int NET_CAPABILITY_FOREGROUND = 19;
@@ -252,8 +252,20 @@
*/
public static final int NET_CAPABILITY_NOT_CONGESTED = 20;
+ /**
+ * Indicates that this network is not currently suspended.
+ * <p>
+ * When a network is suspended, the network's IP addresses and any connections
+ * established on the network remain valid, but the network is temporarily unable
+ * to transfer data. This can happen, for example, if a cellular network experiences
+ * a temporary loss of signal, such as when driving through a tunnel, etc.
+ * A network with this capability is not suspended, so is expected to be able to
+ * transfer data.
+ */
+ public static final int NET_CAPABILITY_NOT_SUSPENDED = 21;
+
private static final int MIN_NET_CAPABILITY = NET_CAPABILITY_MMS;
- private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_NOT_CONGESTED;
+ private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_NOT_SUSPENDED;
/**
* Network capabilities that are expected to be mutable, i.e., can change while a particular
@@ -262,12 +274,13 @@
private static final long MUTABLE_CAPABILITIES =
// TRUSTED can change when user explicitly connects to an untrusted network in Settings.
// http://b/18206275
- (1 << NET_CAPABILITY_TRUSTED) |
- (1 << NET_CAPABILITY_VALIDATED) |
- (1 << NET_CAPABILITY_CAPTIVE_PORTAL) |
- (1 << NET_CAPABILITY_NOT_ROAMING) |
- (1 << NET_CAPABILITY_FOREGROUND) |
- (1 << NET_CAPABILITY_NOT_CONGESTED);
+ (1 << NET_CAPABILITY_TRUSTED)
+ | (1 << NET_CAPABILITY_VALIDATED)
+ | (1 << NET_CAPABILITY_CAPTIVE_PORTAL)
+ | (1 << NET_CAPABILITY_NOT_ROAMING)
+ | (1 << NET_CAPABILITY_FOREGROUND)
+ | (1 << NET_CAPABILITY_NOT_CONGESTED)
+ | (1 << NET_CAPABILITY_NOT_SUSPENDED);
/**
* Network capabilities that are not allowed in NetworkRequests. This exists because the
@@ -1299,6 +1312,7 @@
case NET_CAPABILITY_NOT_ROAMING: return "NOT_ROAMING";
case NET_CAPABILITY_FOREGROUND: return "FOREGROUND";
case NET_CAPABILITY_NOT_CONGESTED: return "NOT_CONGESTED";
+ case NET_CAPABILITY_NOT_SUSPENDED: return "NOT_SUSPENDED";
default: return Integer.toString(capability);
}
}
diff --git a/core/java/android/net/OWNERS b/core/java/android/net/OWNERS
index 3a40cf4..3cd37bf 100644
--- a/core/java/android/net/OWNERS
+++ b/core/java/android/net/OWNERS
@@ -1,3 +1,5 @@
+set noparent
+
ek@google.com
jsharkey@android.com
jchalard@google.com
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 5d7cf1e..7cd58e8 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -2240,12 +2240,16 @@
public static final int DATA_CONNECTION_LTE = 13;
public static final int DATA_CONNECTION_EHRPD = 14;
public static final int DATA_CONNECTION_HSPAP = 15;
- public static final int DATA_CONNECTION_OTHER = 16;
+ public static final int DATA_CONNECTION_GSM = 16;
+ public static final int DATA_CONNECTION_TD_SCDMA = 17;
+ public static final int DATA_CONNECTION_IWLAN = 18;
+ public static final int DATA_CONNECTION_LTE_CA = 19;
+ public static final int DATA_CONNECTION_OTHER = 20;
static final String[] DATA_CONNECTION_NAMES = {
"none", "gprs", "edge", "umts", "cdma", "evdo_0", "evdo_A",
"1xrtt", "hsdpa", "hsupa", "hspa", "iden", "evdo_b", "lte",
- "ehrpd", "hspap", "other"
+ "ehrpd", "hspap", "gsm", "td_scdma", "iwlan", "lte_ca", "other"
};
public static final int NUM_DATA_CONNECTION_TYPES = DATA_CONNECTION_OTHER+1;
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index 65e9473..5e23932 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -71,6 +71,7 @@
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 7e7af1a..1856200 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -1678,6 +1678,18 @@
}
/**
+ * @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 1223271..05ab486 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -5385,6 +5385,17 @@
"autofill_user_data_max_field_classification_size";
/**
+ * Defines value returned by
+ * {@link android.service.autofill.UserData#getMaxCategoryCount()}.
+ *
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ public static final String AUTOFILL_USER_DATA_MAX_CATEGORY_COUNT =
+ "autofill_user_data_max_category_count";
+
+ /**
* Defines value returned by {@link android.service.autofill.UserData#getMaxValueLength()}.
*
* @hide
@@ -5443,32 +5454,6 @@
*/
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 +7694,6 @@
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 +7799,6 @@
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 +11365,8 @@
"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
*/
@@ -12097,6 +12081,28 @@
"enable_gnss_raw_meas_full_tracking";
/**
+ * Whether the notification should be ongoing (persistent) when a carrier app install is
+ * required.
+ *
+ * The value is a boolean (1 or 0).
+ * @hide
+ */
+ @SystemApi
+ public static final String INSTALL_CARRIER_APP_NOTIFICATION_PERSISTENT =
+ "install_carrier_app_notification_persistent";
+
+ /**
+ * The amount of time (ms) to hide the install carrier app notification after the user has
+ * ignored it. After this time passes, the notification will be shown again
+ *
+ * The value is a long
+ * @hide
+ */
+ @SystemApi
+ public static final String INSTALL_CARRIER_APP_NOTIFICATION_SLEEP_MILLIS =
+ "install_carrier_app_notification_sleep_millis";
+
+ /**
* Whether we've enabled zram on this device. Takes effect on
* reboot. The value "1" enables zram; "0" disables it, and
* everything else is unspecified.
diff --git a/core/java/android/security/keystore/OWNERS b/core/java/android/security/keystore/OWNERS
new file mode 100644
index 0000000..bb487fb
--- /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 f644887..70c4ec0 100644
--- a/core/java/android/service/autofill/AutofillServiceInfo.java
+++ b/core/java/android/service/autofill/AutofillServiceInfo.java
@@ -45,7 +45,6 @@
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 @@
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 @@
}
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 @@
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 @@
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/service/autofill/DateTransformation.java b/core/java/android/service/autofill/DateTransformation.java
index 4e1425d..ec24a09 100644
--- a/core/java/android/service/autofill/DateTransformation.java
+++ b/core/java/android/service/autofill/DateTransformation.java
@@ -20,6 +20,7 @@
import android.annotation.NonNull;
import android.annotation.TestApi;
+import android.icu.text.DateFormat;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Log;
@@ -30,7 +31,6 @@
import com.android.internal.util.Preconditions;
-import java.text.DateFormat;
import java.util.Date;
/**
diff --git a/core/java/android/service/autofill/DateValueSanitizer.java b/core/java/android/service/autofill/DateValueSanitizer.java
index 0f7b540..4f797f4 100644
--- a/core/java/android/service/autofill/DateValueSanitizer.java
+++ b/core/java/android/service/autofill/DateValueSanitizer.java
@@ -21,6 +21,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.TestApi;
+import android.icu.text.DateFormat;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Log;
@@ -28,7 +29,6 @@
import com.android.internal.util.Preconditions;
-import java.text.DateFormat;
import java.util.Date;
/**
diff --git a/core/java/android/service/autofill/FieldClassification.java b/core/java/android/service/autofill/FieldClassification.java
index cd1efd6..5bf56cb9 100644
--- a/core/java/android/service/autofill/FieldClassification.java
+++ b/core/java/android/service/autofill/FieldClassification.java
@@ -108,21 +108,21 @@
*/
public static final class Match {
- private final String mRemoteId;
+ private final String mCategoryId;
private final float mScore;
/** @hide */
- public Match(String remoteId, float score) {
- mRemoteId = Preconditions.checkNotNull(remoteId);
+ public Match(String categoryId, float score) {
+ mCategoryId = Preconditions.checkNotNull(categoryId);
mScore = score;
}
/**
- * Gets the remote id of the {@link UserData} entry.
+ * Gets the category id of the {@link UserData} entry.
*/
@NonNull
- public String getRemoteId() {
- return mRemoteId;
+ public String getCategoryId() {
+ return mCategoryId;
}
/**
@@ -149,13 +149,13 @@
public String toString() {
if (!sDebug) return super.toString();
- final StringBuilder string = new StringBuilder("Match: remoteId=");
- Helper.appendRedacted(string, mRemoteId);
+ final StringBuilder string = new StringBuilder("Match: categoryId=");
+ Helper.appendRedacted(string, mCategoryId);
return string.append(", score=").append(mScore).toString();
}
private void writeToParcel(@NonNull Parcel parcel) {
- parcel.writeString(mRemoteId);
+ parcel.writeString(mCategoryId);
parcel.writeFloat(mScore);
}
diff --git a/core/java/android/service/autofill/UserData.java b/core/java/android/service/autofill/UserData.java
index 6bab6aa..a1dd1f8 100644
--- a/core/java/android/service/autofill/UserData.java
+++ b/core/java/android/service/autofill/UserData.java
@@ -15,6 +15,7 @@
*/
package android.service.autofill;
+import static android.provider.Settings.Secure.AUTOFILL_USER_DATA_MAX_CATEGORY_COUNT;
import static android.provider.Settings.Secure.AUTOFILL_USER_DATA_MAX_FIELD_CLASSIFICATION_IDS_SIZE;
import static android.provider.Settings.Secure.AUTOFILL_USER_DATA_MAX_USER_DATA_SIZE;
import static android.provider.Settings.Secure.AUTOFILL_USER_DATA_MAX_VALUE_LENGTH;
@@ -31,6 +32,7 @@
import android.provider.Settings;
import android.service.autofill.FieldClassification.Match;
import android.text.TextUtils;
+import android.util.ArraySet;
import android.util.Log;
import android.view.autofill.AutofillManager;
import android.view.autofill.Helper;
@@ -48,23 +50,24 @@
private static final String TAG = "UserData";
- private static final int DEFAULT_MAX_USER_DATA_SIZE = 10;
+ private static final int DEFAULT_MAX_USER_DATA_SIZE = 50;
+ private static final int DEFAULT_MAX_CATEGORY_COUNT = 10;
private static final int DEFAULT_MAX_FIELD_CLASSIFICATION_IDS_SIZE = 10;
- private static final int DEFAULT_MIN_VALUE_LENGTH = 5;
+ private static final int DEFAULT_MIN_VALUE_LENGTH = 3;
private static final int DEFAULT_MAX_VALUE_LENGTH = 100;
private final String mId;
private final String mAlgorithm;
private final Bundle mAlgorithmArgs;
- private final String[] mRemoteIds;
+ private final String[] mCategoryIds;
private final String[] mValues;
private UserData(Builder builder) {
mId = builder.mId;
mAlgorithm = builder.mAlgorithm;
mAlgorithmArgs = builder.mAlgorithmArgs;
- mRemoteIds = new String[builder.mRemoteIds.size()];
- builder.mRemoteIds.toArray(mRemoteIds);
+ mCategoryIds = new String[builder.mCategoryIds.size()];
+ builder.mCategoryIds.toArray(mCategoryIds);
mValues = new String[builder.mValues.size()];
builder.mValues.toArray(mValues);
}
@@ -91,8 +94,8 @@
}
/** @hide */
- public String[] getRemoteIds() {
- return mRemoteIds;
+ public String[] getCategoryIds() {
+ return mCategoryIds;
}
/** @hide */
@@ -106,11 +109,11 @@
pw.print(prefix); pw.print("Algorithm: "); pw.print(mAlgorithm);
pw.print(" Args: "); pw.println(mAlgorithmArgs);
- // Cannot disclose remote ids or values because they could contain PII
- pw.print(prefix); pw.print("Remote ids size: "); pw.println(mRemoteIds.length);
- for (int i = 0; i < mRemoteIds.length; i++) {
+ // Cannot disclose field ids or values because they could contain PII
+ pw.print(prefix); pw.print("Field ids size: "); pw.println(mCategoryIds.length);
+ for (int i = 0; i < mCategoryIds.length; i++) {
pw.print(prefix); pw.print(prefix); pw.print(i); pw.print(": ");
- pw.println(Helper.getRedacted(mRemoteIds[i]));
+ pw.println(Helper.getRedacted(mCategoryIds[i]));
}
pw.print(prefix); pw.print("Values size: "); pw.println(mValues.length);
for (int i = 0; i < mValues.length; i++) {
@@ -124,6 +127,7 @@
pw.print(prefix); pw.print("maxUserDataSize: "); pw.println(getMaxUserDataSize());
pw.print(prefix); pw.print("maxFieldClassificationIdsSize: ");
pw.println(getMaxFieldClassificationIdsSize());
+ pw.print(prefix); pw.print("maxCategoryCount: "); pw.println(getMaxCategoryCount());
pw.print(prefix); pw.print("minValueLength: "); pw.println(getMinValueLength());
pw.print(prefix); pw.print("maxValueLength: "); pw.println(getMaxValueLength());
}
@@ -133,44 +137,59 @@
*/
public static final class Builder {
private final String mId;
- private final ArrayList<String> mRemoteIds;
+ private final ArrayList<String> mCategoryIds;
private final ArrayList<String> mValues;
private String mAlgorithm;
private Bundle mAlgorithmArgs;
private boolean mDestroyed;
+ // Non-persistent array used to limit the number of unique ids.
+ private final ArraySet<String> mUniqueCategoryIds;
+
/**
* Creates a new builder for the user data used for <a href="#FieldClassification">field
* classification</a>.
*
- * <p>The user data must contain at least one pair of {@code remoteId} -> {@code value}, and
- * more pairs can be added through the {@link #add(String, String)} method.
+ * <p>The user data must contain at least one pair of {@code value} -> {@code categoryId},
+ * and more pairs can be added through the {@link #add(String, String)} method. For example:
+ *
+ * <pre class="prettyprint">
+ * new UserData.Builder("v1", "Bart Simpson", "name")
+ * .add("bart.simpson@example.com", "email")
+ * .add("el_barto@example.com", "email")
+ * .build();
+ * </pre>
*
* @param id id used to identify the whole {@link UserData} object. This id is also returned
* by {@link AutofillManager#getUserDataId()}, which can be used to check if the
* {@link UserData} is up-to-date without fetching the whole object (through
* {@link AutofillManager#getUserData()}).
- * @param remoteId unique string used to identify a user data value.
+ *
* @param value value of the user data.
+ * @param categoryId string used to identify the category the value is associated with.
*
* @throws IllegalArgumentException if any of the following occurs:
* <ol>
- * <li>{@code id} is empty
- * <li>{@code remoteId} is empty
- * <li>{@code value} is empty
- * <li>the length of {@code value} is lower than {@link UserData#getMinValueLength()}
- * <li>the length of {@code value} is higher than {@link UserData#getMaxValueLength()}
+ * <li>{@code id} is empty</li>
+ * <li>{@code categoryId} is empty</li>
+ * <li>{@code value} is empty</li>
+ * <li>the length of {@code value} is lower than {@link UserData#getMinValueLength()}</li>
+ * <li>the length of {@code value} is higher than
+ * {@link UserData#getMaxValueLength()}</li>
* </ol>
+ *
*/
- public Builder(@NonNull String id, @NonNull String remoteId, @NonNull String value) {
+ // TODO(b/70407264): ignore entry instead of throwing exception when settings changed
+ public Builder(@NonNull String id, @NonNull String value, @NonNull String categoryId) {
mId = checkNotEmpty("id", id);
- checkNotEmpty("remoteId", remoteId);
+ checkNotEmpty("categoryId", categoryId);
checkValidValue(value);
- final int capacity = getMaxUserDataSize();
- mRemoteIds = new ArrayList<>(capacity);
- mValues = new ArrayList<>(capacity);
- mRemoteIds.add(remoteId);
- mValues.add(value);
+ final int maxUserDataSize = getMaxUserDataSize();
+ mCategoryIds = new ArrayList<>(maxUserDataSize);
+ mValues = new ArrayList<>(maxUserDataSize);
+ mUniqueCategoryIds = new ArraySet<>(getMaxCategoryCount());
+
+ addMapping(value, categoryId);
}
/**
@@ -190,6 +209,7 @@
*/
public Builder setFieldClassificationAlgorithm(@Nullable String name,
@Nullable Bundle args) {
+ throwIfDestroyed();
mAlgorithm = name;
mAlgorithmArgs = args;
return this;
@@ -198,37 +218,58 @@
/**
* Adds a new value for user data.
*
- * @param remoteId unique string used to identify the user data.
* @param value value of the user data.
+ * @param categoryId string used to identify the category the value is associated with.
*
- * @throws IllegalStateException if {@link #build()} or
- * {@link #add(String, String)} with the same {@code remoteId} has already
- * been called, or if the number of values add (i.e., calls made to this method plus
- * constructor) is more than {@link UserData#getMaxUserDataSize()}.
+ * @throws IllegalStateException if:
+ * <ol>
+ * <li>{@link #build()} already called</li>
+ * <li>the {@code value} has already been added</li>
+ * <li>the number of unique {@code categoryId} values added so far is more than
+ * {@link UserData#getMaxCategoryCount()}</li>
+ * <li>the number of {@code values} added so far is is more than
+ * {@link UserData#getMaxUserDataSize()}</li>
+ * </ol>
*
- * @throws IllegalArgumentException if {@code remoteId} or {@code value} are empty or if the
- * length of {@code value} is lower than {@link UserData#getMinValueLength()}
- * or higher than {@link UserData#getMaxValueLength()}.
+ * @throws IllegalArgumentException if any of the following occurs:
+ * <ol>
+ * <li>{@code id} is empty</li>
+ * <li>{@code categoryId} is empty</li>
+ * <li>{@code value} is empty</li>
+ * <li>the length of {@code value} is lower than {@link UserData#getMinValueLength()}</li>
+ * <li>the length of {@code value} is higher than
+ * {@link UserData#getMaxValueLength()}</li>
+ * </ol>
*/
- public Builder add(@NonNull String remoteId, @NonNull String value) {
+ // TODO(b/70407264): ignore entry instead of throwing exception when settings changed
+ public Builder add(@NonNull String value, @NonNull String categoryId) {
throwIfDestroyed();
- checkNotEmpty("remoteId", remoteId);
+ checkNotEmpty("categoryId", categoryId);
checkValidValue(value);
- Preconditions.checkState(!mRemoteIds.contains(remoteId),
- // Don't include remoteId on message because it could contain PII
- "already has entry with same remoteId");
+ if (!mUniqueCategoryIds.contains(categoryId)) {
+ // New category - check size
+ Preconditions.checkState(mUniqueCategoryIds.size() < getMaxCategoryCount(),
+ "already added " + mUniqueCategoryIds.size() + " unique category ids");
+
+ }
+
Preconditions.checkState(!mValues.contains(value),
- // Don't include remoteId on message because it could contain PII
+ // Don't include value on message because it could contain PII
"already has entry with same value");
- Preconditions.checkState(mRemoteIds.size() < getMaxUserDataSize(),
- "already added " + mRemoteIds.size() + " elements");
- mRemoteIds.add(remoteId);
- mValues.add(value);
+ Preconditions.checkState(mValues.size() < getMaxUserDataSize(),
+ "already added " + mValues.size() + " elements");
+ addMapping(value, categoryId);
return this;
}
+ private void addMapping(@NonNull String value, @NonNull String categoryId) {
+ mCategoryIds.add(categoryId);
+ mValues.add(value);
+ mUniqueCategoryIds.add(categoryId);
+ }
+
private String checkNotEmpty(@NonNull String name, @Nullable String value) {
Preconditions.checkNotNull(value);
Preconditions.checkArgument(!TextUtils.isEmpty(value), "%s cannot be empty", name);
@@ -273,9 +314,9 @@
final StringBuilder builder = new StringBuilder("UserData: [id=").append(mId)
.append(", algorithm=").append(mAlgorithm);
- // Cannot disclose remote ids or values because they could contain PII
- builder.append(", remoteIds=");
- Helper.appendRedacted(builder, mRemoteIds);
+ // Cannot disclose category ids or values because they could contain PII
+ builder.append(", categoryIds=");
+ Helper.appendRedacted(builder, mCategoryIds);
builder.append(", values=");
Helper.appendRedacted(builder, mValues);
return builder.append("]").toString();
@@ -293,7 +334,7 @@
@Override
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeString(mId);
- parcel.writeStringArray(mRemoteIds);
+ parcel.writeStringArray(mCategoryIds);
parcel.writeStringArray(mValues);
parcel.writeString(mAlgorithm);
parcel.writeBundle(mAlgorithmArgs);
@@ -307,12 +348,12 @@
// the system obeys the contract of the builder to avoid attacks
// using specially crafted parcels.
final String id = parcel.readString();
- final String[] remoteIds = parcel.readStringArray();
+ final String[] categoryIds = parcel.readStringArray();
final String[] values = parcel.readStringArray();
- final Builder builder = new Builder(id, remoteIds[0], values[0])
+ final Builder builder = new Builder(id, values[0], categoryIds[0])
.setFieldClassificationAlgorithm(parcel.readString(), parcel.readBundle());
- for (int i = 1; i < remoteIds.length; i++) {
- builder.add(remoteIds[i], values[i]);
+ for (int i = 1; i < categoryIds.length; i++) {
+ builder.add(values[i], categoryIds[i]);
}
return builder.build();
}
@@ -340,6 +381,14 @@
}
/**
+ * Gets the maximum number of unique category ids that can be passed to
+ * the builder's constructor and {@link Builder#add(String, String)}.
+ */
+ public static int getMaxCategoryCount() {
+ return getInt(AUTOFILL_USER_DATA_MAX_CATEGORY_COUNT, DEFAULT_MAX_CATEGORY_COUNT);
+ }
+
+ /**
* Gets the minimum length of values passed to the builder's constructor or
* or {@link Builder#add(String, String)}.
*/
diff --git a/core/java/android/text/BoringLayout.java b/core/java/android/text/BoringLayout.java
index dbe4157..6fa5312 100644
--- a/core/java/android/text/BoringLayout.java
+++ b/core/java/android/text/BoringLayout.java
@@ -322,12 +322,6 @@
*/
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 @@
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 10444f0..18431ca 100644
--- a/core/java/android/text/DynamicLayout.java
+++ b/core/java/android/text/DynamicLayout.java
@@ -363,7 +363,7 @@
@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 d5d3590..aa97b2a 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -245,13 +245,6 @@
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 @@
}
mText = text;
- mPrecomputed = precomputed;
mPaint = paint;
mWidth = width;
mAlignment = align;
@@ -570,7 +562,7 @@
// 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 @@
}
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 0000000..bb7a9e0
--- /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 39fc2bd..0000000
--- 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 5869802..e62f421 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -78,23 +78,6 @@
/**
* 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 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 @@
// set default initial values
b.mText = source;
- b.mPrecomputed = text;
b.mStart = start;
b.mEnd = end;
b.mPaint = paint;
@@ -452,7 +428,6 @@
}
private CharSequence mText;
- private PrecomputedText mPrecomputed;
private int mStart;
private int mEnd;
private TextPaint mPaint;
@@ -515,7 +490,7 @@
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 @@
* @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 @@
: (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 @@
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 be5bb4d..55367dc 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 @@
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 @@
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 @@
* @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 @@
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 @@
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/ExceptionUtils.java b/core/java/android/util/ExceptionUtils.java
index da7387f..1a397b3 100644
--- a/core/java/android/util/ExceptionUtils.java
+++ b/core/java/android/util/ExceptionUtils.java
@@ -86,4 +86,16 @@
while (t.getCause() != null) t = t.getCause();
return t;
}
-}
+
+ /**
+ * Appends {@code cause} at the end of the causal chain of {@code t}
+ *
+ * @return {@code t} for convenience
+ */
+ public static @NonNull Throwable appendCause(@NonNull Throwable t, @Nullable Throwable cause) {
+ if (cause != null) {
+ getRootCause(t).initCause(cause);
+ }
+ return t;
+ }
+}
\ No newline at end of file
diff --git a/core/java/android/util/OWNERS b/core/java/android/util/OWNERS
new file mode 100644
index 0000000..86ed122
--- /dev/null
+++ b/core/java/android/util/OWNERS
@@ -0,0 +1,2 @@
+per-file FeatureFlagUtils.java = sbasi@google.com
+per-file FeatureFlagUtils.java = zhfan@google.com
diff --git a/core/java/android/view/RecordingCanvas.java b/core/java/android/view/RecordingCanvas.java
index fc7d828..fbb862b 100644
--- a/core/java/android/view/RecordingCanvas.java
+++ b/core/java/android/view/RecordingCanvas.java
@@ -34,7 +34,7 @@
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 @@
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 @@
@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 c4a7160..d26a2f6 100644
--- a/core/java/android/view/RenderNodeAnimator.java
+++ b/core/java/android/view/RenderNodeAnimator.java
@@ -158,7 +158,7 @@
}
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/View.java b/core/java/android/view/View.java
index 2af2467..2d85a58 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -7272,7 +7272,7 @@
// becomes true where it should issue notifyViewEntered().
afm.notifyViewEntered(this);
}
- } else if (!isFocused()) {
+ } else if (!enter && !isFocused()) {
afm.notifyViewExited(this);
}
}
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 41d05a5..cf81b97 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -356,6 +356,13 @@
@GuardedBy("mLock")
@Nullable private ArraySet<AutofillId> mFillableIds;
+ /**
+ * Views that were already "entered" - if they're entered again when the session is not active,
+ * they're ignored
+ * */
+ @GuardedBy("mLock")
+ @Nullable private ArraySet<AutofillId> mEnteredIds;
+
/** If set, session is commited when the field is clicked. */
@GuardedBy("mLock")
@Nullable private AutofillId mSaveTriggerId;
@@ -711,17 +718,29 @@
}
@GuardedBy("mLock")
- private boolean shouldIgnoreViewEnteredLocked(@NonNull View view, int flags) {
+ private boolean shouldIgnoreViewEnteredLocked(@NonNull AutofillId id, int flags) {
if (isDisabledByServiceLocked()) {
if (sVerbose) {
- Log.v(TAG, "ignoring notifyViewEntered(flags=" + flags + ", view=" + view
- + ") on state " + getStateAsStringLocked());
+ Log.v(TAG, "ignoring notifyViewEntered(flags=" + flags + ", view=" + id
+ + ") on state " + getStateAsStringLocked() + " because disabled by svc");
}
return true;
}
- if (sVerbose && isFinishedLocked()) {
- Log.v(TAG, "not ignoring notifyViewEntered(flags=" + flags + ", view=" + view
- + ") on state " + getStateAsStringLocked());
+ if (isFinishedLocked()) {
+ // Session already finished: ignore if automatic request and view already entered
+ if ((flags & FLAG_MANUAL_REQUEST) == 0 && mEnteredIds != null
+ && mEnteredIds.contains(id)) {
+ if (sVerbose) {
+ Log.v(TAG, "ignoring notifyViewEntered(flags=" + flags + ", view=" + id
+ + ") on state " + getStateAsStringLocked()
+ + " because view was already entered: " + mEnteredIds);
+ }
+ return true;
+ }
+ }
+ if (sVerbose) {
+ Log.v(TAG, "not ignoring notifyViewEntered(flags=" + flags + ", view=" + id
+ + ", state " + getStateAsStringLocked() + ", enteredIds=" + mEnteredIds);
}
return false;
}
@@ -753,7 +772,8 @@
/** Returns AutofillCallback if need fire EVENT_INPUT_UNAVAILABLE */
@GuardedBy("mLock")
private AutofillCallback notifyViewEnteredLocked(@NonNull View view, int flags) {
- if (shouldIgnoreViewEnteredLocked(view, flags)) return null;
+ final AutofillId id = getAutofillId(view);
+ if (shouldIgnoreViewEnteredLocked(id, flags)) return null;
AutofillCallback callback = null;
@@ -766,7 +786,6 @@
} else {
// don't notify entered when Activity is already in background
if (!isClientDisablingEnterExitEvent()) {
- final AutofillId id = getAutofillId(view);
final AutofillValue value = view.getAutofillValue();
if (!isActiveLocked()) {
@@ -776,6 +795,7 @@
// Update focus on existing session.
updateSessionLocked(id, null, value, ACTION_VIEW_ENTERED, flags);
}
+ addEnteredIdLocked(id);
}
}
return callback;
@@ -900,8 +920,9 @@
@GuardedBy("mLock")
private AutofillCallback notifyViewEnteredLocked(View view, int virtualId, Rect bounds,
int flags) {
+ final AutofillId id = getAutofillId(view, virtualId);
AutofillCallback callback = null;
- if (shouldIgnoreViewEnteredLocked(view, flags)) return callback;
+ if (shouldIgnoreViewEnteredLocked(id, flags)) return callback;
ensureServiceClientAddedIfNeededLocked();
@@ -912,8 +933,6 @@
} else {
// don't notify entered when Activity is already in background
if (!isClientDisablingEnterExitEvent()) {
- final AutofillId id = getAutofillId(view, virtualId);
-
if (!isActiveLocked()) {
// Starts new session.
startSessionLocked(id, bounds, null, flags);
@@ -921,11 +940,20 @@
// Update focus on existing session.
updateSessionLocked(id, bounds, null, ACTION_VIEW_ENTERED, flags);
}
+ addEnteredIdLocked(id);
}
}
return callback;
}
+ @GuardedBy("mLock")
+ private void addEnteredIdLocked(@NonNull AutofillId id) {
+ if (mEnteredIds == null) {
+ mEnteredIds = new ArraySet<>(1);
+ }
+ mEnteredIds.add(id);
+ }
+
/**
* Called when a virtual view that supports autofill is exited.
*
@@ -992,9 +1020,9 @@
}
if (!mEnabled || !isActiveLocked()) {
- if (sVerbose && mEnabled) {
- Log.v(TAG, "notifyValueChanged(" + view + "): ignoring on state "
- + getStateAsStringLocked());
+ if (sVerbose) {
+ Log.v(TAG, "notifyValueChanged(" + view.getAutofillId()
+ + "): ignoring on state " + getStateAsStringLocked());
}
return;
}
@@ -1024,6 +1052,10 @@
}
synchronized (mLock) {
if (!mEnabled || !isActiveLocked()) {
+ if (sVerbose) {
+ Log.v(TAG, "notifyValueChanged(" + view.getAutofillId() + ":" + virtualId
+ + "): ignoring on state " + getStateAsStringLocked());
+ }
return;
}
@@ -1032,7 +1064,6 @@
}
}
-
/**
* Called when a {@link View} is clicked. Currently only used by views that should trigger save.
*
@@ -1392,7 +1423,8 @@
if (sVerbose) {
Log.v(TAG, "startSessionLocked(): id=" + id + ", bounds=" + bounds + ", value=" + value
+ ", flags=" + flags + ", state=" + getStateAsStringLocked()
- + ", compatMode=" + isCompatibilityModeEnabledLocked());
+ + ", compatMode=" + isCompatibilityModeEnabledLocked()
+ + ", enteredIds=" + mEnteredIds);
}
if (mState != STATE_UNKNOWN && !isFinishedLocked() && (flags & FLAG_MANUAL_REQUEST) == 0) {
if (sVerbose) {
@@ -1430,7 +1462,7 @@
throw e.rethrowFromSystemServer();
}
- resetSessionLocked();
+ resetSessionLocked(/* resetEnteredIds= */ true);
}
@GuardedBy("mLock")
@@ -1445,22 +1477,25 @@
throw e.rethrowFromSystemServer();
}
- resetSessionLocked();
+ resetSessionLocked(/* resetEnteredIds= */ true);
}
@GuardedBy("mLock")
- private void resetSessionLocked() {
+ private void resetSessionLocked(boolean resetEnteredIds) {
mSessionId = NO_SESSION;
mState = STATE_UNKNOWN;
mTrackedViews = null;
mFillableIds = null;
mSaveTriggerId = null;
+ if (resetEnteredIds) {
+ mEnteredIds = null;
+ }
}
@GuardedBy("mLock")
private void updateSessionLocked(AutofillId id, Rect bounds, AutofillValue value, int action,
int flags) {
- if (sVerbose && action != ACTION_VIEW_EXITED) {
+ if (sVerbose) {
Log.v(TAG, "updateSessionLocked(): id=" + id + ", bounds=" + bounds
+ ", value=" + value + ", action=" + action + ", flags=" + flags);
}
@@ -1630,7 +1665,7 @@
mEnabled = (flags & SET_STATE_FLAG_ENABLED) != 0;
if (!mEnabled || (flags & SET_STATE_FLAG_RESET_SESSION) != 0) {
// Reset the session state
- resetSessionLocked();
+ resetSessionLocked(/* resetEnteredIds= */ true);
}
if ((flags & SET_STATE_FLAG_RESET_CLIENT) != 0) {
// Reset connection to system
@@ -1826,7 +1861,7 @@
private void setSessionFinished(int newState) {
synchronized (mLock) {
if (sVerbose) Log.v(TAG, "setSessionFinished(): from " + mState + " to " + newState);
- resetSessionLocked();
+ resetSessionLocked(/* resetEnteredIds= */ false);
mState = newState;
}
}
@@ -1954,6 +1989,7 @@
pw.print(pfx2); pw.print("invisible:"); pw.println(mTrackedViews.mInvisibleTrackedIds);
}
pw.print(pfx); pw.print("fillable ids: "); pw.println(mFillableIds);
+ pw.print(pfx); pw.print("entered ids: "); pw.println(mEnteredIds);
pw.print(pfx); pw.print("save trigger id: "); pw.println(mSaveTriggerId);
pw.print(pfx); pw.print("save on finish(): "); pw.println(mSaveOnFinish);
pw.print(pfx); pw.print("compat mode enabled: "); pw.println(
diff --git a/core/java/android/view/autofill/AutofillPopupWindow.java b/core/java/android/view/autofill/AutofillPopupWindow.java
index e80fdd9..1da998d 100644
--- a/core/java/android/view/autofill/AutofillPopupWindow.java
+++ b/core/java/android/view/autofill/AutofillPopupWindow.java
@@ -46,6 +46,7 @@
private final WindowPresenter mWindowPresenter;
private WindowManager.LayoutParams mWindowLayoutParams;
+ private boolean mFullScreen;
private final View.OnAttachStateChangeListener mOnAttachStateChangeListener =
new View.OnAttachStateChangeListener() {
@@ -104,12 +105,17 @@
*/
public void update(View anchor, int offsetX, int offsetY, int width, int height,
Rect virtualBounds) {
+ mFullScreen = width == LayoutParams.MATCH_PARENT && height == LayoutParams.MATCH_PARENT;
// If we are showing the popup for a virtual view we use a fake view which
// delegates to the anchor but present itself with the same bounds as the
// virtual view. This ensures that the location logic in popup works
// symmetrically when the dropdown is below and above the anchor.
final View actualAnchor;
- if (virtualBounds != null) {
+ if (mFullScreen) {
+ offsetX = 0;
+ offsetY = 0;
+ actualAnchor = anchor;
+ } else if (virtualBounds != null) {
final int[] mLocationOnScreen = new int[] {virtualBounds.left, virtualBounds.top};
actualAnchor = new View(anchor.getContext()) {
@Override
@@ -209,6 +215,17 @@
}
@Override
+ protected boolean findDropDownPosition(View anchor, LayoutParams outParams,
+ int xOffset, int yOffset, int width, int height, int gravity, boolean allowScroll) {
+ if (mFullScreen) {
+ // Do not patch LayoutParams if force full screen
+ return false;
+ }
+ return super.findDropDownPosition(anchor, outParams, xOffset, yOffset,
+ width, height, gravity, allowScroll);
+ }
+
+ @Override
public void showAsDropDown(View anchor, int xoff, int yoff, int gravity) {
if (sVerbose) {
Log.v(TAG, "showAsDropDown(): anchor=" + anchor + ", xoff=" + xoff + ", yoff=" + yoff
diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java
index e91db13..7217def 100644
--- a/core/java/android/widget/PopupWindow.java
+++ b/core/java/android/widget/PopupWindow.java
@@ -1583,7 +1583,7 @@
*
* @hide
*/
- protected final boolean findDropDownPosition(View anchor, WindowManager.LayoutParams outParams,
+ protected boolean findDropDownPosition(View anchor, WindowManager.LayoutParams outParams,
int xOffset, int yOffset, int width, int height, int gravity, boolean allowScroll) {
final int anchorHeight = anchor.getHeight();
final int anchorWidth = anchor.getWidth();
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index f6e771a..5710db3 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -80,8 +80,8 @@
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 @@
private CharSequence mText;
private CharSequence mTransformed;
private BufferType mBufferType = BufferType.NORMAL;
- private PrecomputedText mPrecomputed;
private CharSequence mHint;
private Layout mHintLayout;
@@ -4086,80 +4085,6 @@
}
/**
- * 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 @@
private void setText(CharSequence text, BufferType type,
boolean notifyBefore, int oldlen) {
- mPrecomputed = null;
mTextSetFromXmlOrResourceId = false;
if (text == null) {
text = "";
@@ -5653,7 +5577,7 @@
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 @@
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 @@
}
}
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 @@
}
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 @@
}
/**
- * Returns the current {@link TextDirectionHeuristic}
- *
- * @return A {@link TextDirectionHeuristic}.
* @hide
*/
protected TextDirectionHeuristic getTextDirectionHeuristic() {
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 6a56f45..4c5991e 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -5286,6 +5286,18 @@
case TelephonyManager.NETWORK_TYPE_HSPAP:
bin = DATA_CONNECTION_HSPAP;
break;
+ case TelephonyManager.NETWORK_TYPE_GSM:
+ bin = DATA_CONNECTION_GSM;
+ break;
+ case TelephonyManager.NETWORK_TYPE_TD_SCDMA:
+ bin = DATA_CONNECTION_TD_SCDMA;
+ break;
+ case TelephonyManager.NETWORK_TYPE_IWLAN:
+ bin = DATA_CONNECTION_IWLAN;
+ break;
+ case TelephonyManager.NETWORK_TYPE_LTE_CA:
+ bin = DATA_CONNECTION_LTE_CA;
+ break;
default:
bin = DATA_CONNECTION_OTHER;
break;
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 9c89976..30d81a7 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -576,7 +576,8 @@
installd.dexopt(classPathElement, Process.SYSTEM_UID, packageName,
instructionSet, dexoptNeeded, outputPath, dexFlags, compilerFilter,
uuid, classLoaderContext, seInfo, false /* downgrade */,
- targetSdkVersion, /*profileName*/ null, /*dexMetadataPath*/ null);
+ targetSdkVersion, /*profileName*/ null, /*dexMetadataPath*/ null,
+ "server-dexopt");
} catch (RemoteException | ServiceSpecificException e) {
// Ignore (but log), we need this on the classpath for fallback mode.
Log.w(TAG, "Failed compiling classpath element for system server: "
diff --git a/core/java/com/android/internal/print/DumpUtils.java b/core/java/com/android/internal/print/DumpUtils.java
index 1916c11..f44a1d1 100644
--- a/core/java/com/android/internal/print/DumpUtils.java
+++ b/core/java/com/android/internal/print/DumpUtils.java
@@ -213,10 +213,9 @@
PrintAttributes.MediaSize mediaSize = attributes.getMediaSize();
if (mediaSize != null) {
writeMediaSize(context, proto, "media_size", PrintAttributesProto.MEDIA_SIZE, mediaSize);
+ proto.write("is_portrait", PrintAttributesProto.IS_PORTRAIT, attributes.isPortrait());
}
- proto.write("is_portrait", PrintAttributesProto.IS_PORTRAIT, attributes.isPortrait());
-
PrintAttributes.Resolution res = attributes.getResolution();
if (res != null) {
writeResolution(proto, "resolution", PrintAttributesProto.RESOLUTION, res);
diff --git a/core/java/com/android/internal/util/OWNERS b/core/java/com/android/internal/util/OWNERS
new file mode 100644
index 0000000..21d750c
--- /dev/null
+++ b/core/java/com/android/internal/util/OWNERS
@@ -0,0 +1,24 @@
+per-file AsyncChannel*=lorenzo@google.com
+per-file AsyncChannel*=satk@google.com
+per-file AsyncChannel*=silberst@google.com
+per-file BitUtils*=ek@google.com
+per-file BitUtils*=lorenzo@google.com
+per-file BitUtils*=satk@google.com
+per-file MessageUtils*=ek@google.com
+per-file MessageUtils*=lorenzo@google.com
+per-file MessageUtils*=satk@google.com
+per-file Protocol*=ek@google.com
+per-file Protocol*=lorenzo@google.com
+per-file Protocol*=quiche@google.com
+per-file Protocol*=satk@google.com
+per-file Protocol*=silberst@google.com
+per-file RingBuffer*=ek@google.com
+per-file RingBuffer*=lorenzo@google.com
+per-file RingBuffer*=satk@google.com
+per-file State*=ek@google.com
+per-file State*=lorenzo@google.com
+per-file State*=quiche@google.com
+per-file State*=silberst@google.com
+per-file TokenBucket*=ek@google.com
+per-file TokenBucket*=lorenzo@google.com
+per-file TokenBucket*=satk@google.com
diff --git a/core/jni/OWNERS b/core/jni/OWNERS
new file mode 100644
index 0000000..ce79049
--- /dev/null
+++ b/core/jni/OWNERS
@@ -0,0 +1,12 @@
+# 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
+
+# Connectivity
+per-file android_net_*=ek@google.com
+per-file android_net_*=lorenzo@google.com
+per-file android_net_*=satk@google.com
diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp
index 2c05d0b..482d028 100644
--- a/core/jni/android/graphics/Paint.cpp
+++ b/core/jni/android/graphics/Paint.cpp
@@ -1008,23 +1008,6 @@
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 @@
{"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/proto/android/net/OWNERS b/core/proto/android/net/OWNERS
new file mode 100644
index 0000000..a845dcb
--- /dev/null
+++ b/core/proto/android/net/OWNERS
@@ -0,0 +1,5 @@
+set noparent
+
+ek@google.com
+lorenzo@google.com
+satk@google.com
diff --git a/core/proto/android/os/cpufreq.proto b/core/proto/android/os/cpufreq.proto
index 8481ffc..46f4901 100644
--- a/core/proto/android/os/cpufreq.proto
+++ b/core/proto/android/os/cpufreq.proto
@@ -16,32 +16,30 @@
syntax = "proto2";
option java_multiple_files = true;
-option java_outer_classname = "CpuFreqProto";
import "frameworks/base/libs/incident/proto/android/privacy.proto";
package android.os;
// cpu frequency time from /sys/devices/system/cpu/cpufreq/all_time_in_state
-message CpuFreq {
+message CpuFreqProto {
option (android.msg_privacy).dest = DEST_AUTOMATIC;
optional int32 jiffy_hz = 1; // obtain by system config.
- repeated CpuFreqStats cpu_freqs = 2;
-}
-
-// frequency time pre cpu, unit in jiffy, TODO: obtain jiffies.
-message CpuFreqStats {
- option (android.msg_privacy).dest = DEST_AUTOMATIC;
-
- optional string cpu_name = 1;
-
- message TimeInState {
+ // frequency time pre cpu, unit in jiffy.
+ message Stats {
option (android.msg_privacy).dest = DEST_AUTOMATIC;
- optional int32 state_khz = 1; // cpu frequency
- optional int64 time_jiffy = 2; // number of jiffies
+ optional string cpu_name = 1;
+
+ message TimeInState {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ optional int32 state_khz = 1; // cpu frequency
+ optional int64 time_jiffy = 2; // number of jiffies
+ }
+ repeated TimeInState times = 2;
}
- repeated TimeInState times = 2;
+ repeated Stats cpu_freqs = 2;
}
diff --git a/core/proto/android/os/cpuinfo.proto b/core/proto/android/os/cpuinfo.proto
index ca43602..ce69fc9 100644
--- a/core/proto/android/os/cpuinfo.proto
+++ b/core/proto/android/os/cpuinfo.proto
@@ -16,7 +16,6 @@
syntax = "proto2";
option java_multiple_files = true;
-option java_outer_classname = "CpuInfoProto";
import "frameworks/base/libs/incident/proto/android/privacy.proto";
@@ -28,7 +27,7 @@
*
* Next Tag: 6
*/
-message CpuInfo {
+message CpuInfoProto {
option (android.msg_privacy).dest = DEST_AUTOMATIC;
message TaskStats {
diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto
index 698f394..9a53b89 100644
--- a/core/proto/android/os/incident.proto
+++ b/core/proto/android/os/incident.proto
@@ -46,18 +46,12 @@
import "frameworks/base/core/proto/android/util/event_log_tags.proto";
import "frameworks/base/core/proto/android/util/log.proto";
import "frameworks/base/libs/incident/proto/android/os/header.proto";
+import "frameworks/base/libs/incident/proto/android/os/metadata.proto";
import "frameworks/base/libs/incident/proto/android/privacy.proto";
import "frameworks/base/libs/incident/proto/android/section.proto";
package android.os;
-// This field contains internal metadata associated with an incident report,
-// such as the section ids and privacy policy specs from caller as well as how long
-// and how many bytes a section takes, etc.
-message IncidentMetadata {
-
-}
-
// privacy field options must not be set at this level because all
// the sections are able to be controlled and configured by section ids.
// Instead privacy field options need to be configured in each section proto message.
@@ -122,32 +116,32 @@
];
// Linux services
- optional Procrank procrank = 2000 [
+ optional ProcrankProto procrank = 2000 [
(section).type = SECTION_NONE, // disable procrank until figure out permission
(section).args = "/system/xbin/procrank"
];
- optional PageTypeInfo page_type_info = 2001 [
+ optional PageTypeInfoProto page_type_info = 2001 [
(section).type = SECTION_FILE,
(section).args = "/proc/pagetypeinfo"
];
- optional KernelWakeSources kernel_wake_sources = 2002 [
+ optional KernelWakeSourcesProto kernel_wake_sources = 2002 [
(section).type = SECTION_FILE,
(section).args = "/d/wakeup_sources"
];
- optional CpuInfo cpu_info = 2003 [
+ optional CpuInfoProto cpu_info = 2003 [
(section).type = SECTION_COMMAND,
(section).args = "top -b -n 1 -H -s 6 -o pid,tid,user,pr,ni,%cpu,s,virt,res,pcy,cmd,name"
];
- optional CpuFreq cpu_freq = 2004 [
+ optional CpuFreqProto cpu_freq = 2004 [
(section).type = SECTION_FILE,
(section).args = "/sys/devices/system/cpu/cpufreq/all_time_in_state"
];
- optional PsDumpProto processes_and_threads = 2005 [
+ optional PsProto processes_and_threads = 2005 [
(section).type = SECTION_COMMAND,
(section).args = "ps -A -T -Z -O pri,nice,rtprio,sched,pcy,time"
];
diff --git a/core/proto/android/os/kernelwake.proto b/core/proto/android/os/kernelwake.proto
index c296dab..5021a06 100644
--- a/core/proto/android/os/kernelwake.proto
+++ b/core/proto/android/os/kernelwake.proto
@@ -16,41 +16,40 @@
syntax = "proto2";
option java_multiple_files = true;
-option java_outer_classname = "WakeupSourcesProto";
import "frameworks/base/libs/incident/proto/android/privacy.proto";
package android.os;
-message KernelWakeSources {
+message KernelWakeSourcesProto {
option (android.msg_privacy).dest = DEST_AUTOMATIC;
// Kernel records of what caused the application processor to wake up
- repeated WakeupSourceProto wakeup_sources = 1;
-}
+ message WakeupSource {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
-// Next Tag: 11
-message WakeupSourceProto {
- option (android.msg_privacy).dest = DEST_AUTOMATIC;
+ // Name of the event which triggers application processor
+ optional string name = 1;
- // Name of the event which triggers application processor
- optional string name = 1;
+ optional int32 active_count = 2;
- optional int32 active_count = 2;
+ optional int32 event_count = 3;
- optional int32 event_count = 3;
+ optional int32 wakeup_count = 4;
- optional int32 wakeup_count = 4;
+ optional int32 expire_count = 5;
- optional int32 expire_count = 5;
+ optional int64 active_since = 6;
- optional int64 active_since = 6;
+ optional int64 total_time = 7;
- optional int64 total_time = 7;
+ optional int64 max_time = 8;
- optional int64 max_time = 8;
+ optional int64 last_change = 9;
- optional int64 last_change = 9;
+ optional int64 prevent_suspend_time = 10;
- optional int64 prevent_suspend_time = 10;
+ // Next Tag: 11
+ }
+ repeated WakeupSource wakeup_sources = 1;
}
diff --git a/core/proto/android/os/pagetypeinfo.proto b/core/proto/android/os/pagetypeinfo.proto
index b8f618b..f5d77d6 100644
--- a/core/proto/android/os/pagetypeinfo.proto
+++ b/core/proto/android/os/pagetypeinfo.proto
@@ -16,7 +16,6 @@
syntax = "proto2";
option java_multiple_files = true;
-option java_outer_classname = "PageTypeInfoProto";
import "frameworks/base/libs/incident/proto/android/privacy.proto";
@@ -37,49 +36,47 @@
*
* Next tag: 5
*/
-message PageTypeInfo {
+message PageTypeInfoProto {
option (android.msg_privacy).dest = DEST_AUTOMATIC;
optional int32 page_block_order = 1;
optional int32 pages_per_block = 2;
- repeated MigrateTypeProto migrate_types = 3;
+ // Next tag: 5
+ message MigrateType {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
- repeated BlockProto blocks = 4;
-}
+ optional int32 node = 1;
-// Next tag: 5
-message MigrateTypeProto {
- option (android.msg_privacy).dest = DEST_AUTOMATIC;
+ optional string zone = 2;
- optional int32 node = 1;
+ optional string type = 3;
- optional string zone = 2;
+ // order level starts from 0 for 4KB to page_block_order defined above, e.g. 10 for 4096KB
+ repeated int32 free_pages_count = 4;
+ }
+ repeated MigrateType migrate_types = 3;
- optional string type = 3;
+ // Next tag: 9
+ message Block {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
- // order level starts from 0 for 4KB to page_block_order defined above, e.g. 10 for 4096KB
- repeated int32 free_pages_count = 4;
-}
+ optional int32 node = 1;
-// Next tag: 9
-message BlockProto {
- option (android.msg_privacy).dest = DEST_AUTOMATIC;
+ optional string zone = 2;
- optional int32 node = 1;
+ optional int32 unmovable = 3;
- optional string zone = 2;
+ optional int32 reclaimable = 4;
- optional int32 unmovable = 3;
+ optional int32 movable = 5;
- optional int32 reclaimable = 4;
+ optional int32 cma = 6;
- optional int32 movable = 5;
+ optional int32 reserve = 7;
- optional int32 cma = 6;
-
- optional int32 reserve = 7;
-
- optional int32 isolate = 8;
+ optional int32 isolate = 8;
+ }
+ repeated Block blocks = 4;
}
diff --git a/core/proto/android/os/procrank.proto b/core/proto/android/os/procrank.proto
index 204a5af..ff7515e 100644
--- a/core/proto/android/os/procrank.proto
+++ b/core/proto/android/os/procrank.proto
@@ -16,78 +16,73 @@
syntax = "proto2";
option java_multiple_files = true;
-option java_outer_classname = "ProcrankProto";
import "frameworks/base/libs/incident/proto/android/privacy.proto";
package android.os;
//Memory usage of running processes
-message Procrank {
+message ProcrankProto {
option (android.msg_privacy).dest = DEST_AUTOMATIC;
// Currently running process
- repeated ProcessProto processes = 1;
+ // Next Tag: 11
+ message Process {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ // ID of the process
+ optional int32 pid = 1;
+
+ // virtual set size, unit KB
+ optional int64 vss = 2;
+
+ // resident set size, unit KB
+ optional int64 rss = 3;
+
+ // proportional set size, unit KB
+ optional int64 pss = 4;
+
+ // unique set size, unit KB
+ optional int64 uss = 5;
+
+ // swap size, unit KB
+ optional int64 swap = 6;
+
+ // proportional swap size, unit KB
+ optional int64 pswap = 7;
+
+ // unique swap size, unit KB
+ optional int64 uswap = 8;
+
+ // zswap size, unit KB
+ optional int64 zswap = 9;
+
+ // process command
+ optional string cmdline = 10;
+ }
+ repeated Process processes = 1;
// Summary
- optional SummaryProto summary = 2;
-}
+ // Next Tag: 3
+ message Summary {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
-// Next Tag: 11
-message ProcessProto {
- option (android.msg_privacy).dest = DEST_AUTOMATIC;
+ optional Process total = 1;
- // ID of the process
- optional int32 pid = 1;
+ // TODO: sync on how to use these values
+ message Zram {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
- // virtual set size, unit KB
- optional int64 vss = 2;
+ optional string raw_text = 1;
+ }
+ optional Zram zram = 2;
- // resident set size, unit KB
- optional int64 rss = 3;
+ message Ram {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
- // proportional set size, unit KB
- optional int64 pss = 4;
-
- // unique set size, unit KB
- optional int64 uss = 5;
-
- // swap size, unit KB
- optional int64 swap = 6;
-
- // proportional swap size, unit KB
- optional int64 pswap = 7;
-
- // unique swap size, unit KB
- optional int64 uswap = 8;
-
- // zswap size, unit KB
- optional int64 zswap = 9;
-
- // process command
- optional string cmdline = 10;
-}
-
-// Next Tag: 3
-message SummaryProto {
- option (android.msg_privacy).dest = DEST_AUTOMATIC;
-
- optional ProcessProto total = 1;
-
- optional ZramProto zram = 2;
-
- optional RamProto ram = 3;
-}
-
-// TODO: sync on how to use these values
-message ZramProto {
- option (android.msg_privacy).dest = DEST_AUTOMATIC;
-
- optional string raw_text = 1;
-}
-
-message RamProto {
- option (android.msg_privacy).dest = DEST_AUTOMATIC;
-
- optional string raw_text = 1;
+ optional string raw_text = 1;
+ }
+ optional Ram ram = 3;
+ }
+ optional Summary summary = 2;
}
diff --git a/core/proto/android/os/ps.proto b/core/proto/android/os/ps.proto
index 9cce727..0ab92d7 100644
--- a/core/proto/android/os/ps.proto
+++ b/core/proto/android/os/ps.proto
@@ -22,7 +22,7 @@
import "frameworks/base/libs/incident/proto/android/privacy.proto";
-message PsDumpProto {
+message PsProto {
option (android.msg_privacy).dest = DEST_AUTOMATIC;
message Process {
diff --git a/core/proto/android/providers/settings.proto b/core/proto/android/providers/settings.proto
index 02fc4da..6b92aa8 100644
--- a/core/proto/android/providers/settings.proto
+++ b/core/proto/android/providers/settings.proto
@@ -422,6 +422,8 @@
optional SettingProto enable_deletion_helper_no_threshold_toggle = 340 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto notification_snooze_options = 341 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto enable_gnss_raw_meas_full_tracking = 346 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ optional SettingProto install_carrier_app_notification_persistent = 356 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ optional SettingProto install_carrier_app_notification_sleep_millis = 357 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto zram_enabled = 347 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto smart_replies_in_notifications_flags = 348 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto show_first_crash_dialog = 349 [ (android.privacy).dest = DEST_AUTOMATIC ];
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 6f3c25f..3a527b5 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -174,6 +174,10 @@
<protected-broadcast
android:name="android.bluetooth.headsetclient.profile.action.LAST_VTAG" />
<protected-broadcast
+ android:name="android.bluetooth.hearingaid.profile.action.CONNECTION_STATE_CHANGED" />
+ <protected-broadcast
+ android:name="android.bluetooth.hearingaid.profile.action.PLAYING_STATE_CHANGED" />
+ <protected-broadcast
android:name="android.bluetooth.a2dp.profile.action.CONNECTION_STATE_CHANGED" />
<protected-broadcast
android:name="android.bluetooth.a2dp.profile.action.ACTIVE_DEVICE_CHANGED" />
diff --git a/core/res/res/drawable/ic_signal_cellular_alt_24px.xml b/core/res/res/drawable/ic_signal_cellular_alt_24px.xml
new file mode 100644
index 0000000..29f1f43
--- /dev/null
+++ b/core/res/res/drawable/ic_signal_cellular_alt_24px.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="20dp"
+ android:height="21dp"
+ android:viewportWidth="20"
+ android:viewportHeight="21">
+
+ <group
+ android:translateX="-31.000000"
+ android:translateY="-77.000000">
+ <group
+ android:translateX="24.000000"
+ android:translateY="72.000000">
+ <path
+ android:fillType="evenOdd"
+ android:strokeWidth="1"
+ android:pathData="M 0 0 H 32 V 32 H 0 V 0 Z" />
+ <path
+ android:fillColor="#4285F4"
+ android:strokeWidth="1"
+ android:pathData="M23,5 L27,5 L27,26 L23,26 L23,5 Z M7,18.125 L11,18.125 L11,26 L7,26 L7,18.125 Z
+M15,11.5625 L19,11.5625 L19,26 L15,26 L15,11.5625 Z" />
+ </group>
+ </group>
+</vector>
\ No newline at end of file
diff --git a/core/res/res/layout/autofill_dataset_picker_fullscreen.xml b/core/res/res/layout/autofill_dataset_picker_fullscreen.xml
new file mode 100644
index 0000000..07298c1
--- /dev/null
+++ b/core/res/res/layout/autofill_dataset_picker_fullscreen.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/autofill_dataset_picker"
+ style="@style/AutofillDatasetPicker"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/autofill_window_title"
+ android:layout_above="@+id/autofill_dataset_container"
+ android:layout_alignStart="@+id/autofill_dataset_container"
+ android:textSize="16sp"/>
+
+ <!-- autofill_container is the common parent for inserting authentication item or
+ autofill_dataset_list-->
+ <FrameLayout
+ android:id="@+id/autofill_dataset_container"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerInParent="true">
+ <ListView
+ android:id="@+id/autofill_dataset_list"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:clickable="true"
+ android:divider="@null"
+ android:drawSelectorOnTop="true"
+ android:visibility="gone"/>
+ </FrameLayout>
+
+</RelativeLayout>
diff --git a/core/res/res/values-mcc302-mnc220/config.xml b/core/res/res/values-mcc302-mnc220/config.xml
index 9a3d736..8774334 100644
--- a/core/res/res/values-mcc302-mnc220/config.xml
+++ b/core/res/res/values-mcc302-mnc220/config.xml
@@ -36,7 +36,7 @@
<!-- Values for GPS configuration (Telus) -->
<string-array translatable="false" name="config_gpsParameters">
- <item>SUPL_HOST=supl.telusmobility.com</item>
+ <item>SUPL_HOST=supl.google.com</item>
<item>SUPL_PORT=7275</item>
<item>SUPL_VER=0x20000</item>
<item>SUPL_MODE=1</item>
diff --git a/core/res/res/values-mcc302-mnc221/config.xml b/core/res/res/values-mcc302-mnc221/config.xml
index 007fd04..05896b1 100644
--- a/core/res/res/values-mcc302-mnc221/config.xml
+++ b/core/res/res/values-mcc302-mnc221/config.xml
@@ -34,7 +34,7 @@
<!-- Values for GPS configuration (Telus) -->
<string-array translatable="false" name="config_gpsParameters">
- <item>SUPL_HOST=supl.telusmobility.com</item>
+ <item>SUPL_HOST=supl.google.com</item>
<item>SUPL_PORT=7275</item>
<item>SUPL_VER=0x20000</item>
<item>SUPL_MODE=1</item>
diff --git a/core/res/res/values-television/dimens.xml b/core/res/res/values-television/dimens.xml
index 4c25225..aa56251 100644
--- a/core/res/res/values-television/dimens.xml
+++ b/core/res/res/values-television/dimens.xml
@@ -20,4 +20,8 @@
<item type="dimen" format="float" name="ambient_shadow_alpha">0.15</item>
<item type="dimen" format="float" name="spot_shadow_alpha">0.3</item>
+ <!-- Max width/height of the autofill data set picker as a fraction of the screen width/height -->
+ <dimen name="autofill_dataset_picker_max_width">60%</dimen>
+ <dimen name="autofill_dataset_picker_max_height">70%</dimen>
+
</resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 0218750..e994d37 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2328,6 +2328,10 @@
<string name="config_customVpnAlwaysOnDisconnectedDialogComponent" translatable="false"
>com.android.vpndialogs/com.android.vpndialogs.AlwaysOnDisconnectedDialog</string>
+ <!-- Name of the dialog that is used to install the carrier app when the SIM is inserted -->
+ <string name="config_carrierAppInstallDialogComponent" translatable="false"
+ >com.android.simappdialog/com.android.simappdialog.InstallCarrierAppActivity</string>
+
<!-- Apps that are authorized to access shared accounts, overridden by product overlays -->
<string name="config_appsAuthorizedForSharedAccounts" translatable="false">;com.android.settings;</string>
@@ -3316,4 +3320,5 @@
<string-array name="config_wearActivityModeRadios">
<item>"wifi"</item>
</string-array>
+
</resources>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 0411c6e..cfaab6a 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -626,7 +626,8 @@
<dimen name="messaging_avatar_size">@dimen/notification_right_icon_size</dimen>
<!-- Max width/height of the autofill data set picker as a fraction of the screen width/height -->
- <dimen name="autofill_dataset_picker_max_size">90%</dimen>
+ <dimen name="autofill_dataset_picker_max_width">90%</dimen>
+ <dimen name="autofill_dataset_picker_max_height">90%</dimen>
<!-- Max height of the the autofill save custom subtitle as a fraction of the screen width/height -->
<dimen name="autofill_save_custom_subtitle_max_height">20%</dimen>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 0efb6f9..837113d 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -182,6 +182,8 @@
<string name="notification_channel_voice_mail">Voicemail messages</string>
<!-- Telephony notification channel name for a channel containing wifi calling status notifications. -->
<string name="notification_channel_wfc">Wi-Fi calling</string>
+ <!-- Telephony notification channel name for a channel containing SIM notifications -->
+ <string name="notification_channel_sim">SIM status</string>
<!-- Displayed to tell the user that peer changed TTY mode -->
<string name="peerTtyModeFull">Peer requested TTY Mode FULL</string>
@@ -547,7 +549,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
@@ -2169,6 +2171,9 @@
<!-- Text to show in the auto complete drop down list on a text view when the WebView can auto fill the entire form but the user has not configured an AutoFill profile [CHAR-LIMIT=19] -->
<string name="setup_autofill">Set up Autofill</string>
+ <!-- Title of fullscreen autofill window [CHAR-LIMIT=80] -->
+ <string name="autofill_window_title">Autofill</string>
+
<!-- String used to separate FirstName and LastName when writing out a local name
e.g. John<separator>Smith [CHAR-LIMIT=NONE]-->
<string name="autofill_address_name_separator">\u0020</string>
@@ -3194,10 +3199,12 @@
<!-- See SIM_ADDED_DIALOG. This is the button of that dialog. -->
<string name="sim_restart_button">Restart</string>
<!-- See Carrier_App_Dialog. This is the message of that dialog. -->
- <string name="carrier_app_dialog_message">To get your new SIM working properly, you\'ll need to install and open an app from your carrier.</string>
- <!-- See Carrier_App_Dialog. This is the button of that dialog. -->
- <string name="carrier_app_dialog_button">GET THE APP</string>
- <string name="carrier_app_dialog_not_now">NOT NOW</string>
+ <string name="install_carrier_app_notification_title">Activate mobile service</string>
+ <string name="install_carrier_app_notification_text">
+ Download the carrier app to activate your new SIM
+ </string>
+ <!-- See Carrier_App_Notification. This is the button of that dialog. -->
+ <string name="install_carrier_app_notification_button">Download app</string>
<!-- See carrier_app_notification. This is the headline. -->
<string name="carrier_app_notification_title">New SIM inserted</string>
<string name="carrier_app_notification_text">Tap to set it up</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 5a9dc7f..9995642 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -574,6 +574,7 @@
<java-symbol type="string" name="notification_channel_sms" />
<java-symbol type="string" name="notification_channel_voice_mail" />
<java-symbol type="string" name="notification_channel_wfc" />
+ <java-symbol type="string" name="notification_channel_sim" />
<java-symbol type="string" name="SetupCallDefault" />
<java-symbol type="string" name="accept" />
<java-symbol type="string" name="activity_chooser_view_see_all" />
@@ -1383,6 +1384,7 @@
<java-symbol type="drawable" name="ic_sim_card_multi_24px_clr" />
<java-symbol type="drawable" name="ic_sim_card_multi_48px_clr" />
+ <java-symbol type="drawable" name="ic_signal_cellular_alt_24px" />
<java-symbol type="drawable" name="stat_notify_mmcc_indication_icn" />
<java-symbol type="drawable" name="autofilled_highlight"/>
@@ -2072,6 +2074,7 @@
<java-symbol type="string" name="config_customAdbPublicKeyConfirmationSecondaryUserComponent" />
<java-symbol type="string" name="config_customVpnConfirmDialogComponent" />
<java-symbol type="string" name="config_customVpnAlwaysOnDisconnectedDialogComponent" />
+ <java-symbol type="string" name="config_carrierAppInstallDialogComponent" />
<java-symbol type="string" name="config_defaultNetworkScorerPackageName" />
<java-symbol type="string" name="config_persistentDataPackageName" />
@@ -2771,9 +2774,9 @@
<java-symbol type="array" name="resolver_target_actions_pin" />
<java-symbol type="array" name="resolver_target_actions_unpin" />
- <java-symbol type="string" name="carrier_app_dialog_message" />
- <java-symbol type="string" name="carrier_app_dialog_button" />
- <java-symbol type="string" name="carrier_app_dialog_not_now" />
+ <java-symbol type="string" name="install_carrier_app_notification_title" />
+ <java-symbol type="string" name="install_carrier_app_notification_text" />
+ <java-symbol type="string" name="install_carrier_app_notification_button" />
<java-symbol type="string" name="carrier_app_notification_title" />
<java-symbol type="string" name="carrier_app_notification_text" />
<java-symbol type="string" name="negative_duration" />
@@ -2987,6 +2990,8 @@
<!-- com.android.server.autofill -->
<java-symbol type="layout" name="autofill_save"/>
<java-symbol type="layout" name="autofill_dataset_picker"/>
+ <java-symbol type="layout" name="autofill_dataset_picker_fullscreen"/>
+ <java-symbol type="id" name="autofill_dataset_container"/>
<java-symbol type="id" name="autofill_dataset_list"/>
<java-symbol type="id" name="autofill_dataset_picker"/>
<java-symbol type="id" name="autofill" />
@@ -3015,7 +3020,8 @@
<java-symbol type="drawable" name="autofill_dataset_picker_background" />
<java-symbol type="style" name="AutofillDatasetPicker" />
<java-symbol type="style" name="AutofillSaveAnimation" />
- <java-symbol type="dimen" name="autofill_dataset_picker_max_size"/>
+ <java-symbol type="dimen" name="autofill_dataset_picker_max_width"/>
+ <java-symbol type="dimen" name="autofill_dataset_picker_max_height"/>
<java-symbol type="dimen" name="autofill_save_custom_subtitle_max_height"/>
<java-symbol type="dimen" name="autofill_save_icon_max_size"/>
@@ -3262,4 +3268,5 @@
<java-symbol type="string" name="zen_upgrade_notification_title" />
<java-symbol type="string" name="zen_upgrade_notification_content" />
+
</resources>
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index 9c3d8eb..f6a11bd 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -451,8 +451,8 @@
<item name="tooltipBackgroundColor">@color/tooltip_background_light</item>
<!-- Autofill: max width/height of the dataset picker as a fraction of screen size -->
- <item name="autofillDatasetPickerMaxWidth">@dimen/autofill_dataset_picker_max_size</item>
- <item name="autofillDatasetPickerMaxHeight">@dimen/autofill_dataset_picker_max_size</item>
+ <item name="autofillDatasetPickerMaxWidth">@dimen/autofill_dataset_picker_max_width</item>
+ <item name="autofillDatasetPickerMaxHeight">@dimen/autofill_dataset_picker_max_height</item>
<!-- Autofill: max height of custom save subtitle as a fraction of screen size -->
<item name="autofillSaveCustomSubtitleMaxHeight">@dimen/autofill_save_custom_subtitle_max_height</item>
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index dc8ed9e..67c9754 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -402,6 +402,8 @@
Settings.Global.GPU_DEBUG_APP,
Settings.Global.GPU_DEBUG_LAYERS,
Settings.Global.ENABLE_GNSS_RAW_MEAS_FULL_TRACKING,
+ Settings.Global.INSTALL_CARRIER_APP_NOTIFICATION_PERSISTENT,
+ Settings.Global.INSTALL_CARRIER_APP_NOTIFICATION_SLEEP_MILLIS,
Settings.Global.NETWORK_ACCESS_TIMEOUT_MS,
Settings.Global.WARNING_TEMPERATURE,
Settings.Global.WEBVIEW_DATA_REDUCTION_PROXY_KEY,
@@ -468,6 +470,7 @@
Settings.Secure.ASSIST_SCREENSHOT_ENABLED,
Settings.Secure.ASSIST_STRUCTURE_ENABLED,
Settings.Secure.AUTOFILL_FEATURE_FIELD_CLASSIFICATION,
+ Settings.Secure.AUTOFILL_USER_DATA_MAX_CATEGORY_COUNT,
Settings.Secure.AUTOFILL_USER_DATA_MAX_FIELD_CLASSIFICATION_IDS_SIZE,
Settings.Secure.AUTOFILL_USER_DATA_MAX_USER_DATA_SIZE,
Settings.Secure.AUTOFILL_USER_DATA_MAX_VALUE_LENGTH,
@@ -495,6 +498,7 @@
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/core/tests/featureflagtests/OWNERS b/core/tests/featureflagtests/OWNERS
new file mode 100644
index 0000000..1a8fd2b
--- /dev/null
+++ b/core/tests/featureflagtests/OWNERS
@@ -0,0 +1,2 @@
+sbasi@google.com
+zhfan@google.com
\ No newline at end of file
diff --git a/graphics/java/android/graphics/BaseCanvas.java b/graphics/java/android/graphics/BaseCanvas.java
index 07df045..eacb727 100644
--- a/graphics/java/android/graphics/BaseCanvas.java
+++ b/graphics/java/android/graphics/BaseCanvas.java
@@ -22,7 +22,7 @@
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 @@
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 @@
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 42dac38..ed147e9 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -2835,16 +2835,6 @@
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 @@
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/libs/incident/Android.mk b/libs/incident/Android.mk
index b63400f..08c8346 100644
--- a/libs/incident/Android.mk
+++ b/libs/incident/Android.mk
@@ -33,6 +33,7 @@
../../core/java/android/os/IIncidentManager.aidl \
../../core/java/android/os/IIncidentReportStatusListener.aidl \
proto/android/os/header.proto \
+ proto/android/os/metadata.proto \
src/IncidentReportArgs.cpp
LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
diff --git a/libs/incident/proto/android/os/metadata.proto b/libs/incident/proto/android/os/metadata.proto
new file mode 100644
index 0000000..a1e89b0
--- /dev/null
+++ b/libs/incident/proto/android/os/metadata.proto
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+option java_multiple_files = true;
+
+package android.os;
+
+// This field contains internal metadata associated with an incident report,
+// such as the section ids and privacy policy specs from caller as well as how long
+// and how many bytes a section takes, etc.
+message IncidentMetadata {
+
+ // privacy level of the incident report.
+ enum Destination {
+ AUTOMATIC = 0;
+ EXPLICIT = 1;
+ LOCAL = 2;
+ }
+ optional Destination dest = 1;
+
+ optional int32 request_size = 2;
+
+ optional bool use_dropbox = 3;
+
+ // stats of each section taken in this incident report.
+ message SectionStats {
+ // section id.
+ optional int32 id = 1;
+ // if the section is successfully taken.
+ optional bool success = 2;
+ // number of bytes in the report after filtering.
+ optional int32 report_size_bytes = 3;
+ // the total duration to execute the section.
+ optional int64 exec_duration_ms = 4;
+
+ // number of bytes dumped from the section directly.
+ optional int32 dump_size_bytes = 5;
+ // duration of the dump takes.
+ optional int64 dump_duration_ms = 6;
+ // true if the section timed out.
+ optional bool timed_out = 7;
+ // true if the section is truncated.
+ optional bool is_truncated = 8;
+
+ // Next Tag: 9
+ }
+ repeated SectionStats sections = 4;
+}
+
diff --git a/native/android/OWNERS b/native/android/OWNERS
new file mode 100644
index 0000000..11d4be4
--- /dev/null
+++ b/native/android/OWNERS
@@ -0,0 +1,11 @@
+set noparent
+
+per-file libandroid_net.map.txt=ek@google.com
+per-file libandroid_net.map.txt=jchalard@google.com
+per-file libandroid_net.map.txt=lorenzo@google.com
+per-file libandroid_net.map.txt=satk@google.com
+
+per-file net.c=ek@google.com
+per-file net.c=jchalard@google.com
+per-file net.c=lorenzo@google.com
+per-file net.c=satk@google.com
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
index 44f68ec..d73a5d7 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
@@ -3117,6 +3117,8 @@
private final Consumer<String> mCallback;
+ private boolean mIsTransformationStarted;
+
public DocumentTransformer(Context context, PrintJobInfo printJob,
MutexFileProvider fileProvider, PrintAttributes attributes,
Consumer<String> callback) {
@@ -3144,29 +3146,35 @@
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
- final IPdfEditor editor = IPdfEditor.Stub.asInterface(service);
- new AsyncTask<Void, Void, String>() {
- @Override
- protected String doInBackground(Void... params) {
- // It's OK to access the data members as they are
- // final and this code is the last one to touch
- // them as shredding is the very last step, so the
- // UI is not interactive at this point.
- try {
- doTransform(editor);
- updatePrintJob();
- return null;
- } catch (IOException | RemoteException | IllegalStateException e) {
- return e.toString();
+ // We might get several onServiceConnected if the service crashes and restarts.
+ // mIsTransformationStarted makes sure that we only try once.
+ if (!mIsTransformationStarted) {
+ final IPdfEditor editor = IPdfEditor.Stub.asInterface(service);
+ new AsyncTask<Void, Void, String>() {
+ @Override
+ protected String doInBackground(Void... params) {
+ // It's OK to access the data members as they are
+ // final and this code is the last one to touch
+ // them as shredding is the very last step, so the
+ // UI is not interactive at this point.
+ try {
+ doTransform(editor);
+ updatePrintJob();
+ return null;
+ } catch (IOException | RemoteException | IllegalStateException e) {
+ return e.toString();
+ }
}
- }
- @Override
- protected void onPostExecute(String error) {
- mContext.unbindService(DocumentTransformer.this);
- mCallback.accept(error);
- }
- }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+ @Override
+ protected void onPostExecute(String error) {
+ mContext.unbindService(DocumentTransformer.this);
+ mCallback.accept(error);
+ }
+ }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+
+ mIsTransformationStarted = true;
+ }
}
@Override
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/SelectPrinterActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/SelectPrinterActivity.java
index 7c2e55f..cf73aac 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/SelectPrinterActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/SelectPrinterActivity.java
@@ -285,6 +285,11 @@
final int position = ((AdapterContextMenuInfo) menuInfo).position;
PrinterInfo printer = (PrinterInfo) mListView.getAdapter().getItem(position);
+ // Printer is null if this is a context menu for the "add printer" entry
+ if (printer == null) {
+ return;
+ }
+
menu.setHeaderTitle(printer.getName());
// Add the select menu item if applicable.
diff --git a/packages/SettingsLib/res/drawable/ic_bt_hearing_aid.xml b/packages/SettingsLib/res/drawable/ic_bt_hearing_aid.xml
new file mode 100644
index 0000000..e14c99b
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_bt_hearing_aid.xml
@@ -0,0 +1,24 @@
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M17,20c-0.29,0 -0.56,-0.06 -0.76,-0.15 -0.71,-0.37 -1.21,-0.88 -1.71,-2.38 -0.51,-1.56 -1.47,-2.29 -2.39,-3 -0.79,-0.61 -1.61,-1.24 -2.32,-2.53C9.29,10.98 9,9.93 9,9c0,-2.8 2.2,-5 5,-5s5,2.2 5,5h2c0,-3.93 -3.07,-7 -7,-7S7,5.07 7,9c0,1.26 0.38,2.65 1.07,3.9 0.91,1.65 1.98,2.48 2.85,3.15 0.81,0.62 1.39,1.07 1.71,2.05 0.6,1.82 1.37,2.84 2.73,3.55 0.51,0.23 1.07,0.35 1.64,0.35 2.21,0 4,-1.79 4,-4h-2c0,1.1 -0.9,2 -2,2zM7.64,2.64L6.22,1.22C4.23,3.21 3,5.96 3,9s1.23,5.79 3.22,7.78l1.41,-1.41C6.01,13.74 5,11.49 5,9s1.01,-4.74 2.64,-6.36zM11.5,9c0,1.38 1.12,2.5 2.5,2.5s2.5,-1.12 2.5,-2.5 -1.12,-2.5 -2.5,-2.5 -2.5,1.12 -2.5,2.5z"/>
+</vector>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index e6f4ec6..c78f454 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -177,6 +177,11 @@
<!-- Bluetooth settings. Similar to bluetooth_profile_a2dp_high_quality, but used when the device supports high quality audio but we don't know which codec that will be used. -->
<string name="bluetooth_profile_a2dp_high_quality_unknown_codec">HD audio</string>
+ <!-- Bluetooth settings. The user-visible string that is used whenever referring to the Hearing Aid profile. -->
+ <string name="bluetooth_profile_hearing_aid">Hearing Aid</string>
+ <!-- Bluetooth settings. Connection options screen. The summary for the Hearing Aid checkbox preference when Hearing Aid is connected. -->
+ <string name="bluetooth_hearing_aid_profile_summary_connected">Connected to Hearing Aid</string>
+
<!-- Bluetooth settings. Connection options screen. The summary for the A2DP checkbox preference when A2DP is connected. -->
<string name="bluetooth_a2dp_profile_summary_connected">Connected to media audio</string>
<!-- Bluetooth settings. Connection options screen. The summary for the headset checkbox preference when headset is connected. -->
@@ -214,6 +219,8 @@
for the HID checkbox preference that describes how checking it
will set the HID profile as preferred. -->
<string name="bluetooth_hid_profile_summary_use_for">Use for input</string>
+ <!-- Bluetooth settings. Connection options screen. The summary for the Hearing Aid checkbox preference that describes how checking it will set the Hearing Aid profile as preferred. -->
+ <string name="bluetooth_hearing_aid_profile_summary_use_for">Use for Hearing Aid</string>
<!-- Button text for accepting an incoming pairing request. [CHAR LIMIT=20] -->
<string name="bluetooth_pairing_accept">Pair</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java
index 1f67dfb..9947dec 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.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 @@
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 @@
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 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 @@
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 @@
* 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 @@
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/bluetooth/HearingAidProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
new file mode 100644
index 0000000..8f9e4635
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.bluetooth;
+
+import android.bluetooth.BluetoothHearingAid;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothClass;
+import android.bluetooth.BluetoothCodecConfig;
+import android.bluetooth.BluetoothCodecStatus;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothUuid;
+import android.content.Context;
+import android.os.ParcelUuid;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.settingslib.R;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+public class HearingAidProfile implements LocalBluetoothProfile {
+ private static final String TAG = "HearingAidProfile";
+ private static boolean V = true;
+
+ private Context mContext;
+
+ private BluetoothHearingAid mService;
+ private boolean mIsProfileReady;
+
+ private final LocalBluetoothAdapter mLocalAdapter;
+ private final CachedBluetoothDeviceManager mDeviceManager;
+
+ static final String NAME = "HearingAid";
+ private final LocalBluetoothProfileManager mProfileManager;
+
+ // Order of this profile in device profiles list
+ private static final int ORDINAL = 1;
+
+ // These callbacks run on the main thread.
+ private final class HearingAidServiceListener
+ implements BluetoothProfile.ServiceListener {
+
+ public void onServiceConnected(int profile, BluetoothProfile proxy) {
+ if (V) Log.d(TAG,"Bluetooth service connected");
+ mService = (BluetoothHearingAid) proxy;
+ // We just bound to the service, so refresh the UI for any connected HearingAid devices.
+ List<BluetoothDevice> deviceList = mService.getConnectedDevices();
+ while (!deviceList.isEmpty()) {
+ BluetoothDevice nextDevice = deviceList.remove(0);
+ CachedBluetoothDevice device = mDeviceManager.findDevice(nextDevice);
+ // we may add a new device here, but generally this should not happen
+ if (device == null) {
+ Log.w(TAG, "HearingAidProfile found new device: " + nextDevice);
+ device = mDeviceManager.addDevice(mLocalAdapter, mProfileManager, nextDevice);
+ }
+ device.onProfileStateChanged(HearingAidProfile.this, BluetoothProfile.STATE_CONNECTED);
+ device.refresh();
+ }
+ mIsProfileReady=true;
+ }
+
+ public void onServiceDisconnected(int profile) {
+ if (V) Log.d(TAG,"Bluetooth service disconnected");
+ mIsProfileReady=false;
+ }
+ }
+
+ public boolean isProfileReady() {
+ return mIsProfileReady;
+ }
+
+ HearingAidProfile(Context context, LocalBluetoothAdapter adapter,
+ CachedBluetoothDeviceManager deviceManager,
+ LocalBluetoothProfileManager profileManager) {
+ mContext = context;
+ mLocalAdapter = adapter;
+ mDeviceManager = deviceManager;
+ mProfileManager = profileManager;
+ mLocalAdapter.getProfileProxy(context, new HearingAidServiceListener(),
+ BluetoothProfile.HEARING_AID);
+ }
+
+ public boolean isConnectable() {
+ return true;
+ }
+
+ public boolean isAutoConnectable() {
+ return true;
+ }
+
+ public List<BluetoothDevice> getConnectedDevices() {
+ if (mService == null) return new ArrayList<BluetoothDevice>(0);
+ return mService.getDevicesMatchingConnectionStates(
+ new int[] {BluetoothProfile.STATE_CONNECTED,
+ BluetoothProfile.STATE_CONNECTING,
+ BluetoothProfile.STATE_DISCONNECTING});
+ }
+
+ public boolean connect(BluetoothDevice device) {
+ if (mService == null) return false;
+ return mService.connect(device);
+ }
+
+ public boolean disconnect(BluetoothDevice device) {
+ if (mService == null) return false;
+ // Downgrade priority as user is disconnecting the hearing aid.
+ if (mService.getPriority(device) > BluetoothProfile.PRIORITY_ON){
+ mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
+ }
+ return mService.disconnect(device);
+ }
+
+ public int getConnectionStatus(BluetoothDevice device) {
+ if (mService == null) {
+ return BluetoothProfile.STATE_DISCONNECTED;
+ }
+ return mService.getConnectionState(device);
+ }
+
+ public boolean isPreferred(BluetoothDevice device) {
+ if (mService == null) return false;
+ return mService.getPriority(device) > BluetoothProfile.PRIORITY_OFF;
+ }
+
+ public int getPreferred(BluetoothDevice device) {
+ if (mService == null) return BluetoothProfile.PRIORITY_OFF;
+ return mService.getPriority(device);
+ }
+
+ public void setPreferred(BluetoothDevice device, boolean preferred) {
+ if (mService == null) return;
+ if (preferred) {
+ if (mService.getPriority(device) < BluetoothProfile.PRIORITY_ON) {
+ mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
+ }
+ } else {
+ mService.setPriority(device, BluetoothProfile.PRIORITY_OFF);
+ }
+ }
+
+ public int getVolume() {
+ if (mService == null) {
+ return 0;
+ }
+ return mService.getVolume();
+ }
+
+ public void setVolume(int volume) {
+ if (mService == null) {
+ return;
+ }
+ mService.setVolume(volume);
+ }
+
+ public long getHiSyncId(BluetoothDevice device) {
+ if (mService == null) {
+ return BluetoothHearingAid.HI_SYNC_ID_INVALID;
+ }
+ return mService.getHiSyncId(device);
+ }
+
+ public int getDeviceSide(BluetoothDevice device) {
+ if (mService == null) {
+ return BluetoothHearingAid.SIDE_LEFT;
+ }
+ return mService.getDeviceSide(device);
+ }
+
+ public int getDeviceMode(BluetoothDevice device) {
+ if (mService == null) {
+ return BluetoothHearingAid.MODE_MONAURAL;
+ }
+ return mService.getDeviceMode(device);
+ }
+
+ public String toString() {
+ return NAME;
+ }
+
+ public int getOrdinal() {
+ return ORDINAL;
+ }
+
+ public int getNameResource(BluetoothDevice device) {
+ return R.string.bluetooth_profile_hearing_aid;
+ }
+
+ public int getSummaryResourceForDevice(BluetoothDevice device) {
+ int state = getConnectionStatus(device);
+ switch (state) {
+ case BluetoothProfile.STATE_DISCONNECTED:
+ return R.string.bluetooth_hearing_aid_profile_summary_use_for;
+
+ case BluetoothProfile.STATE_CONNECTED:
+ return R.string.bluetooth_hearing_aid_profile_summary_connected;
+
+ default:
+ return Utils.getConnectionStateSummary(state);
+ }
+ }
+
+ public int getDrawableResource(BluetoothClass btClass) {
+ return R.drawable.ic_bt_hearing_aid;
+ }
+
+ protected void finalize() {
+ if (V) Log.d(TAG, "finalize()");
+ if (mService != null) {
+ try {
+ BluetoothAdapter.getDefaultAdapter().closeProfileProxy(BluetoothProfile.HEARING_AID,
+ mService);
+ mService = null;
+ }catch (Throwable t) {
+ Log.w(TAG, "Error cleaning up Hearing Aid proxy", t);
+ }
+ }
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
index 991d922..34a099c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
@@ -21,6 +21,7 @@
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHeadset;
import android.bluetooth.BluetoothHeadsetClient;
+import android.bluetooth.BluetoothHearingAid;
import android.bluetooth.BluetoothHidHost;
import android.bluetooth.BluetoothMap;
import android.bluetooth.BluetoothMapClient;
@@ -91,6 +92,7 @@
private final PbapServerProfile mPbapProfile;
private final boolean mUsePbapPce;
private final boolean mUseMapClient;
+ private HearingAidProfile mHearingAidProfile;
/**
* Mapping from profile name, e.g. "HEADSET" to profile object.
@@ -143,10 +145,14 @@
//Create PBAP server profile
if(DEBUG) Log.d(TAG, "Adding local PBAP profile");
+
mPbapProfile = new PbapServerProfile(context);
addProfile(mPbapProfile, PbapServerProfile.NAME,
BluetoothPbap.ACTION_CONNECTION_STATE_CHANGED);
+ mHearingAidProfile = new HearingAidProfile(mContext, mLocalAdapter, mDeviceManager, this);
+ addProfile(mHearingAidProfile, HearingAidProfile.NAME,
+ BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED);
if (DEBUG) Log.d(TAG, "LocalBluetoothProfileManager construction complete");
}
@@ -254,6 +260,18 @@
"Warning: PBAP Client profile was previously added but the UUID is now missing.");
}
+ //Hearing Aid Client
+ if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.HearingAid)) {
+ if (mHearingAidProfile == null) {
+ if(DEBUG) Log.d(TAG, "Adding local Hearing Aid profile");
+ mHearingAidProfile = new HearingAidProfile(mContext, mLocalAdapter, mDeviceManager, this);
+ addProfile(mHearingAidProfile, HearingAidProfile.NAME,
+ BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED);
+ }
+ } else if (mHearingAidProfile != null) {
+ Log.w(TAG, "Warning: Hearing Aid profile was previously added but the UUID is now missing.");
+ }
+
mEventManager.registerProfileIntentReceiver();
// There is no local SDP record for HID and Settings app doesn't control PBAP Server.
@@ -416,6 +434,10 @@
return mMapClientProfile;
}
+ public HearingAidProfile getHearingAidProfile() {
+ return mHearingAidProfile;
+ }
+
/**
* Fill in a list of LocalBluetoothProfile objects that are supported by
* the local device and the remote device.
@@ -515,6 +537,12 @@
removedProfiles.remove(mPbapClientProfile);
}
+ if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.HearingAid) &&
+ mHearingAidProfile != null) {
+ profiles.add(mHearingAidProfile);
+ removedProfiles.remove(mHearingAidProfile);
+ }
+
if (DEBUG) {
Log.d(TAG,"New Profiles" + profiles.toString());
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index f699440..b380ac5 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.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 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.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 @@
mId = sLastId.incrementAndGet();
}
- AccessPoint(Context context, AccessPoint other) {
- mContext = context;
- copyFrom(other);
- }
-
AccessPoint(Context context, Collection<ScanResult> results) {
mContext = context;
@@ -346,33 +352,6 @@
}
/**
- * 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 @@
}
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 @@
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 @@
}
}
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 @@
}
}
- if (WifiTracker.sVerboseLogging) {
+ if (isVerboseLoggingEnabled()) {
summary.append(WifiUtils.buildLoggingSummary(this, config));
}
@@ -1070,12 +1049,12 @@
// 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 @@
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 @@
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 @@
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 fac585e..ae544dd 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.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.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 @@
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 @@
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 @@
@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 @@
@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 @@
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 @@
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 @@
* @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 @@
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 @@
mRegistered = false;
}
unregisterScoreCache();
- pauseScanning();
+ pauseScanning(); // and set mStaleScanResults
mWorkHandler.removePendingMessages();
- mMainHandler.removePendingMessages();
- mStaleScanResults = true;
}
}
@@ -409,12 +402,19 @@
}
/**
- * 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 @@
}
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 @@
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 @@
* 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 @@
mInternalAccessPoints.clear();
mInternalAccessPoints.addAll(accessPoints);
- mMainHandler.sendEmptyMessage(MainHandler.MSG_ACCESS_POINT_CHANGED);
+ conditionallyNotifyListeners();
}
@VisibleForTesting
@@ -650,7 +656,6 @@
}
}
final AccessPoint accessPoint = new AccessPoint(mContext, scanResults);
- accessPoint.setListener(mAccessPointListenerAdapter);
return accessPoint;
}
@@ -661,16 +666,17 @@
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 @@
if (DBG()) {
Log.d(TAG, "mLastNetworkInfo set: " + mLastNetworkInfo);
}
+
+ if(networkInfo.isConnected() != mConnected.getAndSet(networkInfo.isConnected())) {
+ mListener.onConnectedChanged();
+ }
}
WifiConfiguration connectionConfig = null;
@@ -711,18 +721,25 @@
}
}
- 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 @@
}
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 @@
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 @@
}
@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 @@
@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 @@
mScanner.resume();
}
} else {
+ clearAccessPointsAndConditionallyUpdate();
mLastInfo = null;
mLastNetworkInfo = null;
if (mScanner != null) {
@@ -920,8 +873,7 @@
mStaleScanResults = true;
}
}
- mMainHandler.obtainMessage(MainHandler.MSG_WIFI_STATE_CHANGED, msg.arg1, 0)
- .sendToTarget();
+ mListener.onWifiStateChanged(msg.arg1);
break;
}
}
@@ -1010,16 +962,26 @@
@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 @@
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);
+ private void conditionallyNotifyListeners() {
+ if (mStaleScanResults) {
+ return;
}
- @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);
- }
- }
-
- 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 1440311..54c02a2 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 @@
}
@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 6be4936..0c49bb6 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 @@
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 @@
}
@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 @@
waitForHandlersToProcessCurrentlyEnqueuedMessages(tracker);
}
- @FlakyTest
@Test
public void scoreCacheUpdateScoresShouldChangeSortOrder() throws InterruptedException {
WifiTracker tracker = createTrackerWithImmediateBroadcastsAndInjectInitialScanResults();
@@ -634,9 +596,9 @@
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 @@
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 @@
});
// 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 @@
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 @@
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 12d3106..706d0c0 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.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 @@
}
@Test
- public void testStorageManagerDaysToRetainUsesResources() {
+ public void testGetDefaultStorageManagerDaysToRetain_storageManagerDaysToRetainUsesResources() {
Resources resources = mock(Resources.class);
when(resources.getInteger(
eq(
@@ -149,6 +153,12 @@
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 91957e1..ad422d8 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 @@
*/
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/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 769b7e9..1e8c523 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -870,6 +870,12 @@
dumpSetting(s, p,
Settings.Global.ENABLE_GNSS_RAW_MEAS_FULL_TRACKING,
GlobalSettingsProto.ENABLE_GNSS_RAW_MEAS_FULL_TRACKING);
+ dumpSetting(s, p,
+ Settings.Global.INSTALL_CARRIER_APP_NOTIFICATION_PERSISTENT,
+ GlobalSettingsProto.INSTALL_CARRIER_APP_NOTIFICATION_PERSISTENT);
+ dumpSetting(s, p,
+ Settings.Global.INSTALL_CARRIER_APP_NOTIFICATION_SLEEP_MILLIS,
+ GlobalSettingsProto.INSTALL_CARRIER_APP_NOTIFICATION_SLEEP_MILLIS);
// Settings.Global.SHOW_PROCESSES intentionally excluded since it's deprecated.
dumpSetting(s, p,
Settings.Global.LOW_POWER_MODE,
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 85a579d..87ea382 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 @@
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 @@
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 @@
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/SimAppDialog/Android.mk b/packages/SimAppDialog/Android.mk
new file mode 100644
index 0000000..00a2e60
--- /dev/null
+++ b/packages/SimAppDialog/Android.mk
@@ -0,0 +1,18 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := SimAppDialog
+LOCAL_CERTIFICATE := platform
+
+
+LOCAL_STATIC_ANDROID_LIBRARIES := \
+ android-support-v4
+
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+include frameworks/opt/setupwizard/library/common-platform-deprecated.mk
+
+include $(BUILD_PACKAGE)
diff --git a/packages/SimAppDialog/AndroidManifest.xml b/packages/SimAppDialog/AndroidManifest.xml
new file mode 100644
index 0000000..873f6c5
--- /dev/null
+++ b/packages/SimAppDialog/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.simappdialog">
+ <application android:label="@string/app_name">
+ <activity
+ android:name=".InstallCarrierAppActivity"
+ android:exported="true"
+ android:permission="android.permission.NETWORK_SETTINGS"
+ android:theme="@style/SuwThemeGlif.Light">
+ </activity>
+ </application>
+</manifest>
diff --git a/packages/SimAppDialog/res/drawable/ic_signal_cellular_alt_rounded_24px.xml b/packages/SimAppDialog/res/drawable/ic_signal_cellular_alt_rounded_24px.xml
new file mode 100644
index 0000000..85896e8
--- /dev/null
+++ b/packages/SimAppDialog/res/drawable/ic_signal_cellular_alt_rounded_24px.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="21dp"
+ android:height="22dp"
+ android:viewportWidth="21"
+ android:viewportHeight="22">
+
+ <group
+ android:translateX="-196.000000"
+ android:translateY="-77.000000">
+ <group
+ android:translateX="190.000000"
+ android:translateY="72.000000">
+ <path
+ android:fillType="evenOdd"
+ android:strokeWidth="1"
+ android:pathData="M 0 0 H 32 V 32 H 0 V 0 Z"/>
+ <group
+ android:translateX="6.666667"
+ android:translateY="5.333333">
+ <path
+ android:fillColor="#4285F4"
+ android:strokeWidth="1"
+ android:pathData="M 17 0 L 19 0 Q 20 0 20 1 L 20 20.3333333 Q 20 21.3333333 19 21.3333333 L 17 21.3333333 Q 16 21.3333333 16 20.3333333 L 16 1 Q 16 0 17 0 Z"/>
+ <path
+ android:fillColor="#4285F4"
+ android:strokeWidth="1"
+ android:pathData="M 1 13.3333333 L 3 13.3333333 Q 4 13.3333333 4 14.3333333 L 4 20.3333333 Q 4 21.3333333 3 21.3333333 L 1 21.3333333 Q 0 21.3333333 0 20.3333333 L 0 14.3333333 Q 0 13.3333333 1 13.3333333 Z"/>
+ <path
+ android:fillColor="#4285F4"
+ android:strokeWidth="1"
+ android:pathData="M 9 6.66666667 L 11 6.66666667 Q 12 6.66666667 12 7.66666667 L 12 20.33333337 Q 12 21.33333337 11 21.33333337 L 9 21.33333337 Q 8 21.33333337 8 20.33333337 L 8 7.66666667 Q 8 6.66666667 9 6.66666667 Z"/>
+ </group>
+ </group>
+ </group>
+</vector>
\ No newline at end of file
diff --git a/packages/SimAppDialog/res/drawable/placeholder.xml b/packages/SimAppDialog/res/drawable/placeholder.xml
new file mode 100644
index 0000000..53eee74
--- /dev/null
+++ b/packages/SimAppDialog/res/drawable/placeholder.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<!-- TODO(b/72511181): replace when illustration is finished -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="270dp"
+ android:height="270dp"
+ android:viewportHeight="270.0"
+ android:viewportWidth="270.0">
+ <path android:fillColor="#E8EAED"
+ android:pathData="M183.54,265H84.88c-7.63,0 -13.81,-6.18 -13.81,-13.81V18.81C71.07,11.18 77.25,5 84.88,5h98.66c7.63,0 13.81,6.18 13.81,13.81v232.38C197.35,258.82 191.17,265 183.54,265z"/>
+ <path android:fillColor="#BDC1C6"
+ android:pathData="M183.54,6.63c6.72,0 12.18,5.46 12.18,12.18v232.38c0,6.72 -5.46,12.18 -12.18,12.18H84.88c-6.72,0 -12.18,-5.46 -12.18,-12.18V18.81c0,-6.72 5.46,-12.18 12.18,-12.18H183.54M183.54,5H84.88c-7.63,0 -13.81,6.18 -13.81,13.81v232.38c0,7.63 6.18,13.81 13.81,13.81h98.66c7.63,0 13.81,-6.18 13.81,-13.81V18.81C197.35,11.18 191.17,5 183.54,5L183.54,5z"/>
+ <path android:fillColor="#FFFFFF"
+ android:pathData="M186.34,243.74H82.08c-2.41,0 -4.36,-1.95 -4.36,-4.36V30.61c0,-2.41 1.95,-4.36 4.36,-4.36h104.26c2.41,0 4.36,1.95 4.36,4.36v208.78C190.7,241.79 188.75,243.74 186.34,243.74z"/>
+ <path android:fillColor="#BDC1C6"
+ android:pathData="M156.07,254.78h-43.72c-0.65,0 -1.18,-0.53 -1.18,-1.18v-0.08c0,-0.65 0.53,-1.18 1.18,-1.18h43.72c0.65,0 1.18,0.53 1.18,1.18v0.08C157.25,254.25 156.72,254.78 156.07,254.78z"/>
+ <path android:fillColor="#BDC1C6"
+ android:pathData="M156.07,17.67h-43.72c-0.65,0 -1.18,-0.53 -1.18,-1.18V16.4c0,-0.65 0.53,-1.18 1.18,-1.18l43.72,0c0.65,0 1.18,0.53 1.18,1.18v0.08C157.25,17.14 156.72,17.67 156.07,17.67z"/>
+ <path android:fillColor="#BDC1C6"
+ android:pathData="M197.85,84.16h-0.5V67.51h0.5c0.6,0 1.08,0.48 1.08,1.08v14.5C198.93,83.68 198.45,84.16 197.85,84.16z"/>
+ <path android:fillColor="#BDC1C6"
+ android:pathData="M197.41,136.45h-0.06v-32.87h0.06c0.84,0 1.52,0.68 1.52,1.52v29.84C198.93,135.77 198.25,136.45 197.41,136.45z"/>
+ <path android:fillColor="#BDC1C6"
+ android:pathData="M119.3,74.73l2.71,2.71c6.74,-6.74 17.67,-6.74 24.4,0l2.71,-2.71C140.89,66.49 127.54,66.49 119.3,74.73zM130.15,85.57l4.07,4.07l4.07,-4.07C136.04,83.33 132.39,83.33 130.15,85.57zM124.72,80.15l2.71,2.71c3.74,-3.74 9.82,-3.74 13.56,0l2.71,-2.71C138.46,74.91 129.96,74.91 124.72,80.15z"/>
+ <path android:fillColor="#BDC1C6"
+ android:pathData="M143.7,179h-1.36v-2.71h-2.71V179h-10.85v-2.71h-2.71V179h-1.36c-1.5,0 -2.7,1.21 -2.7,2.71l-0.01,18.98c0,1.5 1.21,2.71 2.71,2.71h18.98c1.5,0 2.71,-1.21 2.71,-2.71v-18.98C146.41,180.22 145.2,179 143.7,179zM143.7,200.7h-18.98v-14.91h18.98V200.7zM127.43,188.49h6.78v6.78h-6.78V188.49z"/>
+ <path android:fillColor="#BDC1C6"
+ android:pathData="M146.41,144.49v-18.98c0,-1.5 -1.21,-2.71 -2.71,-2.71h-18.98c-1.5,0 -2.71,1.21 -2.71,2.71v18.98c0,1.5 1.21,2.71 2.71,2.71h18.98C145.2,147.2 146.41,145.99 146.41,144.49zM129.47,137.03l3.39,4.07l4.75,-6.11l6.1,8.13h-18.98L129.47,137.03z"/>
+</vector>
diff --git a/packages/SimAppDialog/res/layout/install_carrier_app_activity.xml b/packages/SimAppDialog/res/layout/install_carrier_app_activity.xml
new file mode 100644
index 0000000..0462a93
--- /dev/null
+++ b/packages/SimAppDialog/res/layout/install_carrier_app_activity.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<com.android.setupwizardlib.GlifLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/setup_wizard_layout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:icon="@drawable/ic_signal_cellular_alt_rounded_24px"
+ app:suwHeaderText="@string/install_carrier_app_title"
+ app:suwFooter="@layout/install_carrier_app_footer">
+
+ <LinearLayout
+ style="@style/SuwContentFrame"
+ android:id="@+id/content_frame"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <TextView
+ android:id="@+id/install_carrier_app_description"
+ style="@style/SuwDescription.Glif"
+ android:text="@string/install_carrier_app_description_default"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"/>
+
+ <com.android.setupwizardlib.view.FillContentLayout
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1">
+
+ <!-- TODO(b/72511181): final illo and content description update -->
+ <ImageView
+ android:src="@drawable/placeholder"
+ style="@style/SuwContentIllustration"
+ android:contentDescription="@null"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
+
+ </com.android.setupwizardlib.view.FillContentLayout>
+ </LinearLayout>
+
+</com.android.setupwizardlib.GlifLayout>
diff --git a/packages/SimAppDialog/res/layout/install_carrier_app_footer.xml b/packages/SimAppDialog/res/layout/install_carrier_app_footer.xml
new file mode 100644
index 0000000..10dcb77
--- /dev/null
+++ b/packages/SimAppDialog/res/layout/install_carrier_app_footer.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<com.android.setupwizardlib.view.ButtonBarLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/footer"
+ style="@style/SuwGlifButtonBar.Stackable"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <Button
+ android:id="@+id/skip_button"
+ style="@style/SuwGlifButton.Secondary"
+ android:text="@string/install_carrier_app_defer_action"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+
+ <Space
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:layout_weight="1"/>
+
+ <Button
+ android:id="@+id/download_button"
+ style="@style/SuwGlifButton.Primary"
+ android:text="@string/install_carrier_app_download_action"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+</com.android.setupwizardlib.view.ButtonBarLayout>
diff --git a/packages/SimAppDialog/res/values/strings.xml b/packages/SimAppDialog/res/values/strings.xml
new file mode 100644
index 0000000..0c3930d
--- /dev/null
+++ b/packages/SimAppDialog/res/values/strings.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- TODO character limits -->
+ <!-- The name of this application -->
+ <string name="app_name">Sim App Dialog</string>
+ <!-- Install Carrier App Activity -->
+ <!-- Title of screen asking user to download the carrier app to match the inserted SIM card -->
+ <string name="install_carrier_app_title">Activate mobile service</string>
+ <!-- Description of screen asking user to download the carrier app to match the inserted SIM card if we know the name of the carrier-->
+ <string name="install_carrier_app_description">To get your new SIM working properly, you\'ll
+ need to install the <xliff:g name="carrier_name" example="Project Fi">%1$s</xliff:g> app
+ </string>
+ <!-- Description of screen asking user to download the carrier app to match the inserted SIM card if we don't know the name of the carrier-->
+ <string name="install_carrier_app_description_default">To get your new SIM working properly,
+ you\'ll need to install the carrier app
+ </string>
+ <!-- Name of the button used to defer downloading the carrier app -->
+ <string name="install_carrier_app_defer_action">Not now</string>
+ <!-- Name of the button for downloading the carrier app -->
+ <string name="install_carrier_app_download_action">Download app</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SimAppDialog/src/com/android/simappdialog/InstallCarrierAppActivity.java b/packages/SimAppDialog/src/com/android/simappdialog/InstallCarrierAppActivity.java
new file mode 100644
index 0000000..9e9b80d
--- /dev/null
+++ b/packages/SimAppDialog/src/com/android/simappdialog/InstallCarrierAppActivity.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.simappdialog;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.SystemProperties;
+import android.text.TextUtils;
+import android.view.View;
+import android.widget.Button;
+import android.widget.TextView;
+
+import com.android.setupwizardlib.util.WizardManagerHelper;
+
+/**
+ * Activity that gives a user the choice to download the SIM app or defer until a later time
+ *
+ * Will finish with result {@link #DEFER_RESULT} on defer button press or {@link #DOWNLOAD_RESULT}
+ * if the download button is pressed
+ *
+ * Can display the carrier app name if its passed into the intent with key
+ * {@link #BUNDLE_KEY_CARRIER_NAME}
+ */
+public class InstallCarrierAppActivity extends Activity implements View.OnClickListener {
+ /**
+ * Key for the carrier app name that will be displayed as the app to download. If unset, a
+ * default description will be used
+ */
+ public static final String BUNDLE_KEY_CARRIER_NAME = "carrier_name";
+ /** Result code when the defer button is pressed */
+ public static final int DEFER_RESULT = 1;
+ /** Result code when the download button is pressed */
+ public static final int DOWNLOAD_RESULT = 2;
+
+ @Override
+ protected void onCreate(Bundle icicle) {
+ // Setup theme for aosp/pixel
+ setTheme(
+ WizardManagerHelper.getThemeRes(
+ SystemProperties.get("setupwizard.theme"),
+ R.style.SuwThemeGlif_Light
+ )
+ );
+
+ super.onCreate(icicle);
+ setContentView(R.layout.install_carrier_app_activity);
+
+ Button notNowButton = findViewById(R.id.skip_button);
+ notNowButton.setOnClickListener(this);
+
+ Button downloadButton = findViewById(R.id.download_button);
+ downloadButton.setOnClickListener(this);
+
+ // Include carrier name in description text if its present in the intent
+ Intent intent = getIntent();
+ if (intent != null) {
+ String carrierName = intent.getStringExtra(BUNDLE_KEY_CARRIER_NAME);
+ if (!TextUtils.isEmpty(carrierName)) {
+ TextView subtitle = findViewById(R.id.install_carrier_app_description);
+ subtitle.setText(getString(R.string.install_carrier_app_description, carrierName));
+ }
+ }
+ }
+
+ @Override
+ public void onClick(View v) {
+ switch (v.getId()) {
+ case R.id.skip_button:
+ finish(DEFER_RESULT);
+ break;
+ case R.id.download_button:
+ finish(DOWNLOAD_RESULT);
+ break;
+ }
+ }
+
+ private void finish(int resultCode) {
+ setResult(resultCode);
+ finish();
+ }
+}
diff --git a/packages/SystemUI/res/drawable/car_ic_hvac.xml b/packages/SystemUI/res/drawable/car_ic_hvac.xml
new file mode 100644
index 0000000..bdc44b3
--- /dev/null
+++ b/packages/SystemUI/res/drawable/car_ic_hvac.xml
@@ -0,0 +1,51 @@
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="37dp"
+ android:height="31dp"
+ android:viewportWidth="37"
+ android:viewportHeight="31">
+
+ <group
+ android:translateX="-4.000000"
+ android:translateY="-6.000000">
+ <group
+ android:translateX="5.000000"
+ android:translateY="5.000000">
+ <path
+ android:fillType="evenOdd"
+ android:strokeColor="#FAFAFA"
+ android:strokeWidth="3.5"
+ android:pathData="M0.320769938,6.07518051 C6.46754647,1.46509811 12.4222362,1.46509811
+18.1848392,6.07518051 C23.9474422,10.6852629 29.3258717,10.4931761
+34.3201276,5.49892021" />
+ <path
+ android:fillType="evenOdd"
+ android:strokeColor="#FAFAFA"
+ android:strokeWidth="3.5"
+ android:pathData="M0.320769938,17.0751805 C6.46754647,12.4650981 12.4222362,12.4650981
+18.1848392,17.0751805 C23.9474422,21.6852629 29.3258717,21.4931761
+34.3201276,16.4989202" />
+ <path
+ android:fillType="evenOdd"
+ android:strokeColor="#FAFAFA"
+ android:strokeWidth="3.5"
+ android:pathData="M0.320769938,28.0751805 C6.46754647,23.4650981 12.4222362,23.4650981
+18.1848392,28.0751805 C23.9474422,32.6852629 29.3258717,32.4931761
+34.3201276,27.4989202" />
+ </group>
+ </group>
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/car_ic_notification.xml b/packages/SystemUI/res/drawable/car_ic_notification.xml
new file mode 100644
index 0000000..61d937b90
--- /dev/null
+++ b/packages/SystemUI/res/drawable/car_ic_notification.xml
@@ -0,0 +1,28 @@
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="56dp"
+ android:height="56dp"
+ android:viewportWidth="48"
+ android:viewportHeight="48">
+
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M24 44c2.21 0 4-1.79 4-4h-8c0 2.21 1.79 4 4
+4zm12-12V22c0-6.15-3.27-11.28-9-12.64V8c0-1.66-1.34-3-3-3s-3 1.34-3 3v1.36c-5.73
+1.36-9 6.49-9 12.64v10l-4 4v2h32v-2l-4-4zm-4 2H16V22c0-4.97 3.03-9 8-9s8 4.03 8
+9v12z" />
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/car_ic_overview.xml b/packages/SystemUI/res/drawable/car_ic_overview.xml
new file mode 100644
index 0000000..4651dcb
--- /dev/null
+++ b/packages/SystemUI/res/drawable/car_ic_overview.xml
@@ -0,0 +1,28 @@
+<!--
+ Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="56dp"
+ android:height="56dp"
+ android:viewportWidth="48"
+ android:viewportHeight="48">
+
+ <path
+ android:pathData="M0 0h48v48H0z" />
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M24 4C12.95 4 4 12.95 4 24s8.95 20 20 20 20-8.95 20-20S35.05 4 24 4zm0 36c-8.82
+0-16-7.18-16-16S15.18 8 24 8s16 7.18 16 16-7.18 16-16 16z" />
+</vector>
diff --git a/packages/SystemUI/res/layout/car_facet_button.xml b/packages/SystemUI/res/layout/car_facet_button.xml
new file mode 100644
index 0000000..f432d36
--- /dev/null
+++ b/packages/SystemUI/res/layout/car_facet_button.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2017, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<merge xmlns:android="http://schemas.android.com/apk/res/android">
+ <LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/car_facet_button"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:gravity="center"
+ android:animateLayoutChanges="true"
+ android:orientation="vertical">
+
+ <com.android.keyguard.AlphaOptimizedImageButton
+ android:id="@+id/car_nav_button_icon"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:animateLayoutChanges="true"
+ android:background="@android:color/transparent"
+ android:scaleType="fitCenter">
+ </com.android.keyguard.AlphaOptimizedImageButton>
+
+ <com.android.keyguard.AlphaOptimizedImageButton
+ android:id="@+id/car_nav_button_more_icon"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:animateLayoutChanges="true"
+ android:background="@android:color/transparent"
+ android:scaleType="fitCenter">
+ </com.android.keyguard.AlphaOptimizedImageButton>
+
+ </LinearLayout>
+</merge>
diff --git a/packages/SystemUI/res/layout/car_left_navigation_bar.xml b/packages/SystemUI/res/layout/car_left_navigation_bar.xml
new file mode 100644
index 0000000..866b5a5
--- /dev/null
+++ b/packages/SystemUI/res/layout/car_left_navigation_bar.xml
@@ -0,0 +1,99 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2016, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<com.android.systemui.statusbar.car.CarNavigationBarView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:systemui="http://schemas.android.com/apk/res-auto"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent"
+ android:orientation="vertical"
+ android:background="@drawable/system_bar_background">
+
+ <LinearLayout
+ android:layout_height="match_parent"
+ android:layout_width="match_parent"
+ android:id="@+id/nav_buttons"
+ android:orientation="vertical"
+ android:gravity="top"
+ android:paddingTop="30dp"
+ android:layout_weight="1"
+ android:background="@drawable/system_bar_background"
+ android:animateLayoutChanges="true">
+
+ <com.android.systemui.statusbar.car.CarNavigationButton
+ android:id="@+id/home"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;end"
+ android:src="@drawable/car_ic_overview"
+ android:background="?android:attr/selectableItemBackground"
+ android:paddingTop="30dp"
+ android:paddingBottom="30dp"
+ />
+
+ <com.android.systemui.statusbar.car.CarNavigationButton
+ android:id="@+id/hvac"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ systemui:intent="intent:#Intent;action=android.car.intent.action.SHOW_HVAC_CONTROLS;end"
+ systemui:broadcast="true"
+ android:src="@drawable/car_ic_hvac"
+ android:background="?android:attr/selectableItemBackground"
+ android:paddingTop="30dp"
+ android:paddingBottom="30dp"
+ />
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:gravity="bottom"
+ android:orientation="vertical">
+
+ <com.android.keyguard.AlphaOptimizedImageButton
+ android:id="@+id/notifications"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:src="@drawable/car_ic_notification"
+ android:background="?android:attr/selectableItemBackground"
+ android:paddingTop="20dp"
+ android:paddingBottom="20dp"
+ android:alpha="0.7"
+ />
+
+ <com.android.systemui.statusbar.policy.Clock
+ android:id="@+id/clock"
+ android:textAppearance="@style/TextAppearance.StatusBar.Clock"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:singleLine="true"
+ android:paddingStart="@dimen/status_bar_clock_starting_padding"
+ android:paddingEnd="@dimen/status_bar_clock_end_padding"
+ android:gravity="center_horizontal"
+ android:paddingBottom="20dp"
+ />
+
+ <Space
+ android:layout_height="10dp"
+ android:layout_width="match_parent"/>
+
+ </LinearLayout>
+
+</com.android.systemui.statusbar.car.CarNavigationBarView>
diff --git a/packages/SystemUI/res/layout/car_navigation_bar.xml b/packages/SystemUI/res/layout/car_navigation_bar.xml
index 999dbac..4666c60 100644
--- a/packages/SystemUI/res/layout/car_navigation_bar.xml
+++ b/packages/SystemUI/res/layout/car_navigation_bar.xml
@@ -19,34 +19,80 @@
<com.android.systemui.statusbar.car.CarNavigationBarView
xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:systemui="http://schemas.android.com/apk/res-auto"
android:layout_height="match_parent"
android:layout_width="match_parent"
- android:gravity="center"
android:background="@drawable/system_bar_background">
- <!-- phone.NavigationBarView has rot0 and rot90 but we expect the car head unit to have a fixed
- rotation so skip this level of the heirarchy.
- -->
<LinearLayout
android:layout_height="match_parent"
- android:layout_width="@dimen/car_navigation_bar_width"
+ android:layout_width="wrap_content"
android:orientation="horizontal"
- android:clipChildren="false"
- android:clipToPadding="false"
android:id="@+id/nav_buttons"
+ android:gravity="left"
+ android:paddingLeft="30dp"
+ android:layout_weight="1"
android:animateLayoutChanges="true">
- <!-- Buttons get populated here from a car_arrays.xml. -->
+ <com.android.systemui.statusbar.car.CarNavigationButton
+ android:id="@+id/home"
+ android:layout_height="match_parent"
+ android:layout_width="wrap_content"
+ systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;end"
+ android:src="@drawable/car_ic_overview"
+ android:background="?android:attr/selectableItemBackground"
+ android:paddingLeft="30dp"
+ android:paddingRight="30dp"
+ />
+
+ <com.android.systemui.statusbar.car.CarNavigationButton
+ android:id="@+id/hvac"
+ android:layout_height="match_parent"
+ android:layout_width="wrap_content"
+ systemui:intent="intent:#Intent;action=android.car.intent.action.SHOW_HVAC_CONTROLS;end"
+ systemui:broadcast="true"
+ android:src="@drawable/car_ic_hvac"
+ android:background="?android:attr/selectableItemBackground"
+ android:paddingLeft="30dp"
+ android:paddingRight="30dp"
+ />
</LinearLayout>
- <!-- lights out layout to match exactly -->
<LinearLayout
+ android:layout_width="wrap_content"
android:layout_height="match_parent"
- android:layout_width="match_parent"
- android:orientation="horizontal"
- android:id="@+id/lights_out"
- android:visibility="gone">
- <!-- Must match nav_buttons. -->
+ android:layout_weight="1"
+ android:gravity="right"
+ android:orientation="horizontal">
+
+ <com.android.keyguard.AlphaOptimizedImageButton
+ android:id="@+id/notifications"
+ android:layout_height="match_parent"
+ android:layout_width="wrap_content"
+ android:src="@drawable/car_ic_notification"
+ android:background="?android:attr/selectableItemBackground"
+ android:paddingLeft="20dp"
+ android:paddingRight="20dp"
+ android:alpha="0.7"
+ />
+
+ <com.android.systemui.statusbar.policy.Clock
+ android:id="@+id/clock"
+ android:textAppearance="@style/TextAppearance.StatusBar.Clock"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:singleLine="true"
+ android:paddingStart="@dimen/status_bar_clock_starting_padding"
+ android:paddingEnd="@dimen/status_bar_clock_end_padding"
+ android:gravity="center_vertical"
+ android:paddingRight="20dp"
+ />
+
+ <Space
+ android:layout_width="10dp"
+ android:layout_height="match_parent"/>
+
</LinearLayout>
</com.android.systemui.statusbar.car.CarNavigationBarView>
+
diff --git a/packages/SystemUI/res/layout/car_navigation_button.xml b/packages/SystemUI/res/layout/car_navigation_button.xml
index 7677646..4062eb8 100644
--- a/packages/SystemUI/res/layout/car_navigation_button.xml
+++ b/packages/SystemUI/res/layout/car_navigation_button.xml
@@ -17,28 +17,13 @@
*/
-->
-<com.android.systemui.statusbar.car.CarNavigationButton
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_height="match_parent"
- android:layout_width="wrap_content"
- android:orientation="horizontal"
- android:background="?android:attr/selectableItemBackground">
+<merge xmlns:android="http://schemas.android.com/apk/res/android">
<com.android.keyguard.AlphaOptimizedImageButton
- android:id="@+id/car_nav_button_icon"
- android:layout_height="match_parent"
- android:layout_width="@dimen/car_navigation_button_width"
- android:layout_centerInParent="true"
- android:animateLayoutChanges="true"
- android:scaleType="fitCenter">
+ android:id="@+id/car_nav_button_icon"
+ android:layout_height="wrap_content"
+ android:layout_width="@dimen/car_navigation_button_width"
+ android:layout_centerInParent="true"
+ android:animateLayoutChanges="true"
+ android:scaleType="fitCenter">
</com.android.keyguard.AlphaOptimizedImageButton>
-
- <com.android.keyguard.AlphaOptimizedImageButton
- android:id="@+id/car_nav_button_more_icon"
- android:layout_height="match_parent"
- android:layout_width="wrap_content"
- android:layout_centerVertical="true"
- android:layout_toRightOf="@+id/car_nav_button_icon"
- android:animateLayoutChanges="true"
- android:scaleType="fitCenter">
- </com.android.keyguard.AlphaOptimizedImageButton>
-</com.android.systemui.statusbar.car.CarNavigationButton>
+</merge>
diff --git a/packages/SystemUI/res/layout/car_right_navigation_bar.xml b/packages/SystemUI/res/layout/car_right_navigation_bar.xml
new file mode 100644
index 0000000..99ab802
--- /dev/null
+++ b/packages/SystemUI/res/layout/car_right_navigation_bar.xml
@@ -0,0 +1,101 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2016, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<com.android.systemui.statusbar.car.CarNavigationBarView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:systemui="http://schemas.android.com/apk/res-auto"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent"
+ android:orientation="vertical"
+ android:background="@drawable/system_bar_background">
+
+ <LinearLayout
+ android:layout_height="match_parent"
+ android:layout_width="match_parent"
+ android:id="@+id/nav_buttons"
+ android:orientation="vertical"
+ android:gravity="top"
+ android:paddingTop="30dp"
+ android:layout_weight="1"
+ android:background="@drawable/system_bar_background"
+ android:animateLayoutChanges="true">
+
+ <com.android.systemui.statusbar.car.CarNavigationButton
+ android:id="@+id/home"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;end"
+ android:src="@drawable/car_ic_overview"
+ android:background="?android:attr/selectableItemBackground"
+ android:paddingTop="30dp"
+ android:paddingBottom="30dp"
+ />
+
+ <com.android.systemui.statusbar.car.CarNavigationButton
+ android:id="@+id/hvac"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ systemui:intent="intent:#Intent;action=android.car.intent.action.SHOW_HVAC_CONTROLS;end"
+ systemui:broadcast="true"
+ android:src="@drawable/car_ic_hvac"
+ android:background="?android:attr/selectableItemBackground"
+ android:paddingTop="30dp"
+ android:paddingBottom="30dp"
+ />
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:gravity="bottom"
+ android:orientation="vertical">
+
+ <com.android.keyguard.AlphaOptimizedImageButton
+ android:id="@+id/notifications"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:src="@drawable/car_ic_notification"
+ android:background="?android:attr/selectableItemBackground"
+ android:paddingTop="20dp"
+ android:paddingBottom="20dp"
+ android:alpha="0.7"
+ />
+
+
+ <com.android.systemui.statusbar.policy.Clock
+ android:id="@+id/clock"
+ android:textAppearance="@style/TextAppearance.StatusBar.Clock"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:singleLine="true"
+ android:paddingStart="@dimen/status_bar_clock_starting_padding"
+ android:paddingEnd="@dimen/status_bar_clock_end_padding"
+ android:gravity="center_horizontal"
+ android:paddingBottom="20dp"
+ />
+
+ <Space
+ android:layout_height="10dp"
+ android:layout_width="match_parent"/>
+
+ </LinearLayout>
+
+</com.android.systemui.statusbar.car.CarNavigationBarView>
diff --git a/packages/SystemUI/res/layout/quick_settings_header.xml b/packages/SystemUI/res/layout/quick_settings_header.xml
new file mode 100644
index 0000000..43197c4
--- /dev/null
+++ b/packages/SystemUI/res/layout/quick_settings_header.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2018 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<com.android.systemui.qs.QSTooltipView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/qs_header_tooltip_height"
+ android:alpha="0"
+ android:gravity="center_horizontal|bottom"
+ android:visibility="invisible">
+
+ <TextView
+ android:id="@+id/header_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/quick_settings_header_onboarding_text"
+ android:textAppearance="@style/TextAppearance.QS.TileLabel"
+ android:textColor="?android:attr/colorAccent" />
+
+</com.android.systemui.qs.QSTooltipView>
diff --git a/packages/SystemUI/res/values-sw600dp-land/dimens.xml b/packages/SystemUI/res/values-sw600dp-land/dimens.xml
index a5e37d5..13ca114 100644
--- a/packages/SystemUI/res/values-sw600dp-land/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp-land/dimens.xml
@@ -40,7 +40,7 @@
<dimen name="battery_detail_graph_space_top">27dp</dimen>
<dimen name="battery_detail_graph_space_bottom">27dp</dimen>
- <dimen name="qs_tile_margin_top">16dp</dimen>
+ <dimen name="qs_tile_margin_top">32dp</dimen>
<dimen name="qs_brightness_padding_top">6dp</dimen>
<dimen name="qs_detail_margin_top">28dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values/attrs_car.xml b/packages/SystemUI/res/values/attrs_car.xml
new file mode 100644
index 0000000..b1097c3
--- /dev/null
+++ b/packages/SystemUI/res/values/attrs_car.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<resources>
+ <!-- Allow for custom attribs to be added to a facet button -->
+ <declare-styleable name="CarFacetButton">
+ <!-- icon to be rendered (drawable) -->
+ <attr name="icon" format="reference"/>
+ <!-- intent to start when button is click -->
+ <attr name="intent" format="string"/>
+ <!-- intent to start when a long press has happened -->
+ <attr name="longIntent" format="string"/>
+ <!-- categories that will be added as extras to the fired intents -->
+ <attr name="categories" format="string"/>
+ <!-- package names that will be added as extras to the fired intents -->
+ <attr name="packages" format="string" />
+ </declare-styleable>
+
+
+ <!-- Allow for custom attribs to be added to a nav button -->
+ <declare-styleable name="CarNavigationButton">
+ <!-- intent to start when button is click -->
+ <attr name="intent" format="string"/>
+ <!-- intent to start when a long press has happened -->
+ <attr name="longIntent" format="string"/>
+ <!-- start the intent as a broad cast instead of an activity if true-->
+ <attr name="broadcast" format="boolean"/>
+ </declare-styleable>
+</resources>
diff --git a/packages/SystemUI/res/values/config_car.xml b/packages/SystemUI/res/values/config_car.xml
index 9c8dcb1..db829f2 100644
--- a/packages/SystemUI/res/values/config_car.xml
+++ b/packages/SystemUI/res/values/config_car.xml
@@ -22,4 +22,9 @@
uri that will be launched into the docked window. -->
<bool name="config_enablePersistentDockedActivity">false</bool>
<string name="config_persistentDockedActivityIntentUri" translatable="false"></string>
+
+ <!-- configure which system ui bars should be displayed -->
+ <bool name="config_enableLeftNavigationBar">false</bool>
+ <bool name="config_enableRightNavigationBar">false</bool>
+ <bool name="config_enableBottomNavigationBar">true</bool>
</resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index d11ab42..bc828ff 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -290,7 +290,7 @@
<dimen name="qs_tile_height">106dp</dimen>
<dimen name="qs_tile_margin">19dp</dimen>
- <dimen name="qs_tile_margin_top">16dp</dimen>
+ <dimen name="qs_tile_margin_top">32dp</dimen>
<dimen name="qs_quick_tile_size">48dp</dimen>
<dimen name="qs_quick_tile_padding">12dp</dimen>
<dimen name="qs_header_gear_translation">16dp</dimen>
@@ -309,6 +309,7 @@
<dimen name="qs_tile_padding_bottom">16dp</dimen>
<dimen name="qs_tile_spacing">4dp</dimen>
<dimen name="qs_panel_padding_bottom">0dp</dimen>
+ <dimen name="qs_panel_padding_top">32dp</dimen>
<dimen name="qs_detail_header_height">56dp</dimen>
<dimen name="qs_detail_header_padding">0dp</dimen>
<dimen name="qs_detail_image_width">56dp</dimen>
@@ -333,6 +334,9 @@
<dimen name="qs_detail_item_icon_width">32dp</dimen>
<dimen name="qs_detail_item_icon_marginStart">0dp</dimen>
<dimen name="qs_detail_item_icon_marginEnd">20dp</dimen>
+ <dimen name="qs_header_padding_start">16dp</dimen>
+ <dimen name="qs_header_padding_end">24dp</dimen>
+ <dimen name="qs_header_tooltip_height">32dp</dimen>
<dimen name="qs_footer_padding_start">16dp</dimen>
<dimen name="qs_footer_padding_end">24dp</dimen>
<dimen name="qs_footer_icon_size">16dp</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 8c59e75..86cab22 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -666,6 +666,8 @@
<!-- Textual description of Ethernet connections -->
<string name="ethernet_label">Ethernet</string>
+ <!-- QuickSettings: Onboarding text that introduces users to long press on an option in order to view the option's menu in Settings [CHAR LIMIT=NONE] -->
+ <string name="quick_settings_header_onboarding_text">Press & hold on the icons for more options</string>
<!-- QuickSettings: Do not disturb [CHAR LIMIT=NONE] -->
<string name="quick_settings_dnd_label">Do not disturb</string>
<!-- QuickSettings: Do not disturb - Priority only [CHAR LIMIT=NONE] -->
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 4cf817e..b8319a8e 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 @@
* 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 f622d4a..17191868 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 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 d0128ef..1185f45 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.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 @@
private IOverviewProxy mOverviewProxy;
private int mConnectionBackoffAttempts;
private CharSequence mOnboardingText;
+ private @InteractionType int mInteractionFlags;
private ISystemUiProxy mSysUiProxy = new ISystemUiProxy.Stub() {
@@ -108,6 +111,22 @@
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 @@
return mOnboardingText;
}
+ public int getInteractionFlags() {
+ return mInteractionFlags;
+ }
+
private void disconnectFromLauncherService() {
if (mOverviewProxy != null) {
mOverviewProxy.asBinder().unlinkToDeath(mOverviewServiceDeathRcpt, 0);
@@ -263,5 +286,6 @@
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/Prefs.java b/packages/SystemUI/src/com/android/systemui/Prefs.java
index adb4e33..8b57740 100644
--- a/packages/SystemUI/src/com/android/systemui/Prefs.java
+++ b/packages/SystemUI/src/com/android/systemui/Prefs.java
@@ -47,6 +47,7 @@
Key.QS_INVERT_COLORS_ADDED,
Key.QS_WORK_ADDED,
Key.QS_NIGHTDISPLAY_ADDED,
+ Key.QS_LONG_PRESS_TOOLTIP_SHOWN_COUNT,
Key.SEEN_MULTI_USER,
Key.NUM_APPS_LAUNCHED,
Key.HAS_SEEN_RECENTS_ONBOARDING,
@@ -76,6 +77,11 @@
String QS_WORK_ADDED = "QsWorkAdded";
@Deprecated
String QS_NIGHTDISPLAY_ADDED = "QsNightDisplayAdded";
+ /**
+ * Used for tracking how many times the user has seen the long press tooltip in the Quick
+ * Settings panel.
+ */
+ String QS_LONG_PRESS_TOOLTIP_SHOWN_COUNT = "QsLongPressTooltipShownCount";
String SEEN_MULTI_USER = "HasSeenMultiUser";
String NUM_APPS_LAUNCHED = "NumAppsLaunched";
String HAS_SEEN_RECENTS_ONBOARDING = "HasSeenRecentsOnboarding";
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
index 222c6e82..fccd9ce 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
@@ -44,6 +44,7 @@
public static final float EXPANDED_TILE_DELAY = .86f;
+
private final ArrayList<View> mAllViews = new ArrayList<>();
/**
* List of {@link View}s representing Quick Settings that are being animated from the quick QS
@@ -65,6 +66,11 @@
private TouchAnimator mNonfirstPageDelayedAnimator;
private TouchAnimator mBrightnessAnimator;
+ /**
+ * Whether the animation is stable and not in the middle of animating between the collapsed and
+ * expanded states.
+ */
+ private boolean mIsInStableState;
private boolean mOnKeyguard;
private boolean mAllowFancy;
@@ -89,6 +95,10 @@
Log.w(TAG, "QS Not using page layout");
}
panel.setPageListener(this);
+
+ // At time of creation, the QS panel is always considered stable as it's not in the middle
+ // of collapse/expanded.
+ mIsInStableState = true;
}
public void onRtlChanged() {
@@ -243,6 +253,11 @@
} else {
mBrightnessAnimator = null;
}
+ View headerView = mQsPanel.getHeaderView();
+ if (headerView!= null) {
+ firstPageBuilder.addFloat(headerView, "translationY", heightDiff, 0);
+ mAllViews.add(headerView);
+ }
mFirstPageAnimator = firstPageBuilder
.setListener(this)
.build();
@@ -326,11 +341,21 @@
@Override
public void onAnimationAtStart() {
+ if (!mIsInStableState) {
+ mQsPanel.onCollapse();
+ }
+ mIsInStableState = true;
+
mQuickQsPanel.setVisibility(View.VISIBLE);
}
@Override
public void onAnimationAtEnd() {
+ if (!mIsInStableState) {
+ mQsPanel.onExpanded();
+ }
+ mIsInStableState = true;
+
mQuickQsPanel.setVisibility(View.INVISIBLE);
final int N = mQuickQsViews.size();
for (int i = 0; i < N; i++) {
@@ -340,6 +365,11 @@
@Override
public void onAnimationStarted() {
+ if (mIsInStableState) {
+ mQsPanel.onAnimating();
+ }
+ mIsInStableState = false;
+
mQuickQsPanel.setVisibility(mOnKeyguard ? View.INVISIBLE : View.VISIBLE);
if (mOnFirstPage) {
final int N = mQuickQsViews.size();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index f7c388d..5640be5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -18,6 +18,7 @@
import static com.android.systemui.qs.tileimpl.QSTileImpl.getColorForState;
+import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Context;
import android.content.res.Configuration;
@@ -58,6 +59,7 @@
public class QSPanel extends LinearLayout implements Tunable, Callback, BrightnessMirrorListener {
public static final String QS_SHOW_BRIGHTNESS = "qs_show_brightness";
+ public static final String QS_SHOW_LONG_PRESS_TOOLTIP = "qs_show_long_press";
protected final Context mContext;
protected final ArrayList<TileRecord> mRecords = new ArrayList<TileRecord>();
@@ -72,6 +74,7 @@
private BrightnessController mBrightnessController;
protected QSTileHost mHost;
+ protected QSTooltipView mTooltipView;
protected QSSecurityFooter mFooter;
private boolean mGridContentVisible = true;
@@ -94,6 +97,9 @@
setOrientation(VERTICAL);
+ mTooltipView = (QSTooltipView) LayoutInflater.from(mContext)
+ .inflate(R.layout.quick_settings_header, this, false);
+
mBrightnessView = LayoutInflater.from(mContext).inflate(
R.layout.quick_settings_brightness_dialog, this, false);
mTileLayout = new TileLayout(mContext);
@@ -101,7 +107,12 @@
Space space = new Space(mContext);
space.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,
mContext.getResources().getDimensionPixelSize(R.dimen.qs_footer_height)));
- mScrollLayout = new QSScrollLayout(mContext, mBrightnessView, (View) mTileLayout, space);
+ mScrollLayout = new QSScrollLayout(
+ mContext,
+ mTooltipView,
+ mBrightnessView,
+ (View) mTileLayout,
+ space);
addView(mScrollLayout);
addDivider();
@@ -134,7 +145,10 @@
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
- Dependency.get(TunerService.class).addTunable(this, QS_SHOW_BRIGHTNESS);
+ final TunerService tunerService = Dependency.get(TunerService.class);
+ tunerService.addTunable(this, QS_SHOW_BRIGHTNESS);
+ tunerService.addTunable(this, QS_SHOW_LONG_PRESS_TOOLTIP);
+
if (mHost != null) {
setTiles(mHost.getTiles());
}
@@ -166,11 +180,16 @@
@Override
public void onTuningChanged(String key, String newValue) {
if (QS_SHOW_BRIGHTNESS.equals(key)) {
- mBrightnessView.setVisibility(newValue == null || Integer.parseInt(newValue) != 0
- ? VISIBLE : GONE);
+ updateViewVisibilityForTuningValue(mBrightnessView, newValue);
+ } else if (QS_SHOW_LONG_PRESS_TOOLTIP.equals(key)) {
+ updateViewVisibilityForTuningValue(mTooltipView, newValue);
}
}
+ private void updateViewVisibilityForTuningValue(View view, @Nullable String newValue) {
+ view.setVisibility(newValue == null || Integer.parseInt(newValue) != 0 ? VISIBLE : GONE);
+ }
+
public void openDetails(String subPanel) {
QSTile tile = getTile(subPanel);
showDetailAdapter(true, tile.getDetailAdapter(), new int[]{getWidth() / 2, 0});
@@ -205,6 +224,10 @@
return mBrightnessView;
}
+ View getHeaderView() {
+ return mTooltipView;
+ }
+
public void setCallback(QSDetail.Callback callback) {
mCallback = callback;
}
@@ -266,11 +289,27 @@
if (mCustomizePanel != null && mCustomizePanel.isShown()) {
mCustomizePanel.hide(mCustomizePanel.getWidth() / 2, mCustomizePanel.getHeight() / 2);
}
+
+ // Instantly hide the header here since we don't want it to still be animating.
+ mTooltipView.setVisibility(View.INVISIBLE);
+ }
+
+ /**
+ * Called when the panel is fully animated out/expanded. This is different from the state
+ * tracked by {@link #mExpanded}, which only checks if the panel is even partially pulled out.
+ */
+ public void onExpanded() {
+ mTooltipView.fadeIn();
+ }
+
+ public void onAnimating() {
+ mTooltipView.fadeOut();
}
public void setExpanded(boolean expanded) {
if (mExpanded == expanded) return;
mExpanded = expanded;
+
if (!mExpanded) {
if (mTileLayout instanceof PagedTileLayout) {
((PagedTileLayout) mTileLayout).setCurrentItem(0, false);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTooltipView.java b/packages/SystemUI/src/com/android/systemui/qs/QSTooltipView.java
new file mode 100644
index 0000000..d1f9741
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTooltipView.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.qs;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.content.Context;
+import android.os.Handler;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.LinearLayout;
+
+import com.android.systemui.Prefs;
+
+import java.util.concurrent.TimeUnit;
+
+
+/**
+ * Tooltip/header view for the Quick Settings panel.
+ */
+public class QSTooltipView extends LinearLayout {
+
+ private static final int FADE_ANIMATION_DURATION_MS = 300;
+ private static final long AUTO_FADE_OUT_DELAY_MS = TimeUnit.SECONDS.toMillis(6);
+ private static final int TOOLTIP_NOT_YET_SHOWN_COUNT = 0;
+ public static final int MAX_TOOLTIP_SHOWN_COUNT = 3;
+
+ private final Handler mHandler = new Handler();
+ private final Runnable mAutoFadeOutRunnable = () -> fadeOut();
+
+ private int mShownCount;
+
+ public QSTooltipView(Context context) {
+ this(context, null);
+ }
+
+ public QSTooltipView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ mShownCount = getStoredShownCount();
+ }
+
+ /** Returns the latest stored tooltip shown count from SharedPreferences. */
+ private int getStoredShownCount() {
+ return Prefs.getInt(
+ mContext,
+ Prefs.Key.QS_LONG_PRESS_TOOLTIP_SHOWN_COUNT,
+ TOOLTIP_NOT_YET_SHOWN_COUNT);
+ }
+
+ /**
+ * Fades in the header view if we can show the tooltip - short circuits any running animation.
+ */
+ public void fadeIn() {
+ if (mShownCount < MAX_TOOLTIP_SHOWN_COUNT) {
+ animate().cancel();
+ setVisibility(View.VISIBLE);
+ animate()
+ .alpha(1f)
+ .setDuration(FADE_ANIMATION_DURATION_MS)
+ .setListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mHandler.postDelayed(mAutoFadeOutRunnable, AUTO_FADE_OUT_DELAY_MS);
+ }
+ })
+ .start();
+
+ // Increment and drop the shown count in prefs for the next time we're deciding to
+ // fade in the tooltip. We first sanity check that the tooltip count hasn't changed yet
+ // in prefs (say, from a long press).
+ if (getStoredShownCount() <= mShownCount) {
+ Prefs.putInt(mContext, Prefs.Key.QS_LONG_PRESS_TOOLTIP_SHOWN_COUNT, ++mShownCount);
+ }
+ }
+ }
+
+ /**
+ * Fades out the header view if it's partially visible - short circuits any running animation.
+ */
+ public void fadeOut() {
+ animate().cancel();
+ if (getVisibility() == View.VISIBLE && getAlpha() != 0f) {
+ mHandler.removeCallbacks(mAutoFadeOutRunnable);
+ animate()
+ .alpha(0f)
+ .setDuration(FADE_ANIMATION_DURATION_MS)
+ .setListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ perhapsMakeViewInvisible();
+ }
+ })
+ .start();
+ } else {
+ perhapsMakeViewInvisible();
+ }
+ }
+
+ /**
+ * Only update visibility if the view is currently being shown. Otherwise, it's already been
+ * hidden by some other manner.
+ */
+ private void perhapsMakeViewInvisible() {
+ if (getVisibility() == View.VISIBLE) {
+ setVisibility(View.INVISIBLE);
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
index 8314855..1b4b7df 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
@@ -124,9 +124,8 @@
@Override
public void onTuningChanged(String key, String newValue) {
- // No tunings for you.
- if (key.equals(QS_SHOW_BRIGHTNESS)) {
- // No Brightness for you.
+ if (QS_SHOW_BRIGHTNESS.equals(key) || QS_SHOW_LONG_PRESS_TOOLTIP.equals(key)) {
+ // No Brightness or Tooltip for you!
super.onTuningChanged(key, "0");
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
index 65135ab..9fa7beb 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
@@ -99,7 +99,11 @@
record.tileView.measure(exactly(mCellWidth), exactly(mCellHeight));
previousView = record.tileView.updateAccessibilityOrder(previousView);
}
- int height = (mCellHeight + mCellMargin) * rows + (mCellMarginTop - mCellMargin);
+
+ // Only include the top margin in our measurement if we have more than 1 row to show.
+ // Otherwise, don't add the extra margin buffer at top.
+ int height = (mCellHeight + mCellMargin) * rows +
+ rows != 0 ? (mCellMarginTop - mCellMargin) : 0;
if (height < 0) height = 0;
setMeasuredDimension(width, height);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/TouchAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/TouchAnimator.java
index 37f2528..6263efa 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/TouchAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/TouchAnimator.java
@@ -107,7 +107,7 @@
void onAnimationAtStart();
/**
- * Called when the animator moves into a position of "0". Start and end delays are
+ * Called when the animator moves into a position of "1". Start and end delays are
* taken into account, so this position may cover a range of fractional inputs.
*/
void onAnimationAtEnd();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
index 72592829..016cbd6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
@@ -42,6 +42,7 @@
import com.android.settingslib.RestrictedLockUtils;
import com.android.settingslib.Utils;
import com.android.systemui.Dependency;
+import com.android.systemui.Prefs;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.qs.DetailAdapter;
import com.android.systemui.plugins.qs.QSIconView;
@@ -49,6 +50,7 @@
import com.android.systemui.plugins.qs.QSTile.State;
import com.android.systemui.qs.PagedTileLayout.TilePage;
import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.QSTooltipView;
import java.util.ArrayList;
@@ -191,6 +193,11 @@
public void longClick() {
mMetricsLogger.write(populate(new LogMaker(ACTION_QS_LONG_PRESS).setType(TYPE_ACTION)));
mHandler.sendEmptyMessage(H.LONG_CLICK);
+
+ Prefs.putInt(
+ mContext,
+ Prefs.Key.QS_LONG_PRESS_TOOLTIP_SHOWN_COUNT,
+ QSTooltipView.MAX_TOOLTIP_SHOWN_COUNT);
}
public LogMaker populate(LogMaker logMaker) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java
new file mode 100644
index 0000000..53101a5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java
@@ -0,0 +1,161 @@
+package com.android.systemui.statusbar.car;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.TypedArray;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+
+import com.android.keyguard.AlphaOptimizedImageButton;
+import com.android.systemui.R;
+
+/**
+ * CarFacetButton is a ui component designed to be used as a shortcut for an app of a defined
+ * category. It can also render a indicator impling that there are more options of apps to launch
+ * using this component. This is done with a "More icon" currently an arrow as defined in the layout
+ * file. The class is to serve as an example.
+ * Usage example: A button that allows a user to select a music app and indicate that there are
+ * other music apps installed.
+ */
+public class CarFacetButton extends LinearLayout {
+ private static final float SELECTED_ALPHA = 1f;
+ private static final float UNSELECTED_ALPHA = 0.7f;
+
+ private static final String FACET_FILTER_DELIMITER = ";";
+ /**
+ * Extra information to be sent to a helper to make the decision of what app to launch when
+ * clicked.
+ */
+ private static final String EXTRA_FACET_CATEGORIES = "categories";
+ private static final String EXTRA_FACET_PACKAGES = "packages";
+ private static final String EXTRA_FACET_ID = "filter_id";
+ private static final String EXTRA_FACET_LAUNCH_PICKER = "launch_picker";
+
+ private Context mContext;
+ private AlphaOptimizedImageButton mIcon;
+ private AlphaOptimizedImageButton mMoreIcon;
+ private boolean mSelected = false;
+ /** App categories that are to be used with this widget */
+ private String[] mFacetCategories;
+ /** App packages that are allowed to be used with this widget */
+ private String[] mFacetPackages;
+
+
+ public CarFacetButton(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ mContext = context;
+ View.inflate(context, R.layout.car_facet_button, this);
+
+ // extract custom attributes
+ TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CarFacetButton);
+ setupIntents(typedArray);
+ setupIcons(typedArray);
+ }
+
+ /**
+ * Reads the custom attributes to setup click handlers for this component.
+ */
+ private void setupIntents(TypedArray typedArray) {
+ String intentString = typedArray.getString(R.styleable.CarFacetButton_intent);
+ String longPressIntentString = typedArray.getString(R.styleable.CarFacetButton_longIntent);
+ String categoryString = typedArray.getString(R.styleable.CarFacetButton_categories);
+ String packageString = typedArray.getString(R.styleable.CarFacetButton_packages);
+ try {
+ final Intent intent = Intent.parseUri(intentString, Intent.URI_INTENT_SCHEME);
+ intent.putExtra(EXTRA_FACET_ID, Integer.toString(getId()));
+
+ if (packageString != null) {
+ mFacetPackages = packageString.split(FACET_FILTER_DELIMITER);
+ intent.putExtra(EXTRA_FACET_PACKAGES, mFacetPackages);
+ }
+ if (categoryString != null) {
+ mFacetCategories = categoryString.split(FACET_FILTER_DELIMITER);
+ intent.putExtra(EXTRA_FACET_CATEGORIES, mFacetCategories);
+ }
+
+ setOnClickListener(v -> {
+ intent.putExtra(EXTRA_FACET_LAUNCH_PICKER, mSelected);
+ mContext.startActivity(intent);
+ });
+
+ if (longPressIntentString != null) {
+ final Intent longPressIntent = Intent.parseUri(longPressIntentString,
+ Intent.URI_INTENT_SCHEME);
+ setOnLongClickListener(v -> {
+ mContext.startActivity(longPressIntent);
+ return true;
+ });
+ }
+ } catch (Exception e) {
+ throw new RuntimeException("Failed to attach intent", e);
+ }
+ }
+
+
+ private void setupIcons(TypedArray styledAttributes) {
+ mIcon = findViewById(R.id.car_nav_button_icon);
+ mIcon.setScaleType(ImageView.ScaleType.CENTER);
+ mIcon.setClickable(false);
+ mIcon.setAlpha(UNSELECTED_ALPHA);
+ int iconResourceId = styledAttributes.getResourceId(R.styleable.CarFacetButton_icon, 0);
+ if (iconResourceId == 0) {
+ throw new RuntimeException("specified icon resource was not found and is required");
+ }
+ mIcon.setImageResource(iconResourceId);
+
+ mMoreIcon = findViewById(R.id.car_nav_button_more_icon);
+ mMoreIcon.setClickable(false);
+ mMoreIcon.setImageDrawable(getContext().getDrawable(R.drawable.car_ic_arrow));
+ mMoreIcon.setAlpha(UNSELECTED_ALPHA);
+ mMoreIcon.setVisibility(GONE);
+ }
+
+ /**
+ * @return The app categories the component represents
+ */
+ public String[] getCategories() {
+ if (mFacetCategories == null) {
+ return new String[0];
+ }
+ return mFacetCategories;
+ }
+
+ /**
+ * @return The valid packages that should be considered.
+ */
+ public String[] getFacetPackages() {
+ if (mFacetPackages == null) {
+ return new String[0];
+ }
+ return mFacetPackages;
+ }
+
+ /**
+ * Updates the alpha of the icons to "selected" and shows the "More icon"
+ * @param selected true if the view must be selected, false otherwise
+ */
+ public void setSelected(boolean selected) {
+ super.setSelected(selected);
+ setSelected(selected, selected);
+ }
+
+ /**
+ * Updates the visual state to let the user know if it's been selected.
+ * @param selected true if should update the alpha of the icon to selected, false otherwise
+ * @param showMoreIcon true if the "more icon" should be shown, false otherwise
+ */
+ public void setSelected(boolean selected, boolean showMoreIcon) {
+ mSelected = selected;
+ if (selected) {
+ mMoreIcon.setVisibility(showMoreIcon ? VISIBLE : GONE);
+ mMoreIcon.setAlpha(SELECTED_ALPHA);
+ mIcon.setAlpha(SELECTED_ALPHA);
+ } else {
+ mMoreIcon.setVisibility(GONE);
+ mIcon.setAlpha(UNSELECTED_ALPHA);
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java
new file mode 100644
index 0000000..e8c9a5e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java
@@ -0,0 +1,114 @@
+package com.android.systemui.statusbar.car;
+
+import android.app.ActivityManager;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.view.View;
+import android.view.ViewGroup;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * CarFacetButtons placed on the nav bar are designed to have visual indication that the active
+ * application on screen is associated with it. This is basically a similar concept to a radio
+ * button group.
+ */
+public class CarFacetButtonController {
+
+ protected HashMap<String, CarFacetButton> mButtonsByCategory = new HashMap<>();
+ protected HashMap<String, CarFacetButton> mButtonsByPackage = new HashMap<>();
+ protected CarFacetButton mSelectedFacetButton;
+ protected Context mContext;
+
+ public CarFacetButtonController(Context context) {
+ mContext = context;
+ }
+
+ /**
+ * Goes through the supplied CarNavigationBarView and keeps track of all the CarFacetButtons
+ * such that it can select and unselect them based on running task chages
+ * @param bar that may contain CarFacetButtons
+ */
+ public void addCarNavigationBar(CarNavigationBarView bar) {
+ findFacets(bar);
+ }
+
+ private void findFacets(ViewGroup root) {
+ final int childCount = root.getChildCount();
+
+ for (int i = 0; i < childCount; ++i) {
+ final View v = root.getChildAt(i);
+ if (v instanceof CarFacetButton) {
+ CarFacetButton facetButton = (CarFacetButton) v;
+ String[] categories = facetButton.getCategories();
+ for (int j = 0; j < categories.length; j++) {
+ String category = categories[j];
+ mButtonsByCategory.put(category, facetButton);
+ }
+
+ String[] facetPackages = facetButton.getFacetPackages();
+ for (int j = 0; j < facetPackages.length; j++) {
+ String facetPackage = facetPackages[j];
+ mButtonsByPackage.put(facetPackage, facetButton);
+ }
+ } else if (v instanceof ViewGroup) {
+ findFacets((ViewGroup) v);
+ }
+ }
+ }
+
+
+ /**
+ * This will unselect the currently selected CarFacetButton and determine which one should be
+ * selected next. It does this by reading the properties on the CarFacetButton and seeing if
+ * they are a match with the supplied taskino.
+ * @param taskInfo of the currently running application
+ */
+ public void taskChanged(ActivityManager.RunningTaskInfo taskInfo) {
+ if (taskInfo == null || taskInfo.baseActivity == null) {
+ return;
+ }
+ String packageName = taskInfo.baseActivity.getPackageName();
+
+ // If the package name belongs to a filter, then highlight appropriate button in
+ // the navigation bar.
+ if (mSelectedFacetButton != null) {
+ mSelectedFacetButton.setSelected(false);
+ }
+ CarFacetButton facetButton = mButtonsByPackage.get(packageName);
+ if (facetButton != null) {
+ facetButton.setSelected(true);
+ mSelectedFacetButton = facetButton;
+ } else {
+ String category = getPackageCategory(packageName);
+ if (category != null) {
+ facetButton = mButtonsByCategory.get(category);
+ facetButton.setSelected(true);
+ mSelectedFacetButton = facetButton;
+ }
+ }
+ }
+
+ protected String getPackageCategory(String packageName) {
+ PackageManager pm = mContext.getPackageManager();
+ Set<String> supportedCategories = mButtonsByCategory.keySet();
+ for (String category : supportedCategories) {
+ Intent intent = new Intent();
+ intent.setPackage(packageName);
+ intent.setAction(Intent.ACTION_MAIN);
+ intent.addCategory(category);
+ List<ResolveInfo> list = pm.queryIntentActivities(intent, 0);
+ if (list.size() > 0) {
+ // Cache this package name into facetPackageMap, so we won't have to query
+ // all categories next time this package name shows up.
+ mButtonsByPackage.put(packageName, mButtonsByCategory.get(category));
+ return category;
+ }
+ }
+ return null;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarController.java
deleted file mode 100644
index 64c52ed..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarController.java
+++ /dev/null
@@ -1,407 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.systemui.statusbar.car;
-
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
-
-import android.app.ActivityManager;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.graphics.drawable.Drawable;
-import android.support.v4.util.SimpleArrayMap;
-import android.text.TextUtils;
-import android.util.Log;
-import android.util.SparseBooleanArray;
-import android.view.View;
-import android.widget.LinearLayout;
-import com.android.systemui.R;
-
-import java.net.URISyntaxException;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * A controller to populate data for CarNavigationBarView and handle user interactions.
- *
- * <p>Each button inside the navigation bar is defined by data in arrays_car.xml. OEMs can
- * customize the navigation buttons by updating arrays_car.xml appropriately in an overlay.
- */
-class CarNavigationBarController {
- private static final String TAG = "CarNavBarController";
-
- private static final String EXTRA_FACET_CATEGORIES = "categories";
- private static final String EXTRA_FACET_PACKAGES = "packages";
- private static final String EXTRA_FACET_ID = "filter_id";
- private static final String EXTRA_FACET_LAUNCH_PICKER = "launch_picker";
-
- /**
- * Each facet of the navigation bar maps to a set of package names or categories defined in
- * arrays_car.xml. Package names for a given facet are delimited by ";".
- */
- private static final String FACET_FILTER_DELIMITER = ";";
-
- private final Context mContext;
- private final CarNavigationBarView mNavBar;
- private final CarStatusBar mStatusBar;
-
- /**
- * Set of categories each facet will filter on.
- */
- private final List<String[]> mFacetCategories = new ArrayList<>();
-
- /**
- * Set of package names each facet will filter on.
- */
- private final List<String[]> mFacetPackages = new ArrayList<>();
-
- private final SimpleArrayMap<String, Integer> mFacetCategoryMap = new SimpleArrayMap<>();
- private final SimpleArrayMap<String, Integer> mFacetPackageMap = new SimpleArrayMap<>();
-
- private final List<CarNavigationButton> mNavButtons = new ArrayList<>();
-
- private final SparseBooleanArray mFacetHasMultipleAppsCache = new SparseBooleanArray();
-
- private int mCurrentFacetIndex;
- private Intent mPersistentTaskIntent;
-
- public CarNavigationBarController(Context context, CarNavigationBarView navBar,
- CarStatusBar activityStarter) {
- mContext = context;
- mNavBar = navBar;
- mStatusBar = activityStarter;
- bind();
-
- if (context.getResources().getBoolean(R.bool.config_enablePersistentDockedActivity)) {
- setupPersistentDockedTask();
- }
- }
-
- private void setupPersistentDockedTask() {
- try {
- mPersistentTaskIntent = Intent.parseUri(
- mContext.getString(R.string.config_persistentDockedActivityIntentUri),
- Intent.URI_INTENT_SCHEME);
- } catch (URISyntaxException e) {
- Log.e(TAG, "Malformed persistent task intent.");
- }
- }
-
- public void taskChanged(String packageName, ActivityManager.RunningTaskInfo taskInfo) {
- // If the package name belongs to a filter, then highlight appropriate button in
- // the navigation bar.
- if (mFacetPackageMap.containsKey(packageName)) {
- setCurrentFacet(mFacetPackageMap.get(packageName));
- }
-
- // Check if the package matches any of the categories for the facets
- String category = getPackageCategory(packageName);
- if (category != null) {
- setCurrentFacet(mFacetCategoryMap.get(category));
- }
-
- // Set up the persistent docked task if needed.
- boolean isHomeTask =
- taskInfo.configuration.windowConfiguration.getActivityType() == ACTIVITY_TYPE_HOME;
- if (mPersistentTaskIntent != null && !mStatusBar.hasDockedTask() && !isHomeTask) {
- mStatusBar.startActivityOnStack(mPersistentTaskIntent,
- WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_UNDEFINED);
- }
- }
-
- public void onPackageChange(String packageName) {
- if (mFacetPackageMap.containsKey(packageName)) {
- int index = mFacetPackageMap.get(packageName);
- mFacetHasMultipleAppsCache.put(index, facetHasMultiplePackages(index));
- // No need to check categories because we've already refreshed the cache.
- return;
- }
-
- String category = getPackageCategory(packageName);
- if (mFacetCategoryMap.containsKey(category)) {
- int index = mFacetCategoryMap.get(category);
- mFacetHasMultipleAppsCache.put(index, facetHasMultiplePackages(index));
- }
- }
-
- /**
- * Iterates through the items in arrays_car.xml and sets up the facet bar buttons to
- * perform the task in that configuration file when clicked or long-pressed.
- */
- private void bind() {
- Resources res = mContext.getResources();
-
- TypedArray icons = res.obtainTypedArray(R.array.car_facet_icons);
- TypedArray intents = res.obtainTypedArray(R.array.car_facet_intent_uris);
- TypedArray longPressIntents = res.obtainTypedArray(R.array.car_facet_longpress_intent_uris);
- TypedArray facetPackageNames = res.obtainTypedArray(R.array.car_facet_package_filters);
- TypedArray facetCategories = res.obtainTypedArray(R.array.car_facet_category_filters);
-
- try {
- if (icons.length() != intents.length()
- || icons.length() != longPressIntents.length()
- || icons.length() != facetPackageNames.length()
- || icons.length() != facetCategories.length()) {
- throw new RuntimeException("car_facet array lengths do not match");
- }
-
- for (int i = 0, size = icons.length(); i < size; i++) {
- Drawable icon = icons.getDrawable(i);
- CarNavigationButton button = createNavButton(icon);
- initClickListeners(button, i, intents.getString(i), longPressIntents.getString(i));
-
- mNavButtons.add(button);
- mNavBar.addButton(button, createNavButton(icon) /* lightsOutButton */);
-
- initFacetFilterMaps(i, facetPackageNames.getString(i).split(FACET_FILTER_DELIMITER),
- facetCategories.getString(i).split(FACET_FILTER_DELIMITER));
- mFacetHasMultipleAppsCache.put(i, facetHasMultiplePackages(i));
- }
- } finally {
- // Clean up all the TypedArrays.
- icons.recycle();
- intents.recycle();
- longPressIntents.recycle();
- facetPackageNames.recycle();
- facetCategories.recycle();
- }
- }
-
- /**
- * Recreates each of the buttons on a density or font scale change. This manual process is
- * necessary since this class is not part of an activity that automatically gets recreated.
- */
- public void onDensityOrFontScaleChanged() {
- TypedArray icons = mContext.getResources().obtainTypedArray(R.array.car_facet_icons);
-
- try {
- int length = icons.length();
- if (length != mNavButtons.size()) {
- // This should not happen since the mNavButtons list is created from the length
- // of the icons array in bind().
- throw new RuntimeException("car_facet array lengths do not match number of "
- + "created buttons.");
- }
-
- for (int i = 0; i < length; i++) {
- Drawable icon = icons.getDrawable(i);
-
- // Setting a new icon will trigger a requestLayout() call if necessary.
- mNavButtons.get(i).setResources(icon);
- }
- } finally {
- icons.recycle();
- }
- }
-
- private void initFacetFilterMaps(int id, String[] packageNames, String[] categories) {
- mFacetCategories.add(categories);
- for (String category : categories) {
- mFacetCategoryMap.put(category, id);
- }
-
- mFacetPackages.add(packageNames);
- for (String packageName : packageNames) {
- mFacetPackageMap.put(packageName, id);
- }
- }
-
- private String getPackageCategory(String packageName) {
- PackageManager pm = mContext.getPackageManager();
- int size = mFacetCategories.size();
- // For each facet, check if the given package name matches one of its categories
- for (int i = 0; i < size; i++) {
- String[] categories = mFacetCategories.get(i);
- for (int j = 0; j < categories.length; j++) {
- String category = categories[j];
- Intent intent = new Intent();
- intent.setPackage(packageName);
- intent.setAction(Intent.ACTION_MAIN);
- intent.addCategory(category);
- List<ResolveInfo> list = pm.queryIntentActivities(intent, 0);
- if (list.size() > 0) {
- // Cache this package name into facetPackageMap, so we won't have to query
- // all categories next time this package name shows up.
- mFacetPackageMap.put(packageName, mFacetCategoryMap.get(category));
- return category;
- }
- }
- }
- return null;
- }
-
- /**
- * Helper method to check if a given facet has multiple packages associated with it. This can
- * be resource defined package names or package names filtered by facet category.
- *
- * @return {@code true} if the facet at the given index has more than one package.
- */
- private boolean facetHasMultiplePackages(int index) {
- PackageManager pm = mContext.getPackageManager();
-
- // Check if the packages defined for the filter actually exists on the device
- String[] packages = mFacetPackages.get(index);
- if (packages.length > 1) {
- int count = 0;
- for (int i = 0; i < packages.length; i++) {
- count += pm.getLaunchIntentForPackage(packages[i]) != null ? 1 : 0;
- if (count > 1) {
- return true;
- }
- }
- }
-
- // If there weren't multiple packages defined for the facet, check the categories
- // and see if they resolve to multiple package names
- String categories[] = mFacetCategories.get(index);
-
- int count = 0;
- for (int i = 0; i < categories.length; i++) {
- String category = categories[i];
- Intent intent = new Intent();
- intent.setAction(Intent.ACTION_MAIN);
- intent.addCategory(category);
- count += pm.queryIntentActivities(intent, 0).size();
- if (count > 1) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * Sets the facet at the given index to be the facet that is currently active. The button will
- * be highlighted appropriately.
- */
- private void setCurrentFacet(int index) {
- if (index == mCurrentFacetIndex) {
- return;
- }
-
- if (mNavButtons.get(mCurrentFacetIndex) != null) {
- mNavButtons.get(mCurrentFacetIndex)
- .setSelected(false /* selected */, false /* showMoreIcon */);
- }
-
- if (mNavButtons.get(index) != null) {
- mNavButtons.get(index).setSelected(true /* selected */,
- mFacetHasMultipleAppsCache.get(index) /* showMoreIcon */);
- }
-
- mCurrentFacetIndex = index;
- }
-
- /**
- * Creates the View that is used for the buttons along the navigation bar.
- *
- * @param icon The icon to be used for the button.
- */
- private CarNavigationButton createNavButton(Drawable icon) {
- CarNavigationButton button = (CarNavigationButton) View.inflate(mContext,
- R.layout.car_navigation_button, null);
- button.setResources(icon);
- LinearLayout.LayoutParams lp =
- new LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.MATCH_PARENT, 1);
- button.setLayoutParams(lp);
-
- return button;
- }
-
- /**
- * Initializes the click and long click listeners that correspond to the given command string.
- * The click listeners are attached to the given button.
- */
- private void initClickListeners(View button, int index, String clickString,
- String longPressString) {
- // Each button at least have an action when pressed.
- if (TextUtils.isEmpty(clickString)) {
- throw new RuntimeException("Facet at index " + index + " does not have click action.");
- }
-
- try {
- Intent intent = Intent.parseUri(clickString, Intent.URI_INTENT_SCHEME);
- button.setOnClickListener(v -> onFacetClicked(intent, index));
- } catch (URISyntaxException e) {
- throw new RuntimeException("Malformed intent uri", e);
- }
-
- if (TextUtils.isEmpty(longPressString)) {
- button.setLongClickable(false);
- return;
- }
-
- try {
- Intent intent = Intent.parseUri(longPressString, Intent.URI_INTENT_SCHEME);
- button.setOnLongClickListener(v -> {
- onFacetLongClicked(intent, index);
- return true;
- });
- } catch (URISyntaxException e) {
- throw new RuntimeException("Malformed long-press intent uri", e);
- }
- }
-
- /**
- * Handles a click on a facet. A click will trigger the given Intent.
- *
- * @param index The index of the facet that was clicked.
- */
- private void onFacetClicked(Intent intent, int index) {
- String packageName = intent.getPackage();
-
- if (packageName == null && !intent.getCategories().contains(Intent.CATEGORY_HOME)) {
- return;
- }
-
- intent.putExtra(EXTRA_FACET_CATEGORIES, mFacetCategories.get(index));
- intent.putExtra(EXTRA_FACET_PACKAGES, mFacetPackages.get(index));
- // The facet is identified by the index in which it was added to the nav bar.
- // This value can be used to determine which facet was selected
- intent.putExtra(EXTRA_FACET_ID, Integer.toString(index));
-
- // If the current facet is clicked, we want to launch the picker by default
- // rather than the "preferred/last run" app.
- intent.putExtra(EXTRA_FACET_LAUNCH_PICKER, index == mCurrentFacetIndex);
-
- int windowingMode = WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
- int activityType = ACTIVITY_TYPE_UNDEFINED;
- if (intent.getCategories().contains(Intent.CATEGORY_HOME)) {
- windowingMode = WINDOWING_MODE_UNDEFINED;
- activityType = ACTIVITY_TYPE_HOME;
- }
-
- setCurrentFacet(index);
- mStatusBar.startActivityOnStack(intent, windowingMode, activityType);
- }
-
- /**
- * Handles a long-press on a facet. The long-press will trigger the given Intent.
- *
- * @param index The index of the facet that was clicked.
- */
- private void onFacetLongClicked(Intent intent, int index) {
- setCurrentFacet(index);
- mStatusBar.startActivityOnStack(intent,
- WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_UNDEFINED);
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java
index e5a311d..1d9ef61 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java
@@ -16,17 +16,15 @@
package com.android.systemui.statusbar.car;
+import android.app.UiModeManager;
import android.content.Context;
-import android.graphics.Canvas;
import android.util.AttributeSet;
-import android.view.MotionEvent;
+import android.util.Log;
import android.view.View;
import android.widget.LinearLayout;
+import com.android.keyguard.AlphaOptimizedImageButton;
import com.android.systemui.R;
-import com.android.systemui.plugins.statusbar.phone.NavGesture;
-import com.android.systemui.statusbar.phone.NavigationBarGestureHelper;
-import com.android.systemui.statusbar.phone.NavigationBarView;
/**
* A custom navigation bar for the automotive use case.
@@ -34,9 +32,10 @@
* The navigation bar in the automotive use case is more like a list of shortcuts, rendered
* in a linear layout.
*/
-class CarNavigationBarView extends NavigationBarView {
+class CarNavigationBarView extends LinearLayout {
private LinearLayout mNavButtons;
- private LinearLayout mLightsOutButtons;
+ private AlphaOptimizedImageButton mNotificationsButton;
+ private CarStatusBar mCarStatusBar;
public CarNavigationBarView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -45,99 +44,16 @@
@Override
public void onFinishInflate() {
mNavButtons = findViewById(R.id.nav_buttons);
- mLightsOutButtons = findViewById(R.id.lights_out);
+
+ mNotificationsButton = findViewById(R.id.notifications);
+ mNotificationsButton.setOnClickListener(this::onNotificationsClick);
}
- public void addButton(CarNavigationButton button, CarNavigationButton lightsOutButton){
- mNavButtons.addView(button);
- mLightsOutButtons.addView(lightsOutButton);
+ void setStatusBar(CarStatusBar carStatusBar) {
+ mCarStatusBar = carStatusBar;
}
- @Override
- public void setDisabledFlags(int disabledFlags, boolean force) {
- // TODO: Populate.
- }
-
- @Override
- public void reorient() {
- // We expect the car head unit to always have a fixed rotation so we ignore this. The super
- // class implentation expects mRotatedViews to be populated, so if you call into it, there
- // is a possibility of a NullPointerException.
- }
-
- @Override
- public View getCurrentView() {
- return this;
- }
-
- @Override
- public void setNavigationIconHints(int hints, boolean force) {
- // We do not need to set the navigation icon hints for a vehicle
- // Calling setNavigationIconHints in the base class will result in a NPE as the car
- // navigation bar does not have a back button.
- }
-
- @Override
- public void onPluginConnected(NavGesture plugin, Context context) {
- // set to null version of the plugin ignoring incoming arg.
- super.onPluginConnected(new NullNavGesture(), context);
- }
-
- @Override
- public void onPluginDisconnected(NavGesture plugin) {
- // reinstall the null nav gesture plugin
- super.onPluginConnected(new NullNavGesture(), getContext());
- }
-
- /**
- * Null object pattern to work around expectations of the base class.
- * This is a temporary solution to have the car system ui working.
- * Already underway is a refactor of they car sys ui as to not use this class
- * hierarchy.
- */
- private static class NullNavGesture implements NavGesture {
- @Override
- public GestureHelper getGestureHelper() {
- return new GestureHelper() {
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- return false;
- }
-
- @Override
- public boolean onInterceptTouchEvent(MotionEvent event) {
- return false;
- }
-
- @Override
- public void setBarState(boolean vertical, boolean isRtl) {
- }
-
- @Override
- public void onDraw(Canvas canvas) {
- }
-
- @Override
- public void onDarkIntensityChange(float intensity) {
- }
-
- @Override
- public void onLayout(boolean changed, int left, int top, int right, int bottom) {
- }
- };
- }
-
- @Override
- public int getVersion() {
- return 0;
- }
-
- @Override
- public void onCreate(Context sysuiContext, Context pluginContext) {
- }
-
- @Override
- public void onDestroy() {
- }
+ protected void onNotificationsClick(View v) {
+ mCarStatusBar.togglePanel();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationButton.java
index 2de358f..0cdaec1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationButton.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationButton.java
@@ -1,72 +1,87 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
package com.android.systemui.statusbar.car;
import android.content.Context;
-import android.graphics.drawable.Drawable;
+import android.content.Intent;
+import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.widget.ImageView;
-import android.widget.RelativeLayout;
-import com.android.keyguard.AlphaOptimizedImageButton;
import com.android.systemui.R;
+import java.net.URISyntaxException;
+
/**
- * A wrapper view for a car navigation facet, which includes a button icon and a drop down icon.
+ * CarNavigationButton is an image button that allows for a bit more configuration at the
+ * xml file level. This allows for more control via overlays instead of having to update
+ * code.
*/
-public class CarNavigationButton extends RelativeLayout {
+public class CarNavigationButton extends com.android.keyguard.AlphaOptimizedImageButton {
+
private static final float SELECTED_ALPHA = 1;
private static final float UNSELECTED_ALPHA = 0.7f;
- private AlphaOptimizedImageButton mIcon;
- private AlphaOptimizedImageButton mMoreIcon;
+ private Context mContext;
+ private String mIntent = null;
+ private String mLongIntent = null;
+ private boolean mBroadcastIntent = false;
+ private boolean mSelected = false;
+
public CarNavigationButton(Context context, AttributeSet attrs) {
super(context, attrs);
+ mContext = context;
+ TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CarNavigationButton);
+ mIntent = typedArray.getString(R.styleable.CarNavigationButton_intent);
+ mLongIntent = typedArray.getString(R.styleable.CarNavigationButton_longIntent);
+ mBroadcastIntent = typedArray.getBoolean(R.styleable.CarNavigationButton_broadcast, false);
}
+
+ /**
+ * After the standard inflate this then adds the xml defined intents to click and long click
+ * actions if defined.
+ */
@Override
public void onFinishInflate() {
super.onFinishInflate();
- mIcon = findViewById(R.id.car_nav_button_icon);
- mIcon.setScaleType(ImageView.ScaleType.CENTER);
- mIcon.setClickable(false);
- mIcon.setBackgroundColor(android.R.color.transparent);
- mIcon.setAlpha(UNSELECTED_ALPHA);
-
- mMoreIcon = findViewById(R.id.car_nav_button_more_icon);
- mMoreIcon.setClickable(false);
- mMoreIcon.setBackgroundColor(android.R.color.transparent);
- mMoreIcon.setVisibility(INVISIBLE);
- mMoreIcon.setImageDrawable(getContext().getDrawable(R.drawable.car_ic_arrow));
- mMoreIcon.setAlpha(UNSELECTED_ALPHA);
- }
-
- public void setResources(Drawable icon) {
- mIcon.setImageDrawable(icon);
- }
-
- public void setSelected(boolean selected, boolean showMoreIcon) {
- if (selected) {
- mMoreIcon.setVisibility(showMoreIcon ? VISIBLE : INVISIBLE);
- mMoreIcon.setAlpha(SELECTED_ALPHA);
- mIcon.setAlpha(SELECTED_ALPHA);
- } else {
- mMoreIcon.setVisibility(INVISIBLE);
- mIcon.setAlpha(UNSELECTED_ALPHA);
+ setScaleType(ImageView.ScaleType.CENTER);
+ setAlpha(UNSELECTED_ALPHA);
+ try {
+ if (mIntent != null) {
+ final Intent intent = Intent.parseUri(mIntent, Intent.URI_INTENT_SCHEME);
+ intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
+ setOnClickListener(v -> {
+ if (mBroadcastIntent) {
+ mContext.sendBroadcast(intent);
+ return;
+ }
+ mContext.startActivity(intent);
+ });
+ }
+ } catch (URISyntaxException e) {
+ throw new RuntimeException("Failed to attach intent", e);
}
+
+ try {
+ if (mLongIntent != null) {
+ final Intent intent = Intent.parseUri(mLongIntent, Intent.URI_INTENT_SCHEME);
+ intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
+ setOnLongClickListener(v -> {
+ mContext.startActivity(intent);
+ return true;
+ });
+ }
+ } catch (URISyntaxException e) {
+ throw new RuntimeException("Failed to attach long press intent", e);
+ }
+ }
+
+ /**
+ * @param selected true if should indicate if this is a selected state, false otherwise
+ */
+ public void setSelected(boolean selected) {
+ super.setSelected(selected);
+ mSelected = selected;
+ setAlpha(mSelected ? SELECTED_ALPHA : UNSELECTED_ALPHA);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
index 3dfb913..c15a013 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -18,17 +18,14 @@
import android.app.ActivityManager;
import android.app.ActivityOptions;
-import android.content.BroadcastReceiver;
-import android.content.Context;
import android.content.Intent;
-import android.content.IntentFilter;
import android.graphics.PixelFormat;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.UserHandle;
-import android.service.notification.StatusBarNotification;
import android.util.Log;
+import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
@@ -46,10 +43,7 @@
import com.android.systemui.fragments.FragmentHostManager;
import com.android.systemui.recents.Recents;
import com.android.systemui.recents.misc.SysUiTaskStackChangeListener;
-import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
-import com.android.systemui.statusbar.NotificationData;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.phone.CollapsedStatusBarFragment;
import com.android.systemui.statusbar.phone.NavigationBarView;
@@ -69,7 +63,6 @@
private TaskStackListenerImpl mTaskStackListener;
- private CarNavigationBarController mController;
private FullscreenUserSwitcher mFullscreenUserSwitcher;
private CarBatteryController mCarBatteryController;
@@ -78,15 +71,23 @@
private ConnectedDeviceSignalController mConnectedDeviceSignalController;
private ViewGroup mNavigationBarWindow;
+ private ViewGroup mLeftNavigationBarWindow;
+ private ViewGroup mRightNavigationBarWindow;
private CarNavigationBarView mNavigationBarView;
+ private CarNavigationBarView mLeftNavigationBarView;
+ private CarNavigationBarView mRightNavigationBarView;
private final Object mQueueLock = new Object();
+ private boolean mShowLeft;
+ private boolean mShowRight;
+ private boolean mShowBottom;
+ private CarFacetButtonController mCarFacetButtonController;
+
@Override
public void start() {
super.start();
mTaskStackListener = new TaskStackListenerImpl();
ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
- registerPackageChangeReceivers();
mStackScroller.setScrollingEnabled(true);
@@ -104,6 +105,16 @@
mNavigationBarView = null;
}
+ if (mLeftNavigationBarWindow != null) {
+ mWindowManager.removeViewImmediate(mLeftNavigationBarWindow);
+ mLeftNavigationBarView = null;
+ }
+
+ if (mRightNavigationBarWindow != null) {
+ mWindowManager.removeViewImmediate(mRightNavigationBarWindow);
+ mRightNavigationBarView = null;
+ }
+
super.destroy();
}
@@ -153,10 +164,36 @@
@Override
protected void createNavigationBar() {
+ mCarFacetButtonController = new CarFacetButtonController(mContext);
if (mNavigationBarView != null) {
return;
}
+ mShowBottom = mContext.getResources().getBoolean(R.bool.config_enableBottomNavigationBar);
+ if (mShowBottom) {
+ buildBottomBar();
+ }
+
+ int widthForSides = mContext.getResources().getDimensionPixelSize(
+ R.dimen.navigation_bar_height_car_mode);
+
+
+ mShowLeft = mContext.getResources().getBoolean(R.bool.config_enableLeftNavigationBar);
+
+ if (mShowLeft) {
+ buildLeft(widthForSides);
+ }
+
+ mShowRight = mContext.getResources().getBoolean(R.bool.config_enableRightNavigationBar);
+
+ if (mShowRight) {
+ buildRight(widthForSides);
+ }
+
+ }
+
+
+ private void buildBottomBar() {
// SystemUI requires that the navigation bar view have a parent. Since the regular
// StatusBar inflates navigation_bar_window as this parent view, use the same view for the
// CarNavigationBarView.
@@ -171,17 +208,15 @@
mNavigationBarView = (CarNavigationBarView) mNavigationBarWindow.getChildAt(0);
if (mNavigationBarView == null) {
Log.e(TAG, "CarStatusBar failed inflate for R.layout.car_navigation_bar");
+ throw new RuntimeException("Unable to build botom nav bar due to missing layout");
}
+ mNavigationBarView.setStatusBar(this);
- mController = new CarNavigationBarController(mContext, mNavigationBarView,
- this /* ActivityStarter*/);
- mNavigationBarView.getBarTransitions().setAlwaysOpaque(true);
WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.TYPE_NAVIGATION_BAR,
- WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
- | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
| WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
| WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
@@ -189,9 +224,74 @@
lp.setTitle("CarNavigationBar");
lp.windowAnimations = 0;
+
+ mCarFacetButtonController.addCarNavigationBar(mNavigationBarView);
mWindowManager.addView(mNavigationBarWindow, lp);
}
+ private void buildLeft(int widthForSides) {
+ mLeftNavigationBarWindow = (ViewGroup) View.inflate(mContext,
+ R.layout.navigation_bar_window, null);
+ if (mLeftNavigationBarWindow == null) {
+ Log.e(TAG, "CarStatusBar failed inflate for R.layout.navigation_bar_window");
+ }
+
+ View.inflate(mContext, R.layout.car_left_navigation_bar, mLeftNavigationBarWindow);
+ mLeftNavigationBarView = (CarNavigationBarView) mLeftNavigationBarWindow.getChildAt(0);
+ if (mLeftNavigationBarView == null) {
+ Log.e(TAG, "CarStatusBar failed inflate for R.layout.car_navigation_bar");
+ throw new RuntimeException("Unable to build left nav bar due to missing layout");
+ }
+ mLeftNavigationBarView.setStatusBar(this);
+ mCarFacetButtonController.addCarNavigationBar(mLeftNavigationBarView);
+
+ WindowManager.LayoutParams leftlp = new WindowManager.LayoutParams(
+ widthForSides, LayoutParams.MATCH_PARENT,
+ WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
+ WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+ | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
+ | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
+ PixelFormat.TRANSLUCENT);
+ leftlp.setTitle("LeftCarNavigationBar");
+ leftlp.windowAnimations = 0;
+ leftlp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR;
+ leftlp.gravity = Gravity.LEFT;
+ mWindowManager.addView(mLeftNavigationBarWindow, leftlp);
+ }
+
+
+ private void buildRight(int widthForSides) {
+ mRightNavigationBarWindow = (ViewGroup) View.inflate(mContext,
+ R.layout.navigation_bar_window, null);
+ if (mRightNavigationBarWindow == null) {
+ Log.e(TAG, "CarStatusBar failed inflate for R.layout.navigation_bar_window");
+ }
+
+ View.inflate(mContext, R.layout.car_right_navigation_bar, mRightNavigationBarWindow);
+ mRightNavigationBarView = (CarNavigationBarView) mRightNavigationBarWindow.getChildAt(0);
+ if (mRightNavigationBarView == null) {
+ Log.e(TAG, "CarStatusBar failed inflate for R.layout.car_navigation_bar");
+ throw new RuntimeException("Unable to build right nav bar due to missing layout");
+ }
+ mRightNavigationBarView.setStatusBar(this);
+ mCarFacetButtonController.addCarNavigationBar(mRightNavigationBarView);
+
+ WindowManager.LayoutParams rightlp = new WindowManager.LayoutParams(
+ widthForSides, LayoutParams.MATCH_PARENT,
+ WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
+ WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+ | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
+ | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
+ PixelFormat.TRANSLUCENT);
+ rightlp.setTitle("RightCarNavigationBar");
+ rightlp.windowAnimations = 0;
+ rightlp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR;
+ rightlp.gravity = Gravity.RIGHT;
+ mWindowManager.addView(mRightNavigationBarWindow, rightlp);
+ }
+
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
//When executing dump() funciton simultaneously, we need to serialize them
@@ -204,8 +304,8 @@
}
pw.print(" mTaskStackListener="); pw.println(mTaskStackListener);
- pw.print(" mController=");
- pw.println(mController);
+ pw.print(" mCarFacetButtonController=");
+ pw.println(mCarFacetButtonController);
pw.print(" mFullscreenUserSwitcher="); pw.println(mFullscreenUserSwitcher);
pw.print(" mCarBatteryController=");
pw.println(mCarBatteryController);
@@ -229,10 +329,6 @@
}
}
- @Override
- public NavigationBarView getNavigationBarView() {
- return mNavigationBarView;
- }
@Override
public View getNavigationBarWindow() {
@@ -269,24 +365,6 @@
}
}
- private BroadcastReceiver mPackageChangeReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (intent.getData() == null || mController == null) {
- return;
- }
- String packageName = intent.getData().getSchemeSpecificPart();
- mController.onPackageChange(packageName);
- }
- };
-
- private void registerPackageChangeReceivers() {
- IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_PACKAGE_ADDED);
- filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
- filter.addDataScheme("package");
- mContext.registerReceiver(mPackageChangeReceiver, filter);
- }
public boolean hasDockedTask() {
return Recents.getSystemServices().hasDockedTask();
@@ -301,10 +379,7 @@
public void onTaskStackChanged() {
ActivityManager.RunningTaskInfo runningTaskInfo =
ActivityManagerWrapper.getInstance().getRunningTask();
- if (runningTaskInfo != null && runningTaskInfo.baseActivity != null) {
- mController.taskChanged(runningTaskInfo.baseActivity.getPackageName(),
- runningTaskInfo);
- }
+ mCarFacetButtonController.taskChanged(runningTaskInfo);
}
}
@@ -346,33 +421,6 @@
// Do nothing, we don't want to display media art in the lock screen for a car.
}
- private int startActivityWithOptions(Intent intent, Bundle options) {
- int result = ActivityManager.START_CANCELED;
- try {
- result = ActivityManager.getService().startActivityAsUser(null /* caller */,
- mContext.getBasePackageName(),
- intent,
- intent.resolveTypeIfNeeded(mContext.getContentResolver()),
- null /* resultTo*/,
- null /* resultWho*/,
- 0 /* requestCode*/,
- Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP,
- null /* profilerInfo*/,
- options,
- UserHandle.CURRENT.getIdentifier());
- } catch (RemoteException e) {
- Log.w(TAG, "Unable to start activity", e);
- }
-
- return result;
- }
-
- public int startActivityOnStack(Intent intent, int windowingMode, int activityType) {
- final ActivityOptions options = ActivityOptions.makeBasic();
- options.setLaunchWindowingMode(windowingMode);
- options.setLaunchActivityType(activityType);
- return startActivityWithOptions(intent, options.toBundle());
- }
@Override
public void animateExpandNotificationsPanel() {
@@ -390,8 +438,6 @@
@Override
public void onDensityOrFontScaleChanged() {
super.onDensityOrFontScaleChanged();
- mController.onDensityOrFontScaleChanged();
-
// Need to update the background on density changed in case the change was due to night
// mode.
mNotificationPanelBackground = getDefaultWallpaper();
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 11d20b2..ef44ad1 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 @@
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 72938c2..79c605e 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.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.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 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 63f2ceb..8970ea0 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 @@
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 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 880a9d7..0dea6b8 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.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.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 @@
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 @@
}
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 @@
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 @@
updateSlippery();
}
+ public void updateStates() {
+ updateSlippery();
+ setDisabledFlags(mDisabledFlags, true);
+ }
+
private void updateSlippery() {
setSlippery(mOverviewProxyService.getProxy() != null && mPanelView.isFullyExpanded());
}
@@ -794,9 +823,8 @@
}
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/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index 6444cc8..747a551 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -575,7 +575,7 @@
Intent browserIntent = getTaskIntent(taskId, userId);
Notification.Builder builder = new Notification.Builder(mContext, NotificationChannels.GENERAL);
- if (browserIntent != null) {
+ if (browserIntent != null && browserIntent.isWebIntent()) {
// Make sure that this doesn't resolve back to an instant app
browserIntent.setComponent(null)
.setPackage(null)
@@ -597,8 +597,9 @@
.addCategory("unique:" + System.currentTimeMillis())
.putExtra(Intent.EXTRA_PACKAGE_NAME, appInfo.packageName)
.putExtra(Intent.EXTRA_VERSION_CODE, (int) (appInfo.versionCode & 0x7fffffff))
- .putExtra(Intent.EXTRA_VERSION_CODE, appInfo.versionCode)
- .putExtra(Intent.EXTRA_EPHEMERAL_FAILURE, pendingIntent);
+ .putExtra(Intent.EXTRA_LONG_VERSION_CODE, appInfo.versionCode)
+ .putExtra(Intent.EXTRA_EPHEMERAL_FAILURE, pendingIntent)
+ .putExtra(Intent.EXTRA_INSTANT_APP_FAILURE, pendingIntent);
PendingIntent webPendingIntent = PendingIntent.getActivity(mContext, 0, goToWebIntent, 0);
Action webAction = new Notification.Action.Builder(null, mContext.getString(R.string.go_to_web),
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 f4da0c3..0bf01b0 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.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 @@
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 @@
*/
@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 @@
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 @@
: 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 @@
}
}
- 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 94ebc1b..2b50853 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 @@
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 @@
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 @@
* @param fraction From 0 to 1 where 0 means collapse and 1 expanded.
*/
public void setPanelExpansion(float fraction) {
- if (mExpansionFraction != fraction) {
- mExpansionFraction = fraction;
+ if (mFraction != fraction) {
+ mFraction = fraction;
- if (!(mState == ScrimState.UNLOCKED || mState == ScrimState.KEYGUARD)) {
- return;
- }
+ 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;
+ }
- applyExpansionToAlpha();
-
- 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 @@
}
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 @@
} 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 3b63d6c..24920cb 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.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 @@
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 @@
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 @@
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 @@
}
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 @@
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/qs/tileimpl/QSTileImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
index 1c9c794..6764634 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
@@ -37,6 +37,7 @@
import android.content.Intent;
import android.metrics.LogMaker;
import android.support.test.filters.SmallTest;
+import android.support.test.InstrumentationRegistry;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
@@ -73,6 +74,7 @@
mMetricsLogger = mDependency.injectMockDependency(MetricsLogger.class);
mHost = mock(QSTileHost.class);
when(mHost.indexOf(spec)).thenReturn(POSITION);
+ when(mHost.getContext()).thenReturn(mContext.getBaseContext());
mTile = spy(new TileImpl(mHost));
mTile.mHandler = mTile.new H(mTestableLooper.getLooper());
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 4702793..43e16db 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 @@
@Test
public void transitionToUnlocked() {
- mScrimController.setPanelExpansion(0f);
mScrimController.transitionTo(ScrimState.UNLOCKED);
mScrimController.finishAnimationsImmediately();
// Front scrim should be transparent
@@ -198,7 +197,6 @@
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 @@
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 ae5e133..320c37f 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -5213,6 +5213,50 @@
// 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;
+
+ // Events for battery saver turning on/off and/or the interactive state changes.
+ // OS: P
+ BATTERY_SAVER = 1302;
+
+ // Device interactive state -- i.e. the screen ON (=1) or OFF (=1)
+ // OS: P
+ FIELD_INTERACTIVE = 1303;
+
+ // Time spent in milliseconds in the current mode.
+ // OS: P
+ FIELD_DURATION_MILLIS = 1304;
+
+ // Battery level in uA (0 - ~3,000,000 depending on device) when the current "mode" started.
+ // OS: P
+ FIELD_START_BATTERY_UA = 1305;
+
+ // Battery level in uA (0 - ~3,000,000 depending on device) when this event was created.
+ // OS: P
+ FIELD_END_BATTERY_UA = 1306;
+
+ // Battery level in % (0-100) when the current "mode" started.
+ // OS: P
+ FIELD_START_BATTERY_PERCENT = 1307;
+
+ // Battery level in % (0-100) when this event was created.
+ // OS: P
+ FIELD_END_BATTERY_PERCENT = 1308;
+
// ---- 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/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java
index 52ab85c..6c6dd5b 100644
--- a/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java
@@ -402,9 +402,9 @@
com.android.internal.R.dimen.config_screen_magnification_scaling_threshold,
scaleValue, false);
mScalingThreshold = scaleValue.getFloat();
- mScaleGestureDetector = new ScaleGestureDetector(context, this);
+ mScaleGestureDetector = new ScaleGestureDetector(context, this, Handler.getMain());
mScaleGestureDetector.setQuickScaleEnabled(false);
- mScrollGestureDetector = new GestureDetector(context, this);
+ mScrollGestureDetector = new GestureDetector(context, this, Handler.getMain());
}
@Override
@@ -638,7 +638,7 @@
@VisibleForTesting boolean mShortcutTriggered;
- @VisibleForTesting Handler mHandler = new Handler(this);
+ @VisibleForTesting Handler mHandler = new Handler(Looper.getMainLooper(), this);
public DetectingState(Context context) {
mLongTapMinDelay = ViewConfiguration.getLongPressTimeout();
@@ -654,7 +654,9 @@
final int type = message.what;
switch (type) {
case MESSAGE_ON_TRIPLE_TAP_AND_HOLD: {
- onTripleTapAndHold(/* down */ (MotionEvent) message.obj);
+ MotionEvent down = (MotionEvent) message.obj;
+ transitionToViewportDraggingStateAndClear(down);
+ down.recycle();
}
break;
case MESSAGE_TRANSITION_TO_DELEGATING_STATE: {
@@ -720,8 +722,7 @@
// over insta-delegating on 3tap&swipe
// (which is a rare combo to be used aside from magnification)
if (isMultiTapTriggered(2 /* taps */)) {
- transitionTo(mViewportDraggingState);
- clear();
+ transitionToViewportDraggingStateAndClear(event);
} else {
transitionToDelegatingStateAndClear();
}
@@ -806,7 +807,8 @@
/** -> {@link ViewportDraggingState} */
public void afterLongTapTimeoutTransitionToDraggingState(MotionEvent event) {
mHandler.sendMessageDelayed(
- mHandler.obtainMessage(MESSAGE_ON_TRIPLE_TAP_AND_HOLD, event),
+ mHandler.obtainMessage(MESSAGE_ON_TRIPLE_TAP_AND_HOLD,
+ MotionEvent.obtain(event)),
ViewConfiguration.getLongPressTimeout());
}
@@ -890,7 +892,7 @@
}
}
- void onTripleTapAndHold(MotionEvent down) {
+ void transitionToViewportDraggingStateAndClear(MotionEvent down) {
if (DEBUG_DETECTING) Slog.i(LOG_TAG, "onTripleTapAndHold()");
clear();
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index ebb5040..4b3abea 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.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.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.Arrays;
import java.util.List;
import java.util.Objects;
+import java.util.Set;
/**
* Entry point service for autofill management.
@@ -98,6 +103,8 @@
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 @@
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 @@
@Override
public void onStart() {
publishBinderService(AUTOFILL_MANAGER_SERVICE, new AutoFillManagerServiceStub());
- publishLocalService(AutofillManagerInternal.class, new LocalService());
+ publishLocalService(AutofillManagerInternal.class, mLocalService);
}
@Override
@@ -317,6 +327,11 @@
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 @@
if (service != null) {
mServicesCache.delete(userId);
service.destroyLocked();
+ mAutofillCompatState.removeCompatibilityModeRequests(userId);
}
}
@@ -498,18 +514,60 @@
*/
@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 @@
@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 75ae2dc..712ea36 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.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;
@@ -869,7 +870,7 @@
}
mUserData = userData;
// Log it
- int numberFields = mUserData == null ? 0: mUserData.getRemoteIds().length;
+ int numberFields = mUserData == null ? 0: mUserData.getCategoryIds().length;
mMetricsLogger.write(Helper.newLogMaker(MetricsEvent.AUTOFILL_USERDATA_UPDATED,
getServicePackageName(), null)
.setCounterValue(numberFields));
@@ -906,7 +907,10 @@
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 @@
}
@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/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 55c36b0..d047581 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -1174,12 +1174,12 @@
@NonNull UserData userData, @NonNull Collection<ViewState> viewStates) {
final String[] userValues = userData.getValues();
- final String[] remoteIds = userData.getRemoteIds();
+ final String[] categoryIds = userData.getCategoryIds();
// Sanity check
- if (userValues == null || remoteIds == null || userValues.length != remoteIds.length) {
+ if (userValues == null || categoryIds == null || userValues.length != categoryIds.length) {
final int valuesLength = userValues == null ? -1 : userValues.length;
- final int idsLength = remoteIds == null ? -1 : remoteIds.length;
+ final int idsLength = categoryIds == null ? -1 : categoryIds.length;
Slog.w(TAG, "setScores(): user data mismatch: values.length = "
+ valuesLength + ", ids.length = " + idsLength);
return;
@@ -1196,12 +1196,12 @@
final int viewsSize = viewStates.size();
// First, we get all scores.
- final AutofillId[] fieldIds = new AutofillId[viewsSize];
+ final AutofillId[] autofillIds = new AutofillId[viewsSize];
final ArrayList<AutofillValue> currentValues = new ArrayList<>(viewsSize);
int k = 0;
for (ViewState viewState : viewStates) {
currentValues.add(viewState.getCurrentValue());
- fieldIds[k++] = viewState.id;
+ autofillIds[k++] = viewState.id;
}
// Then use the results, asynchronously
@@ -1221,32 +1221,53 @@
}
int i = 0, j = 0;
try {
+ // Iteract over all autofill fields first
for (i = 0; i < viewsSize; i++) {
- final AutofillId fieldId = fieldIds[i];
+ final AutofillId autofillId = autofillIds[i];
- ArrayList<Match> matches = null;
+ // Search the best scores for each category (as some categories could have
+ // multiple user values
+ ArrayMap<String, Float> scoresByField = null;
for (j = 0; j < userValues.length; j++) {
- String remoteId = remoteIds[j];
+ final String categoryId = categoryIds[j];
final float score = scores.scores[i][j];
if (score > 0) {
+ if (scoresByField == null) {
+ scoresByField = new ArrayMap<>(userValues.length);
+ }
+ final Float currentScore = scoresByField.get(categoryId);
+ if (currentScore != null && currentScore > score) {
+ if (sVerbose) {
+ Slog.v(TAG, "skipping score " + score
+ + " because it's less than " + currentScore);
+ }
+ continue;
+ }
if (sVerbose) {
Slog.v(TAG, "adding score " + score + " at index " + j + " and id "
- + fieldId);
+ + autofillId);
}
- if (matches == null) {
- matches = new ArrayList<>(userValues.length);
- }
- matches.add(new Match(remoteId, score));
+ scoresByField.put(categoryId, score);
}
else if (sVerbose) {
- Slog.v(TAG, "skipping score 0 at index " + j + " and id " + fieldId);
+ Slog.v(TAG, "skipping score 0 at index " + j + " and id " + autofillId);
}
}
- if (matches != null) {
- detectedFieldIds.add(fieldId);
- detectedFieldClassifications.add(new FieldClassification(matches));
+ if (scoresByField == null) {
+ if (sVerbose) Slog.v(TAG, "no score for autofillId=" + autofillId);
+ continue;
}
- }
+
+ // Then create the matches for that autofill id
+ final ArrayList<Match> matches = new ArrayList<>(scoresByField.size());
+ for (j = 0; j < scoresByField.size(); j++) {
+ final String fieldId = scoresByField.keyAt(j);
+ final float score = scoresByField.valueAt(j);
+ matches.add(new Match(fieldId, score));
+ }
+ detectedFieldIds.add(autofillId);
+ detectedFieldClassifications.add(new FieldClassification(matches));
+ } // for i
} catch (ArrayIndexOutOfBoundsException e) {
wtf(e, "Error accessing FC score at [%d, %d] (%s): %s", i, j, scores, e);
return;
diff --git a/services/autofill/java/com/android/server/autofill/ui/FillUi.java b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
index 8240e4b..c023efb 100644
--- a/services/autofill/java/com/android/server/autofill/ui/FillUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
@@ -24,6 +24,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender;
+import android.content.pm.PackageManager;
import android.graphics.Point;
import android.graphics.Rect;
import android.service.autofill.Dataset;
@@ -37,6 +38,7 @@
import android.view.View;
import android.view.View.MeasureSpec;
import android.view.ViewGroup;
+import android.view.ViewGroup.LayoutParams;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityManager;
import android.view.autofill.AutofillId;
@@ -98,22 +100,28 @@
private @Nullable AnnounceFilterResult mAnnounceFilterResult;
+ private final boolean mFullScreen;
private int mContentWidth;
private int mContentHeight;
private boolean mDestroyed;
+ public static boolean isFullScreen(Context context) {
+ return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK);
+ }
+
FillUi(@NonNull Context context, @NonNull FillResponse response,
- @NonNull AutofillId focusedViewId, @NonNull @Nullable String filterText,
- @NonNull OverlayControl overlayControl, @NonNull Callback callback) {
+ @NonNull AutofillId focusedViewId, @NonNull @Nullable String filterText,
+ @NonNull OverlayControl overlayControl, @NonNull Callback callback) {
mContext = context;
mCallback = callback;
+ mFullScreen = isFullScreen(context);
final LayoutInflater inflater = LayoutInflater.from(context);
final ViewGroup decor = (ViewGroup) inflater.inflate(
- R.layout.autofill_dataset_picker, null);
-
+ mFullScreen ? R.layout.autofill_dataset_picker_fullscreen
+ : R.layout.autofill_dataset_picker, null);
final RemoteViews.OnClickHandler interceptionHandler = new RemoteViews.OnClickHandler() {
@Override
@@ -130,31 +138,41 @@
mListView = null;
mAdapter = null;
+ // insert authentication item under autofill_dataset_container or decor
+ ViewGroup container = decor.findViewById(R.id.autofill_dataset_container);
+ if (container == null) {
+ container = decor;
+ }
final View content;
try {
content = response.getPresentation().apply(context, decor, interceptionHandler);
- decor.addView(content);
+ container.addView(content);
} catch (RuntimeException e) {
callback.onCanceled();
Slog.e(TAG, "Error inflating remote views", e);
mWindow = null;
return;
}
+ content.setFocusable(true);
+ content.setOnClickListener(v -> mCallback.onResponsePicked(response));
Point maxSize = mTempPoint;
resolveMaxWindowSize(context, maxSize);
+ // fullScreen mode occupy the full width defined by autofill_dataset_picker_max_width
+ content.getLayoutParams().width = mFullScreen ? maxSize.x
+ : ViewGroup.LayoutParams.WRAP_CONTENT;
+ content.getLayoutParams().height = ViewGroup.LayoutParams.WRAP_CONTENT;
final int widthMeasureSpec = MeasureSpec.makeMeasureSpec(maxSize.x,
MeasureSpec.AT_MOST);
final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(maxSize.y,
MeasureSpec.AT_MOST);
decor.measure(widthMeasureSpec, heightMeasureSpec);
- decor.setOnClickListener(v -> mCallback.onResponsePicked(response));
mContentWidth = content.getMeasuredWidth();
mContentHeight = content.getMeasuredHeight();
mWindow = new AnchoredWindow(decor, overlayControl);
- mCallback.requestShowFillUi(mContentWidth, mContentHeight, mWindowPresenter);
+ requestShowFillUi();
} else {
final int datasetCount = response.getDatasets().size();
@@ -261,6 +279,15 @@
}
}
+ void requestShowFillUi() {
+ if (mFullScreen) {
+ mCallback.requestShowFillUi(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
+ mWindowPresenter);
+ } else {
+ mCallback.requestShowFillUi(mContentWidth, mContentHeight, mWindowPresenter);
+ }
+ }
+
/**
* Creates a remoteview interceptor used to block clicks.
*/
@@ -289,7 +316,13 @@
mCallback.requestHideFillUi();
} else {
if (updateContentSize()) {
- mCallback.requestShowFillUi(mContentWidth, mContentHeight, mWindowPresenter);
+ if (mFullScreen) {
+ LayoutParams lp = mListView.getLayoutParams();
+ lp.width = mContentWidth;
+ lp.height = mContentHeight;
+ mListView.setLayoutParams(lp);
+ }
+ requestShowFillUi();
}
if (mAdapter.getCount() > VISIBLE_OPTIONS_MAX_COUNT) {
mListView.setVerticalScrollBarEnabled(true);
@@ -310,7 +343,7 @@
// ViewState doesn't not support filtering - typically when it's for an authenticated
// FillResponse.
if (TextUtils.isEmpty(filterText)) {
- mCallback.requestShowFillUi(mContentWidth, mContentHeight, mWindowPresenter);
+ requestShowFillUi();
} else {
mCallback.requestHideFillUi();
}
@@ -366,23 +399,39 @@
final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(maxSize.y,
MeasureSpec.AT_MOST);
final int itemCount = mAdapter.getCount();
+ if (mFullScreen) {
+ // fullScreen mode occupy the full width defined by autofill_dataset_picker_max_width
+ changed = true;
+ mContentWidth = maxSize.x;
+ }
for (int i = 0; i < itemCount; i++) {
final View view = mAdapter.getItem(i).view;
view.measure(widthMeasureSpec, heightMeasureSpec);
- final int clampedMeasuredWidth = Math.min(view.getMeasuredWidth(), maxSize.x);
- final int newContentWidth = Math.max(mContentWidth, clampedMeasuredWidth);
- if (newContentWidth != mContentWidth) {
- mContentWidth = newContentWidth;
- changed = true;
- }
- // Update the width to fit only the first items up to max count
- if (i < VISIBLE_OPTIONS_MAX_COUNT) {
- final int clampedMeasuredHeight = Math.min(view.getMeasuredHeight(), maxSize.y);
- final int newContentHeight = mContentHeight + clampedMeasuredHeight;
- if (newContentHeight != mContentHeight) {
- mContentHeight = newContentHeight;
+ if (mFullScreen) {
+ // for fullscreen, add up all children height until hit max height.
+ final int newContentHeight = mContentHeight + view.getMeasuredHeight();
+ final int clampedNewHeight = Math.min(newContentHeight, maxSize.y);
+ if (clampedNewHeight != mContentHeight) {
+ mContentHeight = clampedNewHeight;
+ } else if (view.getMeasuredHeight() > 0) {
+ break;
+ }
+ } else {
+ final int clampedMeasuredWidth = Math.min(view.getMeasuredWidth(), maxSize.x);
+ final int newContentWidth = Math.max(mContentWidth, clampedMeasuredWidth);
+ if (newContentWidth != mContentWidth) {
+ mContentWidth = newContentWidth;
changed = true;
}
+ // Update the width to fit only the first items up to max count
+ if (i < VISIBLE_OPTIONS_MAX_COUNT) {
+ final int clampedMeasuredHeight = Math.min(view.getMeasuredHeight(), maxSize.y);
+ final int newContentHeight = mContentHeight + clampedMeasuredHeight;
+ if (newContentHeight != mContentHeight) {
+ mContentHeight = newContentHeight;
+ changed = true;
+ }
+ }
}
}
return changed;
@@ -575,6 +624,7 @@
public void dump(PrintWriter pw, String prefix) {
pw.print(prefix); pw.print("mCallback: "); pw.println(mCallback != null);
+ pw.print(prefix); pw.print("mFullScreen: "); pw.println(mFullScreen);
pw.print(prefix); pw.print("mListView: "); pw.println(mListView);
pw.print(prefix); pw.print("mAdapter: "); pw.println(mAdapter);
pw.print(prefix); pw.print("mFilterText: ");
diff --git a/services/backup/OWNERS b/services/backup/OWNERS
new file mode 100644
index 0000000..1c9a43a
--- /dev/null
+++ b/services/backup/OWNERS
@@ -0,0 +1,7 @@
+artikz@google.com
+brufino@google.com
+bryanmawhinney@google.com
+ctate@google.com
+jorlow@google.com
+mkarpinski@google.com
+
diff --git a/services/backup/java/com/android/server/backup/TransportManager.java b/services/backup/java/com/android/server/backup/TransportManager.java
index 7e179e5..501ff29 100644
--- a/services/backup/java/com/android/server/backup/TransportManager.java
+++ b/services/backup/java/com/android/server/backup/TransportManager.java
@@ -26,6 +26,7 @@
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
+import android.os.Bundle;
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.ArrayMap;
@@ -57,6 +58,8 @@
@VisibleForTesting
public static final String SERVICE_ACTION_TRANSPORT_HOST = "android.backup.TRANSPORT_HOST";
+ private static final String EXTRA_TRANSPORT_REGISTRATION = "transport_registration";
+
private final Intent mTransportServiceIntent = new Intent(SERVICE_ACTION_TRANSPORT_HOST);
private final Context mContext;
private final PackageManager mPackageManager;
@@ -582,8 +585,12 @@
String transportString = transportComponent.flattenToShortString();
String callerLogString = "TransportManager.registerTransport()";
- TransportClient transportClient =
- mTransportClientManager.getTransportClient(transportComponent, callerLogString);
+
+ Bundle extras = new Bundle();
+ extras.putBoolean(EXTRA_TRANSPORT_REGISTRATION, true);
+
+ TransportClient transportClient = mTransportClientManager.getTransportClient(
+ transportComponent, extras, callerLogString);
final IBackupTransport transport;
try {
transport = transportClient.connectOrThrow(callerLogString);
diff --git a/services/backup/java/com/android/server/backup/transport/TransportClientManager.java b/services/backup/java/com/android/server/backup/transport/TransportClientManager.java
index 4041932..96e7d2f 100644
--- a/services/backup/java/com/android/server/backup/transport/TransportClientManager.java
+++ b/services/backup/java/com/android/server/backup/transport/TransportClientManager.java
@@ -22,10 +22,9 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
-
+import android.os.Bundle;
import com.android.server.backup.TransportManager;
import com.android.server.backup.transport.TransportUtils.Priority;
-
import java.io.PrintWriter;
import java.util.Map;
import java.util.WeakHashMap;
@@ -59,6 +58,32 @@
public TransportClient getTransportClient(ComponentName transportComponent, String caller) {
Intent bindIntent =
new Intent(SERVICE_ACTION_TRANSPORT_HOST).setComponent(transportComponent);
+
+ return getTransportClient(transportComponent, caller, bindIntent);
+ }
+
+ /**
+ * Retrieves a {@link TransportClient} for the transport identified by {@param
+ * transportComponent} whose binding intent will have the {@param extras} extras.
+ *
+ * @param transportComponent The {@link ComponentName} of the transport.
+ * @param extras A {@link Bundle} of extras to pass to the binding intent.
+ * @param caller A {@link String} identifying the caller for logging/debugging purposes. Check
+ * {@link TransportClient#connectAsync(TransportConnectionListener, String)} for more
+ * details.
+ * @return A {@link TransportClient}.
+ */
+ public TransportClient getTransportClient(
+ ComponentName transportComponent, Bundle extras, String caller) {
+ Intent bindIntent =
+ new Intent(SERVICE_ACTION_TRANSPORT_HOST).setComponent(transportComponent);
+ bindIntent.putExtras(extras);
+
+ return getTransportClient(transportComponent, caller, bindIntent);
+ }
+
+ private TransportClient getTransportClient(
+ ComponentName transportComponent, String caller, Intent bindIntent) {
synchronized (mTransportClientsLock) {
TransportClient transportClient =
new TransportClient(
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index efad7d5..f633003 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -30,6 +30,7 @@
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
import static android.net.NetworkCapabilities.TRANSPORT_VPN;
@@ -1290,11 +1291,7 @@
for (Network network : networks) {
nai = getNetworkAgentInfoForNetwork(network);
nc = getNetworkCapabilitiesInternal(nai);
- // nc is a copy of the capabilities in nai, so it's fine to mutate it
- // TODO : don't remove the UIDs when communicating with processes
- // that have the NETWORK_SETTINGS permission.
if (nc != null) {
- nc.setSingleUid(userId);
result.put(network, nc);
}
}
@@ -1362,7 +1359,9 @@
if (nai != null) {
synchronized (nai) {
if (nai.networkCapabilities != null) {
- return new NetworkCapabilities(nai.networkCapabilities);
+ // TODO : don't remove the UIDs when communicating with processes
+ // that have the NETWORK_SETTINGS permission.
+ return networkCapabilitiesWithoutUids(nai.networkCapabilities);
}
}
}
@@ -1375,6 +1374,10 @@
return getNetworkCapabilitiesInternal(getNetworkAgentInfoForNetwork(network));
}
+ private NetworkCapabilities networkCapabilitiesWithoutUids(NetworkCapabilities nc) {
+ return new NetworkCapabilities(nc).setUids(null);
+ }
+
@Override
public NetworkState[] getAllNetworkState() {
// Require internal since we're handing out IMSI details
@@ -1384,6 +1387,10 @@
for (Network network : getAllNetworks()) {
final NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
if (nai != null) {
+ // TODO (b/73321673) : NetworkState contains a copy of the
+ // NetworkCapabilities, which may contain UIDs of apps to which the
+ // network applies. Should the UIDs be cleared so as not to leak or
+ // interfere ?
result.add(nai.getNetworkState());
}
}
@@ -4542,10 +4549,12 @@
lp.ensureDirectlyConnectedRoutes();
// TODO: Instead of passing mDefaultRequest, provide an API to determine whether a Network
// satisfies mDefaultRequest.
+ final NetworkCapabilities nc = new NetworkCapabilities(networkCapabilities);
final NetworkAgentInfo nai = new NetworkAgentInfo(messenger, new AsyncChannel(),
- new Network(reserveNetId()), new NetworkInfo(networkInfo), lp,
- new NetworkCapabilities(networkCapabilities), currentScore,
+ new Network(reserveNetId()), new NetworkInfo(networkInfo), lp, nc, currentScore,
mContext, mTrackerHandler, new NetworkMisc(networkMisc), mDefaultRequest, this);
+ // Make sure the network capabilities reflect what the agent info says.
+ nai.networkCapabilities = mixInCapabilities(nai, nc);
synchronized (this) {
nai.networkMonitor.systemReady = mSystemReady;
}
@@ -4774,6 +4783,11 @@
} else {
newNc.addCapability(NET_CAPABILITY_FOREGROUND);
}
+ if (nai.isSuspended()) {
+ newNc.removeCapability(NET_CAPABILITY_NOT_SUSPENDED);
+ } else {
+ newNc.addCapability(NET_CAPABILITY_NOT_SUSPENDED);
+ }
return newNc;
}
@@ -4954,7 +4968,7 @@
releasePendingNetworkRequestWithDelay(pendingIntent);
}
- private static void callCallbackForRequest(NetworkRequestInfo nri,
+ private void callCallbackForRequest(NetworkRequestInfo nri,
NetworkAgentInfo networkAgent, int notificationType, int arg1) {
if (nri.messenger == null) {
return; // Default request has no msgr
@@ -4967,16 +4981,19 @@
putParcelable(bundle, networkAgent.network);
}
switch (notificationType) {
+ case ConnectivityManager.CALLBACK_AVAILABLE: {
+ putParcelable(bundle, new NetworkCapabilities(networkAgent.networkCapabilities));
+ putParcelable(bundle, new LinkProperties(networkAgent.linkProperties));
+ break;
+ }
case ConnectivityManager.CALLBACK_LOSING: {
msg.arg1 = arg1;
break;
}
case ConnectivityManager.CALLBACK_CAP_CHANGED: {
+ // networkAgent can't be null as it has been accessed a few lines above.
final NetworkCapabilities nc =
- new NetworkCapabilities(networkAgent.networkCapabilities);
- // TODO : don't remove the UIDs when communicating with processes
- // that have the NETWORK_SETTINGS permission.
- nc.setSingleUid(nri.mUid);
+ networkCapabilitiesWithoutUids(networkAgent.networkCapabilities);
putParcelable(bundle, nc);
break;
}
@@ -5509,6 +5526,10 @@
if (networkAgent.getCurrentScore() != oldScore) {
rematchAllNetworksAndRequests(networkAgent, oldScore);
}
+ updateCapabilities(networkAgent.getCurrentScore(), networkAgent,
+ networkAgent.networkCapabilities);
+ // TODO (b/73132094) : remove this call once the few users of onSuspended and
+ // onResumed have been removed.
notifyNetworkCallbacks(networkAgent, (state == NetworkInfo.State.SUSPENDED ?
ConnectivityManager.CALLBACK_SUSPENDED :
ConnectivityManager.CALLBACK_RESUMED));
@@ -5545,14 +5566,6 @@
}
callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_AVAILABLE, 0);
- // Whether a network is currently suspended is also an important
- // element of state to be transferred (it would not otherwise be
- // delivered by any currently available mechanism).
- if (nai.networkInfo.getState() == NetworkInfo.State.SUSPENDED) {
- callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_SUSPENDED, 0);
- }
- callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_CAP_CHANGED, 0);
- callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_IP_CHANGED, 0);
}
private void sendLegacyNetworkBroadcast(NetworkAgentInfo nai, DetailedState state, int type) {
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index bdeb231..7cd007b 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.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 @@
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 @@
|| 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 @@
}
}
- // 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 @@
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/ActivityManagerDebugConfig.java b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
index 87e8121..8367916 100644
--- a/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
+++ b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
@@ -81,7 +81,7 @@
static final boolean DEBUG_FOREGROUND_SERVICE = DEBUG_ALL || false;
static final boolean DEBUG_SERVICE_EXECUTING = DEBUG_ALL || false;
static final boolean DEBUG_STACK = DEBUG_ALL || false;
- static final boolean DEBUG_STATES = DEBUG_ALL_ACTIVITIES || false;
+ static final boolean DEBUG_STATES = DEBUG_ALL_ACTIVITIES || true;
static final boolean DEBUG_SWITCH = DEBUG_ALL || false;
static final boolean DEBUG_TASKS = DEBUG_ALL || false;
static final boolean DEBUG_TRANSITION = DEBUG_ALL || false;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index e8b7839..5f8a5ab 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.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;
@@ -579,6 +580,10 @@
// How long we wait until we timeout on key dispatching during instrumentation.
static final int INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT = 60*1000;
+ // Disable hidden API checks for the newly started instrumentation.
+ // Must be kept in sync with Am.
+ private static final int INSTRUMENTATION_FLAG_DISABLE_HIDDEN_API_CHECKS = 1 << 0;
+
// How long to wait in getAssistContextExtras for the activity and foreground services
// to respond with the result.
static final int PENDING_ASSIST_EXTRAS_TIMEOUT = 500;
@@ -4002,12 +4007,19 @@
startProcessLocked(app, hostingType, hostingNameStr, null /* abiOverride */);
}
+ @GuardedBy("this")
+ private final boolean startProcessLocked(ProcessRecord app,
+ String hostingType, String hostingNameStr, String abiOverride) {
+ return startProcessLocked(app, hostingType, hostingNameStr,
+ false /* disableHiddenApiChecks */, abiOverride);
+ }
+
/**
* @return {@code true} if process start is successful, false otherwise.
*/
@GuardedBy("this")
private final boolean startProcessLocked(ProcessRecord app, String hostingType,
- String hostingNameStr, String abiOverride) {
+ String hostingNameStr, boolean disableHiddenApiChecks, String abiOverride) {
if (app.pendingStart) {
return true;
}
@@ -4130,7 +4142,9 @@
runtimeFlags |= Zygote.ONLY_USE_SYSTEM_OAT_FILES;
}
- if (!app.info.isAllowedToUseHiddenApi() && !mHiddenApiBlacklist.isDisabled()) {
+ if (!app.info.isAllowedToUseHiddenApi() &&
+ !disableHiddenApiChecks &&
+ !mHiddenApiBlacklist.isDisabled()) {
// This app is not allowed to use undocumented and private APIs, or blacklisting is
// enabled. Set up its runtime with the appropriate flag.
runtimeFlags |= Zygote.ENABLE_HIDDEN_API_CHECKS;
@@ -10099,6 +10113,75 @@
}
/**
+ * Updates (grants or revokes) a persitable URI permission.
+ *
+ * @param uri URI to be granted or revoked.
+ * @param prefix if {@code false}, permission apply to this specific URI; if {@code true}, it
+ * applies to all URIs that are prefixed by this URI.
+ * @param packageName target package.
+ * @param grant if {@code true} a new permission will be granted, otherwise an existing
+ * permission will be revoked.
+ * @param userId user handle
+ *
+ * @return whether or not the requested succeeded.
+ *
+ * @deprecated TODO(b/72055774): caller should use takePersistableUriPermission() or
+ * releasePersistableUriPermission() instead, but such change will be made in a separate CL
+ * so it can be easily reverted if it breaks existing functionality.
+ */
+ @Deprecated // STOPSHIP if not removed
+ @Override
+ public boolean updatePersistableUriPermission(Uri uri, boolean prefix, String packageName,
+ boolean grant, int userId) {
+ enforceCallingPermission(android.Manifest.permission.GET_APP_GRANTED_URI_PERMISSIONS,
+ "updatePersistableUriPermission");
+ final int uid = mPackageManagerInt.getPackageUid(packageName, 0, userId);
+
+ final GrantUri grantUri = new GrantUri(userId, uri, prefix);
+
+ boolean persistChanged = false;
+ synchronized (this) {
+ if (grant) { // Grant
+ final String authority = uri.getAuthority();
+ final ProviderInfo pi = getProviderInfoLocked(authority, userId, 0);
+ if (pi == null) {
+ Slog.w(TAG, "No content provider found for authority " + authority);
+ return false;
+ }
+ final UriPermission permission = findOrCreateUriPermissionLocked(pi.packageName,
+ packageName, uid, grantUri);
+ if (permission.isNew()) {
+ final int modeFlags = Intent.FLAG_GRANT_READ_URI_PERMISSION
+ | Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
+ permission.initPersistedModes(modeFlags, System.currentTimeMillis());
+ persistChanged = true;
+ } else {
+ // Caller should not try to grant permission that is already granted.
+ Slog.w(TAG_URI_PERMISSION,
+ "permission already granted for " + grantUri.toSafeString());
+ return false;
+ }
+ persistChanged |= maybePrunePersistedUriGrantsLocked(uid);
+ } else { // Revoke
+ final UriPermission permission = findUriPermissionLocked(uid, grantUri);
+ if (permission == null) {
+ // Caller should not try to revoke permission that is not granted.
+ Slog.v(TAG_URI_PERMISSION, "no permission for " + grantUri.toSafeString());
+ return false;
+ } else {
+ permission.modeFlags = 0;
+ removeUriPermissionIfNeededLocked(permission);
+ persistChanged = true;
+ }
+ }
+ if (persistChanged) {
+ schedulePersistUriGrants();
+ }
+ }
+ return true;
+ }
+
+ /**
* @param uri This uri must NOT contain an embedded userId.
* @param userId The userId in which the uri is to be resolved.
*/
@@ -11362,9 +11445,6 @@
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 +11454,9 @@
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);
@@ -12782,6 +12865,12 @@
@GuardedBy("this")
final ProcessRecord addAppLocked(ApplicationInfo info, String customProcess, boolean isolated,
String abiOverride) {
+ return addAppLocked(info, customProcess, isolated, false /* disableHiddenApiChecks */,
+ abiOverride);
+ }
+
+ final ProcessRecord addAppLocked(ApplicationInfo info, String customProcess, boolean isolated,
+ boolean disableHiddenApiChecks, String abiOverride) {
ProcessRecord app;
if (!isolated) {
app = getProcessRecordLocked(customProcess != null ? customProcess : info.processName,
@@ -12813,7 +12902,8 @@
if (app.thread == null && mPersistentStartingProcesses.indexOf(app) < 0) {
mPersistentStartingProcesses.add(app);
startProcessLocked(app, "added application",
- customProcess != null ? customProcess : app.processName, abiOverride);
+ customProcess != null ? customProcess : app.processName, disableHiddenApiChecks,
+ abiOverride);
}
return app;
@@ -21624,7 +21714,10 @@
mUsageStatsService.reportEvent(ii.targetPackage, userId,
UsageEvents.Event.SYSTEM_INTERACTION);
}
- ProcessRecord app = addAppLocked(ai, defProcess, false, abiOverride);
+ boolean disableHiddenApiChecks =
+ (flags & INSTRUMENTATION_FLAG_DISABLE_HIDDEN_API_CHECKS) != 0;
+ ProcessRecord app = addAppLocked(ai, defProcess, false, disableHiddenApiChecks,
+ abiOverride);
app.instr = activeInstr;
activeInstr.mFinished = false;
activeInstr.mRunningProcesses.add(app);
@@ -26336,4 +26429,20 @@
}
}
}
+
+ @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/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 24a77c7..fa0df56 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -2608,7 +2608,7 @@
pw.println(" specified then send to all users.");
pw.println(" --receiver-permission <PERMISSION>: Require receiver to hold permission.");
pw.println(" instrument [-r] [-e <NAME> <VALUE>] [-p <FILE>] [-w]");
- pw.println(" [--user <USER_ID> | current]");
+ pw.println(" [--user <USER_ID> | current] [--no-hidden-api-checks]");
pw.println(" [--no-window-animation] [--abi <ABI>] <COMPONENT>");
pw.println(" Start an Instrumentation. Typically this target <COMPONENT> is in the");
pw.println(" form <TEST_PACKAGE>/<RUNNER_CLASS> or only <TEST_PACKAGE> if there");
@@ -2626,6 +2626,7 @@
pw.println(" test runners.");
pw.println(" --user <USER_ID> | current: Specify user instrumentation runs in;");
pw.println(" current user if not specified.");
+ pw.println(" --no-hidden-api-checks: disable restrictions on use of hidden API.");
pw.println(" --no-window-animation: turn off window animations while running.");
pw.println(" --abi <ABI>: Launch the instrumented process with the selected ABI.");
pw.println(" This assumes that the process supports the selected ABI.");
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 9838851..ddba349 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 @@
}
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 @@
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 055a1aa..812de88 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
|| 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 @@
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 @@
+ 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 4928e90..5c30764 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 @@
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 @@
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 da11f68..868f90d 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.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 @@
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 @@
mHandler = new StartHandler(mService.mHandlerThread.getLooper());
mFactory = factory;
mFactory.setController(this);
+ mPendingRemoteAnimationRegistry = new PendingRemoteAnimationRegistry(service,
+ service.mHandler);
}
/**
@@ -399,6 +404,15 @@
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 055b89b..8205265 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 @@
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
@@ -930,6 +932,7 @@
// Don't modify the client's object!
intent = new Intent(intent);
if (componentSpecified
+ && !(Intent.ACTION_VIEW.equals(intent.getAction()) && intent.getData() == null)
&& !Intent.ACTION_INSTALL_INSTANT_APP_PACKAGE.equals(intent.getAction())
&& !Intent.ACTION_RESOLVE_INSTANT_APP_PACKAGE.equals(intent.getAction())
&& mService.getPackageManagerInternalLocked()
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 0000000..77713f5
--- /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/am/UriPermission.java b/services/core/java/com/android/server/am/UriPermission.java
index 1e071aa..3bf1cf4 100644
--- a/services/core/java/com/android/server/am/UriPermission.java
+++ b/services/core/java/com/android/server/am/UriPermission.java
@@ -124,6 +124,10 @@
updateModeFlags();
}
+ boolean isNew() {
+ return persistedCreateTime == INVALID_TIME;
+ }
+
void grantModes(int modeFlags, UriPermissionOwner owner) {
final boolean persistable = (modeFlags & Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION) != 0;
modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index c5424b7..76e0d89 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 @@
}
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 @@
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 @@
* @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);
+ 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);
- }
- }
+ 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 @@
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/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index 85b70ca..a24f97e 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -392,6 +392,15 @@
return !isVPN() && numForegroundNetworkRequests() == 0 && mNumBackgroundNetworkRequests > 0;
}
+ /**
+ * Returns whether this network is currently suspended. A network is suspended if it is still
+ * connected but data temporarily fails to transfer. See {@link NetworkInfo.State#SUSPENDED}
+ * and {@link NetworkCapabilities#NET_CAPABILITY_NOT_SUSPENDED}.
+ */
+ public boolean isSuspended() {
+ return networkInfo.getState() == NetworkInfo.State.SUSPENDED;
+ }
+
// Does this network satisfy request?
public boolean satisfies(NetworkRequest request) {
return created &&
@@ -458,7 +467,7 @@
public NetworkState getNetworkState() {
synchronized (this) {
- // Network objects are outwardly immutable so there is no point to duplicating.
+ // Network objects are outwardly immutable so there is no point in duplicating.
// Duplicating also precludes sharing socket factories and connection pools.
final String subscriberId = (networkMisc != null) ? networkMisc.subscriberId : null;
return new NetworkState(new NetworkInfo(networkInfo),
diff --git a/services/core/java/com/android/server/content/SyncManagerConstants.java b/services/core/java/com/android/server/content/SyncManagerConstants.java
index 2f35687..3139d54 100644
--- a/services/core/java/com/android/server/content/SyncManagerConstants.java
+++ b/services/core/java/com/android/server/content/SyncManagerConstants.java
@@ -22,6 +22,8 @@
import android.util.KeyValueListParser;
import android.util.Slog;
+import com.android.internal.os.BackgroundThread;
+
import java.io.PrintWriter;
public class SyncManagerConstants extends ContentObserver {
@@ -57,9 +59,11 @@
}
public void start() {
- mContext.getContentResolver().registerContentObserver(Settings.Global.getUriFor(
- Settings.Global.SYNC_MANAGER_CONSTANTS), false, this);
- refresh();
+ BackgroundThread.getHandler().post(() -> {
+ mContext.getContentResolver().registerContentObserver(Settings.Global.getUriFor(
+ Settings.Global.SYNC_MANAGER_CONSTANTS), false, this);
+ refresh();
+ });
}
@Override
diff --git a/services/core/java/com/android/server/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java
index b5f94b1..3da3551 100644
--- a/services/core/java/com/android/server/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/fingerprint/FingerprintService.java
@@ -915,7 +915,7 @@
notifyLockoutResetMonitors();
}
- private class FingerprintServiceLockoutResetMonitor {
+ private class FingerprintServiceLockoutResetMonitor implements IBinder.DeathRecipient {
private static final long WAKELOCK_TIMEOUT_MS = 2000;
private final IFingerprintServiceLockoutResetCallback mCallback;
@@ -926,6 +926,11 @@
mCallback = callback;
mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
"lockout reset callback");
+ try {
+ mCallback.asBinder().linkToDeath(FingerprintServiceLockoutResetMonitor.this, 0);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "caught remote exception in linkToDeath", e);
+ }
}
public void sendLockoutReset() {
@@ -959,6 +964,12 @@
removeLockoutResetCallback(FingerprintServiceLockoutResetMonitor.this);
}
};
+
+ @Override
+ public void binderDied() {
+ Slog.e(TAG, "Lockout reset callback binder died");
+ mHandler.post(mRemoveCallbackRunnable);
+ }
}
private IBiometricsFingerprintClientCallback mDaemonCallback =
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index be48f69..c33d7f4 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -23,6 +23,7 @@
import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
+import android.app.AlarmManager;
import android.app.AppGlobals;
import android.app.IUidObserver;
import android.app.job.IJobScheduler;
@@ -184,6 +185,7 @@
IBatteryStats mBatteryStats;
DeviceIdleController.LocalService mLocalDeviceIdleController;
AppStateTracker mAppStateTracker;
+ final UsageStatsManagerInternal mUsageStats;
/**
* Set to true once we are allowed to run third party apps.
@@ -225,7 +227,10 @@
*/
final long[] mNextBucketHeartbeat = { 0, 0, 0, 0, Long.MAX_VALUE };
long mHeartbeat = 0;
- long mLastHeartbeatTime = 0;
+ long mLastHeartbeatTime = sElapsedRealtimeClock.millis();
+
+ static final String HEARTBEAT_TAG = "*job.heartbeat*";
+ final HeartbeatAlarmListener mHeartbeatAlarm = new HeartbeatAlarmListener();
// -- Pre-allocated temporaries only for use in assignJobsToContextsLocked --
@@ -495,6 +500,9 @@
STANDBY_BEATS[3] = mParser.getInt(KEY_STANDBY_RARE_BEATS,
DEFAULT_STANDBY_RARE_BEATS);
}
+
+ // Reset the heartbeat alarm based on the new heartbeat duration
+ setNextHeartbeatAlarm();
}
void dump(PrintWriter pw) {
@@ -1090,9 +1098,9 @@
mJobSchedulerStub = new JobSchedulerStub();
// Set up the app standby bucketing tracker
- UsageStatsManagerInternal usageStats = LocalServices.getService(UsageStatsManagerInternal.class);
- mStandbyTracker = new StandbyTracker(usageStats);
- usageStats.addAppIdleStateChangeListener(mStandbyTracker);
+ mStandbyTracker = new StandbyTracker();
+ mUsageStats = LocalServices.getService(UsageStatsManagerInternal.class);
+ mUsageStats.addAppIdleStateChangeListener(mStandbyTracker);
// The job store needs to call back
publishLocalService(JobSchedulerInternal.class, new LocalService());
@@ -1177,6 +1185,7 @@
mAppStateTracker = Preconditions.checkNotNull(
LocalServices.getService(AppStateTracker.class));
+ setNextHeartbeatAlarm();
// Register br for package removals and user removals.
final IntentFilter filter = new IntentFilter();
@@ -1418,6 +1427,23 @@
periodicToReschedule.getLastFailedRunTime());
}
+ long heartbeatWhenJobsLastRun(String packageName, final @UserIdInt int userId) {
+ final long heartbeat;
+ final long timeSinceLastJob = mUsageStats.getTimeSinceLastJobRun(packageName, userId);
+ synchronized (mLock) {
+ heartbeat = mHeartbeat - (timeSinceLastJob / mConstants.STANDBY_HEARTBEAT_TIME);
+ }
+ if (DEBUG_STANDBY) {
+ Slog.v(TAG, "Last job heartbeat " + heartbeat + " for " + packageName + "/" + userId
+ + " delta=" + timeSinceLastJob);
+ }
+ return heartbeat;
+ }
+
+ long heartbeatWhenJobsLastRun(JobStatus job) {
+ return heartbeatWhenJobsLastRun(job.getSourcePackageName(), job.getSourceUserId());
+ }
+
// JobCompletedListener implementations.
/**
@@ -1560,9 +1586,7 @@
noteJobsNonpending(mPendingJobs);
mPendingJobs.clear();
stopNonReadyActiveJobsLocked();
- boolean updated = updateStandbyHeartbeatLocked();
mJobs.forEachJob(mReadyQueueFunctor);
- if (updated) updateNextStandbyHeartbeatsLocked();
mReadyQueueFunctor.postProcess();
if (DEBUG) {
@@ -1716,36 +1740,78 @@
noteJobsNonpending(mPendingJobs);
mPendingJobs.clear();
stopNonReadyActiveJobsLocked();
- boolean updated = updateStandbyHeartbeatLocked();
mJobs.forEachJob(mMaybeQueueFunctor);
- if (updated) updateNextStandbyHeartbeatsLocked();
mMaybeQueueFunctor.postProcess();
}
- private boolean updateStandbyHeartbeatLocked() {
- final long sinceLast = sElapsedRealtimeClock.millis() - mLastHeartbeatTime;
- final long beatsElapsed = sinceLast / mConstants.STANDBY_HEARTBEAT_TIME;
- if (beatsElapsed > 0) {
- mHeartbeat += beatsElapsed;
- mLastHeartbeatTime += beatsElapsed * mConstants.STANDBY_HEARTBEAT_TIME;
- if (DEBUG_STANDBY) {
- Slog.v(TAG, "Advancing standby heartbeat by " + beatsElapsed + " to " + mHeartbeat);
+ /**
+ * Heartbeat tracking. The heartbeat alarm is intentionally non-wakeup.
+ */
+ class HeartbeatAlarmListener implements AlarmManager.OnAlarmListener {
+
+ @Override
+ public void onAlarm() {
+ synchronized (mLock) {
+ final long sinceLast = sElapsedRealtimeClock.millis() - mLastHeartbeatTime;
+ final long beatsElapsed = sinceLast / mConstants.STANDBY_HEARTBEAT_TIME;
+ if (beatsElapsed > 0) {
+ mLastHeartbeatTime += beatsElapsed * mConstants.STANDBY_HEARTBEAT_TIME;
+ advanceHeartbeatLocked(beatsElapsed);
+ }
}
- return true;
+ setNextHeartbeatAlarm();
}
- return false;
}
- private void updateNextStandbyHeartbeatsLocked() {
- // don't update ACTIVE or NEVER bucket milestones
+ // Intentionally does not touch the alarm timing
+ void advanceHeartbeatLocked(long beatsElapsed) {
+ mHeartbeat += beatsElapsed;
+ if (DEBUG_STANDBY) {
+ Slog.v(TAG, "Advancing standby heartbeat by " + beatsElapsed
+ + " to " + mHeartbeat);
+ }
+ // Don't update ACTIVE or NEVER bucket milestones. Note that mHeartbeat
+ // will be equal to mNextBucketHeartbeat[bucket] for one beat, during which
+ // new jobs scheduled by apps in that bucket will be permitted to run
+ // immediately.
+ boolean didAdvanceBucket = false;
for (int i = 1; i < mNextBucketHeartbeat.length - 1; i++) {
- while (mHeartbeat >= mNextBucketHeartbeat[i]) {
+ // Did we reach or cross a bucket boundary?
+ if (mHeartbeat >= mNextBucketHeartbeat[i]) {
+ didAdvanceBucket = true;
+ }
+ while (mHeartbeat > mNextBucketHeartbeat[i]) {
mNextBucketHeartbeat[i] += mConstants.STANDBY_BEATS[i];
}
if (DEBUG_STANDBY) {
- Slog.v(TAG, " Bucket " + i + " next heartbeat " + mNextBucketHeartbeat[i]);
+ Slog.v(TAG, " Bucket " + i + " next heartbeat "
+ + mNextBucketHeartbeat[i]);
}
}
+
+ if (didAdvanceBucket) {
+ if (DEBUG_STANDBY) {
+ Slog.v(TAG, "Hit bucket boundary; reevaluating job runnability");
+ }
+ mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
+ }
+ }
+
+ void setNextHeartbeatAlarm() {
+ final long heartbeatLength;
+ synchronized (mLock) {
+ heartbeatLength = mConstants.STANDBY_HEARTBEAT_TIME;
+ }
+ final long now = sElapsedRealtimeClock.millis();
+ final long nextBeatOrdinal = (now + heartbeatLength) / heartbeatLength;
+ final long nextHeartbeat = nextBeatOrdinal * heartbeatLength;
+ if (DEBUG_STANDBY) {
+ Slog.i(TAG, "Setting heartbeat alarm for " + nextHeartbeat
+ + " = " + TimeUtils.formatDuration(nextHeartbeat - now));
+ }
+ AlarmManager am = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
+ am.setExact(AlarmManager.ELAPSED_REALTIME, nextHeartbeat,
+ HEARTBEAT_TAG, mHeartbeatAlarm, mHandler);
}
/**
@@ -1811,17 +1877,20 @@
if (!mInParole && !job.getJob().isExemptedFromAppStandby()) {
final int bucket = job.getStandbyBucket();
if (mHeartbeat < mNextBucketHeartbeat[bucket]) {
- // Only skip this job if it's still waiting for the end of its (initial) nominal
+ // Only skip this job if the app is still waiting for the end of its nominal
// bucket interval. Once it's waited that long, we let it go ahead and clear.
// The final (NEVER) bucket is special; we never age those apps' jobs into
// runnability.
+ final long appLastRan = heartbeatWhenJobsLastRun(job);
if (bucket >= mConstants.STANDBY_BEATS.length
- || (mHeartbeat < job.getBaseHeartbeat() + mConstants.STANDBY_BEATS[bucket])) {
+ || (mHeartbeat > appLastRan
+ && mHeartbeat < appLastRan + mConstants.STANDBY_BEATS[bucket])) {
// TODO: log/trace that we're deferring the job due to bucketing if we hit this
if (job.getWhenStandbyDeferred() == 0) {
if (DEBUG_STANDBY) {
Slog.v(TAG, "Bucket deferral: " + mHeartbeat + " < "
- + mNextBucketHeartbeat[job.getStandbyBucket()] + " for " + job);
+ + (appLastRan + mConstants.STANDBY_BEATS[bucket])
+ + " for " + job);
}
job.setWhenStandbyDeferred(sElapsedRealtimeClock.millis());
}
@@ -2078,18 +2147,19 @@
// ACTIVE => everything can be run right away
// NEVER => we won't run them anyway, so let them go in the future
// as soon as the app enters normal use
+ if (DEBUG_STANDBY) {
+ Slog.v(TAG, "Base heartbeat forced ZERO for new job in "
+ + packageName + "/" + userId);
+ }
return 0;
}
- final long timeSinceLastJob = mStandbyTracker.getTimeSinceLastJobRun(
- packageName, userId);
- final long bucketLength = mConstants.STANDBY_BEATS[appStandbyBucket];
- final long bucketsAgo = timeSinceLastJob / bucketLength;
-
- // If we haven't run any jobs for more than the app's current bucket period, just
- // consider anything new to be immediately runnable. Otherwise, base it on the
- // bucket at which we last ran jobs.
- return (bucketsAgo > bucketLength) ? 0 : (getCurrentHeartbeat() - bucketsAgo);
+ final long baseHeartbeat = heartbeatWhenJobsLastRun(packageName, userId);
+ if (DEBUG_STANDBY) {
+ Slog.v(TAG, "Base heartbeat " + baseHeartbeat + " for new job in "
+ + packageName + "/" + userId);
+ }
+ return baseHeartbeat;
}
/**
@@ -2166,15 +2236,6 @@
* Tracking of app assignments to standby buckets
*/
final class StandbyTracker extends AppIdleStateChangeListener {
- final UsageStatsManagerInternal mUsageStats;
-
- StandbyTracker(UsageStatsManagerInternal usageStats) {
- mUsageStats = usageStats;
- }
-
- public long getTimeSinceLastJobRun(String packageName, final @UserIdInt int userId) {
- return mUsageStats.getTimeSinceLastJobRun(packageName, userId);
- }
// AppIdleStateChangeListener interface for live updates
@@ -2256,6 +2317,7 @@
else return 0;
}
+ // Static to support external callers
public static int standbyBucketForPackage(String packageName, int userId, long elapsedNow) {
UsageStatsManagerInternal usageStats = LocalServices.getService(
UsageStatsManagerInternal.class);
@@ -2682,6 +2744,7 @@
}
}
+ // Shell command infrastructure
int getJobState(PrintWriter pw, String pkgName, int userId, int jobId) {
try {
final int uid = AppGlobals.getPackageManager().getPackageUid(pkgName, 0,
@@ -2759,6 +2822,21 @@
return 0;
}
+ // Shell command infrastructure
+ int executeHeartbeatCommand(PrintWriter pw, int numBeats) {
+ if (numBeats < 1) {
+ pw.println(getCurrentHeartbeat());
+ return 0;
+ }
+
+ pw.print("Advancing standby heartbeat by ");
+ pw.println(numBeats);
+ synchronized (mLock) {
+ advanceHeartbeatLocked(numBeats);
+ }
+ return 0;
+ }
+
private String printContextIdToJobMap(JobStatus[] map, String initial) {
StringBuilder s = new StringBuilder(initial + ": ");
for (int i=0; i<map.length; i++) {
diff --git a/services/core/java/com/android/server/job/JobSchedulerShellCommand.java b/services/core/java/com/android/server/job/JobSchedulerShellCommand.java
index d630aab..63225f3 100644
--- a/services/core/java/com/android/server/job/JobSchedulerShellCommand.java
+++ b/services/core/java/com/android/server/job/JobSchedulerShellCommand.java
@@ -64,6 +64,8 @@
return getStorageNotLow(pw);
case "get-job-state":
return getJobState(pw);
+ case "heartbeat":
+ return doHeartbeat(pw);
default:
return handleDefaultCommands(cmd);
}
@@ -333,6 +335,20 @@
}
}
+ private int doHeartbeat(PrintWriter pw) throws Exception {
+ checkPermission("manipulate scheduler heartbeat");
+
+ final String arg = getNextArg();
+ final int numBeats = (arg != null) ? Integer.parseInt(arg) : 0;
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ return mInternal.executeHeartbeatCommand(pw, numBeats);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
@Override
public void onHelp() {
final PrintWriter pw = getOutPrintWriter();
@@ -359,6 +375,9 @@
pw.println(" Options:");
pw.println(" -u or --user: specify which user's job is to be run; the default is");
pw.println(" the primary or system user");
+ pw.println(" heartbeat [num]");
+ pw.println(" With no argument, prints the current standby heartbeat. With a positive");
+ pw.println(" argument, advances the standby heartbeat by that number.");
pw.println(" monitor-battery [on|off]");
pw.println(" Control monitoring of all battery changes. Off by default. Turning");
pw.println(" on makes get-battery-seq useful.");
diff --git a/services/core/java/com/android/server/job/JobServiceContext.java b/services/core/java/com/android/server/job/JobServiceContext.java
index 4988974..1f8cf76 100644
--- a/services/core/java/com/android/server/job/JobServiceContext.java
+++ b/services/core/java/com/android/server/job/JobServiceContext.java
@@ -240,11 +240,6 @@
}
}
- UsageStatsManagerInternal usageStats =
- LocalServices.getService(UsageStatsManagerInternal.class);
- usageStats.setLastJobRunTime(job.getSourcePackageName(), job.getSourceUserId(),
- mExecutionStartTimeElapsed);
-
// Once we'e begun executing a job, we by definition no longer care whether
// it was inflated from disk with not-yet-coherent delay/deadline bounds.
job.clearPersistedUtcTimes();
@@ -267,12 +262,16 @@
removeOpTimeOutLocked();
return false;
}
+ mJobPackageTracker.noteActive(job);
try {
mBatteryStats.noteJobStart(job.getBatteryName(), job.getSourceUid());
} catch (RemoteException e) {
// Whatever.
}
- mJobPackageTracker.noteActive(job);
+ UsageStatsManagerInternal usageStats =
+ LocalServices.getService(UsageStatsManagerInternal.class);
+ usageStats.setLastJobRunTime(job.getSourcePackageName(), job.getSourceUserId(),
+ mExecutionStartTimeElapsed);
mAvailable = false;
mStoppedReason = null;
mStoppedTime = 0;
diff --git a/services/core/java/com/android/server/job/controllers/JobStatus.java b/services/core/java/com/android/server/job/controllers/JobStatus.java
index 08ff7bd..3867306 100644
--- a/services/core/java/com/android/server/job/controllers/JobStatus.java
+++ b/services/core/java/com/android/server/job/controllers/JobStatus.java
@@ -1346,6 +1346,15 @@
}
pw.print(prefix); pw.print("Standby bucket: ");
pw.println(bucketName(standbyBucket));
+ if (standbyBucket > 0) {
+ pw.print(prefix); pw.print("Base heartbeat: ");
+ pw.println(baseHeartbeat);
+ }
+ if (whenStandbyDeferred != 0) {
+ pw.print(prefix); pw.print(" Deferred since: ");
+ TimeUtils.formatDuration(whenStandbyDeferred, elapsedRealtimeMillis, pw);
+ pw.println();
+ }
pw.print(prefix); pw.print("Enqueue time: ");
TimeUtils.formatDuration(enqueueTime, elapsedRealtimeMillis, pw);
pw.println();
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsStrongAuth.java b/services/core/java/com/android/server/locksettings/LockSettingsStrongAuth.java
index c9c9329..c4f1f3d 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 @@
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.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.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 @@
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 @@
}
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 @@
}
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 @@
}
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 @@
}
}
};
+
}
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 0000000..bb487fb
--- /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 f346629..e2b2d46 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.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 @@
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/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index 1746dd1..9bba9ed 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -289,14 +289,14 @@
int dexoptNeeded, @Nullable String outputPath, int dexFlags,
String compilerFilter, @Nullable String volumeUuid, @Nullable String sharedLibraries,
@Nullable String seInfo, boolean downgrade, int targetSdkVersion,
- @Nullable String profileName, @Nullable String dexMetadataPath)
- throws InstallerException {
+ @Nullable String profileName, @Nullable String dexMetadataPath,
+ @Nullable String compilationReason) throws InstallerException {
assertValidInstructionSet(instructionSet);
if (!checkBeforeRemote()) return;
try {
mInstalld.dexopt(apkPath, uid, pkgName, instructionSet, dexoptNeeded, outputPath,
dexFlags, compilerFilter, volumeUuid, sharedLibraries, seInfo, downgrade,
- targetSdkVersion, profileName, dexMetadataPath);
+ targetSdkVersion, profileName, dexMetadataPath, compilationReason);
} catch (Exception e) {
throw InstallerException.from(e);
}
diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java
index fc73142..9420a6c 100644
--- a/services/core/java/com/android/server/pm/OtaDexoptService.java
+++ b/services/core/java/com/android/server/pm/OtaDexoptService.java
@@ -262,11 +262,12 @@
int dexFlags, String compilerFilter, @Nullable String volumeUuid,
@Nullable String sharedLibraries, @Nullable String seInfo, boolean downgrade,
int targetSdkVersion, @Nullable String profileName,
- @Nullable String dexMetadataPath) throws InstallerException {
+ @Nullable String dexMetadataPath, @Nullable String dexoptCompilationReason)
+ throws InstallerException {
final StringBuilder builder = new StringBuilder();
- // The version. Right now it's 6.
- builder.append("6 ");
+ // The version. Right now it's 7.
+ builder.append("7 ");
builder.append("dexopt");
@@ -285,6 +286,7 @@
encodeParameter(builder, targetSdkVersion);
encodeParameter(builder, profileName);
encodeParameter(builder, dexMetadataPath);
+ encodeParameter(builder, dexoptCompilationReason);
commands.add(builder.toString());
}
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 458d725..77bf67d 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -34,7 +34,6 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.pm.Installer.InstallerException;
-import com.android.server.pm.dex.DexManager;
import com.android.server.pm.dex.DexoptOptions;
import com.android.server.pm.dex.DexoptUtils;
import com.android.server.pm.dex.PackageDexUsage;
@@ -63,7 +62,8 @@
import static com.android.server.pm.PackageManagerService.WATCHDOG_TIMEOUT;
-import static dalvik.system.DexFile.getNonProfileGuidedCompilerFilter;
+import static com.android.server.pm.PackageManagerServiceCompilerMapping.getReasonName;
+
import static dalvik.system.DexFile.getSafeModeCompilerFilter;
import static dalvik.system.DexFile.isProfileGuidedCompilerFilter;
@@ -231,7 +231,8 @@
for (String dexCodeIsa : dexCodeInstructionSets) {
int newResult = dexOptPath(pkg, path, dexCodeIsa, compilerFilter,
profileUpdated, classLoaderContexts[i], dexoptFlags, sharedGid,
- packageStats, options.isDowngrade(), profileName, dexMetadataPath);
+ packageStats, options.isDowngrade(), profileName, dexMetadataPath,
+ options.getCompilationReason());
// The end result is:
// - FAILED if any path failed,
// - PERFORMED if at least one path needed compilation,
@@ -256,7 +257,7 @@
private int dexOptPath(PackageParser.Package pkg, String path, String isa,
String compilerFilter, boolean profileUpdated, String classLoaderContext,
int dexoptFlags, int uid, CompilerStats.PackageStats packageStats, boolean downgrade,
- String profileName, String dexMetadataPath) {
+ String profileName, String dexMetadataPath, int compilationReason) {
int dexoptNeeded = getDexoptNeeded(path, isa, compilerFilter, classLoaderContext,
profileUpdated, downgrade);
if (Math.abs(dexoptNeeded) == DexFile.NO_DEXOPT_NEEDED) {
@@ -283,7 +284,7 @@
mInstaller.dexopt(path, uid, pkg.packageName, isa, dexoptNeeded, oatDir, dexoptFlags,
compilerFilter, pkg.volumeUuid, classLoaderContext, pkg.applicationInfo.seInfo,
false /* downgrade*/, pkg.applicationInfo.targetSdkVersion,
- profileName, dexMetadataPath);
+ profileName, dexMetadataPath, getReasonName(compilationReason));
if (packageStats != null) {
long endTime = System.currentTimeMillis();
@@ -394,7 +395,7 @@
// Note this trades correctness for performance since the resulting slow down is
// unacceptable in some cases until b/64530081 is fixed.
String classLoaderContext = SKIP_SHARED_LIBRARY_CHECK;
-
+ int reason = options.getCompilationReason();
try {
for (String isa : dexUseInfo.getLoaderIsas()) {
// Reuse the same dexopt path as for the primary apks. We don't need all the
@@ -405,7 +406,7 @@
/*oatDir*/ null, dexoptFlags,
compilerFilter, info.volumeUuid, classLoaderContext, info.seInfoUser,
options.isDowngrade(), info.targetSdkVersion, /*profileName*/ null,
- /*dexMetadataPath*/ null);
+ /*dexMetadataPath*/ null, getReasonName(reason));
}
return DEX_OPT_PERFORMED;
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 7ca6bb95c..884606d2 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -598,6 +598,7 @@
}
// Compilation reasons.
+ public static final int REASON_UNKNOWN = -1;
public static final int REASON_FIRST_BOOT = 0;
public static final int REASON_BOOT = 1;
public static final int REASON_INSTALL = 2;
@@ -8836,7 +8837,7 @@
final long startTime = System.nanoTime();
final int[] stats = performDexOptUpgrade(pkgs, mIsPreNUpgrade /* showDialog */,
- getCompilerFilterForReason(causeFirstBoot ? REASON_FIRST_BOOT : REASON_BOOT),
+ causeFirstBoot ? REASON_FIRST_BOOT : REASON_BOOT,
false /* bootComplete */);
final int elapsedTimeSeconds =
@@ -8863,7 +8864,7 @@
* and {@code numberOfPackagesFailed}.
*/
private int[] performDexOptUpgrade(List<PackageParser.Package> pkgs, boolean showDialog,
- final String compilerFilter, boolean bootComplete) {
+ final int compilationReason, boolean bootComplete) {
int numberOfPackagesVisited = 0;
int numberOfPackagesOptimized = 0;
@@ -8963,13 +8964,11 @@
}
}
- String pkgCompilerFilter = compilerFilter;
+ int pkgCompilationReason = compilationReason;
if (useProfileForDexopt) {
// Use background dexopt mode to try and use the profile. Note that this does not
// guarantee usage of the profile.
- pkgCompilerFilter =
- PackageManagerServiceCompilerMapping.getCompilerFilterForReason(
- PackageManagerService.REASON_BACKGROUND_DEXOPT);
+ pkgCompilationReason = PackageManagerService.REASON_BACKGROUND_DEXOPT;
}
// checkProfiles is false to avoid merging profiles during boot which
@@ -8980,7 +8979,7 @@
int dexoptFlags = bootComplete ? DexoptOptions.DEXOPT_BOOT_COMPLETE : 0;
int primaryDexOptStaus = performDexOptTraced(new DexoptOptions(
pkg.packageName,
- pkgCompilerFilter,
+ pkgCompilationReason,
dexoptFlags));
switch (primaryDexOptStaus) {
@@ -9081,8 +9080,8 @@
int flags = (checkProfiles ? DexoptOptions.DEXOPT_CHECK_FOR_PROFILES_UPDATES : 0) |
(force ? DexoptOptions.DEXOPT_FORCE : 0) |
(bootComplete ? DexoptOptions.DEXOPT_BOOT_COMPLETE : 0);
- return performDexOpt(new DexoptOptions(packageName, targetCompilerFilter,
- splitName, flags));
+ return performDexOpt(new DexoptOptions(packageName, REASON_UNKNOWN,
+ targetCompilerFilter, splitName, flags));
}
/**
@@ -9191,7 +9190,8 @@
final String[] instructionSets = getAppDexInstructionSets(p.applicationInfo);
if (!deps.isEmpty()) {
DexoptOptions libraryOptions = new DexoptOptions(options.getPackageName(),
- options.getCompilerFilter(), options.getSplitName(),
+ options.getCompilationReason(), options.getCompilerFilter(),
+ options.getSplitName(),
options.getFlags() | DexoptOptions.DEXOPT_AS_SHARED_LIBRARY);
for (PackageParser.Package depPackage : deps) {
// TODO: Analyze and investigate if we (should) profile libraries.
@@ -20644,10 +20644,6 @@
@Override
public String getInstallerPackageName(String packageName) {
final int callingUid = Binder.getCallingUid();
- if (getInstantAppPackageName(callingUid) != null) {
- return null;
- }
- // reader
synchronized (mPackages) {
final PackageSetting ps = mSettings.mPackages.get(packageName);
if (filterAppAccessLPr(ps, callingUid, UserHandle.getUserId(callingUid))) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java b/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java
index 19b0d9b..fce8285 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java
@@ -123,4 +123,14 @@
return value;
}
+
+ public static String getReasonName(int reason) {
+ if (reason == PackageManagerService.REASON_UNKNOWN) {
+ return "unknown";
+ }
+ if (reason < 0 || reason >= REASON_STRINGS.length) {
+ throw new IllegalArgumentException("reason " + reason + " invalid");
+ }
+ return REASON_STRINGS[reason];
+ }
}
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index a0577b1..0eeaf66 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 @@
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 23185d7..41570c4 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.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 @@
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/core/java/com/android/server/pm/dex/DexManager.java b/services/core/java/com/android/server/pm/dex/DexManager.java
index 0e2730c..3e63fb4 100644
--- a/services/core/java/com/android/server/pm/dex/DexManager.java
+++ b/services/core/java/com/android/server/pm/dex/DexManager.java
@@ -549,13 +549,12 @@
mPackageDexUsage.maybeWriteAsync();
}
- // Try to optimize the package according to the install reason.
- String compilerFilter = PackageManagerServiceCompilerMapping.getCompilerFilterForReason(
- PackageManagerService.REASON_INSTALL);
DexUseInfo dexUseInfo = mPackageDexUsage.getPackageUseInfo(searchResult.mOwningPackageName)
.getDexUseInfoMap().get(dexPath);
- DexoptOptions options = new DexoptOptions(info.packageName, compilerFilter, /*flags*/0);
+ // Try to optimize the package according to the install reason.
+ DexoptOptions options = new DexoptOptions(info.packageName,
+ PackageManagerService.REASON_INSTALL, /*flags*/0);
int result = mPackageDexOptimizer.dexOptSecondaryDexPath(info, dexPath, dexUseInfo,
options);
diff --git a/services/core/java/com/android/server/pm/dex/DexoptOptions.java b/services/core/java/com/android/server/pm/dex/DexoptOptions.java
index d4f95cb..a7a7686 100644
--- a/services/core/java/com/android/server/pm/dex/DexoptOptions.java
+++ b/services/core/java/com/android/server/pm/dex/DexoptOptions.java
@@ -77,15 +77,21 @@
// It only applies for primary apk and it's always null if mOnlySecondaryDex is true.
private final String mSplitName;
+ // The reason for invoking dexopt (see PackageManagerService.REASON_* constants).
+ // A -1 value denotes an unknown reason.
+ private final int mCompilationReason;
+
public DexoptOptions(String packageName, String compilerFilter, int flags) {
- this(packageName, compilerFilter, /*splitName*/ null, flags);
+ this(packageName, /*compilationReason*/ -1, compilerFilter, /*splitName*/ null, flags);
}
- public DexoptOptions(String packageName, int compilerReason, int flags) {
- this(packageName, getCompilerFilterForReason(compilerReason), flags);
+ public DexoptOptions(String packageName, int compilationReason, int flags) {
+ this(packageName, compilationReason, getCompilerFilterForReason(compilationReason),
+ /*splitName*/ null, flags);
}
- public DexoptOptions(String packageName, String compilerFilter, String splitName, int flags) {
+ public DexoptOptions(String packageName, int compilationReason, String compilerFilter,
+ String splitName, int flags) {
int validityMask =
DEXOPT_CHECK_FOR_PROFILES_UPDATES |
DEXOPT_FORCE |
@@ -104,6 +110,7 @@
mCompilerFilter = compilerFilter;
mFlags = flags;
mSplitName = splitName;
+ mCompilationReason = compilationReason;
}
public String getPackageName() {
@@ -157,4 +164,8 @@
public int getFlags() {
return mFlags;
}
+
+ public int getCompilationReason() {
+ return mCompilationReason;
+ }
}
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySavingStats.java b/services/core/java/com/android/server/power/batterysaver/BatterySavingStats.java
index 37df94f..5d76329 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySavingStats.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySavingStats.java
@@ -15,6 +15,7 @@
*/
package com.android.server.power.batterysaver;
+import android.metrics.LogMaker;
import android.os.BatteryManagerInternal;
import android.os.SystemClock;
import android.util.ArrayMap;
@@ -23,6 +24,8 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.server.EventLogTags;
import com.android.server.LocalServices;
import com.android.server.power.BatterySaverPolicy;
@@ -43,6 +46,9 @@
private static final boolean DEBUG = BatterySaverPolicy.DEBUG;
+ @VisibleForTesting
+ static final boolean SEND_TRON_EVENTS = true;
+
private final Object mLock = new Object();
/** Whether battery saver is on or off. */
@@ -132,15 +138,6 @@
}
}
- @VisibleForTesting
- static final String COUNTER_POWER_PERCENT_PREFIX = "battery_saver_stats_percent_";
-
- @VisibleForTesting
- static final String COUNTER_POWER_MILLIAMPS_PREFIX = "battery_saver_stats_milliamps_";
-
- @VisibleForTesting
- static final String COUNTER_TIME_SECONDS_PREFIX = "battery_saver_stats_seconds_";
-
private static BatterySavingStats sInstance;
private BatteryManagerInternal mBatteryManagerInternal;
@@ -427,10 +424,9 @@
if (stateChanging) {
if (mLastState >= 0) {
final long deltaTime = now - mStartTime;
- final int deltaBattery = mStartBatteryLevel - batteryLevel;
- final int deltaPercent = mStartPercent - batteryPercent;
- report(mLastState, deltaTime, deltaBattery, deltaPercent);
+ report(mLastState, deltaTime, mStartBatteryLevel, mStartPercent,
+ batteryLevel, batteryPercent);
}
mStartTime = now;
mStartBatteryLevel = batteryLevel;
@@ -439,23 +435,28 @@
mLastState = newState;
}
- String getCounterSuffix(int state) {
- final boolean batterySaver =
+ void report(int state, long deltaTimeMs,
+ int startBatteryLevelUa, int startBatteryLevelPercent,
+ int endBatteryLevelUa, int endBatteryLevelPercent) {
+ if (!SEND_TRON_EVENTS) {
+ return;
+ }
+ final boolean batterySaverOn =
BatterySaverState.fromIndex(state) != BatterySaverState.OFF;
final boolean interactive =
InteractiveState.fromIndex(state) != InteractiveState.NON_INTERACTIVE;
- if (batterySaver) {
- return interactive ? "11" : "10";
- } else {
- return interactive ? "01" : "00";
- }
- }
- void report(int state, long deltaTimeMs, int deltaBatteryUa, int deltaPercent) {
- final String suffix = getCounterSuffix(state);
- mMetricsLogger.count(COUNTER_POWER_MILLIAMPS_PREFIX + suffix, deltaBatteryUa / 1000);
- mMetricsLogger.count(COUNTER_POWER_PERCENT_PREFIX + suffix, deltaPercent);
- mMetricsLogger.count(COUNTER_TIME_SECONDS_PREFIX + suffix, (int) (deltaTimeMs / 1000));
+ final LogMaker logMaker = new LogMaker(MetricsProto.MetricsEvent.BATTERY_SAVER)
+ .setSubtype(batterySaverOn ? 1 : 0)
+ .addTaggedData(MetricsEvent.FIELD_INTERACTIVE, interactive ? 1 : 0)
+ .addTaggedData(MetricsEvent.FIELD_DURATION_MILLIS, deltaTimeMs)
+ .addTaggedData(MetricsEvent.FIELD_START_BATTERY_UA, startBatteryLevelUa)
+ .addTaggedData(MetricsEvent.FIELD_START_BATTERY_PERCENT,
+ startBatteryLevelPercent)
+ .addTaggedData(MetricsEvent.FIELD_END_BATTERY_UA, endBatteryLevelUa)
+ .addTaggedData(MetricsEvent.FIELD_END_BATTERY_PERCENT, endBatteryLevelPercent);
+
+ mMetricsLogger.write(logMaker);
}
}
}
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index 9f9b1af..fa7e535 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -172,7 +172,6 @@
public void sendSubscriberBroadcast(IBinder intentSenderBinder, long configUid, long configKey,
long subscriptionId, long subscriptionRuleId,
StatsDimensionsValue dimensionsValue) {
- if (DEBUG) Slog.d(TAG, "Statsd requested to sendSubscriberBroadcast.");
enforceCallingPermission();
IntentSender intentSender = new IntentSender(intentSenderBinder);
Intent intent = new Intent()
@@ -181,16 +180,16 @@
.putExtra(StatsManager.EXTRA_STATS_SUBSCRIPTION_ID, subscriptionId)
.putExtra(StatsManager.EXTRA_STATS_SUBSCRIPTION_RULE_ID, subscriptionRuleId)
.putExtra(StatsManager.EXTRA_STATS_DIMENSIONS_VALUE, dimensionsValue);
+ if (DEBUG) {
+ Slog.d(TAG, String.format("Statsd sendSubscriberBroadcast with params {%d %d %d %d %s}",
+ configUid, configKey, subscriptionId,
+ subscriptionRuleId, dimensionsValue));
+ }
try {
intentSender.sendIntent(mContext, CODE_SUBSCRIBER_BROADCAST, intent, null, null);
} catch (IntentSender.SendIntentException e) {
Slog.w(TAG, "Unable to send using IntentSender from uid " + configUid
+ "; presumably it had been cancelled.");
- if (DEBUG) {
- Slog.d(TAG, String.format("SubscriberBroadcast params {%d %d %d %d %s}",
- configUid, configKey, subscriptionId,
- subscriptionRuleId, dimensionsValue));
- }
}
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 75bb5e4..61c8b79 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -196,7 +196,7 @@
private static final String THERMAL_OBSERVER_CLASS =
"com.google.android.clockwork.ThermalObserver";
private static final String WEAR_CONNECTIVITY_SERVICE_CLASS =
- "com.google.android.clockwork.connectivity.WearConnectivityService";
+ "com.android.clockwork.connectivity.WearConnectivityService";
private static final String WEAR_SIDEKICK_SERVICE_CLASS =
"com.google.android.clockwork.sidekick.SidekickService";
private static final String WEAR_DISPLAY_SERVICE_CLASS =
diff --git a/services/robotests/src/com/android/server/backup/TransportManagerTest.java b/services/robotests/src/com/android/server/backup/TransportManagerTest.java
index 44ac803..503adb2 100644
--- a/services/robotests/src/com/android/server/backup/TransportManagerTest.java
+++ b/services/robotests/src/com/android/server/backup/TransportManagerTest.java
@@ -19,14 +19,12 @@
import static com.android.server.backup.testing.TransportData.genericTransport;
import static com.android.server.backup.testing.TransportTestUtils.mockTransport;
import static com.android.server.backup.testing.TransportTestUtils.setUpTransportsForTransportManager;
+
import static com.google.common.truth.Truth.assertThat;
-import static java.util.Arrays.asList;
-import static java.util.Collections.emptyList;
-import static java.util.Collections.singletonList;
-import static java.util.stream.Collectors.toList;
-import static java.util.stream.Collectors.toSet;
-import static java.util.stream.Stream.concat;
+
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
@@ -35,6 +33,13 @@
import static org.robolectric.shadow.api.Shadow.extract;
import static org.testng.Assert.expectThrows;
+import static java.util.Arrays.asList;
+import static java.util.Collections.emptyList;
+import static java.util.Collections.singletonList;
+import static java.util.stream.Collectors.toList;
+import static java.util.stream.Collectors.toSet;
+import static java.util.stream.Stream.concat;
+
import android.annotation.Nullable;
import android.app.backup.BackupManager;
import android.content.ComponentName;
@@ -43,6 +48,7 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.platform.test.annotations.Presubmit;
+
import com.android.server.backup.testing.ShadowContextImplForBackup;
import com.android.server.backup.testing.TransportData;
import com.android.server.backup.testing.TransportTestUtils.TransportMock;
@@ -54,11 +60,7 @@
import com.android.server.testing.SystemLoaderPackages;
import com.android.server.testing.shadows.FrameworkShadowContextImpl;
import com.android.server.testing.shadows.FrameworkShadowPackageManager;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Objects;
-import java.util.Set;
-import java.util.stream.Stream;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -69,6 +71,12 @@
import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowPackageManager;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Stream;
+
@RunWith(FrameworkRobolectricTestRunner.class)
@Config(
manifest = Config.NONE,
@@ -81,6 +89,12 @@
private static final String PACKAGE_A = "some.package.a";
private static final String PACKAGE_B = "some.package.b";
+ /**
+ * GMSCore depends on this constant so we define it here on top of the definition in
+ * {@link TransportManager} to verify this extra is passed
+ */
+ private static final String EXTRA_TRANSPORT_REGISTRATION = "transport_registration";
+
@Mock private OnTransportRegisteredListener mListener;
@Mock private TransportClientManager mTransportClientManager;
private TransportData mTransportA1;
@@ -195,6 +209,22 @@
}
@Test
+ public void testRegisterTransports_passesRegistrationExtraToGetTransportClient()
+ throws Exception {
+ setUpPackage(PACKAGE_A, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
+ setUpTransports(mTransportA1);
+ TransportManager transportManager = createTransportManager(mTransportA1);
+
+ transportManager.registerTransports();
+
+ verify(mTransportClientManager)
+ .getTransportClient(
+ eq(mTransportA1.getTransportComponent()),
+ argThat(bundle -> bundle.getBoolean(EXTRA_TRANSPORT_REGISTRATION)),
+ anyString());
+ }
+
+ @Test
public void testOnPackageAdded_registerTransports() throws Exception {
setUpPackage(PACKAGE_A, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
setUpTransports(mTransportA1);
@@ -580,6 +610,9 @@
when(mTransportClientManager.getTransportClient(
eq(transport.getTransportComponent()), any()))
.thenReturn(transportMock.transportClient);
+ when(mTransportClientManager.getTransportClient(
+ eq(transport.getTransportComponent()), any(), any()))
+ .thenReturn(transportMock.transportClient);
transportMocks.add(transportMock);
}
return transportMocks;
diff --git a/services/robotests/src/com/android/server/backup/transport/TransportClientManagerTest.java b/services/robotests/src/com/android/server/backup/transport/TransportClientManagerTest.java
new file mode 100644
index 0000000..5e3c974
--- /dev/null
+++ b/services/robotests/src/com/android/server/backup/transport/TransportClientManagerTest.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.backup.transport;
+
+import static com.android.server.backup.TransportManager.SERVICE_ACTION_TRANSPORT_HOST;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.argThat;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.platform.test.annotations.Presubmit;
+import com.android.server.testing.FrameworkRobolectricTestRunner;
+import com.android.server.testing.SystemLoaderPackages;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentMatcher;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+
+@RunWith(FrameworkRobolectricTestRunner.class)
+@Config(manifest = Config.NONE, sdk = 26)
+@SystemLoaderPackages({"com.android.server.backup"})
+@Presubmit
+public class TransportClientManagerTest {
+
+ private static final String PACKAGE_NAME = "random.package.name";
+ private static final String CLASS_NAME = "random.package.name.transport.Transport";
+
+ @Mock private Context mContext;
+ @Mock private TransportConnectionListener mTransportConnectionListener;
+ private TransportClientManager mTransportClientManager;
+ private ComponentName mTransportComponent;
+ private Intent mBindIntent;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ mTransportClientManager = new TransportClientManager(mContext);
+ mTransportComponent = new ComponentName(PACKAGE_NAME, CLASS_NAME);
+ mBindIntent = new Intent(SERVICE_ACTION_TRANSPORT_HOST).setComponent(mTransportComponent);
+
+ when(mContext.bindServiceAsUser(
+ any(Intent.class),
+ any(ServiceConnection.class),
+ anyInt(),
+ any(UserHandle.class)))
+ .thenReturn(true);
+ }
+
+ @Test
+ public void testGetTransportClient_withExtras_createsTransportClientWithCorrectIntent() {
+ Bundle extras = new Bundle();
+ extras.putBoolean("random_extra", true);
+ mBindIntent.putExtras(extras);
+
+ TransportClient transportClient =
+ mTransportClientManager.getTransportClient(mTransportComponent, extras, "caller");
+
+ transportClient.connectAsync(mTransportConnectionListener, "caller");
+ verify(mContext)
+ .bindServiceAsUser(
+ argThat(matchesIntentAndExtras(mBindIntent)),
+ any(ServiceConnection.class),
+ anyInt(),
+ any(UserHandle.class));
+ }
+
+ private ArgumentMatcher<Intent> matchesIntentAndExtras(Intent expectedIntent) {
+ return (Intent actualIntent) -> {
+ if (!expectedIntent.filterEquals(actualIntent)) {
+ return false;
+ }
+
+ Bundle expectedExtras = expectedIntent.getExtras();
+ Bundle actualExtras = actualIntent.getExtras();
+
+ if (expectedExtras == null && actualExtras == null) {
+ return true;
+ }
+
+ if (expectedExtras == null || actualExtras == null) {
+ return false;
+ }
+
+ if (expectedExtras.size() != actualExtras.size()) {
+ return false;
+ }
+
+ for (String key : expectedExtras.keySet()) {
+ if (!expectedExtras.get(key).equals(actualExtras.get(key))) {
+ return false;
+ }
+ }
+
+ return true;
+ };
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationGestureHandlerTest.java
index 07262e1..23fe0ff 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationGestureHandlerTest.java
@@ -34,7 +34,6 @@
import android.annotation.NonNull;
import android.content.Context;
-import android.os.Handler;
import android.os.Message;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
@@ -49,9 +48,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
-import java.util.concurrent.CompletableFuture;
import java.util.function.IntConsumer;
-import java.util.function.Supplier;
/**
@@ -283,6 +280,19 @@
verify(mMgh.getNext(), times(2)).onMotionEvent(any(), any(), anyInt());
}
+ @Test
+ public void testTripleTapAndHold_zoomsImmediately() {
+ assertZoomsImmediatelyOnSwipeFrom(STATE_2TAPS);
+ assertZoomsImmediatelyOnSwipeFrom(STATE_SHORTCUT_TRIGGERED);
+ }
+
+ private void assertZoomsImmediatelyOnSwipeFrom(int state) {
+ goFromStateIdleTo(state);
+ swipeAndHold();
+ assertIn(STATE_DRAGGING_TMP);
+ returnToNormalFrom(STATE_DRAGGING_TMP);
+ }
+
private void assertTransition(int fromState, Runnable transitionAction, int toState) {
goFromStateIdleTo(fromState);
transitionAction.run();
@@ -339,11 +349,13 @@
check(tapCount() == 2, state);
} break;
case STATE_DRAGGING: {
+ check(isZoomed(), state);
check(mMgh.mCurrentState == mMgh.mViewportDraggingState,
state);
check(mMgh.mViewportDraggingState.mZoomedInBeforeDrag, state);
} break;
case STATE_DRAGGING_TMP: {
+ check(isZoomed(), state);
check(mMgh.mCurrentState == mMgh.mViewportDraggingState,
state);
check(!mMgh.mViewportDraggingState.mZoomedInBeforeDrag, state);
@@ -353,11 +365,13 @@
check(!isZoomed(), state);
} break;
case STATE_PANNING: {
+ check(isZoomed(), state);
check(mMgh.mCurrentState == mMgh.mPanningScalingState,
state);
check(!mMgh.mPanningScalingState.mScaling, state);
} break;
case STATE_SCALING_AND_PANNING: {
+ check(isZoomed(), state);
check(mMgh.mCurrentState == mMgh.mPanningScalingState,
state);
check(mMgh.mPanningScalingState.mScaling, state);
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 0000000..2baf995
--- /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 0000000..bb487fb
--- /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/services/tests/servicestests/src/com/android/server/pm/dex/DexoptOptionsTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexoptOptionsTests.java
index f559986a..93064bc 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DexoptOptionsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexoptOptionsTests.java
@@ -118,7 +118,7 @@
public void testCreateDexoptOptionsSplit() {
int flags = DexoptOptions.DEXOPT_FORCE | DexoptOptions.DEXOPT_BOOT_COMPLETE;
- DexoptOptions opt = new DexoptOptions(mPackageName, mCompilerFilter, mSplitName, flags);
+ DexoptOptions opt = new DexoptOptions(mPackageName, -1, mCompilerFilter, mSplitName, flags);
assertEquals(mPackageName, opt.getPackageName());
assertEquals(mCompilerFilter, opt.getCompilerFilter());
assertEquals(mSplitName, opt.getSplitName());
diff --git a/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySavingStatsTest.java b/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySavingStatsTest.java
index f7516b2..f7112d4 100644
--- a/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySavingStatsTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySavingStatsTest.java
@@ -15,25 +15,30 @@
*/
package com.android.server.power.batterysaver;
+import static com.android.server.power.batterysaver.BatterySavingStats.SEND_TRON_EVENTS;
+
import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import android.metrics.LogMaker;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.server.power.batterysaver.BatterySavingStats.BatterySaverState;
import com.android.server.power.batterysaver.BatterySavingStats.DozeState;
import com.android.server.power.batterysaver.BatterySavingStats.InteractiveState;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import java.io.ByteArrayOutputStream;
import java.io.PrintWriter;
@@ -222,8 +227,28 @@
target.toDebugString());
}
- private void assertMetricsLog(String counter, int value) {
- verify(mMetricsLogger, times(1)).count(eq(counter), eq(value));
+ private void assertLog(boolean batterySaver, boolean interactive, long deltaTimeMs,
+ int deltaBatteryLevelUa, int deltaBatteryLevelPercent) {
+ if (SEND_TRON_EVENTS) {
+ ArgumentCaptor<LogMaker> ac = ArgumentCaptor.forClass(LogMaker.class);
+ verify(mMetricsLogger, times(1)).write(ac.capture());
+
+ LogMaker lm = ac.getValue();
+ assertEquals(MetricsEvent.BATTERY_SAVER, lm.getCategory());
+ assertEquals(batterySaver ? 1 : 0,
+ lm.getTaggedData(MetricsEvent.RESERVED_FOR_LOGBUILDER_SUBTYPE));
+ assertEquals(interactive ? 1 : 0, lm.getTaggedData(MetricsEvent.FIELD_INTERACTIVE));
+ assertEquals(deltaTimeMs, lm.getTaggedData(MetricsEvent.FIELD_DURATION_MILLIS));
+
+ assertEquals(deltaBatteryLevelUa,
+ (int) lm.getTaggedData(MetricsEvent.FIELD_START_BATTERY_UA)
+ - (int) lm.getTaggedData(MetricsEvent.FIELD_END_BATTERY_UA));
+ assertEquals(deltaBatteryLevelPercent,
+ (int) lm.getTaggedData(MetricsEvent.FIELD_START_BATTERY_PERCENT)
+ - (int) lm.getTaggedData(MetricsEvent.FIELD_END_BATTERY_PERCENT));
+ } else {
+ verify(mMetricsLogger, times(0)).write(any(LogMaker.class));
+ }
}
@Test
@@ -249,9 +274,7 @@
InteractiveState.NON_INTERACTIVE,
DozeState.NOT_DOZING);
- assertMetricsLog(BatterySavingStats.COUNTER_POWER_MILLIAMPS_PREFIX + "01", 2);
- assertMetricsLog(BatterySavingStats.COUNTER_POWER_PERCENT_PREFIX + "01", 200);
- assertMetricsLog(BatterySavingStats.COUNTER_TIME_SECONDS_PREFIX + "01", 60);
+ assertLog(false, true, 60_000, 2000, 200);
target.advanceClock(1);
target.drainBattery(2000);
@@ -282,9 +305,7 @@
InteractiveState.INTERACTIVE,
DozeState.NOT_DOZING);
- assertMetricsLog(BatterySavingStats.COUNTER_POWER_MILLIAMPS_PREFIX + "00", 2 * 3);
- assertMetricsLog(BatterySavingStats.COUNTER_POWER_PERCENT_PREFIX + "00", 200 * 3);
- assertMetricsLog(BatterySavingStats.COUNTER_TIME_SECONDS_PREFIX + "00", 60 * 3);
+ assertLog(false, false, 60_000 * 3, 2000 * 3, 200 * 3);
target.advanceClock(10);
target.drainBattery(10000);
@@ -292,9 +313,7 @@
reset(mMetricsLogger);
target.startCharging();
- assertMetricsLog(BatterySavingStats.COUNTER_POWER_MILLIAMPS_PREFIX + "11", 10);
- assertMetricsLog(BatterySavingStats.COUNTER_POWER_PERCENT_PREFIX + "11", 1000);
- assertMetricsLog(BatterySavingStats.COUNTER_TIME_SECONDS_PREFIX + "11", 60 * 10);
+ assertLog(true, true, 60_000 * 10, 10000, 1000);
target.advanceClock(1);
target.drainBattery(2000);
@@ -312,8 +331,6 @@
target.startCharging();
- assertMetricsLog(BatterySavingStats.COUNTER_POWER_MILLIAMPS_PREFIX + "10", 2);
- assertMetricsLog(BatterySavingStats.COUNTER_POWER_PERCENT_PREFIX + "10", 200);
- assertMetricsLog(BatterySavingStats.COUNTER_TIME_SECONDS_PREFIX + "10", 60);
+ assertLog(true, false, 60_000, 2000, 200);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/testutils/TestHandler.java b/services/tests/servicestests/src/com/android/server/testutils/TestHandler.java
index 029d9f1..1222b59 100644
--- a/services/tests/servicestests/src/com/android/server/testutils/TestHandler.java
+++ b/services/tests/servicestests/src/com/android/server/testutils/TestHandler.java
@@ -16,10 +16,11 @@
package com.android.server.testutils;
-import static android.util.ExceptionUtils.getRootCause;
+import static android.util.ExceptionUtils.appendCause;
import static android.util.ExceptionUtils.propagate;
import android.os.Handler;
+import android.os.Looper;
import android.os.Message;
import android.os.SystemClock;
import android.util.ArrayMap;
@@ -60,7 +61,7 @@
}
public TestHandler(Callback callback, LongSupplier clock) {
- super(callback);
+ super(Looper.getMainLooper(), callback);
mClock = clock;
}
@@ -132,7 +133,7 @@
} catch (Throwable t) {
// Append stack trace of this message being posted as a cause for a helpful
// test error message
- throw propagate(getRootCause(t).initCause(msg.postPoint));
+ throw propagate(appendCause(t, msg.postPoint));
} finally {
msg.message.recycle();
}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 7afd28c..fefc03d 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -53,6 +53,7 @@
import android.telephony.ims.aidl.IImsRcsFeature;
import android.telephony.ims.aidl.IImsRegistration;
import android.telephony.ims.feature.ImsFeature;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
import android.util.Log;
import com.android.ims.internal.IImsServiceFeatureCallback;
@@ -6410,84 +6411,106 @@
return false;
}
- /**
- * Returns the IMS Registration Status
- * @hide
- */
- public boolean isImsRegistered() {
- try {
- ITelephony telephony = getITelephony();
- if (telephony == null)
- return false;
- return telephony.isImsRegistered();
- } catch (RemoteException ex) {
- return false;
- } catch (NullPointerException ex) {
- return false;
- }
- }
-
/**
- * Returns the IMS Registration Status for a particular Subscription ID
+ * Returns the IMS Registration Status for a particular Subscription ID.
*
* @param subId Subscription ID
* @return true if IMS status is registered, false if the IMS status is not registered or a
* RemoteException occurred.
- *
* @hide
*/
public boolean isImsRegistered(int subId) {
- try {
- return getITelephony().isImsRegisteredForSubscriber(subId);
- } catch (RemoteException ex) {
- return false;
- } catch (NullPointerException ex) {
- return false;
- }
- }
-
- /**
- * Returns the Status of Volte
- * @hide
- */
- public boolean isVolteAvailable() {
- try {
- return getITelephony().isVolteAvailable();
- } catch (RemoteException ex) {
- return false;
- } catch (NullPointerException ex) {
- return false;
- }
- }
-
- /**
- * Returns the Status of video telephony (VT)
- * @hide
- */
- public boolean isVideoTelephonyAvailable() {
try {
- return getITelephony().isVideoTelephonyAvailable();
- } catch (RemoteException ex) {
- return false;
- } catch (NullPointerException ex) {
+ return getITelephony().isImsRegistered(subId);
+ } catch (RemoteException | NullPointerException ex) {
return false;
}
}
/**
- * Returns the Status of Wi-Fi Calling
+ * Returns the IMS Registration Status for a particular Subscription ID, which is determined
+ * when the TelephonyManager is created using {@link #createForSubscriptionId(int)}. If an
+ * invalid subscription ID is used during creation, will the default subscription ID will be
+ * used.
+ *
+ * @return true if IMS status is registered, false if the IMS status is not registered or a
+ * RemoteException occurred.
+ * @see SubscriptionManager#getDefaultSubscriptionId()
+ * @hide
+ */
+ public boolean isImsRegistered() {
+ try {
+ return getITelephony().isImsRegistered(getSubId());
+ } catch (RemoteException | NullPointerException ex) {
+ return false;
+ }
+ }
+
+ /**
+ * The current status of Voice over LTE for the subscription associated with this instance when
+ * it was created using {@link #createForSubscriptionId(int)}. If an invalid subscription ID was
+ * used during creation, the default subscription ID will be used.
+ * @return true if Voice over LTE is available or false if it is unavailable or unknown.
+ * @see SubscriptionManager#getDefaultSubscriptionId()
+ * @hide
+ */
+ public boolean isVolteAvailable() {
+ try {
+ return getITelephony().isVolteAvailable(getSubId());
+ } catch (RemoteException | NullPointerException ex) {
+ return false;
+ }
+ }
+
+ /**
+ * The availability of Video Telephony (VT) for the subscription ID specified when this instance
+ * was created using {@link #createForSubscriptionId(int)}. If an invalid subscription ID was
+ * used during creation, the default subscription ID will be used. To query the
+ * underlying technology that VT is available on, use {@link #getImsRegTechnologyForMmTel}.
+ * @return true if VT is available, or false if it is unavailable or unknown.
+ * @hide
+ */
+ public boolean isVideoTelephonyAvailable() {
+ try {
+ return getITelephony().isVideoTelephonyAvailable(getSubId());
+ } catch (RemoteException | NullPointerException ex) {
+ return false;
+ }
+ }
+
+ /**
+ * Returns the Status of Wi-Fi calling (Voice over WiFi) for the subscription ID specified.
+ * @param subId the subscription ID.
+ * @return true if VoWiFi is available, or false if it is unavailable or unknown.
* @hide
*/
public boolean isWifiCallingAvailable() {
try {
- return getITelephony().isWifiCallingAvailable();
- } catch (RemoteException ex) {
- return false;
- } catch (NullPointerException ex) {
+ return getITelephony().isWifiCallingAvailable(getSubId());
+ } catch (RemoteException | NullPointerException ex) {
return false;
}
}
+ /**
+ * The technology that IMS is registered for for the MMTEL feature.
+ * @param subId subscription ID to get IMS registration technology for.
+ * @return The IMS registration technology that IMS is registered to for the MMTEL feature.
+ * Valid return results are:
+ * - {@link ImsRegistrationImplBase#REGISTRATION_TECH_LTE} for LTE registration,
+ * - {@link ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN} for IWLAN registration, or
+ * - {@link ImsRegistrationImplBase#REGISTRATION_TECH_NONE} if we are not registered or the
+ * result is unavailable.
+ * @hide
+ */
+ public @ImsRegistrationImplBase.ImsRegistrationTech int getImsRegTechnologyForMmTel() {
+ try {
+ return getITelephony().getImsRegTechnologyForMmTel(getSubId());
+ } catch (RemoteException ex) {
+ return ImsRegistrationImplBase.REGISTRATION_TECH_NONE;
+ }
+ }
+
/**
* Set TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC for the default phone.
*
diff --git a/telephony/java/android/telephony/ims/feature/ImsFeature.java b/telephony/java/android/telephony/ims/feature/ImsFeature.java
index bfdd453..1fdbae9 100644
--- a/telephony/java/android/telephony/ims/feature/ImsFeature.java
+++ b/telephony/java/android/telephony/ims/feature/ImsFeature.java
@@ -80,7 +80,7 @@
public static final String EXTRA_PHONE_ID = "android:phone_id";
/**
- * Invalid feature value\
+ * Invalid feature value
* @hide
*/
public static final int FEATURE_INVALID = -1;
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 2b4c059..02cc82c 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -1123,33 +1123,33 @@
boolean isHearingAidCompatibilitySupported();
/**
- * Get IMS Registration Status
- */
- boolean isImsRegistered();
-
- /**
* Get IMS Registration Status on a particular subid.
*
* @param subId user preferred subId.
*
* @return {@code true} if the IMS status is registered.
*/
- boolean isImsRegisteredForSubscriber(int subId);
+ boolean isImsRegistered(int subId);
/**
- * Returns the Status of Wi-Fi Calling
+ * Returns the Status of Wi-Fi Calling for the subscription id specified.
*/
- boolean isWifiCallingAvailable();
+ boolean isWifiCallingAvailable(int subId);
/**
- * Returns the Status of Volte
+ * Returns the Status of VoLTE for the subscription ID specified.
*/
- boolean isVolteAvailable();
+ boolean isVolteAvailable(int subId);
/**
- * Returns the Status of VT (video telephony)
+ * Returns the Status of VT (video telephony) for the subscription ID specified.
*/
- boolean isVideoTelephonyAvailable();
+ boolean isVideoTelephonyAvailable(int subId);
+
+ /**
+ * Returns the MMTEL IMS registration technology for the subsciption ID specified.
+ */
+ int getImsRegTechnologyForMmTel(int subId);
/**
* Returns the unique device ID of phone, for example, the IMEI for
diff --git a/tests/ActivityManagerPerfTests/README.txt b/tests/ActivityManagerPerfTests/README.txt
index 77e0e90..1040ed1 100644
--- a/tests/ActivityManagerPerfTests/README.txt
+++ b/tests/ActivityManagerPerfTests/README.txt
@@ -1,12 +1,15 @@
ActivityManagerPerfTests
Performance tests for various ActivityManager components, e.g. Services, Broadcasts
+* These are only for tests that don't require a target package to test against
+* Self-contained perf tests should go in frameworks/base/apct-tests/perftests
-Command to run tests (not working yet, atest seems buggy)
-* atest .../frameworks/base/tests/ActivityManagerPerfTests
+Command to run tests
+* atest .../frameworks/base/tests/ActivityManagerPerfTests/tests/
+ * Command currently not working: b/71859981
* m ActivityManagerPerfTests ActivityManagerPerfTestsTestApp && \
- adb install $OUT/data/app/ActivityManagerPerfTests/ActivityManagerPerfTests.apk && \
- adb install $OUT/data/app/ActivityManagerPerfTestsTestApp/ActivityManagerPerfTestsTestApp.apk && \
+ adb install "$OUT"/data/app/ActivityManagerPerfTests/ActivityManagerPerfTests.apk && \
+ adb install "$OUT"/data/app/ActivityManagerPerfTestsTestApp/ActivityManagerPerfTestsTestApp.apk && \
adb shell am instrument -w \
com.android.frameworks.perftests.amtests/android.support.test.runner.AndroidJUnitRunner
@@ -15,20 +18,42 @@
* For example, the time it takes from sending an Intent to start a Service
to the time the Service runs its callbacks
* System.nanoTime() is monotonic and consistent between processes, so we use that for measuring time
-* To make sure the test app is running, we start an Activity
* If the test app is involved, it will measure the time and send it back to the instrumentation test
- * The time is sent back through a Binder interface in the Intent
+ * The time is sent back through a Binder interface in the Intent with the help of Utils.sendTime()
* Each sent time is tagged with an id since there can be multiple events that send back a time
- * For example, one is sent when the Activity is started, and another could be sent when a
- Broadcast is received
+* Each test will run multiple times to account for variation in test runs
Structure
* tests
* Instrumentation test which runs the various performance tests and reports the results
-
* test-app
* Target package which contains the Services, BroadcastReceivers, etc. to test against
* Sends the time it measures back to the test package
-
* utils
* Utilities that both the instrumentation test and test app can use
+
+Adding tests
+* Example
+ * Look at tests/src/com/android/frameworks/perftests/am/BroadcastPerfTest and
+ test-app/src/com/android/frameworks/perftests/amteststestapp/TestBroadcastReceiver
+ for simple examples using this framework
+* Steps
+ * Add any components you will test against in the target package under
+ test-app/src/com/android/frameworks/perftests/amteststestapp/
+ * Add the test class under tests/src/com/android/frameworks/perftests/am/tests/
+ * The class should extend BasePerfTest
+ * Each test should call runPerfFunction() returning the elapsed time for a single iteration
+ * The test has access to a Context through mContext
+ * If you are measuring the time elapsed of something that either starts or ends in the target
+ package
+ * The target package can report the time it measures through an ITimeReceiverCallback passed
+ through an Intent through Utils.sendTime(intent, "tag")
+ (or however a Binder needs to be passed to the target package)
+ * The instrumentation test can collect that time by calling getReceivedTimeNs("tag") and
+ calculate the elapsed time
+ * Each timestamp sent to the instrumentation test is tagged with a tag since multiple timestamps
+ can be reported in an iteration
+ * If the target package should be running before your test logic starts, add startTargetPackage();
+ at the beginning of the iteration
+* Reporting
+ * Look at go/am-perf for how to add new tests to dashboards and receive notification on regression
diff --git a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
index 063060f..228f9bb 100644
--- a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
+++ b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
@@ -65,6 +65,7 @@
private static final int JOIN_TIMEOUT = 10000;
private static final String TAG = AppLaunch.class.getSimpleName();
+
// optional parameter: comma separated list of required account types before proceeding
// with the app launch
private static final String KEY_REQUIRED_ACCOUNTS = "required_accounts";
@@ -73,32 +74,36 @@
private static final String KEY_LAUNCH_ITERATIONS = "launch_iterations";
private static final String KEY_LAUNCH_ORDER = "launch_order";
private static final String KEY_DROP_CACHE = "drop_cache";
- private static final String KEY_SIMULATE_MAINTANANCE = "simulate_maintanance";
- private static final String KEY_SIMPLEPPERF_CMD = "simpleperf_cmd";
+ private static final String KEY_SIMPLEPERF_CMD = "simpleperf_cmd";
+ private static final String KEY_SIMPLEPERF_APP = "simpleperf_app";
private static final String KEY_TRACE_ITERATIONS = "trace_iterations";
private static final String KEY_LAUNCH_DIRECTORY = "launch_directory";
private static final String KEY_TRACE_DIRECTORY = "trace_directory";
private static final String KEY_TRACE_CATEGORY = "trace_categories";
private static final String KEY_TRACE_BUFFERSIZE = "trace_bufferSize";
private static final String KEY_TRACE_DUMPINTERVAL = "tracedump_interval";
+ private static final String KEY_COMPILER_FILTERS = "compiler_filters";
+
+ private static final String SIMPLEPERF_APP_CMD =
+ "simpleperf --log fatal stat --csv -e cpu-cycles,major-faults --app %s & %s";
private static final String WEARABLE_ACTION_GOOGLE =
"com.google.android.wearable.action.GOOGLE";
- private static final int INITIAL_LAUNCH_IDLE_TIMEOUT = 5000; //5s to allow app to idle
- private static final int POST_LAUNCH_IDLE_TIMEOUT = 750; //750ms idle for non initial launches
- private static final int BETWEEN_LAUNCH_SLEEP_TIMEOUT = 5000; //5s between launching apps
+ private static final int INITIAL_LAUNCH_IDLE_TIMEOUT = 5000; // 5s to allow app to idle
+ private static final int POST_LAUNCH_IDLE_TIMEOUT = 750; // 750ms idle for non initial launches
+ private static final int BETWEEN_LAUNCH_SLEEP_TIMEOUT = 5000; // 5s between launching apps
private static final String LAUNCH_SUB_DIRECTORY = "launch_logs";
private static final String LAUNCH_FILE = "applaunch.txt";
private static final String TRACE_SUB_DIRECTORY = "atrace_logs";
- private static final String DEFAULT_TRACE_CATEGORIES = "sched,freq,gfx,view,dalvik,webview,"
- + "input,wm,disk,am,wm";
+ private static final String DEFAULT_TRACE_CATEGORIES =
+ "sched,freq,gfx,view,dalvik,webview,input,wm,disk,am,wm";
private static final String DEFAULT_TRACE_BUFFER_SIZE = "20000";
private static final String DEFAULT_TRACE_DUMP_INTERVAL = "10";
- private static final String TRIAL_LAUNCH = "TRAIL_LAUNCH";
+ private static final String TRIAL_LAUNCH = "TRIAL_LAUNCH";
private static final String DELIMITER = ",";
private static final String DROP_CACHE_SCRIPT = "/data/local/tmp/dropCache.sh";
private static final String APP_LAUNCH_CMD = "am start -W -n";
private static final String SUCCESS_MESSAGE = "Status: ok";
- private static final String PROFILE_COMPILE_SUCCESS = "Success";
+ private static final String COMPILE_SUCCESS = "Success";
private static final String THIS_TIME = "ThisTime:";
private static final String LAUNCH_ITERATION = "LAUNCH_ITERATION - %d";
private static final String TRACE_ITERATION = "TRACE_ITERATION-%d";
@@ -106,14 +111,15 @@
private static final String TRACE_ITERATION_PREFIX = "TRACE_ITERATION";
private static final String LAUNCH_ORDER_CYCLIC = "cyclic";
private static final String LAUNCH_ORDER_SEQUENTIAL = "sequential";
- private static final String SPEED_PROFILE_CMD = "cmd package compile -f -m speed-profile %s";
-
-
+ private static final String COMPILE_CMD = "cmd package compile -f -m %s %s";
+ private static final String SPEED_PROFILE_FILTER = "speed-profile";
+ private static final String VERIFY_FILTER = "verify";
+ private static final String LAUNCH_SCRIPT_NAME = "appLaunch";
private Map<String, Intent> mNameToIntent;
private List<LaunchOrder> mLaunchOrderList = new ArrayList<LaunchOrder>();
private Map<String, String> mNameToResultKey;
- private Map<String, List<Long>> mNameToLaunchTime;
+ private Map<String, Map<String, List<AppLaunchResult>>> mNameToLaunchTime;
private IActivityManager mAm;
private String mSimplePerfCmd = null;
private String mLaunchOrder = null;
@@ -123,12 +129,10 @@
private String mTraceDirectoryStr = null;
private Bundle mResult = new Bundle();
private Set<String> mRequiredAccounts;
- private boolean mTrailLaunch = true;
- private File mFile = null;
- private FileOutputStream mOutputStream = null;
+ private boolean mTrialLaunch = false;
private BufferedWriter mBufferedWriter = null;
- private boolean mSimulateMaintanance = false;
-
+ private boolean mSimplePerfAppOnly = false;
+ private String[] mCompilerFilters = null;
@Override
protected void setUp() throws Exception {
@@ -142,6 +146,16 @@
super.tearDown();
}
+ private void addLaunchResult(LaunchOrder launch, AppLaunchResult result) {
+ mNameToLaunchTime.get(launch.getApp()).get(launch.getCompilerFilter()).add(result);
+ }
+
+ private boolean hasFailureOnFirstLaunch(LaunchOrder launch) {
+ List<AppLaunchResult> results =
+ mNameToLaunchTime.get(launch.getApp()).get(launch.getCompilerFilter());
+ return (results.size() > 0) && (results.get(0).mLaunchTime < 0);
+ }
+
public void testMeasureStartUpTime() throws RemoteException, NameNotFoundException,
IOException, InterruptedException {
InstrumentationTestRunner instrumentation =
@@ -149,11 +163,6 @@
Bundle args = instrumentation.getArguments();
mAm = ActivityManager.getService();
String launchDirectory = args.getString(KEY_LAUNCH_DIRECTORY);
- mTraceDirectoryStr = args.getString(KEY_TRACE_DIRECTORY);
- mDropCache = Boolean.parseBoolean(args.getString(KEY_DROP_CACHE));
- mSimplePerfCmd = args.getString(KEY_SIMPLEPPERF_CMD);
- mLaunchOrder = args.getString(KEY_LAUNCH_ORDER, LAUNCH_ORDER_CYCLIC);
- mSimulateMaintanance = Boolean.parseBoolean(args.getString(KEY_SIMULATE_MAINTANANCE));
createMappings();
parseArgs(args);
@@ -171,13 +180,14 @@
try {
File launchSubDir = new File(launchRootDir, LAUNCH_SUB_DIRECTORY);
+
if (!launchSubDir.exists() && !launchSubDir.mkdirs()) {
throw new IOException("Unable to create the lauch file sub directory");
}
- mFile = new File(launchSubDir, LAUNCH_FILE);
- mOutputStream = new FileOutputStream(mFile);
+ File file = new File(launchSubDir, LAUNCH_FILE);
+ FileOutputStream outputStream = new FileOutputStream(file);
mBufferedWriter = new BufferedWriter(new OutputStreamWriter(
- mOutputStream));
+ outputStream));
// Root directory for trace file during the launches
File rootTrace = null;
@@ -217,70 +227,67 @@
setLaunchOrder();
for (LaunchOrder launch : mLaunchOrderList) {
+ dropCache();
// App launch times for trial launch will not be used for final
// launch time calculations.
if (launch.getLaunchReason().equals(TRIAL_LAUNCH)) {
// In the "applaunch.txt" file, trail launches is referenced using
// "TRIAL_LAUNCH"
- long launchTime = startApp(launch.getApp(), true, launch.getLaunchReason());
- if (launchTime < 0) {
- List<Long> appLaunchList = new ArrayList<Long>();
- appLaunchList.add(-1L);
- mNameToLaunchTime.put(launch.getApp(), appLaunchList);
+ String appPkgName = mNameToIntent.get(launch.getApp())
+ .getComponent().getPackageName();
+ if (SPEED_PROFILE_FILTER.equals(launch.getCompilerFilter())) {
+ assertTrue(String.format("Not able to compile the app : %s", appPkgName),
+ compileApp(VERIFY_FILTER, appPkgName));
+ } else if (launch.getCompilerFilter() != null) {
+ assertTrue(String.format("Not able to compile the app : %s", appPkgName),
+ compileApp(launch.getCompilerFilter(), appPkgName));
+ }
+ // We only need to run a trial for the speed-profile filter, but we always
+ // run one for "applaunch.txt" consistency.
+ AppLaunchResult launchResult =
+ startApp(launch.getApp(), true, launch.getLaunchReason());
+ if (launchResult.mLaunchTime < 0) {
+ addLaunchResult(launch, new AppLaunchResult());
// simply pass the app if launch isn't successful
// error should have already been logged by startApp
continue;
}
sleep(INITIAL_LAUNCH_IDLE_TIMEOUT);
- closeApp(launch.getApp(), true);
- dropCache();
- if (mSimulateMaintanance) {
- String appPkgName = mNameToIntent.get(launch.getApp())
- .getComponent().getPackageName();
- assertTrue(String.format("Not able to speed profile the app : %s",
- appPkgName), profileCompileApp(appPkgName));
+ if (SPEED_PROFILE_FILTER.equals(launch.getCompilerFilter())) {
+ // Send SIGUSR1 to force dumping a profile.
+ String sendSignalCommand =
+ String.format("killall -s SIGUSR1 %s", appPkgName);
+ getInstrumentation().getUiAutomation().executeShellCommand(
+ sendSignalCommand);
+ assertTrue(String.format("Not able to compile the app : %s", appPkgName),
+ compileApp(launch.getCompilerFilter(), appPkgName));
}
- sleep(BETWEEN_LAUNCH_SLEEP_TIMEOUT);
}
// App launch times used for final calculation
- if (launch.getLaunchReason().contains(LAUNCH_ITERATION_PREFIX)) {
- long launchTime = -1;
- if (null != mNameToLaunchTime.get(launch.getApp())) {
- long firstLaunchTime = mNameToLaunchTime.get(launch.getApp()).get(0);
- if (firstLaunchTime < 0) {
- // skip if the app has failures while launched first
- continue;
- }
+ else if (launch.getLaunchReason().contains(LAUNCH_ITERATION_PREFIX)) {
+ AppLaunchResult launchResults = null;
+ if (hasFailureOnFirstLaunch(launch)) {
+ // skip if the app has failures while launched first
+ continue;
}
// In the "applaunch.txt" file app launches are referenced using
// "LAUNCH_ITERATION - ITERATION NUM"
- launchTime = startApp(launch.getApp(), true, launch.getLaunchReason());
- if (launchTime < 0) {
+ launchResults = startApp(launch.getApp(), true, launch.getLaunchReason());
+ if (launchResults.mLaunchTime < 0) {
+ addLaunchResult(launch, new AppLaunchResult());
// if it fails once, skip the rest of the launches
- List<Long> appLaunchList = new ArrayList<Long>();
- appLaunchList.add(-1L);
- mNameToLaunchTime.put(launch.getApp(), appLaunchList);
continue;
} else {
- if (null != mNameToLaunchTime.get(launch.getApp())) {
- mNameToLaunchTime.get(launch.getApp()).add(launchTime);
- } else {
- List<Long> appLaunchList = new ArrayList<Long>();
- appLaunchList.add(launchTime);
- mNameToLaunchTime.put(launch.getApp(), appLaunchList);
- }
+ addLaunchResult(launch, launchResults);
}
sleep(POST_LAUNCH_IDLE_TIMEOUT);
- closeApp(launch.getApp(), true);
- dropCache();
- sleep(BETWEEN_LAUNCH_SLEEP_TIMEOUT);
}
// App launch times for trace launch will not be used for final
// launch time calculations.
- if (launch.getLaunchReason().contains(TRACE_ITERATION_PREFIX)) {
+ else if (launch.getLaunchReason().contains(TRACE_ITERATION_PREFIX)) {
AtraceLogger atraceLogger = AtraceLogger
.getAtraceLoggerInstance(getInstrumentation());
// Start the trace
@@ -293,11 +300,10 @@
} finally {
// Stop the trace
atraceLogger.atraceStop();
- closeApp(launch.getApp(), true);
- dropCache();
- sleep(BETWEEN_LAUNCH_SLEEP_TIMEOUT);
}
}
+ closeApp(launch.getApp(), true);
+ sleep(BETWEEN_LAUNCH_SLEEP_TIMEOUT);
}
} finally {
if (null != mBufferedWriter) {
@@ -306,29 +312,45 @@
}
for (String app : mNameToResultKey.keySet()) {
- StringBuilder launchTimes = new StringBuilder();
- for (Long launch : mNameToLaunchTime.get(app)) {
- launchTimes.append(launch);
- launchTimes.append(",");
+ for (String compilerFilter : mCompilerFilters) {
+ StringBuilder launchTimes = new StringBuilder();
+ StringBuilder cpuCycles = new StringBuilder();
+ StringBuilder majorFaults = new StringBuilder();
+ for (AppLaunchResult result : mNameToLaunchTime.get(app).get(compilerFilter)) {
+ launchTimes.append(result.mLaunchTime);
+ launchTimes.append(",");
+ if (mSimplePerfAppOnly) {
+ cpuCycles.append(result.mCpuCycles);
+ cpuCycles.append(",");
+ majorFaults.append(result.mMajorFaults);
+ majorFaults.append(",");
+ }
+ }
+ String filterName = (compilerFilter == null) ? "" : ("-" + compilerFilter);
+ mResult.putString(mNameToResultKey.get(app) + filterName, launchTimes.toString());
+ if (mSimplePerfAppOnly) {
+ mResult.putString(mNameToResultKey.get(app) + filterName + "-cpuCycles",
+ cpuCycles.toString());
+ mResult.putString(mNameToResultKey.get(app) + filterName + "-majorFaults",
+ majorFaults.toString());
+ }
}
- mResult.putString(mNameToResultKey.get(app), launchTimes.toString());
}
instrumentation.sendStatus(0, mResult);
}
/**
- * Compile the app package using speed compile command and return true or false
+ * Compile the app package using compilerFilter and return true or false
* based on status of the compilation command.
*/
- private boolean profileCompileApp(String appPkgName) throws IOException {
- Log.i(TAG, "Starting to speed profile " + appPkgName);
+ private boolean compileApp(String compilerFilter, String appPkgName) throws IOException {
try (ParcelFileDescriptor result = getInstrumentation().getUiAutomation().
- executeShellCommand(String.format(SPEED_PROFILE_CMD, appPkgName));
+ executeShellCommand(String.format(COMPILE_CMD, compilerFilter, appPkgName));
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(
new FileInputStream(result.getFileDescriptor())))) {
String line;
while ((line = bufferedReader.readLine()) != null) {
- if (line.contains(PROFILE_COMPILE_SUCCESS)) {
+ if (line.contains(COMPILE_SUCCESS)) {
return true;
}
}
@@ -344,38 +366,42 @@
*/
private void setLaunchOrder() {
if (LAUNCH_ORDER_CYCLIC.equalsIgnoreCase(mLaunchOrder)) {
- if (mTrailLaunch) {
- for (String app : mNameToResultKey.keySet()) {
- mLaunchOrderList.add(new LaunchOrder(app, TRIAL_LAUNCH));
- }
- }
- for (int launchCount = 0; launchCount < mLaunchIterations; launchCount++) {
- for (String app : mNameToResultKey.keySet()) {
- mLaunchOrderList.add(new LaunchOrder(app,
- String.format(LAUNCH_ITERATION, launchCount)));
- }
- }
- if (mTraceDirectoryStr != null && !mTraceDirectoryStr.isEmpty()) {
- for (int traceCount = 0; traceCount < mTraceLaunchCount; traceCount++) {
+ for (String compilerFilter : mCompilerFilters) {
+ if (mTrialLaunch) {
for (String app : mNameToResultKey.keySet()) {
- mLaunchOrderList.add(new LaunchOrder(app,
- String.format(TRACE_ITERATION, traceCount)));
+ mLaunchOrderList.add(new LaunchOrder(app, compilerFilter, TRIAL_LAUNCH));
+ }
+ }
+ for (int launchCount = 0; launchCount < mLaunchIterations; launchCount++) {
+ for (String app : mNameToResultKey.keySet()) {
+ mLaunchOrderList.add(new LaunchOrder(app, compilerFilter,
+ String.format(LAUNCH_ITERATION, launchCount)));
+ }
+ }
+ if (mTraceDirectoryStr != null && !mTraceDirectoryStr.isEmpty()) {
+ for (int traceCount = 0; traceCount < mTraceLaunchCount; traceCount++) {
+ for (String app : mNameToResultKey.keySet()) {
+ mLaunchOrderList.add(new LaunchOrder(app, compilerFilter,
+ String.format(TRACE_ITERATION, traceCount)));
+ }
}
}
}
} else if (LAUNCH_ORDER_SEQUENTIAL.equalsIgnoreCase(mLaunchOrder)) {
- for (String app : mNameToResultKey.keySet()) {
- if (mTrailLaunch) {
- mLaunchOrderList.add(new LaunchOrder(app, TRIAL_LAUNCH));
- }
- for (int launchCount = 0; launchCount < mLaunchIterations; launchCount++) {
- mLaunchOrderList.add(new LaunchOrder(app,
- String.format(LAUNCH_ITERATION, launchCount)));
- }
- if (mTraceDirectoryStr != null && !mTraceDirectoryStr.isEmpty()) {
- for (int traceCount = 0; traceCount < mTraceLaunchCount; traceCount++) {
- mLaunchOrderList.add(new LaunchOrder(app,
- String.format(TRACE_ITERATION, traceCount)));
+ for (String compilerFilter : mCompilerFilters) {
+ for (String app : mNameToResultKey.keySet()) {
+ if (mTrialLaunch) {
+ mLaunchOrderList.add(new LaunchOrder(app, compilerFilter, TRIAL_LAUNCH));
+ }
+ for (int launchCount = 0; launchCount < mLaunchIterations; launchCount++) {
+ mLaunchOrderList.add(new LaunchOrder(app, compilerFilter,
+ String.format(LAUNCH_ITERATION, launchCount)));
+ }
+ if (mTraceDirectoryStr != null && !mTraceDirectoryStr.isEmpty()) {
+ for (int traceCount = 0; traceCount < mTraceLaunchCount; traceCount++) {
+ mLaunchOrderList.add(new LaunchOrder(app, compilerFilter,
+ String.format(TRACE_ITERATION, traceCount)));
+ }
}
}
}
@@ -385,7 +411,7 @@
}
private void dropCache() {
- if (true == mDropCache) {
+ if (mDropCache) {
assertNotNull("Issue in dropping the cache",
getInstrumentation().getUiAutomation()
.executeShellCommand(DROP_CACHE_SCRIPT));
@@ -394,7 +420,7 @@
private void parseArgs(Bundle args) {
mNameToResultKey = new LinkedHashMap<String, String>();
- mNameToLaunchTime = new HashMap<String, List<Long>>();
+ mNameToLaunchTime = new HashMap<>();
String launchIterations = args.getString(KEY_LAUNCH_ITERATIONS);
if (launchIterations != null) {
mLaunchIterations = Integer.parseInt(launchIterations);
@@ -421,7 +447,38 @@
mRequiredAccounts.add(accountType);
}
}
- mTrailLaunch = "true".equals(args.getString(KEY_TRIAL_LAUNCH));
+
+ String compilerFilterList = args.getString(KEY_COMPILER_FILTERS);
+ if (compilerFilterList != null) {
+ // If a compiler filter is passed, we make a trial launch to force compilation
+ // of the apps.
+ mTrialLaunch = true;
+ mCompilerFilters = compilerFilterList.split("\\|");
+ } else {
+ // Just pass a null compiler filter to use the current state of the app.
+ mCompilerFilters = new String[1];
+ }
+
+ // Pre-populate the results map to avoid null checks.
+ for (String app : mNameToLaunchTime.keySet()) {
+ HashMap<String, List<AppLaunchResult>> map = new HashMap<>();
+ mNameToLaunchTime.put(app, map);
+ for (String compilerFilter : mCompilerFilters) {
+ map.put(compilerFilter, new ArrayList<>());
+ }
+ }
+
+ mTraceDirectoryStr = args.getString(KEY_TRACE_DIRECTORY);
+ mDropCache = Boolean.parseBoolean(args.getString(KEY_DROP_CACHE));
+ mSimplePerfCmd = args.getString(KEY_SIMPLEPERF_CMD);
+ mLaunchOrder = args.getString(KEY_LAUNCH_ORDER, LAUNCH_ORDER_CYCLIC);
+ mSimplePerfAppOnly = Boolean.parseBoolean(args.getString(KEY_SIMPLEPERF_APP));
+ mTrialLaunch = mTrialLaunch || Boolean.parseBoolean(args.getString(KEY_TRIAL_LAUNCH));
+
+ if (mSimplePerfCmd != null && mSimplePerfAppOnly) {
+ Log.w(TAG, String.format("Passing both %s and %s is not supported, ignoring %s",
+ KEY_SIMPLEPERF_CMD, KEY_SIMPLEPERF_APP));
+ }
}
private boolean hasLeanback(Context context) {
@@ -465,17 +522,17 @@
}
}
- private long startApp(String appName, boolean forceStopBeforeLaunch, String launchReason)
- throws NameNotFoundException, RemoteException {
+ private AppLaunchResult startApp(String appName, boolean forceStopBeforeLaunch,
+ String launchReason) throws NameNotFoundException, RemoteException {
Log.i(TAG, "Starting " + appName);
Intent startIntent = mNameToIntent.get(appName);
if (startIntent == null) {
Log.w(TAG, "App does not exist: " + appName);
mResult.putString(mNameToResultKey.get(appName), "App does not exist");
- return -1L;
+ return new AppLaunchResult();
}
- AppLaunchRunnable runnable = new AppLaunchRunnable(startIntent, forceStopBeforeLaunch ,
+ AppLaunchRunnable runnable = new AppLaunchRunnable(startIntent, forceStopBeforeLaunch,
launchReason);
Thread t = new Thread(runnable);
t.start();
@@ -569,10 +626,12 @@
private class LaunchOrder {
private String mApp;
+ private String mCompilerFilter;
private String mLaunchReason;
- LaunchOrder(String app,String launchReason){
+ LaunchOrder(String app, String compilerFilter, String launchReason){
mApp = app;
+ mCompilerFilter = compilerFilter;
mLaunchReason = launchReason;
}
@@ -584,6 +643,10 @@
mApp = app;
}
+ public String getCompilerFilter() {
+ return mCompilerFilter;
+ }
+
public String getLaunchReason() {
return mLaunchReason;
}
@@ -593,9 +656,31 @@
}
}
+ private class AppLaunchResult {
+ long mLaunchTime;
+ long mCpuCycles;
+ long mMajorFaults;
+
+ AppLaunchResult() {
+ mLaunchTime = -1L;
+ mCpuCycles = -1L;
+ mMajorFaults = -1L;
+ }
+
+ AppLaunchResult(String launchTime, String cpuCycles, String majorFaults) {
+ try {
+ mLaunchTime = Long.parseLong(launchTime, 10);
+ mCpuCycles = Long.parseLong(cpuCycles, 10);
+ mMajorFaults = Long.parseLong(majorFaults, 10);
+ } catch (NumberFormatException e) {
+ Log.e(TAG, "Error parsing result", e);
+ }
+ }
+ }
+
private class AppLaunchRunnable implements Runnable {
private Intent mLaunchIntent;
- private Long mResult;
+ private AppLaunchResult mLaunchResult;
private boolean mForceStopBeforeLaunch;
private String mLaunchReason;
@@ -604,14 +689,15 @@
mLaunchIntent = intent;
mForceStopBeforeLaunch = forceStopBeforeLaunch;
mLaunchReason = launchReason;
- mResult = -1L;
+ mLaunchResult = new AppLaunchResult();
}
- public Long getResult() {
- return mResult;
+ public AppLaunchResult getResult() {
+ return mLaunchResult;
}
public void run() {
+ File launchFile = null;
try {
String packageName = mLaunchIntent.getComponent().getPackageName();
String componentName = mLaunchIntent.getComponent().flattenToShortString();
@@ -619,17 +705,38 @@
mAm.forceStopPackage(packageName, UserHandle.USER_CURRENT);
}
String launchCmd = String.format("%s %s", APP_LAUNCH_CMD, componentName);
- if (null != mSimplePerfCmd) {
+ if (mSimplePerfAppOnly) {
+ try {
+ // executeShellCommand cannot handle shell specific actions, like '&'.
+ // Therefore, we create a file containing the command and make that
+ // the command to launch.
+ launchFile = File.createTempFile(LAUNCH_SCRIPT_NAME, ".sh");
+ launchFile.setExecutable(true);
+ try (FileOutputStream stream = new FileOutputStream(launchFile);
+ BufferedWriter writer =
+ new BufferedWriter(new OutputStreamWriter(stream))) {
+ String cmd = String.format(SIMPLEPERF_APP_CMD, packageName, launchCmd);
+ writer.write(cmd);
+ }
+ launchCmd = launchFile.getAbsolutePath();
+ } catch (IOException e) {
+ Log.w(TAG, "Error writing the launch command", e);
+ return;
+ }
+ } else if (null != mSimplePerfCmd) {
launchCmd = String.format("%s %s", mSimplePerfCmd, launchCmd);
}
Log.v(TAG, "Final launch cmd:" + launchCmd);
ParcelFileDescriptor parcelDesc = getInstrumentation().getUiAutomation()
.executeShellCommand(launchCmd);
- mResult = Long.parseLong(parseLaunchTimeAndWrite(parcelDesc, String.format
- ("App Launch :%s %s",
- componentName, mLaunchReason)), 10);
+ mLaunchResult = parseLaunchTimeAndWrite(parcelDesc, String.format
+ ("App Launch :%s %s", componentName, mLaunchReason));
} catch (RemoteException e) {
Log.w(TAG, "Error launching app", e);
+ } finally {
+ if (launchFile != null) {
+ launchFile.delete();
+ }
}
}
@@ -639,12 +746,14 @@
* @param parcelDesc
* @return
*/
- private String parseLaunchTimeAndWrite(ParcelFileDescriptor parcelDesc, String headerInfo) {
+ private AppLaunchResult parseLaunchTimeAndWrite(ParcelFileDescriptor parcelDesc,
+ String headerInfo) {
String launchTime = "-1";
+ String cpuCycles = "-1";
+ String majorFaults = "-1";
boolean launchSuccess = false;
try {
InputStream inputStream = new FileInputStream(parcelDesc.getFileDescriptor());
- StringBuilder appLaunchOuput = new StringBuilder();
/* SAMPLE OUTPUT :
Starting: Intent { cmp=com.google.android.calculator/com.android.calculator2.Calculator }
Status: ok
@@ -653,6 +762,11 @@
TotalTime: 357
WaitTime: 377
Complete*/
+ /* WITH SIMPLEPERF :
+ Performance counter statistics,
+ 6595722690,cpu-cycles,4.511040,GHz,(100%),
+ 0,major-faults,0.000,/sec,(100%),
+ Total test time,1.462129,seconds,*/
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(
inputStream));
String line = null;
@@ -669,6 +783,23 @@
String launchSplit[] = line.split(":");
launchTime = launchSplit[1].trim();
}
+
+ if (mSimplePerfAppOnly) {
+ // Parse simpleperf output.
+ if (lineCount == 9) {
+ if (!line.contains("cpu-cycles")) {
+ Log.e(TAG, "Error in simpleperf output");
+ } else {
+ cpuCycles = line.split(",")[0].trim();
+ }
+ } else if (lineCount == 10) {
+ if (!line.contains("major-faults")) {
+ Log.e(TAG, "Error in simpleperf output");
+ } else {
+ majorFaults = line.split(",")[0].trim();
+ }
+ }
+ }
mBufferedWriter.write(line);
mBufferedWriter.newLine();
lineCount++;
@@ -678,7 +809,7 @@
} catch (IOException e) {
Log.w(TAG, "Error writing the launch file", e);
}
- return launchTime;
+ return new AppLaunchResult(launchTime, cpuCycles, majorFaults);
}
}
diff --git a/tests/net/java/android/net/ConnectivityManagerTest.java b/tests/net/java/android/net/ConnectivityManagerTest.java
index cc792cc..03a617c 100644
--- a/tests/net/java/android/net/ConnectivityManagerTest.java
+++ b/tests/net/java/android/net/ConnectivityManagerTest.java
@@ -38,6 +38,7 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyBoolean;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.timeout;
@@ -217,7 +218,8 @@
// callback triggers
captor.getValue().send(makeMessage(request, ConnectivityManager.CALLBACK_AVAILABLE));
- verify(callback, timeout(500).times(1)).onAvailable(any());
+ verify(callback, timeout(500).times(1)).onAvailable(any(Network.class),
+ any(NetworkCapabilities.class), any(LinkProperties.class));
// unregister callback
manager.unregisterNetworkCallback(callback);
@@ -244,7 +246,8 @@
// callback triggers
captor.getValue().send(makeMessage(req1, ConnectivityManager.CALLBACK_AVAILABLE));
- verify(callback, timeout(100).times(1)).onAvailable(any());
+ verify(callback, timeout(100).times(1)).onAvailable(any(Network.class),
+ any(NetworkCapabilities.class), any(LinkProperties.class));
// unregister callback
manager.unregisterNetworkCallback(callback);
@@ -335,6 +338,10 @@
static Message makeMessage(NetworkRequest req, int messageType) {
Bundle bundle = new Bundle();
bundle.putParcelable(NetworkRequest.class.getSimpleName(), req);
+ // Pass default objects as we don't care which get passed here
+ bundle.putParcelable(Network.class.getSimpleName(), new Network(1));
+ bundle.putParcelable(NetworkCapabilities.class.getSimpleName(), new NetworkCapabilities());
+ bundle.putParcelable(LinkProperties.class.getSimpleName(), new LinkProperties());
Message msg = Message.obtain();
msg.what = messageType;
msg.setData(bundle);
diff --git a/tests/net/java/android/net/IpSecConfigTest.java b/tests/net/java/android/net/IpSecConfigTest.java
index f6c5532..f186ee5 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 @@
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 @@
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 0000000..b4342df
--- /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/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index e7abede..24639e9 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -35,6 +35,7 @@
import static android.net.NetworkCapabilities.NET_CAPABILITY_MMS;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
import static android.net.NetworkCapabilities.NET_CAPABILITY_RCS;
import static android.net.NetworkCapabilities.NET_CAPABILITY_SUPL;
@@ -528,6 +529,11 @@
mNetworkAgent.sendNetworkInfo(mNetworkInfo);
}
+ public void resume() {
+ mNetworkInfo.setDetailedState(DetailedState.CONNECTED, null, null);
+ mNetworkAgent.sendNetworkInfo(mNetworkInfo);
+ }
+
public void disconnect() {
mNetworkInfo.setDetailedState(DetailedState.DISCONNECTED, null, null);
mNetworkAgent.sendNetworkInfo(mNetworkInfo);
@@ -569,6 +575,10 @@
assertTrue(mNetworkStatusReceived.block(TIMEOUT_MS));
return mRedirectUrl;
}
+
+ public NetworkCapabilities getNetworkCapabilities() {
+ return mNetworkCapabilities;
+ }
}
/**
@@ -1273,6 +1283,7 @@
NETWORK_CAPABILITIES,
LINK_PROPERTIES,
SUSPENDED,
+ RESUMED,
LOSING,
LOST,
UNAVAILABLE
@@ -1344,6 +1355,11 @@
}
@Override
+ public void onNetworkResumed(Network network) {
+ setLastCallback(CallbackState.RESUMED, network, null);
+ }
+
+ @Override
public void onLosing(Network network, int maxMsToLive) {
setLastCallback(CallbackState.LOSING, network, maxMsToLive /* autoboxed int */);
}
@@ -2459,16 +2475,31 @@
// Suspend the network.
mCellNetworkAgent.suspend();
+ cellNetworkCallback.expectCapabilitiesWithout(NET_CAPABILITY_NOT_SUSPENDED,
+ mCellNetworkAgent);
cellNetworkCallback.expectCallback(CallbackState.SUSPENDED, mCellNetworkAgent);
cellNetworkCallback.assertNoCallback();
// Register a garden variety default network request.
- final TestNetworkCallback dfltNetworkCallback = new TestNetworkCallback();
+ TestNetworkCallback dfltNetworkCallback = new TestNetworkCallback();
mCm.registerDefaultNetworkCallback(dfltNetworkCallback);
// We should get onAvailable(), onCapabilitiesChanged(), onLinkPropertiesChanged(),
// as well as onNetworkSuspended() in rapid succession.
dfltNetworkCallback.expectAvailableAndSuspendedCallbacks(mCellNetworkAgent, true);
dfltNetworkCallback.assertNoCallback();
+ mCm.unregisterNetworkCallback(dfltNetworkCallback);
+
+ mCellNetworkAgent.resume();
+ cellNetworkCallback.expectCapabilitiesWith(NET_CAPABILITY_NOT_SUSPENDED,
+ mCellNetworkAgent);
+ cellNetworkCallback.expectCallback(CallbackState.RESUMED, mCellNetworkAgent);
+ cellNetworkCallback.assertNoCallback();
+
+ dfltNetworkCallback = new TestNetworkCallback();
+ mCm.registerDefaultNetworkCallback(dfltNetworkCallback);
+ // This time onNetworkSuspended should not be called.
+ dfltNetworkCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
+ dfltNetworkCallback.assertNoCallback();
mCm.unregisterNetworkCallback(dfltNetworkCallback);
mCm.unregisterNetworkCallback(cellNetworkCallback);
@@ -3682,8 +3713,7 @@
vpnNetworkCallback.expectAvailableCallbacksUnvalidated(vpnNetworkAgent);
genericNetworkCallback.expectCallback(CallbackState.NETWORK_CAPABILITIES, vpnNetworkAgent);
- vpnNetworkCallback.expectCapabilitiesLike(
- nc -> nc.appliesToUid(uid) && !nc.appliesToUid(uid + 1), vpnNetworkAgent);
+ vpnNetworkCallback.expectCapabilitiesLike(nc -> null == nc.getUids(), vpnNetworkAgent);
ranges.clear();
vpnNetworkAgent.setUids(ranges);
diff --git a/tools/aapt2/Debug.cpp b/tools/aapt2/Debug.cpp
index 5831875..f064cb1 100644
--- a/tools/aapt2/Debug.cpp
+++ b/tools/aapt2/Debug.cpp
@@ -414,59 +414,78 @@
public:
using xml::ConstVisitor::Visit;
+ XmlPrinter(Printer* printer) : printer_(printer) {
+ }
+
void Visit(const xml::Element* el) override {
- const size_t previous_size = prefix_.size();
-
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("\"");
+ printer_->Print(attr.value);
+ printer_->Print("\"");
}
- std::cerr << "\n";
+
+ if (!attr.value.empty()) {
+ printer_->Print(" (Raw: \"");
+ printer_->Print(attr.value);
+ printer_->Print("\")");
+ }
+ 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 6209a04..382707e 100644
--- a/tools/aapt2/Debug.h
+++ b/tools/aapt2/Debug.h
@@ -37,7 +37,7 @@
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 20a9f41..ac28227 100644
--- a/tools/aapt2/LoadedApk.cpp
+++ b/tools/aapt2/LoadedApk.cpp
@@ -222,7 +222,9 @@
} 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 02ac86c..628466d 100644
--- a/tools/aapt2/ResourceUtils.cpp
+++ b/tools/aapt2/ResourceUtils.cpp
@@ -520,6 +520,10 @@
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 36f6c2b..f83d49e 100644
--- a/tools/aapt2/ResourceUtils.h
+++ b/tools/aapt2/ResourceUtils.h
@@ -165,6 +165,9 @@
*/
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/Convert.cpp b/tools/aapt2/cmd/Convert.cpp
index d80307c..7f956c5 100644
--- a/tools/aapt2/cmd/Convert.cpp
+++ b/tools/aapt2/cmd/Convert.cpp
@@ -139,6 +139,7 @@
BigBuffer buffer(4096);
XmlFlattenerOptions options = {};
options.use_utf16 = utf16;
+ options.keep_raw_values = true;
XmlFlattener flattener(&buffer, options);
if (!flattener.Consume(context_, xml)) {
return false;
diff --git a/tools/aapt2/cmd/Dump.cpp b/tools/aapt2/cmd/Dump.cpp
index 3d2fb55..8e7e5e5 100644
--- a/tools/aapt2/cmd/Dump.cpp
+++ b/tools/aapt2/cmd/Dump.cpp
@@ -38,6 +38,13 @@
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 @@
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 @@
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 @@
<< "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 @@
if (!parser.Parse()) {
return false;
}
-
- printer.Println("Binary APK");
}
- Debug::PrintTable(table, print_options, &printer);
- return true;
+ 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;
+ }
+
+ 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 @@
}
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 @@
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 @@
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 eabeb47..902334b 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 @@
};
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 @@
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 @@
// 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 @@
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 @@
return false;
}
group.push_back(abi->second);
- return true;
+ return valid;
}
for (auto* child : root_element->GetChildElements()) {
@@ -553,8 +585,15 @@
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 @@
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 @@
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 @@
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 a583057..f071a69 100644
--- a/tools/aapt2/configuration/ConfigurationParser.internal.h
+++ b/tools/aapt2/configuration/ConfigurationParser.internal.h
@@ -33,18 +33,31 @@
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 @@
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 @@
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 0329846..febbb2e 100644
--- a/tools/aapt2/configuration/ConfigurationParser_test.cpp
+++ b/tools/aapt2/configuration/ConfigurationParser_test.cpp
@@ -82,22 +82,22 @@
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 @@
</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 @@
</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 @@
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, 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_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 @@
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, 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_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 @@
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, 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_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 @@
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, 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, 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 @@
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 fb2f49b..a28e28b 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 d8aba09..e6db2a0 100644
--- a/tools/aapt2/configuration/example/config.xml
+++ b/tools/aapt2/configuration/example/config.xml
@@ -36,25 +36,19 @@
</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>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>
@@ -62,29 +56,35 @@
<screen-density>xxhdpi</screen-density>
<screen-density>xxxhdpi</screen-density>
</screen-density-group>
+
+ <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-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/link/XmlCompatVersioner_test.cpp b/tools/aapt2/link/XmlCompatVersioner_test.cpp
index 1ed4536..a98ab0f 100644
--- a/tools/aapt2/link/XmlCompatVersioner_test.cpp
+++ b/tools/aapt2/link/XmlCompatVersioner_test.cpp
@@ -23,6 +23,7 @@
using ::testing::Eq;
using ::testing::IsNull;
using ::testing::NotNull;
+using ::testing::Pointee;
using ::testing::SizeIs;
namespace aapt {
@@ -287,13 +288,13 @@
ASSERT_THAT(attr, NotNull());
ASSERT_THAT(attr->compiled_value, NotNull());
ASSERT_TRUE(attr->compiled_attribute);
- ASSERT_THAT(*attr->compiled_value, ValueEq(padding_horizontal_value));
+ ASSERT_THAT(attr->compiled_value, Pointee(ValueEq(padding_horizontal_value)));
attr = el->FindAttribute(xml::kSchemaAndroid, "paddingRight");
ASSERT_THAT(attr, NotNull());
ASSERT_THAT(attr->compiled_value, NotNull());
ASSERT_TRUE(attr->compiled_attribute);
- ASSERT_THAT(*attr->compiled_value, ValueEq(padding_horizontal_value));
+ ASSERT_THAT(attr->compiled_value, Pointee(ValueEq(padding_horizontal_value)));
EXPECT_THAT(versioned_docs[1]->file.config.sdkVersion, Eq(SDK_LOLLIPOP_MR1));
el = versioned_docs[1]->root.get();
@@ -302,21 +303,20 @@
attr = el->FindAttribute(xml::kSchemaAndroid, "paddingHorizontal");
ASSERT_THAT(attr, NotNull());
- ASSERT_THAT(attr->compiled_value, NotNull());
ASSERT_TRUE(attr->compiled_attribute);
- ASSERT_THAT(*attr->compiled_value, ValueEq(padding_horizontal_value));
+ ASSERT_THAT(attr->compiled_value, Pointee(ValueEq(padding_horizontal_value)));
attr = el->FindAttribute(xml::kSchemaAndroid, "paddingLeft");
ASSERT_THAT(attr, NotNull());
ASSERT_THAT(attr->compiled_value, NotNull());
ASSERT_TRUE(attr->compiled_attribute);
- ASSERT_THAT(*attr->compiled_value, ValueEq(padding_horizontal_value));
+ ASSERT_THAT(attr->compiled_value, Pointee(ValueEq(padding_horizontal_value)));
attr = el->FindAttribute(xml::kSchemaAndroid, "paddingRight");
ASSERT_THAT(attr, NotNull());
ASSERT_THAT(attr->compiled_value, NotNull());
ASSERT_TRUE(attr->compiled_attribute);
- ASSERT_THAT(*attr->compiled_value, ValueEq(padding_horizontal_value));
+ ASSERT_THAT(attr->compiled_value, Pointee(ValueEq(padding_horizontal_value)));
}
} // namespace aapt
diff --git a/tools/aapt2/optimize/MultiApkGenerator.cpp b/tools/aapt2/optimize/MultiApkGenerator.cpp
index 991faad..588b331 100644
--- a/tools/aapt2/optimize/MultiApkGenerator.cpp
+++ b/tools/aapt2/optimize/MultiApkGenerator.cpp
@@ -322,22 +322,56 @@
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 19f64cc..c858879 100644
--- a/tools/aapt2/optimize/MultiApkGenerator.h
+++ b/tools/aapt2/optimize/MultiApkGenerator.h
@@ -63,6 +63,11 @@
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_;
};
diff --git a/tools/aapt2/test/Common.h b/tools/aapt2/test/Common.h
index 4e318a9..aca161a 100644
--- a/tools/aapt2/test/Common.h
+++ b/tools/aapt2/test/Common.h
@@ -146,97 +146,70 @@
return android::StringPiece16(arg) == a;
}
-class ValueEq {
+template <typename T>
+class ValueEqImpl : public ::testing::MatcherInterface<T> {
public:
- template <typename arg_type>
- class BaseImpl : public ::testing::MatcherInterface<arg_type> {
- BaseImpl(const BaseImpl&) = default;
-
- void DescribeTo(::std::ostream* os) const override {
- *os << "is equal to " << *expected_;
- }
-
- void DescribeNegationTo(::std::ostream* os) const override {
- *os << "is not equal to " << *expected_;
- }
-
- protected:
- BaseImpl(const Value* expected) : expected_(expected) {
- }
-
- const Value* expected_;
- };
-
- template <typename T, bool>
- class Impl {};
-
- template <typename T>
- class Impl<T, false> : public ::testing::MatcherInterface<T> {
- public:
- explicit Impl(const Value* expected) : expected_(expected) {
- }
-
- bool MatchAndExplain(T x, ::testing::MatchResultListener* listener) const override {
- return expected_->Equals(&x);
- }
-
- void DescribeTo(::std::ostream* os) const override {
- *os << "is equal to " << *expected_;
- }
-
- void DescribeNegationTo(::std::ostream* os) const override {
- *os << "is not equal to " << *expected_;
- }
-
- private:
- DISALLOW_COPY_AND_ASSIGN(Impl);
-
- const Value* expected_;
- };
-
- template <typename T>
- class Impl<T, true> : public ::testing::MatcherInterface<T> {
- public:
- explicit Impl(const Value* expected) : expected_(expected) {
- }
-
- bool MatchAndExplain(T x, ::testing::MatchResultListener* listener) const override {
- return expected_->Equals(x);
- }
-
- void DescribeTo(::std::ostream* os) const override {
- *os << "is equal to " << *expected_;
- }
-
- void DescribeNegationTo(::std::ostream* os) const override {
- *os << "is not equal to " << *expected_;
- }
-
- private:
- DISALLOW_COPY_AND_ASSIGN(Impl);
-
- const Value* expected_;
- };
-
- ValueEq(const Value& expected) : expected_(&expected) {
+ explicit ValueEqImpl(const Value* expected) : expected_(expected) {
}
- ValueEq(const Value* expected) : expected_(expected) {
- }
- ValueEq(const ValueEq&) = default;
- template <typename T>
- operator ::testing::Matcher<T>() const {
- return ::testing::Matcher<T>(new Impl<T, std::is_pointer<T>::value>(expected_));
+ bool MatchAndExplain(T x, ::testing::MatchResultListener* listener) const override {
+ return expected_->Equals(&x);
+ }
+
+ void DescribeTo(::std::ostream* os) const override {
+ *os << "is equal to " << *expected_;
+ }
+
+ void DescribeNegationTo(::std::ostream* os) const override {
+ *os << "is not equal to " << *expected_;
}
private:
+ DISALLOW_COPY_AND_ASSIGN(ValueEqImpl);
+
const Value* expected_;
};
-// MATCHER_P(ValueEq, a,
-// std::string(negation ? "isn't" : "is") + " equal to " + ::testing::PrintToString(a)) {
-// return arg.Equals(&a);
-//}
+template <typename TValue>
+class ValueEqMatcher {
+ public:
+ ValueEqMatcher(TValue expected) : expected_(std::move(expected)) {
+ }
+
+ template <typename T>
+ operator ::testing::Matcher<T>() const {
+ return ::testing::Matcher<T>(new ValueEqImpl<T>(&expected_));
+ }
+
+ private:
+ TValue expected_;
+};
+
+template <typename TValue>
+class ValueEqPointerMatcher {
+ public:
+ ValueEqPointerMatcher(const TValue* expected) : expected_(expected) {
+ }
+
+ template <typename T>
+ operator ::testing::Matcher<T>() const {
+ return ::testing::Matcher<T>(new ValueEqImpl<T>(expected_));
+ }
+
+ private:
+ const TValue* expected_;
+};
+
+template <typename TValue,
+ typename = typename std::enable_if<!std::is_pointer<TValue>::value, void>::type>
+inline ValueEqMatcher<TValue> ValueEq(TValue value) {
+ return ValueEqMatcher<TValue>(std::move(value));
+}
+
+template <typename TValue>
+inline ValueEqPointerMatcher<TValue> ValueEq(const TValue* value) {
+ return ValueEqPointerMatcher<TValue>(value);
+}
MATCHER_P(StrValueEq, a,
std::string(negation ? "isn't" : "is") + " equal to " + ::testing::PrintToString(a)) {
diff --git a/tools/aapt2/xml/XmlDom.cpp b/tools/aapt2/xml/XmlDom.cpp
index fddb6b8..7b748ce 100644
--- a/tools/aapt2/xml/XmlDom.cpp
+++ b/tools/aapt2/xml/XmlDom.cpp
@@ -244,14 +244,13 @@
str16 = parser->getAttributeStringValue(i, &len);
if (str16) {
attr.value = util::Utf16ToUtf8(StringPiece16(str16, len));
- } else {
- android::Res_value res_value;
- if (parser->getAttributeValue(i, &res_value) > 0) {
- attr.compiled_value = ResourceUtils::ParseBinaryResValue(
- ResourceType::kAnim, {}, parser->getStrings(), res_value, out_pool);
- }
}
+ android::Res_value res_value;
+ if (parser->getAttributeValue(i, &res_value) > 0) {
+ attr.compiled_value = ResourceUtils::ParseBinaryResValue(
+ ResourceType::kAnim, {}, parser->getStrings(), res_value, out_pool);
+ }
el->attributes.push_back(std::move(attr));
}
diff --git a/tools/aapt2/xml/XmlDom_test.cpp b/tools/aapt2/xml/XmlDom_test.cpp
index e5012d6..486b53a 100644
--- a/tools/aapt2/xml/XmlDom_test.cpp
+++ b/tools/aapt2/xml/XmlDom_test.cpp
@@ -23,8 +23,10 @@
#include "test/Test.h"
using ::aapt::io::StringInputStream;
+using ::aapt::test::ValueEq;
using ::testing::Eq;
using ::testing::NotNull;
+using ::testing::Pointee;
using ::testing::SizeIs;
using ::testing::StrEq;
@@ -59,6 +61,16 @@
doc->root->name = "Layout";
doc->root->line_number = 2u;
+ xml::Attribute attr;
+ attr.name = "text";
+ attr.namespace_uri = kSchemaAndroid;
+ attr.compiled_attribute = AaptAttribute(
+ aapt::Attribute(android::ResTable_map::TYPE_REFERENCE | android::ResTable_map::TYPE_STRING),
+ ResourceId(0x01010001u));
+ attr.value = "@string/foo";
+ attr.compiled_value = test::BuildReference("string/foo", ResourceId(0x7f010000u));
+ doc->root->attributes.push_back(std::move(attr));
+
NamespaceDecl decl;
decl.uri = kSchemaAndroid;
decl.prefix = "android";
@@ -66,7 +78,9 @@
doc->root->namespace_decls.push_back(decl);
BigBuffer buffer(4096);
- XmlFlattener flattener(&buffer, {});
+ XmlFlattenerOptions options;
+ options.keep_raw_values = true;
+ XmlFlattener flattener(&buffer, options);
ASSERT_TRUE(flattener.Consume(context.get(), doc.get()));
auto block = util::Copy(buffer);
@@ -75,6 +89,21 @@
EXPECT_THAT(new_doc->root->name, StrEq("Layout"));
EXPECT_THAT(new_doc->root->line_number, Eq(2u));
+
+ ASSERT_THAT(new_doc->root->attributes, SizeIs(1u));
+ EXPECT_THAT(new_doc->root->attributes[0].name, StrEq("text"));
+ EXPECT_THAT(new_doc->root->attributes[0].namespace_uri, StrEq(kSchemaAndroid));
+
+ // We only check that the resource ID was preserved. There is no where to encode the types that
+ // the Attribute accepts (eg: string|reference).
+ ASSERT_TRUE(new_doc->root->attributes[0].compiled_attribute);
+ EXPECT_THAT(new_doc->root->attributes[0].compiled_attribute.value().id,
+ Eq(make_value(ResourceId(0x01010001u))));
+
+ EXPECT_THAT(new_doc->root->attributes[0].value, StrEq("@string/foo"));
+ EXPECT_THAT(new_doc->root->attributes[0].compiled_value,
+ Pointee(ValueEq(Reference(ResourceId(0x7f010000u)))));
+
ASSERT_THAT(new_doc->root->namespace_decls, SizeIs(1u));
EXPECT_THAT(new_doc->root->namespace_decls[0].uri, StrEq(kSchemaAndroid));
EXPECT_THAT(new_doc->root->namespace_decls[0].prefix, StrEq("android"));
diff --git a/tools/stats_log_api_gen/main.cpp b/tools/stats_log_api_gen/main.cpp
index a78b1a2..f0628c0 100644
--- a/tools/stats_log_api_gen/main.cpp
+++ b/tools/stats_log_api_gen/main.cpp
@@ -214,6 +214,7 @@
fprintf(out, "{\n");
argIndex = 1;
fprintf(out, " android_log_event_list event(kStatsEventTag);\n");
+ fprintf(out, " event << android::elapsedRealtimeNano();\n\n");
fprintf(out, " event << code;\n\n");
for (vector<java_type_t>::const_iterator arg = signature->begin();
arg != signature->end(); arg++) {
diff --git a/wifi/OWNERS b/wifi/OWNERS
new file mode 100644
index 0000000..0efa464
--- /dev/null
+++ b/wifi/OWNERS
@@ -0,0 +1,5 @@
+set noparent
+
+etancohen@google.com
+satk@google.com
+silberst@google.com
diff --git a/wifi/java/android/net/wifi/rtt/RangingRequest.java b/wifi/java/android/net/wifi/rtt/RangingRequest.java
index 52b3d86..4348ed4 100644
--- a/wifi/java/android/net/wifi/rtt/RangingRequest.java
+++ b/wifi/java/android/net/wifi/rtt/RangingRequest.java
@@ -122,6 +122,11 @@
* Add the device specified by the {@link ScanResult} to the list of devices with
* which to measure range. The total number of peers added to a request cannot exceed the
* limit specified by {@link #getMaxPeers()}.
+ * <p>
+ * Ranging may not be supported if the Access Point does not support IEEE 802.11mc. Use
+ * {@link ScanResult#is80211mcResponder()} to verify the Access Point's capabilities. If
+ * not supported the result status will be
+ * {@link RangingResult#STATUS_RESPONDER_DOES_NOT_SUPPORT_IEEE80211MC}.
*
* @param apInfo Information of an Access Point (AP) obtained in a Scan Result.
* @return The builder to facilitate chaining
@@ -138,6 +143,11 @@
* Add the devices specified by the {@link ScanResult}s to the list of devices with
* which to measure range. The total number of peers added to a request cannot exceed the
* limit specified by {@link #getMaxPeers()}.
+ * <p>
+ * Ranging may not be supported if the Access Point does not support IEEE 802.11mc. Use
+ * {@link ScanResult#is80211mcResponder()} to verify the Access Point's capabilities. If
+ * not supported the result status will be
+ * {@link RangingResult#STATUS_RESPONDER_DOES_NOT_SUPPORT_IEEE80211MC}.
*
* @param apInfos Information of an Access Points (APs) obtained in a Scan Result.
* @return The builder to facilitate chaining
diff --git a/wifi/java/android/net/wifi/rtt/RangingResult.java b/wifi/java/android/net/wifi/rtt/RangingResult.java
index f7c8567..5c9bd19 100644
--- a/wifi/java/android/net/wifi/rtt/RangingResult.java
+++ b/wifi/java/android/net/wifi/rtt/RangingResult.java
@@ -42,7 +42,7 @@
private static final String TAG = "RangingResult";
/** @hide */
- @IntDef({STATUS_SUCCESS, STATUS_FAIL})
+ @IntDef({STATUS_SUCCESS, STATUS_FAIL, STATUS_RESPONDER_DOES_NOT_SUPPORT_IEEE80211MC})
@Retention(RetentionPolicy.SOURCE)
public @interface RangeResultStatus {
}
@@ -67,8 +67,6 @@
* <p>
* On such a failure, the individual result fields of {@link RangingResult} such as
* {@link RangingResult#getDistanceMm()} are invalid.
- *
- * @hide
*/
public static final int STATUS_RESPONDER_DOES_NOT_SUPPORT_IEEE80211MC = 2;