diff options
408 files changed, 9654 insertions, 4555 deletions
diff --git a/Android.bp b/Android.bp index 03abf7563137..e65ba0f6a95f 100644 --- a/Android.bp +++ b/Android.bp @@ -249,8 +249,7 @@ java_library { "core/java/android/os/storage/IStorageEventListener.aidl", "core/java/android/os/storage/IStorageShutdownObserver.aidl", "core/java/android/os/storage/IObbActionListener.aidl", - "core/java/android/security/IConfirmationPromptCallback.aidl", - "core/java/android/security/IKeystoreService.aidl", + ":keystore_aidl", "core/java/android/security/keymaster/IKeyAttestationApplicationIdProvider.aidl", "core/java/android/service/autofill/IAutoFillService.aidl", "core/java/android/service/autofill/IAutofillFieldClassificationService.aidl", @@ -643,6 +642,7 @@ java_library { "system/netd/server/binder", "system/vold/binder", "system/bt/binder", + "system/security/keystore/binder", ], }, diff --git a/apct-tests/perftests/core/src/android/text/PrecomputedTextMemoryUsageTest.java b/apct-tests/perftests/core/src/android/text/MeasuredTextMemoryUsageTest.java index 73e17242ae78..fc6302ea9394 100644 --- a/apct-tests/perftests/core/src/android/text/PrecomputedTextMemoryUsageTest.java +++ b/apct-tests/perftests/core/src/android/text/MeasuredTextMemoryUsageTest.java @@ -45,7 +45,7 @@ import java.util.Random; @LargeTest @RunWith(AndroidJUnit4.class) -public class PrecomputedTextMemoryUsageTest { +public class MeasuredTextMemoryUsageTest { private static final int WORD_LENGTH = 9; // Random word has 9 characters. private static final boolean NO_STYLE_TEXT = false; @@ -53,7 +53,7 @@ public class PrecomputedTextMemoryUsageTest { private static int TRIAL_COUNT = 100; - public PrecomputedTextMemoryUsageTest() {} + public MeasuredTextMemoryUsageTest() {} private TextPerfUtils mTextUtil = new TextPerfUtils(); @@ -77,16 +77,13 @@ public class PrecomputedTextMemoryUsageTest { @Test public void testMemoryUsage_NoHyphenation() { int[] memories = new int[TRIAL_COUNT]; - final PrecomputedText.Params param = new PrecomputedText.Params.Builder(PAINT) + // Report median of randomly generated MeasuredText. + for (int i = 0; i < TRIAL_COUNT; ++i) { + memories[i] = new MeasuredText.Builder( + mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT) .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE) .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE) - .build(); - - // Report median of randomly generated PrecomputedText. - for (int i = 0; i < TRIAL_COUNT; ++i) { - memories[i] = PrecomputedText.create( - mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), param) - .getMemoryUsage(); + .build().getMemoryUsage(); } reportMemoryUsage(median(memories), "MemoryUsage_NoHyphenation"); } @@ -94,16 +91,13 @@ public class PrecomputedTextMemoryUsageTest { @Test public void testMemoryUsage_Hyphenation() { int[] memories = new int[TRIAL_COUNT]; - final PrecomputedText.Params param = new PrecomputedText.Params.Builder(PAINT) + // Report median of randomly generated MeasuredText. + for (int i = 0; i < TRIAL_COUNT; ++i) { + memories[i] = new MeasuredText.Builder( + mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT) .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED) .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL) - .build(); - - // Report median of randomly generated PrecomputedText. - for (int i = 0; i < TRIAL_COUNT; ++i) { - memories[i] = PrecomputedText.create( - mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), param) - .getMemoryUsage(); + .build().getMemoryUsage(); } reportMemoryUsage(median(memories), "MemoryUsage_Hyphenation"); } @@ -111,16 +105,13 @@ public class PrecomputedTextMemoryUsageTest { @Test public void testMemoryUsage_NoHyphenation_WidthOnly() { int[] memories = new int[TRIAL_COUNT]; - final PrecomputedText.Params param = new PrecomputedText.Params.Builder(PAINT) + // Report median of randomly generated MeasuredText. + for (int i = 0; i < TRIAL_COUNT; ++i) { + memories[i] = new MeasuredText.Builder( + mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT) .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE) .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE) - .build(); - - // Report median of randomly generated PrecomputedText. - for (int i = 0; i < TRIAL_COUNT; ++i) { - CharSequence cs = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT); - memories[i] = PrecomputedText.createWidthOnly(cs, param, 0, cs.length()) - .getMemoryUsage(); + .build(false /* width only */).getMemoryUsage(); } reportMemoryUsage(median(memories), "MemoryUsage_NoHyphenation_WidthOnly"); } @@ -128,16 +119,13 @@ public class PrecomputedTextMemoryUsageTest { @Test public void testMemoryUsage_Hyphenatation_WidthOnly() { int[] memories = new int[TRIAL_COUNT]; - final PrecomputedText.Params param = new PrecomputedText.Params.Builder(PAINT) + // Report median of randomly generated MeasuredText. + for (int i = 0; i < TRIAL_COUNT; ++i) { + memories[i] = new MeasuredText.Builder( + mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT) .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED) .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL) - .build(); - - // Report median of randomly generated PrecomputedText. - for (int i = 0; i < TRIAL_COUNT; ++i) { - CharSequence cs = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT); - memories[i] = PrecomputedText.createWidthOnly(cs, param, 0, cs.length()) - .getMemoryUsage(); + .build(false /* width only */).getMemoryUsage(); } reportMemoryUsage(median(memories), "MemoryUsage_Hyphenation_WidthOnly"); } diff --git a/apct-tests/perftests/core/src/android/text/PrecomputedTextPerfTest.java b/apct-tests/perftests/core/src/android/text/MeasuredTextPerfTest.java index 1cd0ae13069b..98f2bd5e5736 100644 --- a/apct-tests/perftests/core/src/android/text/PrecomputedTextPerfTest.java +++ b/apct-tests/perftests/core/src/android/text/MeasuredTextPerfTest.java @@ -42,7 +42,7 @@ import java.util.Random; @LargeTest @RunWith(AndroidJUnit4.class) -public class PrecomputedTextPerfTest { +public class MeasuredTextPerfTest { private static final int WORD_LENGTH = 9; // Random word has 9 characters. private static final int WORDS_IN_LINE = 8; // Roughly, 8 words in a line. private static final boolean NO_STYLE_TEXT = false; @@ -51,7 +51,7 @@ public class PrecomputedTextPerfTest { private static TextPaint PAINT = new TextPaint(); private static final int TEXT_WIDTH = WORDS_IN_LINE * WORD_LENGTH * (int) PAINT.getTextSize(); - public PrecomputedTextPerfTest() {} + public MeasuredTextPerfTest() {} @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter(); @@ -66,136 +66,120 @@ public class PrecomputedTextPerfTest { @Test public void testCreate_NoStyled_Hyphenation() { final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); - final PrecomputedText.Params param = new PrecomputedText.Params.Builder(PAINT) - .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED) - .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL) - .build(); - while (state.keepRunning()) { state.pauseTiming(); final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT); state.resumeTiming(); - PrecomputedText.create(text, param); + new MeasuredText.Builder(text, PAINT) + .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED) + .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL) + .build(true /* do full layout */); } } @Test public void testCreate_NoStyled_NoHyphenation() { final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); - final PrecomputedText.Params param = new PrecomputedText.Params.Builder(PAINT) - .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE) - .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE) - .build(); - while (state.keepRunning()) { state.pauseTiming(); final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT); state.resumeTiming(); - PrecomputedText.create(text, param); + new MeasuredText.Builder(text, PAINT) + .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE) + .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE) + .build(true /* do full layout */); } } @Test public void testCreate_NoStyled_Hyphenation_WidthOnly() { final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); - final PrecomputedText.Params param = new PrecomputedText.Params.Builder(PAINT) - .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED) - .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL) - .build(); - while (state.keepRunning()) { state.pauseTiming(); final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT); state.resumeTiming(); - PrecomputedText.create(text, param); + new MeasuredText.Builder(text, PAINT) + .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED) + .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL) + .build(false /* width only */); } } @Test public void testCreate_NoStyled_NoHyphenation_WidthOnly() { final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); - final PrecomputedText.Params param = new PrecomputedText.Params.Builder(PAINT) - .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE) - .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE) - .build(); - while (state.keepRunning()) { state.pauseTiming(); final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT); state.resumeTiming(); - PrecomputedText.create(text, param); + new MeasuredText.Builder(text, PAINT) + .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE) + .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE) + .build(false /* width only */); } } @Test public void testCreate_Styled_Hyphenation() { final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); - final PrecomputedText.Params param = new PrecomputedText.Params.Builder(PAINT) - .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED) - .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL) - .build(); - while (state.keepRunning()) { state.pauseTiming(); final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT); state.resumeTiming(); - PrecomputedText.create(text, param); + new MeasuredText.Builder(text, PAINT) + .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED) + .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL) + .build(true /* do full layout */); } } @Test public void testCreate_Styled_NoHyphenation() { final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); - final PrecomputedText.Params param = new PrecomputedText.Params.Builder(PAINT) - .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE) - .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE) - .build(); - while (state.keepRunning()) { state.pauseTiming(); final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT); state.resumeTiming(); - PrecomputedText.create(text, param); + new MeasuredText.Builder(text, PAINT) + .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE) + .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE) + .build(true /* do full layout */); } } @Test public void testCreate_Styled_Hyphenation_WidthOnly() { final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); - final PrecomputedText.Params param = new PrecomputedText.Params.Builder(PAINT) - .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED) - .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL) - .build(); - while (state.keepRunning()) { state.pauseTiming(); final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT); state.resumeTiming(); - PrecomputedText.create(text, param); + new MeasuredText.Builder(text, PAINT) + .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED) + .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL) + .build(false /* width only */); } } @Test public void testCreate_Styled_NoHyphenation_WidthOnly() { final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); - final PrecomputedText.Params param = new PrecomputedText.Params.Builder(PAINT) - .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE) - .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE) - .build(); - while (state.keepRunning()) { state.pauseTiming(); final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT); state.resumeTiming(); - PrecomputedText.create(text, param); + new MeasuredText.Builder(text, PAINT) + .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE) + .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE) + .build(false /* width only */); } } } diff --git a/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java b/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java index 8823af1df350..231aaf2ca074 100644 --- a/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java +++ b/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java @@ -63,18 +63,6 @@ public class StaticLayoutPerfTest { mTextUtil.resetRandom(0 /* seed */); } - private PrecomputedText makeMeasured(CharSequence text, TextPaint paint) { - PrecomputedText.Params param = new PrecomputedText.Params.Builder(paint).build(); - return PrecomputedText.create(text, param); - } - - private PrecomputedText makeMeasured(CharSequence text, TextPaint paint, int strategy, - int frequency) { - PrecomputedText.Params param = new PrecomputedText.Params.Builder(paint) - .setHyphenationFrequency(frequency).setBreakStrategy(strategy).build(); - return PrecomputedText.create(text, param); - } - @Test public void testCreate_FixedText_NoStyle_Greedy_NoHyphenation() { final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); @@ -163,16 +151,18 @@ public class StaticLayoutPerfTest { } @Test - public void testCreate_PrecomputedText_NoStyled_Greedy_NoHyphenation() { + public void testCreate_MeasuredText_NoStyled_Greedy_NoHyphenation() { final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); while (state.keepRunning()) { state.pauseTiming(); - final PrecomputedText text = makeMeasured( - mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT, - Layout.BREAK_STRATEGY_SIMPLE, Layout.HYPHENATION_FREQUENCY_NONE); + final MeasuredText text = new MeasuredText.Builder( + mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT) + .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE) + .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE) + .build(); state.resumeTiming(); - StaticLayout.Builder.obtain(text, 0, text.getText().length(), PAINT, TEXT_WIDTH) + StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH) .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE) .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE) .build(); @@ -180,16 +170,18 @@ public class StaticLayoutPerfTest { } @Test - public void testCreate_PrecomputedText_NoStyled_Greedy_Hyphenation() { + public void testCreate_MeasuredText_NoStyled_Greedy_Hyphenation() { final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); while (state.keepRunning()) { state.pauseTiming(); - final PrecomputedText text = makeMeasured( - mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT, - Layout.BREAK_STRATEGY_SIMPLE, Layout.HYPHENATION_FREQUENCY_NORMAL); + final MeasuredText text = new MeasuredText.Builder( + mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT) + .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE) + .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL) + .build(); state.resumeTiming(); - StaticLayout.Builder.obtain(text, 0, text.getText().length(), PAINT, TEXT_WIDTH) + StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH) .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL) .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE) .build(); @@ -197,16 +189,18 @@ public class StaticLayoutPerfTest { } @Test - public void testCreate_PrecomputedText_NoStyled_Balanced_NoHyphenation() { + public void testCreate_MeasuredText_NoStyled_Balanced_NoHyphenation() { final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); while (state.keepRunning()) { state.pauseTiming(); - final PrecomputedText text = makeMeasured( - mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT, - Layout.BREAK_STRATEGY_BALANCED, Layout.HYPHENATION_FREQUENCY_NONE); + final MeasuredText text = new MeasuredText.Builder( + mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT) + .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED) + .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE) + .build(); state.resumeTiming(); - StaticLayout.Builder.obtain(text, 0, text.getText().length(), PAINT, TEXT_WIDTH) + StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH) .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE) .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED) .build(); @@ -214,16 +208,18 @@ public class StaticLayoutPerfTest { } @Test - public void testCreate_PrecomputedText_NoStyled_Balanced_Hyphenation() { + public void testCreate_MeasuredText_NoStyled_Balanced_Hyphenation() { final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); while (state.keepRunning()) { state.pauseTiming(); - final PrecomputedText text = makeMeasured( - mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT, - Layout.BREAK_STRATEGY_BALANCED, Layout.HYPHENATION_FREQUENCY_NORMAL); + final MeasuredText text = new MeasuredText.Builder( + mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT) + .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED) + .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL) + .build(); state.resumeTiming(); - StaticLayout.Builder.obtain(text, 0, text.getText().length(), PAINT, TEXT_WIDTH) + StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH) .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL) .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED) .build(); @@ -231,16 +227,18 @@ public class StaticLayoutPerfTest { } @Test - public void testCreate_PrecomputedText_Styled_Greedy_NoHyphenation() { + public void testCreate_MeasuredText_Styled_Greedy_NoHyphenation() { final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); while (state.keepRunning()) { state.pauseTiming(); - final PrecomputedText text = makeMeasured( - mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT), PAINT, - Layout.BREAK_STRATEGY_SIMPLE, Layout.HYPHENATION_FREQUENCY_NONE); + final MeasuredText text = new MeasuredText.Builder( + mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT), PAINT) + .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE) + .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE) + .build(); state.resumeTiming(); - StaticLayout.Builder.obtain(text, 0, text.getText().length(), PAINT, TEXT_WIDTH) + StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH) .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE) .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE) .build(); @@ -330,16 +328,15 @@ public class StaticLayoutPerfTest { } @Test - public void testDraw_PrecomputedText_Styled() { + public void testDraw_MeasuredText_Styled() { final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); final RenderNode node = RenderNode.create("benchmark", null); while (state.keepRunning()) { state.pauseTiming(); - final PrecomputedText text = makeMeasured( - mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT), PAINT); + final MeasuredText text = new MeasuredText.Builder( + mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT), PAINT).build(); final StaticLayout layout = - StaticLayout.Builder.obtain( - text, 0, text.getText().length(), PAINT, TEXT_WIDTH).build(); + StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build(); final DisplayListCanvas c = node.start(1200, 200); state.resumeTiming(); @@ -348,16 +345,15 @@ public class StaticLayoutPerfTest { } @Test - public void testDraw_PrecomputedText_NoStyled() { + public void testDraw_MeasuredText_NoStyled() { final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); final RenderNode node = RenderNode.create("benchmark", null); while (state.keepRunning()) { state.pauseTiming(); - final PrecomputedText text = makeMeasured( - mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT); + final MeasuredText text = new MeasuredText.Builder( + mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT).build(); final StaticLayout layout = - StaticLayout.Builder.obtain( - text, 0, text.getText().length(), PAINT, TEXT_WIDTH).build(); + StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build(); final DisplayListCanvas c = node.start(1200, 200); state.resumeTiming(); @@ -366,16 +362,15 @@ public class StaticLayoutPerfTest { } @Test - public void testDraw_PrecomputedText_Styled_WithoutCache() { + public void testDraw_MeasuredText_Styled_WithoutCache() { final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); final RenderNode node = RenderNode.create("benchmark", null); while (state.keepRunning()) { state.pauseTiming(); - final PrecomputedText text = makeMeasured( - mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT), PAINT); + final MeasuredText text = new MeasuredText.Builder( + mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT), PAINT).build(); final StaticLayout layout = - StaticLayout.Builder.obtain( - text, 0, text.getText().length(), PAINT, TEXT_WIDTH).build(); + StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build(); final DisplayListCanvas c = node.start(1200, 200); Canvas.freeTextLayoutCaches(); state.resumeTiming(); @@ -385,16 +380,15 @@ public class StaticLayoutPerfTest { } @Test - public void testDraw_PrecomputedText_NoStyled_WithoutCache() { + public void testDraw_MeasuredText_NoStyled_WithoutCache() { final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); final RenderNode node = RenderNode.create("benchmark", null); while (state.keepRunning()) { state.pauseTiming(); - final PrecomputedText text = makeMeasured( - mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT); + final MeasuredText text = new MeasuredText.Builder( + mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT).build(); final StaticLayout layout = - StaticLayout.Builder.obtain( - text, 0, text.getText().length(), PAINT, TEXT_WIDTH).build(); + StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build(); final DisplayListCanvas c = node.start(1200, 200); Canvas.freeTextLayoutCaches(); state.resumeTiming(); diff --git a/api/current.txt b/api/current.txt index 042360e6ff1b..63bc2ecaca13 100644 --- a/api/current.txt +++ b/api/current.txt @@ -1468,6 +1468,7 @@ package android { field public static final deprecated int unfocusedMonthDateColor = 16843588; // 0x1010344 field public static final int unselectedAlpha = 16843278; // 0x101020e field public static final int updatePeriodMillis = 16843344; // 0x1010250 + field public static final int urlBarResourceId = 16844164; // 0x1010584 field public static final int use32bitAbi = 16844053; // 0x1010515 field public static final int useDefaultMargins = 16843641; // 0x1010379 field public static final int useIntrinsicSizeAsMinimum = 16843536; // 0x1010310 @@ -12554,6 +12555,7 @@ package android.database.sqlite { method public abstract void onUpgrade(android.database.sqlite.SQLiteDatabase, int, int); method public void setIdleConnectionTimeout(long); method public void setLookasideConfig(int, int); + method public void setOpenParams(android.database.sqlite.SQLiteDatabase.OpenParams); method public void setWriteAheadLoggingEnabled(boolean); } @@ -13837,7 +13839,6 @@ package android.graphics { method public int breakText(java.lang.String, boolean, float, float[]); method public void clearShadowLayer(); method public float descent(); - method public boolean equalsForTextMeasurement(android.graphics.Paint); method public int getAlpha(); method public int getColor(); method public android.graphics.ColorFilter getColorFilter(); @@ -26755,7 +26756,7 @@ package android.net { 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"; @@ -27031,6 +27032,7 @@ package android.net { 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 @@ -27039,6 +27041,7 @@ package android.net { 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 @@ -28558,6 +28561,7 @@ package android.net.wifi.rtt { 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 } @@ -36636,6 +36640,7 @@ package android.provider { field public static final deprecated java.lang.String RADIO_NFC = "nfc"; field public static final deprecated java.lang.String RADIO_WIFI = "wifi"; field public static final java.lang.String RINGTONE = "ringtone"; + field public static final java.lang.String RTT_CALLING_MODE = "rtt_calling_mode"; field public static final java.lang.String SCREEN_BRIGHTNESS = "screen_brightness"; field public static final java.lang.String SCREEN_BRIGHTNESS_MODE = "screen_brightness_mode"; field public static final int SCREEN_BRIGHTNESS_MODE_AUTOMATIC = 1; // 0x1 @@ -38445,6 +38450,7 @@ package android.security.keystore { method public boolean isRandomizedEncryptionRequired(); method public boolean isStrongBoxBacked(); method public boolean isTrustedUserPresenceRequired(); + method public boolean isUnlockedDeviceRequired(); method public boolean isUserAuthenticationRequired(); method public boolean isUserAuthenticationValidWhileOnBody(); method public boolean isUserConfirmationRequired(); @@ -38472,6 +38478,7 @@ package android.security.keystore { method public android.security.keystore.KeyGenParameterSpec.Builder setRandomizedEncryptionRequired(boolean); method public android.security.keystore.KeyGenParameterSpec.Builder setSignaturePaddings(java.lang.String...); method public android.security.keystore.KeyGenParameterSpec.Builder setTrustedUserPresenceRequired(boolean); + method public android.security.keystore.KeyGenParameterSpec.Builder setUnlockedDeviceRequired(boolean); method public android.security.keystore.KeyGenParameterSpec.Builder setUserAuthenticationRequired(boolean); method public android.security.keystore.KeyGenParameterSpec.Builder setUserAuthenticationValidWhileOnBody(boolean); method public android.security.keystore.KeyGenParameterSpec.Builder setUserAuthenticationValidityDurationSeconds(int); @@ -38563,6 +38570,8 @@ package android.security.keystore { method public boolean isDigestsSpecified(); method public boolean isInvalidatedByBiometricEnrollment(); method public boolean isRandomizedEncryptionRequired(); + method public boolean isTrustedUserPresenceRequired(); + method public boolean isUnlockedDeviceRequired(); method public boolean isUserAuthenticationRequired(); method public boolean isUserAuthenticationValidWhileOnBody(); method public boolean isUserConfirmationRequired(); @@ -38581,6 +38590,8 @@ package android.security.keystore { method public android.security.keystore.KeyProtection.Builder setKeyValidityStart(java.util.Date); method public android.security.keystore.KeyProtection.Builder setRandomizedEncryptionRequired(boolean); method public android.security.keystore.KeyProtection.Builder setSignaturePaddings(java.lang.String...); + method public android.security.keystore.KeyProtection.Builder setTrustedUserPresenceRequired(boolean); + method public android.security.keystore.KeyProtection.Builder setUnlockedDeviceRequired(boolean); method public android.security.keystore.KeyProtection.Builder setUserAuthenticationRequired(boolean); method public android.security.keystore.KeyProtection.Builder setUserAuthenticationValidWhileOnBody(boolean); method public android.security.keystore.KeyProtection.Builder setUserAuthenticationValidityDurationSeconds(int); @@ -38683,14 +38694,14 @@ package android.service.autofill { } 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; @@ -38701,7 +38712,7 @@ package android.service.autofill { } public static final class FieldClassification.Match { - method public java.lang.String getRemoteId(); + method public java.lang.String getCategoryId(); method public float getScore(); } @@ -38866,6 +38877,7 @@ package android.service.autofill { 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(); @@ -40640,6 +40652,7 @@ package android.telecom { field public static final int PROPERTY_HAS_CDMA_VOICE_PRIVACY = 128; // 0x80 field public static final int PROPERTY_HIGH_DEF_AUDIO = 16; // 0x10 field public static final int PROPERTY_IS_EXTERNAL_CALL = 64; // 0x40 + field public static final int PROPERTY_RTT = 1024; // 0x400 field public static final int PROPERTY_SELF_MANAGED = 256; // 0x100 field public static final int PROPERTY_WIFI = 8; // 0x8 } @@ -43227,6 +43240,36 @@ package android.text { method public boolean isAllowed(char); } + public class MeasuredText implements android.text.Spanned { + method public char charAt(int); + method public int getBreakStrategy(); + method public int getEnd(); + method public int getHyphenationFrequency(); + method public android.text.TextPaint getPaint(); + method public int getParagraphCount(); + method public int getParagraphEnd(int); + method public int getParagraphStart(int); + method public int getSpanEnd(java.lang.Object); + method public int getSpanFlags(java.lang.Object); + method public int getSpanStart(java.lang.Object); + method public <T> T[] getSpans(int, int, java.lang.Class<T>); + method public int getStart(); + method public java.lang.CharSequence getText(); + method public android.text.TextDirectionHeuristic getTextDir(); + method public int length(); + method public int nextSpanTransition(int, int, java.lang.Class); + method public java.lang.CharSequence subSequence(int, int); + } + + public static final class MeasuredText.Builder { + ctor public MeasuredText.Builder(java.lang.CharSequence, android.text.TextPaint); + method public android.text.MeasuredText build(); + method public android.text.MeasuredText.Builder setBreakStrategy(int); + method public android.text.MeasuredText.Builder setHyphenationFrequency(int); + method public android.text.MeasuredText.Builder setRange(int, int); + method public android.text.MeasuredText.Builder setTextDirection(android.text.TextDirectionHeuristic); + } + public abstract interface NoCopySpan { } @@ -43238,31 +43281,6 @@ package android.text { method public abstract int getSpanTypeId(); } - public class PrecomputedText { - method public static android.text.PrecomputedText create(java.lang.CharSequence, android.text.PrecomputedText.Params); - method public int getParagraphCount(); - method public int getParagraphEnd(int); - method public int getParagraphStart(int); - method public android.text.PrecomputedText.Params getParams(); - method public java.lang.CharSequence getText(); - } - - public static class PrecomputedText.Params { - method public int getBreakStrategy(); - method public int getHyphenationFrequency(); - method public android.text.TextDirectionHeuristic getTextDirection(); - method public android.text.TextPaint getTextPaint(); - method public boolean sameTextMetrics(android.text.PrecomputedText.Params); - } - - public static class PrecomputedText.Params.Builder { - ctor public PrecomputedText.Params.Builder(android.text.TextPaint); - method public android.text.PrecomputedText.Params build(); - method public android.text.PrecomputedText.Params.Builder setBreakStrategy(int); - method public android.text.PrecomputedText.Params.Builder setHyphenationFrequency(int); - method public android.text.PrecomputedText.Params.Builder setTextDirection(android.text.TextDirectionHeuristic); - } - public class Selection { method public static boolean extendDown(android.text.Spannable, android.text.Layout); method public static boolean extendLeft(android.text.Spannable, android.text.Layout); @@ -43394,7 +43412,6 @@ package android.text { public static final class StaticLayout.Builder { method public android.text.StaticLayout build(); - method public static android.text.StaticLayout.Builder obtain(android.text.PrecomputedText, int, int, android.text.TextPaint, int); method public static android.text.StaticLayout.Builder obtain(java.lang.CharSequence, int, int, android.text.TextPaint, int); method public android.text.StaticLayout.Builder setAlignment(android.text.Layout.Alignment); method public android.text.StaticLayout.Builder setBreakStrategy(int); @@ -53646,7 +53663,6 @@ package android.widget { method public final android.content.res.ColorStateList getTextColors(); method public java.util.Locale getTextLocale(); method public android.os.LocaleList getTextLocales(); - method public android.text.PrecomputedText.Params getTextMetricsParams(); method public float getTextScaleX(); method public float getTextSize(); method public int getTotalPaddingBottom(); @@ -53752,8 +53768,6 @@ package android.widget { method public final void setMovementMethod(android.text.method.MovementMethod); method public void setOnEditorActionListener(android.widget.TextView.OnEditorActionListener); method public void setPaintFlags(int); - method public void setPrecomputedTextAndParams(android.text.PrecomputedText); - method public void setPrecomputedTextOrThrow(android.text.PrecomputedText); method public void setPrivateImeOptions(java.lang.String); method public void setRawInputType(int); method public void setScroller(android.widget.Scroller); @@ -53778,7 +53792,6 @@ package android.widget { method public final void setTextKeepState(java.lang.CharSequence, android.widget.TextView.BufferType); method public void setTextLocale(java.util.Locale); method public void setTextLocales(android.os.LocaleList); - method public void setTextMetricsParams(android.text.PrecomputedText.Params); method public void setTextScaleX(float); method public void setTextSize(float); method public void setTextSize(int, float); diff --git a/api/system-current.txt b/api/system-current.txt index 39cbe90894ee..8001ee3b7f6d 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -229,7 +229,6 @@ package android.app { method public boolean convertToTranslucent(android.app.Activity.TranslucentConversionListener, android.app.ActivityOptions); method public deprecated boolean isBackgroundVisibleBehind(); method public deprecated void onBackgroundVisibleBehindChanged(boolean); - method public void setDisablePreviewScreenshots(boolean); } public static abstract interface Activity.TranslucentConversionListener { @@ -3606,11 +3605,14 @@ package android.os { } public abstract class HwBinder implements android.os.IHwBinder { + ctor public HwBinder(); method public static final void configureRpcThreadpool(long, boolean); method public static void enableInstrumentation(); method public static final android.os.IHwBinder getService(java.lang.String, java.lang.String) throws java.util.NoSuchElementException, android.os.RemoteException; method public static final android.os.IHwBinder getService(java.lang.String, java.lang.String, boolean) throws java.util.NoSuchElementException, android.os.RemoteException; method public static final void joinRpcThreadpool(); + method public abstract void onTransact(int, android.os.HwParcel, android.os.HwParcel, int) throws android.os.RemoteException; + method public final void registerService(java.lang.String) throws android.os.RemoteException; method public final void transact(int, android.os.HwParcel, android.os.HwParcel, int) throws android.os.RemoteException; } @@ -4162,6 +4164,8 @@ package android.provider { 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"; @@ -4173,6 +4177,7 @@ package android.provider { 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 b02da04bbcd1..9bfc105efa8f 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -541,6 +541,7 @@ package android.provider { 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"; @@ -1047,6 +1048,15 @@ package android.view.accessibility { } +package android.view.animation { + + public class AnimationUtils { + method public static void lockAnimationClock(long); + method public static void unlockAnimationClock(); + } + +} + package android.view.autofill { public final class AutofillId implements android.os.Parcelable { diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java index 813335a688ab..c04e61b77274 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 @@ public class Am extends BaseCommand { } 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 d79b1a61eb5b..0dade0b2ba81 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 @@ public class Instrument { 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 @@ public class Instrument { } // 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 000000000000..1a68a32c4308 --- /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 02f1ce7cc0fc..fde17bd97a7f 100644 --- a/cmds/incident_helper/src/parsers/CpuFreqParser.cpp +++ b/cmds/incident_helper/src/parsers/CpuFreqParser.cpp @@ -62,15 +62,15 @@ CpuFreqParser::Parse(const int in, const int out) const 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 d73de54d8c5d..b2b431c62866 100644 --- a/cmds/incident_helper/src/parsers/CpuInfoParser.cpp +++ b/cmds/incident_helper/src/parsers/CpuInfoParser.cpp @@ -54,11 +54,11 @@ CpuInfoParser::Parse(const int in, const int out) const 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 @@ CpuInfoParser::Parse(const int in, const int out) const 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 @@ CpuInfoParser::Parse(const int in, const int out) const 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 cae51abbe57f..28816ea75a75 100644 --- a/cmds/incident_helper/src/parsers/KernelWakesParser.cpp +++ b/cmds/incident_helper/src/parsers/KernelWakesParser.cpp @@ -33,7 +33,9 @@ KernelWakesParser::Parse(const int in, const int out) const 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 @@ KernelWakesParser::Parse(const int in, const int out) const 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 f1b93ff9ec41..45a0e7b459d2 100644 --- a/cmds/incident_helper/src/parsers/PageTypeInfoParser.cpp +++ b/cmds/incident_helper/src/parsers/PageTypeInfoParser.cpp @@ -33,7 +33,9 @@ PageTypeInfoParser::Parse(const int in, const int out) const 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 @@ PageTypeInfoParser::Parse(const int in, const int out) const 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 @@ PageTypeInfoParser::Parse(const int in, const int out) const 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 @@ PageTypeInfoParser::Parse(const int in, const int out) const 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 a4eb0fdfd988..c1c458e69ddf 100644 --- a/cmds/incident_helper/src/parsers/ProcrankParser.cpp +++ b/cmds/incident_helper/src/parsers/ProcrankParser.cpp @@ -33,7 +33,7 @@ ProcrankParser::Parse(const int in, const int out) const 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 @@ ProcrankParser::Parse(const int in, const int out) const 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 @@ ProcrankParser::Parse(const int in, const int out) const } // 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 e9014cacfa0b..420775fb5f04 100644 --- a/cmds/incident_helper/src/parsers/PsParser.cpp +++ b/cmds/incident_helper/src/parsers/PsParser.cpp @@ -33,12 +33,12 @@ status_t PsParser::Parse(const int in, const int out) const { 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 @@ status_t PsParser::Parse(const int in, const int out) const { 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 82deee42687f..0839a7e45edd 100644 --- a/cmds/incident_helper/tests/CpuFreqParser_test.cpp +++ b/cmds/incident_helper/tests/CpuFreqParser_test.cpp @@ -52,14 +52,14 @@ protected: 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 @@ TEST_F(CpuFreqParserTest, Success) { 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 @@ TEST_F(CpuFreqParserTest, Success) { 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 @@ TEST_F(CpuFreqParserTest, Success) { 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 8dce53e46492..2e50d631715e 100644 --- a/cmds/incident_helper/tests/CpuInfoParser_test.cpp +++ b/cmds/incident_helper/tests/CpuInfoParser_test.cpp @@ -52,28 +52,28 @@ protected: 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 @@ TEST_F(CpuInfoParserTest, Success) { 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 a98c62bd6024..f92d81361eab 100644 --- a/cmds/incident_helper/tests/KernelWakesParser_test.cpp +++ b/cmds/incident_helper/tests/KernelWakesParser_test.cpp @@ -52,14 +52,14 @@ protected: 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, Short) { 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 @@ TEST_F(KernelWakesParserTest, Normal) { 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 a9e6e816c6c5..9bad7be4a07e 100644 --- a/cmds/incident_helper/tests/PageTypeInfoParser_test.cpp +++ b/cmds/incident_helper/tests/PageTypeInfoParser_test.cpp @@ -52,12 +52,12 @@ protected: 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 @@ TEST_F(PageTypeInfoParserTest, Success) { 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 @@ TEST_F(PageTypeInfoParserTest, Success) { 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 @@ TEST_F(PageTypeInfoParserTest, Success) { 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 76b25d7f8f47..0b567aec9597 100644 --- a/cmds/incident_helper/tests/ProcrankParser_test.cpp +++ b/cmds/incident_helper/tests/ProcrankParser_test.cpp @@ -52,9 +52,9 @@ protected: 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 @@ TEST_F(ProcrankParserTest, HasSwapInfo) { 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 @@ TEST_F(ProcrankParserTest, HasSwapInfo) { 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, HasSwapInfo) { 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 @@ TEST_F(ProcrankParserTest, NoSwapInfo) { 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 @@ TEST_F(ProcrankParserTest, NoSwapInfo) { 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 1f03a7f3a332..114d63472ffc 100644 --- a/cmds/incident_helper/tests/PsParser_test.cpp +++ b/cmds/incident_helper/tests/PsParser_test.cpp @@ -52,10 +52,10 @@ protected: 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 @@ TEST_F(PsParserTest, Normal) { 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 @@ TEST_F(PsParserTest, Normal) { 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 @@ TEST_F(PsParserTest, Normal) { 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 @@ TEST_F(PsParserTest, Normal) { 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 @@ TEST_F(PsParserTest, Normal) { 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 @@ TEST_F(PsParserTest, Normal) { 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 @@ TEST_F(PsParserTest, Normal) { 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 @@ TEST_F(PsParserTest, Normal) { 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 @@ TEST_F(PsParserTest, Normal) { } 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 2b00d9e6a596..23cd2af512b5 100644 --- a/cmds/incidentd/Android.mk +++ b/cmds/incidentd/Android.mk @@ -56,6 +56,7 @@ LOCAL_SHARED_LIBRARIES := \ libcutils \ libincident \ liblog \ + libprotobuf-cpp-lite \ libprotoutil \ libselinux \ libservices \ diff --git a/cmds/incidentd/OWNERS b/cmds/incidentd/OWNERS new file mode 100644 index 000000000000..1a68a32c4308 --- /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 3f0e331c8b55..c5078f0f7909 100644 --- a/cmds/incidentd/src/Privacy.cpp +++ b/cmds/incidentd/src/Privacy.cpp @@ -73,11 +73,6 @@ PrivacySpec PrivacySpec::new_spec(int dest) 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 4f3db678f765..ce1b8e96529c 100644 --- a/cmds/incidentd/src/Privacy.h +++ b/cmds/incidentd/src/Privacy.h @@ -75,7 +75,6 @@ public: // 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 b9f479bd683f..06baebaf3a4c 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 @@ ReportRequestSet::ReportRequestSet() :mRequests(), mSections(), mMainFd(-1), - mMainDest(-1) + mMainDest(-1), + mMetadata(), + mSectionStats() { } @@ -79,18 +82,32 @@ ReportRequestSet::add(const sp<ReportRequest>& request) { 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 @@ ReportRequestSet::containsSection(int id) { 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::~Reporter() 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 @@ Reporter::runReport() 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 @@ Reporter::runReport() } // 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 @@ Reporter::runReport() } 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 f30ecf0dd648..6058068be331 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 @@ public: 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 @@ public: 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 faeab87f8178..3c76298c284c 100644 --- a/cmds/incidentd/src/Section.cpp +++ b/cmds/incidentd/src/Section.cpp @@ -45,6 +45,7 @@ using namespace std; // 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 @@ write_report_requests(const int id, const FdBuffer& buffer, ReportRequestSet* re 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 @@ write_report_requests(const int id, const FdBuffer& buffer, ReportRequestSet* re // 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 @@ write_report_requests(const int id, const FdBuffer& buffer, ReportRequestSet* re 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 @@ HeaderSection::Execute(ReportRequestSet* requests) const // 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 @@ HeaderSection::Execute(ReportRequestSet* requests) const } 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 d440ee92601c..80cc033d5ca0 100644 --- a/cmds/incidentd/src/Section.h +++ b/cmds/incidentd/src/Section.h @@ -59,6 +59,18 @@ public: }; /** + * 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 c494bd646b0b..a1e3c3430595 100644 --- a/cmds/incidentd/tests/Reporter_test.cpp +++ b/cmds/incidentd/tests/Reporter_test.cpp @@ -180,3 +180,18 @@ TEST_F(ReporterTest, RunReportToGivenDirectory) { 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 2cfd7df6be84..5752a670c6a2 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 @@ TEST(SectionTest, HeaderSection) { 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 @@ TEST(SectionTest, TestMultipleRequests) { 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 @@ TEST(SectionTest, TestMultipleRequestsBySpec) { 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 @@ TEST(SectionTest, TestMultipleRequestsBySpec) { 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/Android.mk b/cmds/statsd/Android.mk index 67b9089c8315..740fdc0af5f1 100644 --- a/cmds/statsd/Android.mk +++ b/cmds/statsd/Android.mk @@ -116,10 +116,8 @@ LOCAL_SRC_FILES := \ LOCAL_CFLAGS += \ -Wall \ + -Wextra \ -Werror \ - -Wno-missing-field-initializers \ - -Wno-unused-variable \ - -Wno-unused-function \ -Wno-unused-parameter ifeq (debug,) diff --git a/cmds/statsd/OWNERS b/cmds/statsd/OWNERS new file mode 100644 index 000000000000..362d411d03a5 --- /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/FieldValue.cpp b/cmds/statsd/src/FieldValue.cpp index 7b0b69a4bcae..6894bcf53e79 100644 --- a/cmds/statsd/src/FieldValue.cpp +++ b/cmds/statsd/src/FieldValue.cpp @@ -23,6 +23,23 @@ namespace android { namespace os { namespace statsd { +int32_t getEncodedField(int32_t pos[], int32_t depth, bool includeDepth) { + int32_t field = 0; + for (int32_t i = 0; i <= depth; i++) { + int32_t shiftBits = 8 * (kMaxLogDepth - i); + field |= (pos[i] << shiftBits); + } + + if (includeDepth) { + field |= (depth << 24); + } + return field; +} + +int32_t encodeMatcherMask(int32_t mask[], int32_t depth) { + return getEncodedField(mask, depth, false) | 0xff000000; +} + bool Field::matches(const Matcher& matcher) const { if (mTag != matcher.mMatcher.getTag()) { return false; @@ -32,7 +49,7 @@ bool Field::matches(const Matcher& matcher) const { } return false; -}; +} void translateFieldMatcher(int tag, const FieldMatcher& matcher, int depth, int* pos, int* mask, std::vector<Matcher>* output) { @@ -71,7 +88,6 @@ void translateFieldMatcher(int tag, const FieldMatcher& matcher, int depth, int* if (matcher.child_size() == 0) { output->push_back(Matcher(Field(tag, pos, depth), encodeMatcherMask(mask, depth))); - Matcher matcher = Matcher(Field(tag, pos, depth), encodeMatcherMask(mask, depth)); } else { for (const auto& child : matcher.child()) { translateFieldMatcher(tag, child, depth + 1, pos, mask, output); diff --git a/cmds/statsd/src/FieldValue.h b/cmds/statsd/src/FieldValue.h index 7484108d9e1a..d17dded8d691 100644 --- a/cmds/statsd/src/FieldValue.h +++ b/cmds/statsd/src/FieldValue.h @@ -33,29 +33,14 @@ const int32_t kClearLastBitDeco = 0x7f; enum Type { INT, LONG, FLOAT, STRING }; +int32_t getEncodedField(int32_t pos[], int32_t depth, bool includeDepth); -static int32_t getEncodedField(int32_t pos[], int32_t depth, bool includeDepth) { - int32_t field = 0; - for (int32_t i = 0; i <= depth; i++) { - int32_t shiftBits = 8 * (kMaxLogDepth - i); - field |= (pos[i] << shiftBits); - } - - if (includeDepth) { - field |= (depth << 24); - } - return field; -} - -static int32_t encodeMatcherMask(int32_t mask[], int32_t depth) { - return getEncodedField(mask, depth, false) | 0xff000000; -} +int32_t encodeMatcherMask(int32_t mask[], int32_t depth); // Get the encoded field for a leaf with a [field] number at depth 0; -static int32_t getSimpleField(size_t field) { +inline int32_t getSimpleField(size_t field) { return ((int32_t)field << 8 * 2); } - /** * Field is a wrapper class for 2 integers that represents the field of a log element in its Atom * proto. diff --git a/cmds/statsd/src/HashableDimensionKey.cpp b/cmds/statsd/src/HashableDimensionKey.cpp index 68e2176c2e6d..d901bd669591 100644 --- a/cmds/statsd/src/HashableDimensionKey.cpp +++ b/cmds/statsd/src/HashableDimensionKey.cpp @@ -68,7 +68,6 @@ bool filterValues(const vector<Matcher>& matcherFields, const vector<FieldValue> for (const auto& value : values) { // TODO: potential optimization here to break early because all fields are naturally // sorted. - int32_t filteredField; if (value.mField.matches(matcher)) { matchedResults.push_back(FieldValue( Field(value.mField.getTag(), (value.mField.getField() & matcher.mMask)), @@ -148,7 +147,6 @@ void filterGaugeValues(const std::vector<Matcher>& matcherFields, const std::vector<FieldValue>& values, std::vector<FieldValue>* output) { for (const auto& field : matcherFields) { for (const auto& value : values) { - int filteredField; if (value.mField.matches(field)) { output->push_back(value); } diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp index 3a20b12500ae..87dec5d1656d 100644 --- a/cmds/statsd/src/StatsLogProcessor.cpp +++ b/cmds/statsd/src/StatsLogProcessor.cpp @@ -21,6 +21,7 @@ #include <android-base/file.h> #include <dirent.h> #include "StatsLogProcessor.h" +#include "stats_log_util.h" #include "android-base/stringprintf.h" #include "guardrail/StatsdStats.h" #include "metrics/CountMetricProducer.h" @@ -57,10 +58,10 @@ const int FIELD_ID_REPORTS = 2; const int FIELD_ID_UID = 1; const int FIELD_ID_ID = 2; // for ConfigMetricsReport -const int FIELD_ID_METRICS = 1; +// const int FIELD_ID_METRICS = 1; // written in MetricsManager.cpp const int FIELD_ID_UID_MAP = 2; -const int FIELD_ID_LAST_REPORT_NANOS = 3; -const int FIELD_ID_CURRENT_REPORT_NANOS = 4; +const int FIELD_ID_LAST_REPORT_ELAPSED_NANOS = 3; +const int FIELD_ID_CURRENT_REPORT_ELAPSED_NANOS = 4; #define STATS_DATA_DIR "/data/misc/stats-data" @@ -136,7 +137,7 @@ void StatsLogProcessor::onIsolatedUidChangedEventLocked(const LogEvent& event) { void StatsLogProcessor::OnLogEvent(LogEvent* event) { std::lock_guard<std::mutex> lock(mMetricsMutex); StatsdStats::getInstance().noteAtomLogged( - event->GetTagId(), event->GetTimestampNs() / NS_PER_SEC); + event->GetTagId(), event->GetElapsedTimestampNs() / NS_PER_SEC); // Hard-coded logic to update the isolated uid's in the uid-map. // The field numbers need to be currently updated by hand with atoms.proto @@ -148,10 +149,10 @@ void StatsLogProcessor::OnLogEvent(LogEvent* event) { return; } - long curTime = time(nullptr); - if (curTime - mLastPullerCacheClearTimeSec > StatsdStats::kPullerCacheClearIntervalSec) { - mStatsPullerManager.ClearPullerCacheIfNecessary(curTime); - mLastPullerCacheClearTimeSec = curTime; + uint64_t curTimeSec = getElapsedRealtimeSec(); + if (curTimeSec - mLastPullerCacheClearTimeSec > StatsdStats::kPullerCacheClearIntervalSec) { + mStatsPullerManager.ClearPullerCacheIfNecessary(curTimeSec); + mLastPullerCacheClearTimeSec = curTimeSec; } if (event->GetTagId() != android::util::ISOLATED_UID_CHANGED) { @@ -162,7 +163,7 @@ void StatsLogProcessor::OnLogEvent(LogEvent* event) { // pass the event to metrics managers. for (auto& pair : mMetricsManagers) { pair.second->onLogEvent(*event); - flushIfNecessaryLocked(event->GetTimestampNs(), pair.first, *(pair.second)); + flushIfNecessaryLocked(event->GetElapsedTimestampNs(), pair.first, *(pair.second)); } } @@ -242,6 +243,7 @@ void StatsLogProcessor::onDumpReportLocked(const ConfigKey& key, const uint64_t long long reportsToken = proto.start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_REPORTS); + int64_t lastReportTimeNs = it->second->getLastReportTimeNs(); // First, fill in ConfigMetricsReport using current data on memory, which // starts from filling in StatsLogReport's. it->second->onDumpReport(dumpTimeStampNs, &proto); @@ -254,10 +256,10 @@ void StatsLogProcessor::onDumpReportLocked(const ConfigKey& key, const uint64_t proto.write(FIELD_TYPE_MESSAGE | FIELD_ID_UID_MAP, uidMapBuffer, uidMapSize); // Fill in the timestamps. - proto.write(FIELD_TYPE_INT64 | FIELD_ID_LAST_REPORT_NANOS, - (long long)it->second->getLastReportTimeNs()); - proto.write(FIELD_TYPE_INT64 | FIELD_ID_CURRENT_REPORT_NANOS, - (long long)::android::elapsedRealtimeNano()); + proto.write(FIELD_TYPE_INT64 | FIELD_ID_LAST_REPORT_ELAPSED_NANOS, + (long long)lastReportTimeNs); + proto.write(FIELD_TYPE_INT64 | FIELD_ID_CURRENT_REPORT_ELAPSED_NANOS, + (long long)dumpTimeStampNs); // End of ConfigMetricsReport (reports). proto.end(reportsToken); @@ -340,8 +342,8 @@ void StatsLogProcessor::WriteDataToDisk() { vector<uint8_t> data; onDumpReportLocked(key, time(nullptr) * NS_PER_SEC, &data); // TODO: Add a guardrail to prevent accumulation of file on disk. - string file_name = StringPrintf("%s/%ld_%d_%lld", STATS_DATA_DIR, time(nullptr), - key.GetUid(), (long long)key.GetId()); + string file_name = StringPrintf("%s/%ld_%d_%lld", STATS_DATA_DIR, + (long)getWallClockSec(), key.GetUid(), (long long)key.GetId()); StorageManager::writeFile(file_name.c_str(), &data[0], data.size()); } } diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp index ee38667b0df9..791fb14a7717 100644 --- a/cmds/statsd/src/StatsService.cpp +++ b/cmds/statsd/src/StatsService.cpp @@ -18,6 +18,7 @@ #include "Log.h" #include "StatsService.h" +#include "stats_log_util.h" #include "android-base/stringprintf.h" #include "config/ConfigKey.h" #include "config/ConfigManager.h" @@ -79,18 +80,20 @@ StatsService::StatsService(const sp<Looper>& handlerLooper) mUidMap = new UidMap(); StatsPuller::SetUidMap(mUidMap); mConfigManager = new ConfigManager(); - mProcessor = new StatsLogProcessor(mUidMap, mAnomalyMonitor, time(nullptr), [this](const ConfigKey& key) { - sp<IStatsCompanionService> sc = getStatsCompanionService(); - auto receiver = mConfigManager->GetConfigReceiver(key); - if (sc == nullptr) { - VLOG("Could not find StatsCompanionService"); - } else if (receiver == nullptr) { - VLOG("Statscompanion could not find a broadcast receiver for %s", - key.ToString().c_str()); - } else { - sc->sendDataBroadcast(receiver); + mProcessor = new StatsLogProcessor(mUidMap, mAnomalyMonitor, getElapsedRealtimeSec(), + [this](const ConfigKey& key) { + sp<IStatsCompanionService> sc = getStatsCompanionService(); + auto receiver = mConfigManager->GetConfigReceiver(key); + if (sc == nullptr) { + VLOG("Could not find StatsCompanionService"); + } else if (receiver == nullptr) { + VLOG("Statscompanion could not find a broadcast receiver for %s", + key.ToString().c_str()); + } else { + sc->sendDataBroadcast(receiver); + } } - }); + ); mConfigManager->AddListener(mProcessor); @@ -121,8 +124,6 @@ void StatsService::init_build_type_callback(void* cookie, const char* /*name*/, */ status_t StatsService::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { - status_t err; - switch (code) { case SHELL_COMMAND_TRANSACTION: { int in = data.readFileDescriptor(); @@ -237,8 +238,8 @@ status_t StatsService::command(FILE* in, FILE* out, FILE* err, Vector<String8>& return cmd_write_data_to_disk(out); } - if (!args[0].compare(String8("log-app-hook"))) { - return cmd_log_app_hook(out, args); + if (!args[0].compare(String8("log-app-breadcrumb"))) { + return cmd_log_app_breadcrumb(out, args); } if (!args[0].compare(String8("clear-puller-cache"))) { @@ -281,8 +282,8 @@ void StatsService::print_cmd_help(FILE* out) { fprintf(out, " Flushes all data on memory to disk.\n"); fprintf(out, "\n"); fprintf(out, "\n"); - fprintf(out, "usage: adb shell cmd stats log-app-hook [UID] LABEL STATE\n"); - fprintf(out, " Writes an AppHook event to the statslog buffer.\n"); + fprintf(out, "usage: adb shell cmd stats log-app-breadcrumb [UID] LABEL STATE\n"); + fprintf(out, " Writes an AppBreadcrumbReported event to the statslog buffer.\n"); fprintf(out, " UID The uid to use. It is only possible to pass a UID\n"); fprintf(out, " parameter on eng builds. If UID is omitted the calling\n"); fprintf(out, " uid is used.\n"); @@ -548,7 +549,7 @@ status_t StatsService::cmd_write_data_to_disk(FILE* out) { return NO_ERROR; } -status_t StatsService::cmd_log_app_hook(FILE* out, const Vector<String8>& args) { +status_t StatsService::cmd_log_app_breadcrumb(FILE* out, const Vector<String8>& args) { bool good = false; int32_t uid; int32_t label; @@ -570,13 +571,13 @@ status_t StatsService::cmd_log_app_hook(FILE* out, const Vector<String8>& args) good = true; } else { fprintf(out, - "Selecting a UID for writing AppHook 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) { - fprintf(out, "Logging AppHook(%d, %d, %d) to statslog.\n", uid, label, state); - android::util::stats_write(android::util::APP_HOOK, uid, label, state); + fprintf(out, "Logging AppBreadcrumbReported(%d, %d, %d) to statslog.\n", uid, label, state); + android::util::stats_write(android::util::APP_BREADCRUMB_REPORTED, uid, label, state); } else { print_cmd_help(out); return UNKNOWN_ERROR; @@ -668,11 +669,7 @@ Status StatsService::informAnomalyAlarmFired() { return Status::fromExceptionCode(Status::EX_SECURITY, "Only system uid can call informAnomalyAlarmFired"); } - - // TODO: This may be a bug. time(nullptr) can be off (wrt AlarmManager's time) and cause us to - // miss the alarm! Eventually we will switch to using elapsedRealTime everywhere, - // which may hopefully fix the problem, so we'll leave this alone for now. - uint64_t currentTimeSec = time(nullptr); + uint64_t currentTimeSec = getElapsedRealtimeSec(); std::unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>> anomalySet = mAnomalyMonitor->popSoonerThan(static_cast<uint32_t>(currentTimeSec)); if (anomalySet.size() > 0) { diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h index 3dc19fe08d94..9690de702c24 100644 --- a/cmds/statsd/src/StatsService.h +++ b/cmds/statsd/src/StatsService.h @@ -193,9 +193,10 @@ private: status_t cmd_write_data_to_disk(FILE* out); /** - * Write an AppHook event to the StatsLog buffer, as though StatsLog.write(APP_HOOK). + * Write an AppBreadcrumbReported event to the StatsLog buffer, as if calling + * StatsLog.write(APP_BREADCRUMB_REPORTED). */ - status_t cmd_log_app_hook(FILE* out, const Vector<String8>& args); + status_t cmd_log_app_breadcrumb(FILE* out, const Vector<String8>& args); /** * Print contents of a pulled metrics source. diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index 4c6a36bd9270..85e209be6413 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -85,7 +85,7 @@ message Atom { PacketWakeupOccurred packet_wakeup_occurred = 44; DropboxErrorChanged dropbox_error_changed = 45; AnomalyDetected anomaly_detected = 46; - AppHook app_hook = 47; + AppBreadcrumbReported app_breadcrumb_reported = 47; AppStartChanged app_start_changed = 48; AppStartCancelChanged app_start_cancel_changed = 49; AppStartFullyDrawnChanged app_start_fully_drawn_changed = 50; @@ -202,9 +202,10 @@ message UidProcessStateChanged { * frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java */ message ProcessLifeCycleStateChanged { - optional int32 uid = 1; // TODO: should be a string tagged w/ uid annotation + // TODO: should be a string tagged w/ uid annotation + optional int32 uid = 1; - // TODO: What is this? + // The process name (usually same as the app name). optional string name = 2; // What lifecycle state the process changed to. @@ -312,7 +313,7 @@ message GpsScanStateChanged { message SyncStateChanged { repeated AttributionNode attribution_node = 1; - // Name of the sync (as named in the app) + // Name of the sync (as named in the app). Can be chosen at run-time. optional string name = 2; enum State { @@ -615,7 +616,7 @@ message KernelWakeupReported { optional string wakeup_reason_name = 1; // Duration (in microseconds) for the wake-up interrupt to be serviced. - optional int64 duration_usec = 2; + optional int64 duration_micros = 2; } /** @@ -692,10 +693,10 @@ message ShutdownSequenceReported { // Beginning of shutdown time in ms using wall clock time since unix epoch. // Default: 0 if no start time received. - optional int64 start_time_ms = 3; + optional int64 start_time_millis = 3; // Duration of shutdown in ms. Default: 0 if no duration received. - optional int64 duration_ms = 4; + optional int64 duration_millis = 4; } @@ -715,13 +716,13 @@ message BootSequenceReported { optional string system_reason = 2; // End of boot time in ms from unix epoch using system wall clock. - optional int64 end_time_ms = 3; + optional int64 end_time_millis = 3; // Total boot duration in ms. - optional int64 total_duration_ms = 4; + optional int64 total_duration_millis = 4; // Bootloader duration in ms. - optional int64 bootloader_duration_ms = 5; + optional int64 bootloader_duration_millis = 5; // Time since last boot in ms. Default: 0 if not available. optional int64 time_since_last_boot = 6; @@ -762,7 +763,7 @@ message CallStateChanged { */ message DaveyOccurred { // Amount of time it took to render the frame. Should be >=700ms. - optional int64 jank_duration_ms = 1; + optional int64 jank_duration_millis = 1; } /** @@ -863,7 +864,7 @@ message DropboxErrorChanged { * Logged from: * frameworks/base/core/java/android/util/StatsLog.java */ -message AppHook { +message AppBreadcrumbReported { // The uid of the application that sent this custom atom. optional int32 uid = 1; @@ -924,16 +925,16 @@ message AppStartChanged { optional bool is_instant_app = 6; // Device uptime when activity started. - optional int64 activity_start_msec = 7; + optional int64 activity_start_millis = 7; optional android.app.AppTransitionReasonEnum reason = 8; - optional int32 transition_delay_msec = 9; + optional int32 transition_delay_millis = 9; // -1 if not set. - optional int32 starting_window_delay_msec = 10; + optional int32 starting_window_delay_millis = 10; // -1 if not set. - optional int32 bind_application_delay_msec = 11; - optional int32 windows_drawn_delay_msec = 12; + optional int32 bind_application_delay_millis = 11; + optional int32 windows_drawn_delay_millis = 12; // Empty if not set. optional string launch_token = 13; @@ -981,7 +982,7 @@ message AppStartFullyDrawnChanged { optional bool transition_process_running = 5; // App startup time (until call to Activity#reportFullyDrawn()). - optional int64 app_startup_time_ms = 6; + optional int64 app_startup_time_millis = 6; } /** @@ -1303,7 +1304,7 @@ message SubsystemSleepState { // The number of times it entered, or voted for entering the sleep state optional uint64 count = 3; // The length of time spent in, or spent voting for, the sleep state - optional uint64 timeMs = 4; + optional uint64 time_millis = 4; } /** @@ -1316,7 +1317,7 @@ message SubsystemSleepState { message CpuTimePerFreq { optional uint32 cluster = 1; optional uint32 freq_index = 2; - optional uint64 time_ms = 3; + optional uint64 time_millis = 3; } /** @@ -1325,8 +1326,8 @@ message CpuTimePerFreq { */ message CpuTimePerUid { optional uint64 uid = 1; - optional uint64 user_time_ms = 2; - optional uint64 sys_time_ms = 3; + optional uint64 user_time_millis = 2; + optional uint64 sys_time_millis = 3; } /** @@ -1337,7 +1338,7 @@ message CpuTimePerUid { message CpuTimePerUidFreq { optional uint64 uid = 1; optional uint64 freq_idx = 2; - optional uint64 time_ms = 3; + optional uint64 time_millis = 3; } /** @@ -1345,16 +1346,16 @@ message CpuTimePerUidFreq { */ message WifiActivityEnergyInfo { // timestamp(wall clock) of record creation - optional uint64 timestamp_ms = 1; + optional uint64 timestamp_millis = 1; // stack reported state // TODO: replace this with proto enum optional int32 stack_state = 2; // tx time in ms - optional uint64 controller_tx_time_ms = 3; + optional uint64 controller_tx_time_millis = 3; // rx time in ms - optional uint64 controller_rx_time_ms = 4; + optional uint64 controller_rx_time_millis = 4; // idle time in ms - optional uint64 controller_idle_time_ms = 5; + optional uint64 controller_idle_time_millis = 5; // product of current(mA), voltage(V) and time(ms) optional uint64 controller_energy_used = 6; } @@ -1364,11 +1365,11 @@ message WifiActivityEnergyInfo { */ message ModemActivityInfo { // timestamp(wall clock) of record creation - optional uint64 timestamp_ms = 1; + optional uint64 timestamp_millis = 1; // sleep time in ms. - optional uint64 sleep_time_ms = 2; + optional uint64 sleep_time_millis = 2; // idle time in ms - optional uint64 controller_idle_time_ms = 3; + optional uint64 controller_idle_time_millis = 3; /** * Tx power index * index 0 = tx_power < 0dBm @@ -1378,17 +1379,17 @@ message ModemActivityInfo { * index 4 = tx_power > 20dBm */ // tx time in ms at power level 0 - optional uint64 controller_tx_time_pl0_ms = 4; + optional uint64 controller_tx_time_pl0_millis = 4; // tx time in ms at power level 1 - optional uint64 controller_tx_time_pl1_ms = 5; + optional uint64 controller_tx_time_pl1_millis = 5; // tx time in ms at power level 2 - optional uint64 controller_tx_time_pl2_ms = 6; + optional uint64 controller_tx_time_pl2_millis = 6; // tx time in ms at power level 3 - optional uint64 controller_tx_time_pl3_ms = 7; + optional uint64 controller_tx_time_pl3_millis = 7; // tx time in ms at power level 4 - optional uint64 controller_tx_time_pl4_ms = 8; + optional uint64 controller_tx_time_pl4_millis = 8; // rx time in ms at power level 5 - optional uint64 controller_rx_time_ms = 9; + optional uint64 controller_rx_time_millis = 9; // product of current(mA), voltage(V) and time(ms) optional uint64 energy_used = 10; } @@ -1399,15 +1400,15 @@ message ModemActivityInfo { */ message BluetoothActivityInfo { // timestamp(wall clock) of record creation - optional uint64 timestamp_ms = 1; + optional uint64 timestamp_millis = 1; // bluetooth stack state optional int32 bluetooth_stack_state = 2; // tx time in ms - optional uint64 controller_tx_time_ms = 3; + optional uint64 controller_tx_time_millis = 3; // rx time in ms - optional uint64 controller_rx_time_ms = 4; + optional uint64 controller_rx_time_millis = 4; // idle time in ms - optional uint64 controller_idle_time_ms = 5; + optional uint64 controller_idle_time_millis = 5; // product of current(mA), voltage(V) and time(ms) optional uint64 energy_used = 6; } @@ -1445,7 +1446,7 @@ message ProcessMemoryState { * Elapsed real time from SystemClock. */ message SystemElapsedRealtime { - optional uint64 time_ms = 1; + optional uint64 time_millis = 1; } /* @@ -1456,7 +1457,7 @@ message SystemUptime { // This clock stops when the system enters deep sleep (CPU off, display dark, device waiting // for external input). // It is not affected by clock scaling, idle, or other power saving mechanisms. - optional uint64 uptime_ms = 1; + optional uint64 uptime_millis = 1; } /* @@ -1470,8 +1471,9 @@ message SystemUptime { */ message CpuActiveTime { optional uint64 uid = 1; - optional uint64 idx = 2; - optional uint64 time_ms = 3; + optional uint32 cluster_number = 2; + optional uint64 idx = 3; + optional uint64 time_millis = 4; } /** @@ -1486,7 +1488,7 @@ message CpuActiveTime { message CpuClusterTime { optional uint64 uid = 1; optional uint64 idx = 2; - optional uint64 time_ms = 3; + optional uint64 time_millis = 3; } /* diff --git a/cmds/statsd/src/external/CpuTimePerUidFreqPuller.cpp b/cmds/statsd/src/external/CpuTimePerUidFreqPuller.cpp index d0d2f938cf0c..d1d9d3778267 100644 --- a/cmds/statsd/src/external/CpuTimePerUidFreqPuller.cpp +++ b/cmds/statsd/src/external/CpuTimePerUidFreqPuller.cpp @@ -25,6 +25,7 @@ #include "guardrail/StatsdStats.h" #include "logd/LogEvent.h" #include "statslog.h" +#include "stats_log_util.h" using std::make_shared; using std::shared_ptr; @@ -62,7 +63,9 @@ bool CpuTimePerUidFreqPuller::PullInternal(vector<shared_ptr<LogEvent>>* data) { return false; } - uint64_t timestamp = time(nullptr) * NS_PER_SEC; + int64_t wallClockTimestampNs = getWallClockNs(); + int64_t elapsedTimestampNs = getElapsedRealtimeNs(); + char buf[kLineBufferSize]; // first line prints the format and frequencies fin.getline(buf, kLineBufferSize); @@ -77,7 +80,8 @@ bool CpuTimePerUidFreqPuller::PullInternal(vector<shared_ptr<LogEvent>>* data) { int idx = 0; do { timeMs = std::stoull(pch); - auto ptr = make_shared<LogEvent>(android::util::CPU_TIME_PER_UID_FREQ, timestamp); + auto ptr = make_shared<LogEvent>(android::util::CPU_TIME_PER_UID_FREQ, + wallClockTimestampNs, elapsedTimestampNs); ptr->write(uid); ptr->write(idx); ptr->write(timeMs); diff --git a/cmds/statsd/src/external/CpuTimePerUidPuller.cpp b/cmds/statsd/src/external/CpuTimePerUidPuller.cpp index d9aeb4656bfe..568b8f0c0c53 100644 --- a/cmds/statsd/src/external/CpuTimePerUidPuller.cpp +++ b/cmds/statsd/src/external/CpuTimePerUidPuller.cpp @@ -24,6 +24,7 @@ #include "guardrail/StatsdStats.h" #include "logd/LogEvent.h" #include "statslog.h" +#include "stats_log_util.h" using std::make_shared; using std::shared_ptr; @@ -57,7 +58,8 @@ bool CpuTimePerUidPuller::PullInternal(vector<shared_ptr<LogEvent>>* data) { return false; } - uint64_t timestamp = time(nullptr) * NS_PER_SEC; + int64_t wallClockTimestampNs = getWallClockNs(); + int64_t elapsedTimestampNs = getElapsedRealtimeNs(); char buf[kLineBufferSize]; char* pch; while (!fin.eof()) { @@ -70,7 +72,8 @@ bool CpuTimePerUidPuller::PullInternal(vector<shared_ptr<LogEvent>>* data) { pch = strtok(buf, " "); uint64_t sysTimeMs = std::stoull(pch); - auto ptr = make_shared<LogEvent>(android::util::CPU_TIME_PER_UID, timestamp); + auto ptr = make_shared<LogEvent>(android::util::CPU_TIME_PER_UID, + wallClockTimestampNs, elapsedTimestampNs); ptr->write(uid); ptr->write(userTimeMs); ptr->write(sysTimeMs); diff --git a/cmds/statsd/src/external/KernelUidCpuActiveTimeReader.cpp b/cmds/statsd/src/external/KernelUidCpuActiveTimeReader.cpp index 0e126e7a4b44..0b545ccb3658 100644 --- a/cmds/statsd/src/external/KernelUidCpuActiveTimeReader.cpp +++ b/cmds/statsd/src/external/KernelUidCpuActiveTimeReader.cpp @@ -23,6 +23,7 @@ #include "guardrail/StatsdStats.h" #include "logd/LogEvent.h" #include "statslog.h" +#include "stats_log_util.h" using std::make_shared; using std::shared_ptr; @@ -57,7 +58,9 @@ bool KernelUidCpuActiveTimeReader::PullInternal(vector<shared_ptr<LogEvent>>* da return false; } - uint64_t timestamp = time(nullptr) * NS_PER_SEC; + int64_t wallClockTimestampNs = getWallClockNs(); + int64_t elapsedTimestampNs = getElapsedRealtimeNs(); + char buf[kLineBufferSize]; char* pch; while (!fin.eof()) { @@ -70,7 +73,7 @@ bool KernelUidCpuActiveTimeReader::PullInternal(vector<shared_ptr<LogEvent>>* da int idx = 0; do { timeMs = std::stoull(pch); - auto ptr = make_shared<LogEvent>(mTagId, timestamp); + auto ptr = make_shared<LogEvent>(mTagId, wallClockTimestampNs, elapsedTimestampNs); ptr->write(uid); ptr->write(idx); ptr->write(timeMs); diff --git a/cmds/statsd/src/external/KernelUidCpuClusterTimeReader.cpp b/cmds/statsd/src/external/KernelUidCpuClusterTimeReader.cpp index 7684ed4eb44b..cc80204b105e 100644 --- a/cmds/statsd/src/external/KernelUidCpuClusterTimeReader.cpp +++ b/cmds/statsd/src/external/KernelUidCpuClusterTimeReader.cpp @@ -22,6 +22,7 @@ #include "guardrail/StatsdStats.h" #include "logd/LogEvent.h" #include "statslog.h" +#include "stats_log_util.h" using std::make_shared; using std::shared_ptr; @@ -56,7 +57,8 @@ bool KernelUidCpuClusterTimeReader::PullInternal(vector<shared_ptr<LogEvent>>* d return false; } - uint64_t timestamp = time(nullptr) * NS_PER_SEC; + int64_t wallClockTimestampNs = getWallClockNs(); + int64_t elapsedTimestampNs = getElapsedRealtimeNs(); char buf[kLineBufferSize]; char* pch; while (!fin.eof()) { @@ -69,7 +71,7 @@ bool KernelUidCpuClusterTimeReader::PullInternal(vector<shared_ptr<LogEvent>>* d int idx = 0; do { timeMs = std::stoull(pch); - auto ptr = make_shared<LogEvent>(mTagId, timestamp); + auto ptr = make_shared<LogEvent>(mTagId, wallClockTimestampNs, elapsedTimestampNs); ptr->write(uid); ptr->write(idx); ptr->write(timeMs); diff --git a/cmds/statsd/src/external/ResourceHealthManagerPuller.cpp b/cmds/statsd/src/external/ResourceHealthManagerPuller.cpp index 72fb5ffd4b90..261cb4332dd6 100644 --- a/cmds/statsd/src/external/ResourceHealthManagerPuller.cpp +++ b/cmds/statsd/src/external/ResourceHealthManagerPuller.cpp @@ -25,6 +25,7 @@ #include "ResourceHealthManagerPuller.h" #include "logd/LogEvent.h" #include "statslog.h" +#include "stats_log_util.h" using android::hardware::hidl_vec; using android::hardware::health::V2_0::get_health_service; @@ -61,7 +62,8 @@ bool ResourceHealthManagerPuller::PullInternal(vector<shared_ptr<LogEvent>>* dat return false; } - uint64_t timestamp = time(nullptr) * NS_PER_SEC; + int64_t wallClockTimestampNs = getWallClockNs(); + int64_t elapsedTimestampNs = getElapsedRealtimeNs(); data->clear(); bool result_success = true; @@ -72,12 +74,14 @@ bool ResourceHealthManagerPuller::PullInternal(vector<shared_ptr<LogEvent>>* dat return; } if (mTagId == android::util::REMAINING_BATTERY_CAPACITY) { - auto ptr = make_shared<LogEvent>(android::util::REMAINING_BATTERY_CAPACITY, timestamp); + auto ptr = make_shared<LogEvent>(android::util::REMAINING_BATTERY_CAPACITY, + wallClockTimestampNs, elapsedTimestampNs); ptr->write(v.legacy.batteryChargeCounter); ptr->init(); data->push_back(ptr); } else if (mTagId == android::util::FULL_BATTERY_CAPACITY) { - auto ptr = make_shared<LogEvent>(android::util::FULL_BATTERY_CAPACITY, timestamp); + auto ptr = make_shared<LogEvent>(android::util::FULL_BATTERY_CAPACITY, + wallClockTimestampNs, elapsedTimestampNs); ptr->write(v.legacy.batteryFullCharge); ptr->init(); data->push_back(ptr); diff --git a/cmds/statsd/src/external/StatsCompanionServicePuller.cpp b/cmds/statsd/src/external/StatsCompanionServicePuller.cpp index 8210c8dcd63d..bd859fd79a09 100644 --- a/cmds/statsd/src/external/StatsCompanionServicePuller.cpp +++ b/cmds/statsd/src/external/StatsCompanionServicePuller.cpp @@ -22,6 +22,7 @@ #include <private/android_filesystem_config.h> #include "StatsCompanionServicePuller.h" #include "StatsService.h" +#include "stats_log_util.h" #include "guardrail/StatsdStats.h" using namespace android; @@ -53,13 +54,13 @@ bool StatsCompanionServicePuller::PullInternal(vector<shared_ptr<LogEvent> >* da return false; } data->clear(); - int timestamp = time(nullptr); + int32_t timestampSec = getWallClockSec(); for (const StatsLogEventWrapper& it : returned_value) { log_msg tmp; tmp.entry_v1.len = it.bytes.size(); // Manually set the header size to 28 bytes to match the pushed log events. tmp.entry.hdr_size = kLogMsgHeaderSize; - tmp.entry_v1.sec = timestamp; + tmp.entry_v1.sec = timestampSec; // And set the received bytes starting after the 28 bytes reserved for header. std::copy(it.bytes.begin(), it.bytes.end(), tmp.buf + kLogMsgHeaderSize); data->push_back(make_shared<LogEvent>(tmp)); diff --git a/cmds/statsd/src/external/StatsPuller.cpp b/cmds/statsd/src/external/StatsPuller.cpp index fc0ad7c5c3fc..9513cc521af7 100644 --- a/cmds/statsd/src/external/StatsPuller.cpp +++ b/cmds/statsd/src/external/StatsPuller.cpp @@ -20,6 +20,7 @@ #include "StatsPuller.h" #include "guardrail/StatsdStats.h" #include "puller_util.h" +#include "stats_log_util.h" namespace android { namespace os { @@ -44,7 +45,7 @@ StatsPuller::StatsPuller(const int tagId) bool StatsPuller::Pull(std::vector<std::shared_ptr<LogEvent>>* data) { lock_guard<std::mutex> lock(mLock); StatsdStats::getInstance().notePull(mTagId); - long curTime = time(nullptr); + long curTime = getElapsedRealtimeSec(); if (curTime - mLastPullTimeSec < mCoolDownSec) { (*data) = mCachedData; StatsdStats::getInstance().notePullFromCache(mTagId); diff --git a/cmds/statsd/src/external/StatsPullerManagerImpl.cpp b/cmds/statsd/src/external/StatsPullerManagerImpl.cpp index 4c676a70363f..08c59cfed518 100644 --- a/cmds/statsd/src/external/StatsPullerManagerImpl.cpp +++ b/cmds/statsd/src/external/StatsPullerManagerImpl.cpp @@ -33,6 +33,7 @@ #include "SubsystemSleepStatePuller.h" #include "logd/LogEvent.h" #include "statslog.h" +#include "stats_log_util.h" #include <iostream> @@ -165,8 +166,9 @@ void StatsPullerManagerImpl::RegisterReceiver(int tagId, wp<PullDataReceiver> re if (roundedIntervalMs < mCurrentPullingInterval) { VLOG("Updating pulling interval %ld", intervalMs); mCurrentPullingInterval = roundedIntervalMs; - long currentTimeMs = time(nullptr) * 1000; - long nextAlarmTimeMs = currentTimeMs + mCurrentPullingInterval - (currentTimeMs - mTimeBaseSec * 1000) % mCurrentPullingInterval; + long currentTimeMs = getElapsedRealtimeMillis(); + long nextAlarmTimeMs = currentTimeMs + mCurrentPullingInterval - + (currentTimeMs - mTimeBaseSec * 1000) % mCurrentPullingInterval; if (mStatsCompanionService != nullptr) { mStatsCompanionService->setPullingAlarms(nextAlarmTimeMs, mCurrentPullingInterval); } else { @@ -195,7 +197,7 @@ void StatsPullerManagerImpl::UnRegisterReceiver(int tagId, wp<PullDataReceiver> void StatsPullerManagerImpl::OnAlarmFired() { AutoMutex _l(mReceiversLock); - uint64_t currentTimeMs = time(nullptr) /60 * 60 * 1000; + uint64_t currentTimeMs = getElapsedRealtimeMillis(); vector<pair<int, vector<ReceiverInfo*>>> needToPull = vector<pair<int, vector<ReceiverInfo*>>>(); diff --git a/cmds/statsd/src/external/SubsystemSleepStatePuller.cpp b/cmds/statsd/src/external/SubsystemSleepStatePuller.cpp index 65a1df0eda20..4501b64ad47e 100644 --- a/cmds/statsd/src/external/SubsystemSleepStatePuller.cpp +++ b/cmds/statsd/src/external/SubsystemSleepStatePuller.cpp @@ -36,6 +36,7 @@ #include "SubsystemSleepStatePuller.h" #include "logd/LogEvent.h" #include "statslog.h" +#include "stats_log_util.h" using android::hardware::hidl_vec; using android::hardware::power::V1_0::IPower; @@ -84,20 +85,22 @@ bool SubsystemSleepStatePuller::PullInternal(vector<shared_ptr<LogEvent>>* data) return false; } - uint64_t timestamp = time(nullptr) * NS_PER_SEC; + int64_t wallClockTimestampNs = getWallClockNs(); + int64_t elapsedTimestampNs = getElapsedRealtimeNs(); data->clear(); Return<void> ret; ret = gPowerHalV1_0->getPlatformLowPowerStats( - [&data, timestamp](hidl_vec<PowerStatePlatformSleepState> states, Status status) { + [&data, wallClockTimestampNs, elapsedTimestampNs](hidl_vec<PowerStatePlatformSleepState> states, Status status) { if (status != Status::SUCCESS) return; for (size_t i = 0; i < states.size(); i++) { const PowerStatePlatformSleepState& state = states[i]; - auto statePtr = make_shared<LogEvent>(android::util::SUBSYSTEM_SLEEP_STATE, - timestamp); + auto statePtr = make_shared<LogEvent>( + android::util::SUBSYSTEM_SLEEP_STATE, + wallClockTimestampNs, elapsedTimestampNs); statePtr->write(state.name); statePtr->write(""); statePtr->write(state.totalTransitions); @@ -109,8 +112,9 @@ bool SubsystemSleepStatePuller::PullInternal(vector<shared_ptr<LogEvent>>* data) (long long)state.totalTransitions, state.supportedOnlyInSuspend ? 1 : 0); for (auto voter : state.voters) { - auto voterPtr = make_shared<LogEvent>(android::util::SUBSYSTEM_SLEEP_STATE, - timestamp); + auto voterPtr = make_shared<LogEvent>( + android::util::SUBSYSTEM_SLEEP_STATE, + wallClockTimestampNs, elapsedTimestampNs); voterPtr->write(state.name); voterPtr->write(voter.name); voterPtr->write(voter.totalNumberOfTimesVotedSinceBoot); @@ -135,7 +139,7 @@ bool SubsystemSleepStatePuller::PullInternal(vector<shared_ptr<LogEvent>>* data) android::hardware::power::V1_1::IPower::castFrom(gPowerHalV1_0); if (gPowerHal_1_1 != nullptr) { ret = gPowerHal_1_1->getSubsystemLowPowerStats( - [&data, timestamp](hidl_vec<PowerStateSubsystem> subsystems, Status status) { + [&data, wallClockTimestampNs, elapsedTimestampNs](hidl_vec<PowerStateSubsystem> subsystems, Status status) { if (status != Status::SUCCESS) return; if (subsystems.size() > 0) { @@ -145,7 +149,8 @@ bool SubsystemSleepStatePuller::PullInternal(vector<shared_ptr<LogEvent>>* data) const PowerStateSubsystemSleepState& state = subsystem.states[j]; auto subsystemStatePtr = make_shared<LogEvent>( - android::util::SUBSYSTEM_SLEEP_STATE, timestamp); + android::util::SUBSYSTEM_SLEEP_STATE, + wallClockTimestampNs, elapsedTimestampNs); subsystemStatePtr->write(subsystem.name); subsystemStatePtr->write(state.name); subsystemStatePtr->write(state.totalTransitions); diff --git a/cmds/statsd/src/guardrail/StatsdStats.cpp b/cmds/statsd/src/guardrail/StatsdStats.cpp index 06c5b0049a3b..66cb1d04a4e1 100644 --- a/cmds/statsd/src/guardrail/StatsdStats.cpp +++ b/cmds/statsd/src/guardrail/StatsdStats.cpp @@ -45,18 +45,9 @@ const int FIELD_ID_CONFIG_STATS = 3; const int FIELD_ID_ATOM_STATS = 7; const int FIELD_ID_UIDMAP_STATS = 8; const int FIELD_ID_ANOMALY_ALARM_STATS = 9; -const int FIELD_ID_PULLED_ATOM_STATS = 10; +// const int FIELD_ID_PULLED_ATOM_STATS = 10; // The proto is written in stats_log_util.cpp const int FIELD_ID_LOGGER_ERROR_STATS = 11; -const int FIELD_ID_MATCHER_STATS_NAME = 1; -const int FIELD_ID_MATCHER_STATS_COUNT = 2; - -const int FIELD_ID_CONDITION_STATS_NAME = 1; -const int FIELD_ID_CONDITION_STATS_COUNT = 2; - -const int FIELD_ID_METRIC_STATS_NAME = 1; -const int FIELD_ID_METRIC_STATS_COUNT = 2; - const int FIELD_ID_ATOM_STATS_TAG = 1; const int FIELD_ID_ATOM_STATS_COUNT = 2; @@ -80,7 +71,7 @@ std::map<int, long> StatsdStats::kPullerCooldownMap = { // TODO: add stats for pulled atoms. StatsdStats::StatsdStats() { mPushedAtomStats.resize(android::util::kMaxPushedAtomId + 1); - mStartTimeSec = time(nullptr); + mStartTimeSec = getWallClockSec(); } StatsdStats& StatsdStats::getInstance() { @@ -99,7 +90,7 @@ void StatsdStats::addToIceBoxLocked(const StatsdStatsReport_ConfigStats& stats) void StatsdStats::noteConfigReceived(const ConfigKey& key, int metricsCount, int conditionsCount, int matchersCount, int alertsCount, bool isValid) { lock_guard<std::mutex> lock(mLock); - int32_t nowTimeSec = time(nullptr); + int32_t nowTimeSec = getWallClockSec(); // If there is an existing config for the same key, icebox the old config. noteConfigRemovedInternalLocked(key); @@ -125,7 +116,7 @@ void StatsdStats::noteConfigReceived(const ConfigKey& key, int metricsCount, int void StatsdStats::noteConfigRemovedInternalLocked(const ConfigKey& key) { auto it = mConfigStats.find(key); if (it != mConfigStats.end()) { - int32_t nowTimeSec = time(nullptr); + int32_t nowTimeSec = getWallClockSec(); it->second.set_deletion_time_sec(nowTimeSec); // Add condition stats, metrics stats, matcher stats, alert stats addSubStatsToConfigLocked(key, it->second); @@ -145,7 +136,7 @@ void StatsdStats::noteConfigRemoved(const ConfigKey& key) { } void StatsdStats::noteBroadcastSent(const ConfigKey& key) { - noteBroadcastSent(key, time(nullptr)); + noteBroadcastSent(key, getWallClockSec()); } void StatsdStats::noteBroadcastSent(const ConfigKey& key, int32_t timeSec) { @@ -164,7 +155,7 @@ void StatsdStats::noteBroadcastSent(const ConfigKey& key, int32_t timeSec) { } void StatsdStats::noteDataDropped(const ConfigKey& key) { - noteDataDropped(key, time(nullptr)); + noteDataDropped(key, getWallClockSec()); } void StatsdStats::noteDataDropped(const ConfigKey& key, int32_t timeSec) { @@ -183,7 +174,7 @@ void StatsdStats::noteDataDropped(const ConfigKey& key, int32_t timeSec) { } void StatsdStats::noteMetricsReportSent(const ConfigKey& key) { - noteMetricsReportSent(key, time(nullptr)); + noteMetricsReportSent(key, getWallClockSec()); } void StatsdStats::noteMetricsReportSent(const ConfigKey& key, int32_t timeSec) { @@ -275,10 +266,6 @@ void StatsdStats::notePullFromCache(int pullAtomId) { void StatsdStats::noteAtomLogged(int atomId, int32_t timeSec) { lock_guard<std::mutex> lock(mLock); - if (timeSec < mStartTimeSec) { - return; - } - if (atomId > android::util::kMaxPushedAtomId) { ALOGW("not interested in atom %d", atomId); return; @@ -293,7 +280,7 @@ void StatsdStats::noteLoggerError(int error) { if (mLoggerErrors.size() == kMaxLoggerErrors) { mLoggerErrors.pop_front(); } - mLoggerErrors.push_back(std::make_pair(time(nullptr), error)); + mLoggerErrors.push_back(std::make_pair(getWallClockSec(), error)); } void StatsdStats::reset() { @@ -303,7 +290,7 @@ void StatsdStats::reset() { void StatsdStats::resetInternalLocked() { // Reset the historical data, but keep the active ConfigStats - mStartTimeSec = time(nullptr); + mStartTimeSec = getWallClockSec(); mIceBox.clear(); mConditionStats.clear(); mMetricsStats.clear(); @@ -495,7 +482,7 @@ void StatsdStats::dumpStats(std::vector<uint8_t>* output, bool reset) { ProtoOutputStream proto; proto.write(FIELD_TYPE_INT32 | FIELD_ID_BEGIN_TIME, mStartTimeSec); - proto.write(FIELD_TYPE_INT32 | FIELD_ID_END_TIME, (int32_t)time(nullptr)); + proto.write(FIELD_TYPE_INT32 | FIELD_ID_END_TIME, (int32_t)getWallClockSec()); for (const auto& configStats : mIceBox) { const int numBytes = configStats.ByteSize(); diff --git a/cmds/statsd/src/guardrail/StatsdStats.h b/cmds/statsd/src/guardrail/StatsdStats.h index f254327fcc16..7baa5e57679e 100644 --- a/cmds/statsd/src/guardrail/StatsdStats.h +++ b/cmds/statsd/src/guardrail/StatsdStats.h @@ -42,6 +42,7 @@ public: 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/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp index 45fb5da3b67b..f07fc66dbcbb 100644 --- a/cmds/statsd/src/logd/LogEvent.cpp +++ b/cmds/statsd/src/logd/LogEvent.cpp @@ -33,7 +33,7 @@ using android::util::ProtoOutputStream; LogEvent::LogEvent(log_msg& msg) { mContext = create_android_log_parser(msg.msg() + sizeof(uint32_t), msg.len() - sizeof(uint32_t)); - mTimestampNs = msg.entry_v1.sec * NS_PER_SEC + msg.entry_v1.nsec; + mLogdTimestampNs = msg.entry_v1.sec * NS_PER_SEC + msg.entry_v1.nsec; mLogUid = msg.entry_v4.uid; init(mContext); if (mContext) { @@ -42,12 +42,24 @@ LogEvent::LogEvent(log_msg& msg) { } } -LogEvent::LogEvent(int32_t tagId, uint64_t timestampNs) { - mTimestampNs = timestampNs; +LogEvent::LogEvent(int32_t tagId, int64_t wallClockTimestampNs, int64_t elapsedTimestampNs) { + mLogdTimestampNs = wallClockTimestampNs; mTagId = tagId; mLogUid = 0; mContext = create_android_logger(1937006964); // the event tag shared by all stats logs if (mContext) { + android_log_write_int64(mContext, elapsedTimestampNs); + android_log_write_int32(mContext, tagId); + } +} + +LogEvent::LogEvent(int32_t tagId, int64_t timestampNs) { + mLogdTimestampNs = timestampNs; + mTagId = tagId; + mLogUid = 0; + mContext = create_android_logger(1937006964); // the event tag shared by all stats logs + if (mContext) { + android_log_write_int64(mContext, timestampNs); android_log_write_int32(mContext, tagId); } } @@ -166,18 +178,14 @@ bool LogEvent::write(const AttributionNode& node) { void LogEvent::init(android_log_context context) { android_log_list_element elem; int i = 0; - - int seenListStart = 0; - - int32_t field = 0; int depth = -1; int pos[] = {1, 1, 1}; do { elem = android_log_read_next(context); switch ((int)elem.type) { case EVENT_TYPE_INT: - // elem at [0] is EVENT_TYPE_LIST, [1] is the tag id. - if (i == 1) { + // elem at [0] is EVENT_TYPE_LIST, [1] is the timestamp, [2] is tag id. + if (i == 2) { mTagId = elem.data.int32; } else { if (depth < 0 || depth > 2) { @@ -214,15 +222,18 @@ void LogEvent::init(android_log_context context) { } break; case EVENT_TYPE_LONG: { - if (depth < 0 || depth > 2) { - ALOGE("Depth > 2. Not supported!"); - return; - } - mValues.push_back( - FieldValue(Field(mTagId, pos, depth), Value((int64_t)elem.data.int64))); - - pos[depth]++; + if (i == 1) { + mElapsedTimestampNs = elem.data.int64; + } else { + if (depth < 0 || depth > 2) { + ALOGE("Depth > 2. Not supported!"); + return; + } + mValues.push_back( + FieldValue(Field(mTagId, pos, depth), Value((int64_t)elem.data.int64))); + pos[depth]++; + } } break; case EVENT_TYPE_LIST: depth++; @@ -370,7 +381,7 @@ float LogEvent::GetFloat(size_t key, status_t* err) const { string LogEvent::ToString() const { ostringstream result; - result << "{ " << mTimestampNs << " (" << mTagId << ")"; + result << "{ " << mLogdTimestampNs << " " << mElapsedTimestampNs << " (" << mTagId << ")"; for (const auto& value : mValues) { result << StringPrintf("%#x", value.mField.getField()); result << "->"; diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h index 0895daa49ad3..b3084d54d848 100644 --- a/cmds/statsd/src/logd/LogEvent.h +++ b/cmds/statsd/src/logd/LogEvent.h @@ -47,14 +47,18 @@ public: /** * Constructs a LogEvent with synthetic data for testing. Must call init() before reading. */ - explicit LogEvent(int32_t tagId, uint64_t timestampNs); + explicit LogEvent(int32_t tagId, int64_t wallClockTimestampNs, int64_t elapsedTimestampNs); + + // For testing. The timestamp is used as both elapsed real time and logd timestamp. + explicit LogEvent(int32_t tagId, int64_t timestampNs); ~LogEvent(); /** * Get the timestamp associated with this event. */ - inline uint64_t GetTimestampNs() const { return mTimestampNs; } + inline int64_t GetLogdTimestampNs() const { return mLogdTimestampNs; } + inline int64_t GetElapsedTimestampNs() const { return mElapsedTimestampNs; } /** * Get the tag for this event. @@ -107,9 +111,18 @@ public: void init(); /** - * Set timestamp if the original timestamp is missing. + * Set elapsed timestamp if the original timestamp is missing. */ - void setTimestampNs(uint64_t timestampNs) {mTimestampNs = timestampNs;} + void setElapsedTimestampNs(int64_t timestampNs) { + mElapsedTimestampNs = timestampNs; + } + + /** + * Set the timestamp if the original logd timestamp is missing. + */ + void setLogdWallClockTimestampNs(int64_t timestampNs) { + mLogdTimestampNs = timestampNs; + } inline int size() const { return mValues.size(); @@ -144,7 +157,11 @@ private: // When the log event is created from log msg, this field is never initiated. android_log_context mContext = NULL; - uint64_t mTimestampNs; + // The timestamp set by the logd. + int64_t mLogdTimestampNs; + + // The elapsed timestamp set by statsd log writer. + int64_t mElapsedTimestampNs; int mTagId; diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp index bd2674b86a46..af2e362368c3 100644 --- a/cmds/statsd/src/metrics/CountMetricProducer.cpp +++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp @@ -52,8 +52,8 @@ const int FIELD_ID_DIMENSION_IN_WHAT = 1; const int FIELD_ID_DIMENSION_IN_CONDITION = 2; const int FIELD_ID_BUCKET_INFO = 3; // for CountBucketInfo -const int FIELD_ID_START_BUCKET_NANOS = 1; -const int FIELD_ID_END_BUCKET_NANOS = 2; +const int FIELD_ID_START_BUCKET_ELAPSED_NANOS = 1; +const int FIELD_ID_END_BUCKET_ELAPSED_NANOS = 2; const int FIELD_ID_COUNT = 3; CountMetricProducer::CountMetricProducer(const ConfigKey& key, const CountMetric& metric, @@ -63,7 +63,8 @@ CountMetricProducer::CountMetricProducer(const ConfigKey& key, const CountMetric : MetricProducer(metric.id(), key, startTimeNs, conditionIndex, wizard) { // TODO: evaluate initial conditions. and set mConditionMet. if (metric.has_bucket()) { - mBucketSizeNs = TimeUnitToBucketSizeInMillis(metric.bucket()) * 1000000; + mBucketSizeNs = + TimeUnitToBucketSizeInMillisGuardrailed(key.GetUid(), metric.bucket()) * 1000000; } else { mBucketSizeNs = LLONG_MAX; } @@ -107,7 +108,6 @@ void CountMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, if (mPastBuckets.empty()) { return; } - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId); long long protoToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_COUNT_METRICS); @@ -132,12 +132,13 @@ void CountMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, } // Then fill bucket_info (CountBucketInfo). + for (const auto& bucket : counter.second) { long long bucketInfoToken = protoOutput->start( FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_BUCKET_INFO); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_START_BUCKET_NANOS, + protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_START_BUCKET_ELAPSED_NANOS, (long long)bucket.mBucketStartNs); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_END_BUCKET_NANOS, + protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_END_BUCKET_ELAPSED_NANOS, (long long)bucket.mBucketEndNs); protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_COUNT, (long long)bucket.mCount); protoOutput->end(bucketInfoToken); @@ -184,7 +185,7 @@ void CountMetricProducer::onMatchedLogEventInternalLocked( const size_t matcherIndex, const MetricDimensionKey& eventKey, const ConditionKey& conditionKey, bool condition, const LogEvent& event) { - uint64_t eventTimeNs = event.GetTimestampNs(); + uint64_t eventTimeNs = event.GetElapsedTimestampNs(); flushIfNeededLocked(eventTimeNs); if (condition == false) { diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp index 6b321e11edcf..3b7936dee497 100644 --- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp +++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp @@ -51,8 +51,8 @@ const int FIELD_ID_DIMENSION_IN_WHAT = 1; const int FIELD_ID_DIMENSION_IN_CONDITION = 2; const int FIELD_ID_BUCKET_INFO = 3; // for DurationBucketInfo -const int FIELD_ID_START_BUCKET_NANOS = 1; -const int FIELD_ID_END_BUCKET_NANOS = 2; +const int FIELD_ID_START_BUCKET_ELAPSED_NANOS = 1; +const int FIELD_ID_END_BUCKET_ELAPSED_NANOS = 2; const int FIELD_ID_DURATION = 3; DurationMetricProducer::DurationMetricProducer(const ConfigKey& key, const DurationMetric& metric, @@ -72,7 +72,8 @@ DurationMetricProducer::DurationMetricProducer(const ConfigKey& key, const Durat // them in the base class, because the proto generated CountMetric, and DurationMetric are // not related. Maybe we should add a template in the future?? if (metric.has_bucket()) { - mBucketSizeNs = TimeUnitToBucketSizeInMillis(metric.bucket()) * 1000000; + mBucketSizeNs = + TimeUnitToBucketSizeInMillisGuardrailed(key.GetUid(), metric.bucket()) * 1000000; } else { mBucketSizeNs = LLONG_MAX; } @@ -150,10 +151,9 @@ void DurationMetricProducer::onSlicedConditionMayChangeLocked(const uint64_t eve std::unordered_set<HashableDimensionKey> conditionDimensionsKeySet; - ConditionState conditionState = mWizard->getMetConditionDimension( - mConditionTrackerIndex, mDimensionsInCondition, &conditionDimensionsKeySet); + mWizard->getMetConditionDimension(mConditionTrackerIndex, mDimensionsInCondition, + &conditionDimensionsKeySet); - bool condition = (conditionState == ConditionState::kTrue); for (auto& pair : mCurrentSlicedDurationTrackerMap) { conditionDimensionsKeySet.erase(pair.first.getDimensionKeyInCondition()); } @@ -221,9 +221,9 @@ void DurationMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, for (const auto& bucket : pair.second) { long long bucketInfoToken = protoOutput->start( FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_BUCKET_INFO); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_START_BUCKET_NANOS, + protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_START_BUCKET_ELAPSED_NANOS, (long long)bucket.mBucketStartNs); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_END_BUCKET_NANOS, + protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_END_BUCKET_ELAPSED_NANOS, (long long)bucket.mBucketEndNs); protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_DURATION, (long long)bucket.mDuration); protoOutput->end(bucketInfoToken); @@ -310,11 +310,11 @@ void DurationMetricProducer::onMatchedLogEventInternalLocked( const size_t matcherIndex, const MetricDimensionKey& eventKey, const ConditionKey& conditionKeys, bool condition, const LogEvent& event) { - flushIfNeededLocked(event.GetTimestampNs()); + flushIfNeededLocked(event.GetElapsedTimestampNs()); if (matcherIndex == mStopAllIndex) { for (auto& pair : mCurrentSlicedDurationTrackerMap) { - pair.second->noteStopAll(event.GetTimestampNs()); + pair.second->noteStopAll(event.GetElapsedTimestampNs()); } return; } @@ -333,16 +333,16 @@ void DurationMetricProducer::onMatchedLogEventInternalLocked( if (values.empty()) { if (matcherIndex == mStartIndex) { it->second->noteStart(DEFAULT_DIMENSION_KEY, condition, - event.GetTimestampNs(), conditionKeys); + event.GetElapsedTimestampNs(), conditionKeys); } else if (matcherIndex == mStopIndex) { - it->second->noteStop(DEFAULT_DIMENSION_KEY, event.GetTimestampNs(), false); + it->second->noteStop(DEFAULT_DIMENSION_KEY, event.GetElapsedTimestampNs(), false); } } else { for (const auto& value : values) { if (matcherIndex == mStartIndex) { - it->second->noteStart(value, condition, event.GetTimestampNs(), conditionKeys); + it->second->noteStart(value, condition, event.GetElapsedTimestampNs(), conditionKeys); } else if (matcherIndex == mStopIndex) { - it->second->noteStop(value, event.GetTimestampNs(), false); + it->second->noteStop(value, event.GetElapsedTimestampNs(), false); } } } diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp index ed7e44de09c0..2585aa3bdbff 100644 --- a/cmds/statsd/src/metrics/EventMetricProducer.cpp +++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp @@ -19,6 +19,7 @@ #include "EventMetricProducer.h" #include "stats_util.h" +#include "stats_log_util.h" #include <limits.h> #include <stdlib.h> @@ -46,8 +47,9 @@ const int FIELD_ID_EVENT_METRICS = 4; // for EventMetricDataWrapper const int FIELD_ID_DATA = 1; // for EventMetricData -const int FIELD_ID_TIMESTAMP_NANOS = 1; +const int FIELD_ID_ELAPSED_TIMESTAMP_NANOS = 1; const int FIELD_ID_ATOMS = 2; +const int FIELD_ID_WALL_CLOCK_TIMESTAMP_NANOS = 3; EventMetricProducer::EventMetricProducer(const ConfigKey& key, const EventMetric& metric, const int conditionIndex, @@ -127,9 +129,12 @@ void EventMetricProducer::onMatchedLogEventInternalLocked( long long wrapperToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA); - mProto->write(FIELD_TYPE_INT64 | FIELD_ID_TIMESTAMP_NANOS, (long long)event.GetTimestampNs()); + mProto->write(FIELD_TYPE_INT64 | FIELD_ID_ELAPSED_TIMESTAMP_NANOS, + (long long)event.GetElapsedTimestampNs()); long long eventToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_ATOMS); event.ToProto(*mProto); + mProto->write(FIELD_TYPE_INT64 | FIELD_ID_WALL_CLOCK_TIMESTAMP_NANOS, + (long long)getWallClockNs()); mProto->end(eventToken); mProto->end(wrapperToken); } diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp index da0cafeca427..0daa506ba42d 100644 --- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp +++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp @@ -52,10 +52,10 @@ const int FIELD_ID_DIMENSION_IN_WHAT = 1; const int FIELD_ID_DIMENSION_IN_CONDITION = 2; const int FIELD_ID_BUCKET_INFO = 3; // for GaugeBucketInfo -const int FIELD_ID_START_BUCKET_NANOS = 1; -const int FIELD_ID_END_BUCKET_NANOS = 2; +const int FIELD_ID_START_BUCKET_ELAPSED_NANOS = 1; +const int FIELD_ID_END_BUCKET_ELAPSED_NANOS = 2; const int FIELD_ID_ATOM = 3; -const int FIELD_ID_TIMESTAMP = 4; +const int FIELD_ID_ELAPSED_ATOM_TIMESTAMP = 4; GaugeMetricProducer::GaugeMetricProducer(const ConfigKey& key, const GaugeMetric& metric, const int conditionIndex, @@ -69,7 +69,7 @@ GaugeMetricProducer::GaugeMetricProducer(const ConfigKey& key, const GaugeMetric mCurrentSlicedBucketForAnomaly = std::make_shared<DimToValMap>(); int64_t bucketSizeMills = 0; if (metric.has_bucket()) { - bucketSizeMills = TimeUnitToBucketSizeInMillis(metric.bucket()); + bucketSizeMills = TimeUnitToBucketSizeInMillisGuardrailed(key.GetUid(), metric.bucket()); } else { bucketSizeMills = TimeUnitToBucketSizeInMillis(ONE_HOUR); } @@ -161,9 +161,9 @@ void GaugeMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, for (const auto& bucket : pair.second) { long long bucketInfoToken = protoOutput->start( FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_BUCKET_INFO); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_START_BUCKET_NANOS, + protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_START_BUCKET_ELAPSED_NANOS, (long long)bucket.mBucketStartNs); - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_END_BUCKET_NANOS, + protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_END_BUCKET_ELAPSED_NANOS, (long long)bucket.mBucketEndNs); if (!bucket.mGaugeAtoms.empty()) { @@ -175,8 +175,9 @@ void GaugeMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, protoOutput->end(atomsToken); for (const auto& atom : bucket.mGaugeAtoms) { - protoOutput->write(FIELD_TYPE_INT64 | FIELD_COUNT_REPEATED | FIELD_ID_TIMESTAMP, - (long long)atom.mTimestamps); + protoOutput->write( + FIELD_TYPE_INT64 | FIELD_COUNT_REPEATED | FIELD_ID_ELAPSED_ATOM_TIMESTAMP, + (long long)atom.mTimestamps); } } protoOutput->end(bucketInfoToken); @@ -293,7 +294,7 @@ void GaugeMetricProducer::onMatchedLogEventInternalLocked( if (condition == false) { return; } - uint64_t eventTimeNs = event.GetTimestampNs(); + uint64_t eventTimeNs = event.GetElapsedTimestampNs(); mTagId = event.GetTagId(); if (eventTimeNs < mCurrentBucketStartTimeNs) { VLOG("Skip event due to late arrival: %lld vs %lld", (long long)eventTimeNs, @@ -333,7 +334,6 @@ void GaugeMetricProducer::onMatchedLogEventInternalLocked( } void GaugeMetricProducer::updateCurrentSlicedBucketForAnomaly() { - status_t err = NO_ERROR; for (const auto& slice : *mCurrentSlicedBucket) { if (slice.second.empty()) { continue; diff --git a/cmds/statsd/src/metrics/MetricProducer.cpp b/cmds/statsd/src/metrics/MetricProducer.cpp index beb90155f183..f3307dc1d1e3 100644 --- a/cmds/statsd/src/metrics/MetricProducer.cpp +++ b/cmds/statsd/src/metrics/MetricProducer.cpp @@ -25,7 +25,7 @@ namespace statsd { using std::map; void MetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, const LogEvent& event) { - uint64_t eventTimeNs = event.GetTimestampNs(); + uint64_t eventTimeNs = event.GetElapsedTimestampNs(); // this is old event, maybe statsd restarted? if (eventTimeNs < mStartTimeNs) { return; diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h index e8f8299abd89..8663e5eed7a7 100644 --- a/cmds/statsd/src/metrics/MetricProducer.h +++ b/cmds/statsd/src/metrics/MetricProducer.h @@ -123,6 +123,7 @@ public: 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 @@ public: return mBucketSizeNs; } + // Only needed for unit-testing to override guardrail. + void setBucketSize(int64_t bucketSize) { + mBucketSizeNs = bucketSize; + } + inline const int64_t& getMetricId() { return mMetricId; } diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp index dd6735b6faca..e75b710cc9db 100644 --- a/cmds/statsd/src/metrics/MetricsManager.cpp +++ b/cmds/statsd/src/metrics/MetricsManager.cpp @@ -26,6 +26,7 @@ #include "matchers/SimpleLogMatchingTracker.h" #include "metrics_manager_util.h" #include "stats_util.h" +#include "stats_log_util.h" #include <log/logprint.h> #include <private/android_filesystem_config.h> @@ -49,9 +50,10 @@ const int FIELD_ID_METRICS = 1; MetricsManager::MetricsManager(const ConfigKey& key, const StatsdConfig& config, const long timeBaseSec, sp<UidMap> uidMap) - : mConfigKey(key), mUidMap(uidMap), mLastReportTimeNs(0) { + : mConfigKey(key), mUidMap(uidMap), mLastReportTimeNs(timeBaseSec * NS_PER_SEC) { mConfigValid = - initStatsdConfig(key, config, *uidMap, timeBaseSec, mTagIds, mAllAtomMatchers, mAllConditionTrackers, + initStatsdConfig(key, config, *uidMap, timeBaseSec, mTagIds, mAllAtomMatchers, + mAllConditionTrackers, mAllMetricProducers, mAllAnomalyTrackers, mConditionToMetricMap, mTrackerToMetricMap, mTrackerToConditionMap, mNoReportMetricIds); @@ -90,8 +92,10 @@ MetricsManager::MetricsManager(const ConfigKey& key, const StatsdConfig& config, 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(), @@ -176,7 +180,7 @@ void MetricsManager::onDumpReport(const uint64_t dumpTimeStampNs, ProtoOutputStr protoOutput->end(token); } } - mLastReportTimeNs = ::android::elapsedRealtimeNano(); + mLastReportTimeNs = dumpTimeStampNs; VLOG("=========================Metric Reports End=========================="); } @@ -186,8 +190,9 @@ void MetricsManager::onLogEvent(const LogEvent& event) { return; } - if (event.GetTagId() == android::util::APP_HOOK) { // 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, @@ -230,7 +235,7 @@ void MetricsManager::onLogEvent(const LogEvent& event) { } int tagId = event.GetTagId(); - uint64_t eventTime = event.GetTimestampNs(); + uint64_t eventTime = event.GetElapsedTimestampNs(); if (mTagIds.find(tagId) == mTagIds.end()) { // not interesting... return; diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp index 45b4ac0cd43a..35fcdc48a08a 100644 --- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp +++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp @@ -72,7 +72,7 @@ ValueMetricProducer::ValueMetricProducer(const ConfigKey& key, const ValueMetric // TODO: valuemetric for pushed events may need unlimited bucket length int64_t bucketSizeMills = 0; if (metric.has_bucket()) { - bucketSizeMills = TimeUnitToBucketSizeInMillis(metric.bucket()); + bucketSizeMills = TimeUnitToBucketSizeInMillisGuardrailed(key.GetUid(), metric.bucket()); } else { bucketSizeMills = TimeUnitToBucketSizeInMillis(ONE_HOUR); } @@ -219,19 +219,19 @@ void ValueMetricProducer::onDataPulled(const std::vector<std::shared_ptr<LogEven } // For scheduled pulled data, the effective event time is snap to the nearest // bucket boundary to make bucket finalize. - uint64_t realEventTime = allData.at(0)->GetTimestampNs(); + uint64_t realEventTime = allData.at(0)->GetElapsedTimestampNs(); uint64_t eventTime = mStartTimeNs + - ((realEventTime - mStartTimeNs)/mBucketSizeNs) * mBucketSizeNs; + ((realEventTime - mStartTimeNs) / mBucketSizeNs) * mBucketSizeNs; mCondition = false; for (const auto& data : allData) { - data->setTimestampNs(eventTime-1); + data->setElapsedTimestampNs(eventTime - 1); onMatchedLogEventLocked(0, *data); } mCondition = true; for (const auto& data : allData) { - data->setTimestampNs(eventTime); + data->setElapsedTimestampNs(eventTime); onMatchedLogEventLocked(0, *data); } } @@ -261,7 +261,7 @@ void ValueMetricProducer::onMatchedLogEventInternalLocked( const size_t matcherIndex, const MetricDimensionKey& eventKey, const ConditionKey& conditionKey, bool condition, const LogEvent& event) { - uint64_t eventTimeNs = event.GetTimestampNs(); + uint64_t eventTimeNs = event.GetElapsedTimestampNs(); if (eventTimeNs < mCurrentBucketStartTimeNs) { VLOG("Skip event due to late arrival: %lld vs %lld", (long long)eventTimeNs, (long long)mCurrentBucketStartTimeNs); diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h index 6701a46acde1..b518f2f841cf 100644 --- a/cmds/statsd/src/metrics/ValueMetricProducer.h +++ b/cmds/statsd/src/metrics/ValueMetricProducer.h @@ -62,7 +62,7 @@ public: // Pretend the pulled data occurs right before the app upgrade event. mCondition = false; for (const auto& data : allData) { - data->setTimestampNs(eventTimeNs - 1); + data->setElapsedTimestampNs(eventTimeNs - 1); onMatchedLogEventLocked(0, *data); } @@ -71,7 +71,7 @@ public: mCondition = true; for (const auto& data : allData) { - data->setTimestampNs(eventTimeNs); + data->setElapsedTimestampNs(eventTimeNs); onMatchedLogEventLocked(0, *data); } } else { // For pushed value metric, we simply flush and reset the current bucket start. diff --git a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp index c29876b5eae0..95df5ae6e8bd 100644 --- a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp +++ b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp @@ -177,7 +177,6 @@ bool MaxDurationTracker::flushCurrentBucket( false; // has either a kStarted or kPaused event across bucket boundaries // meaning we need to carry them over to the new bucket. for (auto it = mInfos.begin(); it != mInfos.end(); ++it) { - int64_t finalDuration = it->second.lastDuration; if (it->second.state == DurationState::kStopped) { // No need to keep buckets for events that were stopped before. mInfos.erase(it); diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp index 769f46dbd7e6..71e5c33b1b88 100644 --- a/cmds/statsd/src/metrics/metrics_manager_util.cpp +++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp @@ -513,10 +513,12 @@ bool initAlerts(const StatsdConfig& config, 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/packages/UidMap.cpp b/cmds/statsd/src/packages/UidMap.cpp index 691423e054b2..e322ca4bb1ac 100644 --- a/cmds/statsd/src/packages/UidMap.cpp +++ b/cmds/statsd/src/packages/UidMap.cpp @@ -16,6 +16,7 @@ #define DEBUG true // STOPSHIP if true #include "Log.h" +#include "stats_log_util.h" #include "guardrail/StatsdStats.h" #include "packages/UidMap.h" @@ -82,7 +83,7 @@ int64_t UidMap::getAppVersion(int uid, const string& packageName) const { void UidMap::updateMap(const vector<int32_t>& uid, const vector<int64_t>& versionCode, const vector<String16>& packageName) { - updateMap(time(nullptr) * NS_PER_SEC, uid, versionCode, packageName); + updateMap(getElapsedRealtimeNs(), uid, versionCode, packageName); } void UidMap::updateMap(const int64_t& timestamp, const vector<int32_t>& uid, @@ -98,7 +99,7 @@ void UidMap::updateMap(const int64_t& timestamp, const vector<int32_t>& uid, } auto snapshot = mOutput.add_snapshots(); - snapshot->set_timestamp_nanos(timestamp); + snapshot->set_elapsed_timestamp_nanos(timestamp); for (size_t j = 0; j < uid.size(); j++) { auto t = snapshot->add_package_info(); t->set_name(string(String8(packageName[j]).string())); @@ -125,7 +126,7 @@ void UidMap::updateMap(const int64_t& timestamp, const vector<int32_t>& uid, } void UidMap::updateApp(const String16& app_16, const int32_t& uid, const int64_t& versionCode) { - updateApp(time(nullptr) * NS_PER_SEC, app_16, uid, versionCode); + updateApp(getElapsedRealtimeNs(), app_16, uid, versionCode); } void UidMap::updateApp(const int64_t& timestamp, const String16& app_16, const int32_t& uid, @@ -137,7 +138,7 @@ void UidMap::updateApp(const int64_t& timestamp, const String16& app_16, const i auto log = mOutput.add_changes(); log->set_deletion(false); - log->set_timestamp_nanos(timestamp); + log->set_elapsed_timestamp_nanos(timestamp); log->set_app(appName); log->set_uid(uid); log->set_version(versionCode); @@ -194,7 +195,7 @@ void UidMap::ensureBytesUsedBelowLimit() { } void UidMap::removeApp(const String16& app_16, const int32_t& uid) { - removeApp(time(nullptr) * NS_PER_SEC, app_16, uid); + removeApp(getElapsedRealtimeNs(), app_16, uid); } void UidMap::getListenerListCopyLocked(vector<wp<PackageInfoListener>>* output) { @@ -218,7 +219,7 @@ void UidMap::removeApp(const int64_t& timestamp, const String16& app_16, const i auto log = mOutput.add_changes(); log->set_deletion(true); - log->set_timestamp_nanos(timestamp); + log->set_elapsed_timestamp_nanos(timestamp); log->set_app(app); log->set_uid(uid); mBytesUsed += log->ByteSize(); @@ -305,7 +306,7 @@ size_t UidMap::getBytesUsed() const { } UidMapping UidMap::getOutput(const ConfigKey& key) { - return getOutput(time(nullptr) * NS_PER_SEC, key); + return getOutput(getElapsedRealtimeNs(), key); } UidMapping UidMap::getOutput(const int64_t& timestamp, const ConfigKey& key) { @@ -321,7 +322,7 @@ UidMapping UidMap::getOutput(const int64_t& timestamp, const ConfigKey& key) { auto snapshots = mOutput.mutable_snapshots(); auto it_snapshots = snapshots->cbegin(); while (it_snapshots != snapshots->cend()) { - if (it_snapshots->timestamp_nanos() < cutoff_nanos) { + if (it_snapshots->elapsed_timestamp_nanos() < cutoff_nanos) { // it_snapshots points to the following element after erasing. it_snapshots = snapshots->erase(it_snapshots); } else { @@ -331,7 +332,7 @@ UidMapping UidMap::getOutput(const int64_t& timestamp, const ConfigKey& key) { auto deltas = mOutput.mutable_changes(); auto it_deltas = deltas->cbegin(); while (it_deltas != deltas->cend()) { - if (it_deltas->timestamp_nanos() < cutoff_nanos) { + if (it_deltas->elapsed_timestamp_nanos() < cutoff_nanos) { // it_snapshots points to the following element after erasing. it_deltas = deltas->erase(it_deltas); } else { @@ -343,7 +344,7 @@ UidMapping UidMap::getOutput(const int64_t& timestamp, const ConfigKey& key) { // Produce another snapshot. This results in extra data being uploaded but helps // ensure we can re-construct the UID->app name, versionCode mapping in server. auto snapshot = mOutput.add_snapshots(); - snapshot->set_timestamp_nanos(timestamp); + snapshot->set_elapsed_timestamp_nanos(timestamp); for (auto it : mMap) { auto t = snapshot->add_package_info(); t->set_name(it.second.packageName); diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto index b56cffb40806..b427485fd705 100644 --- a/cmds/statsd/src/stats_log.proto +++ b/cmds/statsd/src/stats_log.proto @@ -42,15 +42,17 @@ message DimensionsValueTuple { } message EventMetricData { - optional int64 timestamp_nanos = 1; + optional int64 elapsed_timestamp_nanos = 1; optional Atom atom = 2; + + optional int64 wall_clock_timestamp_sec = 3; } message CountBucketInfo { - optional int64 start_bucket_nanos = 1; + optional int64 start_bucket_elapsed_nanos = 1; - optional int64 end_bucket_nanos = 2; + optional int64 end_bucket_elapsed_nanos = 2; optional int64 count = 3; } @@ -64,9 +66,9 @@ message CountMetricData { } message DurationBucketInfo { - optional int64 start_bucket_nanos = 1; + optional int64 start_bucket_elapsed_nanos = 1; - optional int64 end_bucket_nanos = 2; + optional int64 end_bucket_elapsed_nanos = 2; optional int64 duration_nanos = 3; } @@ -80,9 +82,9 @@ message DurationMetricData { } message ValueBucketInfo { - optional int64 start_bucket_nanos = 1; + optional int64 start_bucket_elapsed_nanos = 1; - optional int64 end_bucket_nanos = 2; + optional int64 end_bucket_elapsed_nanos = 2; optional int64 value = 3; } @@ -102,7 +104,7 @@ message GaugeBucketInfo { repeated Atom atom = 3; - repeated int64 timestamp_nanos = 4; + repeated int64 elapsed_timestamp_nanos = 4; } message GaugeMetricData { @@ -122,7 +124,7 @@ message UidMapping { optional int32 uid = 3; } - optional int64 timestamp_nanos = 1; + optional int64 elapsed_timestamp_nanos = 1; repeated PackageInfo package_info = 2; } @@ -131,7 +133,7 @@ message UidMapping { message Change { optional bool deletion = 1; - optional int64 timestamp_nanos = 2; + optional int64 elapsed_timestamp_nanos = 2; optional string app = 3; optional int32 uid = 4; @@ -176,9 +178,9 @@ message ConfigMetricsReport { optional UidMapping uid_map = 2; - optional int64 last_report_nanos = 3; + optional int64 last_report_elapsed_nanos = 3; - optional int64 current_report_nanos = 4; + optional int64 current_report_elapsed_nanos = 4; } message ConfigMetricsReportList { diff --git a/cmds/statsd/src/stats_log_util.cpp b/cmds/statsd/src/stats_log_util.cpp index 86c258bd3dfd..30eef4fef166 100644 --- a/cmds/statsd/src/stats_log_util.cpp +++ b/cmds/statsd/src/stats_log_util.cpp @@ -17,9 +17,12 @@ #include "stats_log_util.h" #include <logd/LogEvent.h> +#include <private/android_filesystem_config.h> #include <utils/Log.h> #include <set> #include <stack> +#include <utils/Log.h> +#include <utils/SystemClock.h> using android::util::FIELD_COUNT_REPEATED; using android::util::FIELD_TYPE_BOOL; @@ -39,15 +42,12 @@ const int DIMENSIONS_VALUE_FIELD = 1; const int DIMENSIONS_VALUE_VALUE_STR = 2; const int DIMENSIONS_VALUE_VALUE_INT = 3; const int DIMENSIONS_VALUE_VALUE_LONG = 4; -const int DIMENSIONS_VALUE_VALUE_BOOL = 5; +// const int DIMENSIONS_VALUE_VALUE_BOOL = 5; // logd doesn't have bool data type. const int DIMENSIONS_VALUE_VALUE_FLOAT = 6; const int DIMENSIONS_VALUE_VALUE_TUPLE = 7; const int DIMENSIONS_VALUE_TUPLE_VALUE = 1; -// for MessageValue Proto -const int FIELD_ID_FIELD_VALUE_IN_MESSAGE_VALUE_PROTO = 1; - // for PulledAtomStats proto const int FIELD_ID_PULLED_ATOM_STATS = 10; const int FIELD_ID_PULL_ATOM_ID = 1; @@ -129,11 +129,6 @@ void writeDimensionToProto(const HashableDimensionKey& dimension, ProtoOutputStr protoOutput->end(topToken); } -// for Field Proto -const int FIELD_FIELD = 1; -const int FIELD_POSITION_INDEX = 2; -const int FIELD_CHILD = 3; - // Supported Atoms format // XYZ_Atom { // repeated SubMsg field_1 = 1; @@ -222,6 +217,14 @@ void writeFieldValueTreeToStream(int tagId, const std::vector<FieldValue>& value protoOutput->end(atomToken); } +int64_t TimeUnitToBucketSizeInMillisGuardrailed(int uid, TimeUnit unit) { + int64_t bucketSizeMillis = TimeUnitToBucketSizeInMillis(unit); + if (bucketSizeMillis > 1000 && bucketSizeMillis < 5 * 60 * 1000LL && uid != AID_SHELL) { + bucketSizeMillis = 5 * 60 * 1000LL; + } + return bucketSizeMillis; +} + int64_t TimeUnitToBucketSizeInMillis(TimeUnit unit) { switch (unit) { case ONE_MINUTE: @@ -263,6 +266,30 @@ void writePullerStatsToStream(const std::pair<int, StatsdStats::PulledAtomStats> protoOutput->end(token); } +int64_t getElapsedRealtimeNs() { + return ::android::elapsedRealtimeNano(); +} + +int64_t getElapsedRealtimeSec() { + return ::android::elapsedRealtimeNano() / NS_PER_SEC; +} + +int64_t getElapsedRealtimeMillis() { + return ::android::elapsedRealtime(); +} + +int64_t getWallClockNs() { + return time(nullptr) * NS_PER_SEC; +} + +int64_t getWallClockSec() { + return time(nullptr); +} + +int64_t getWallClockMillis() { + return time(nullptr) * MS_PER_SEC; +} + } // 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 6583f579648b..6a5123d8c844 100644 --- a/cmds/statsd/src/stats_log_util.h +++ b/cmds/statsd/src/stats_log_util.h @@ -32,9 +32,31 @@ void writeFieldValueTreeToStream(int tagId, const std::vector<FieldValue>& value void writeDimensionToProto(const HashableDimensionKey& dimension, util::ProtoOutputStream* protoOutput); +// Convert the TimeUnit enum to the bucket size in millis with a guardrail on +// bucket size. +int64_t TimeUnitToBucketSizeInMillisGuardrailed(int uid, TimeUnit unit); + // Convert the TimeUnit enum to the bucket size in millis. int64_t TimeUnitToBucketSizeInMillis(TimeUnit unit); +// Gets the elapsed timestamp in ns. +int64_t getElapsedRealtimeNs(); + +// Gets the elapsed timestamp in millis. +int64_t getElapsedRealtimeMillis(); + +// Gets the elapsed timestamp in seconds. +int64_t getElapsedRealtimeSec(); + +// Gets the wall clock timestamp in ns. +int64_t getWallClockNs(); + +// Gets the wall clock timestamp in millis. +int64_t getWallClockMillis(); + +// Gets the wall clock timestamp in seconds. +int64_t getWallClockSec(); + // Helper function to write PulledAtomStats to ProtoOutputStream void writePullerStatsToStream(const std::pair<int, StatsdStats::PulledAtomStats>& pair, util::ProtoOutputStream* protoOutput); @@ -53,4 +75,4 @@ bool parseProtoOutputStream(util::ProtoOutputStream& protoOutput, T* message) { } // namespace statsd } // namespace os -} // namespace android
\ No newline at end of file +} // namespace android diff --git a/cmds/statsd/src/storage/StorageManager.cpp b/cmds/statsd/src/storage/StorageManager.cpp index 23bd55616be2..6a1db72b3911 100644 --- a/cmds/statsd/src/storage/StorageManager.cpp +++ b/cmds/statsd/src/storage/StorageManager.cpp @@ -20,6 +20,7 @@ #include "android-base/stringprintf.h" #include "guardrail/StatsdStats.h" #include "storage/StorageManager.h" +#include "stats_log_util.h" #include <android-base/file.h> #include <dirent.h> @@ -252,7 +253,7 @@ void StorageManager::trimToFit(const char* path) { string file_name = getFilePath(path, timestamp, uid, configID); // Check for timestamp and delete if it's too old. - long fileAge = time(nullptr) - timestamp; + long fileAge = getWallClockSec() - timestamp; if (fileAge > StatsdStats::kMaxAgeSecond) { deleteFile(file_name.c_str()); } diff --git a/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp b/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp index 01743ef1a45c..022800427b11 100644 --- a/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp +++ b/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp @@ -47,7 +47,7 @@ StatsdConfig CreateStatsdConfig() { *countMetric->mutable_dimensions_in_what() = CreateAttributionUidAndTagDimensions( android::util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); - countMetric->set_bucket(ONE_MINUTE); + countMetric->set_bucket(FIVE_MINUTES); return config; } @@ -163,11 +163,11 @@ TEST(AttributionE2eTest, TestAttributionMatchAndSlice) { "App1"); EXPECT_EQ(data.bucket_info_size(), 2); EXPECT_EQ(data.bucket_info(0).count(), 2); - EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs); - EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs); + EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs); EXPECT_EQ(data.bucket_info(1).count(), 1); - EXPECT_EQ(data.bucket_info(1).start_bucket_nanos(), bucketStartTimeNs + 2 * bucketSizeNs); - EXPECT_EQ(data.bucket_info(1).end_bucket_nanos(), bucketStartTimeNs + 3 * bucketSizeNs); + EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(), bucketStartTimeNs + 2 * bucketSizeNs); + EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(), bucketStartTimeNs + 3 * bucketSizeNs); data = countMetrics.data(1); ValidateAttributionUidAndTagDimension( @@ -175,11 +175,11 @@ TEST(AttributionE2eTest, TestAttributionMatchAndSlice) { "GMSCoreModule1"); EXPECT_EQ(data.bucket_info_size(), 2); EXPECT_EQ(data.bucket_info(0).count(), 1); - EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs); - EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs); + EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs); EXPECT_EQ(data.bucket_info(1).count(), 1); - EXPECT_EQ(data.bucket_info(1).start_bucket_nanos(), bucketStartTimeNs + bucketSizeNs); - EXPECT_EQ(data.bucket_info(1).end_bucket_nanos(), bucketStartTimeNs + 2 * bucketSizeNs); + EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(), bucketStartTimeNs + 2 * bucketSizeNs); data = countMetrics.data(2); ValidateAttributionUidAndTagDimension( @@ -187,8 +187,8 @@ TEST(AttributionE2eTest, TestAttributionMatchAndSlice) { "GMSCoreModule3"); EXPECT_EQ(data.bucket_info_size(), 1); EXPECT_EQ(data.bucket_info(0).count(), 1); - EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs + 3 * bucketSizeNs); - EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + 4 * bucketSizeNs); + EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs + 3 * bucketSizeNs); + EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + 4 * bucketSizeNs); data = countMetrics.data(3); ValidateAttributionUidAndTagDimension( @@ -196,8 +196,8 @@ TEST(AttributionE2eTest, TestAttributionMatchAndSlice) { "GMSCoreModule2"); EXPECT_EQ(data.bucket_info_size(), 1); EXPECT_EQ(data.bucket_info(0).count(), 1); - EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs + 2 * bucketSizeNs); - EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + 3 * bucketSizeNs); + EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs + 2 * bucketSizeNs); + EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + 3 * bucketSizeNs); } #else @@ -206,4 +206,4 @@ GTEST_LOG_(INFO) << "This test does nothing.\n"; } // namespace statsd } // namespace os -} // namespace android
\ No newline at end of file +} // namespace android diff --git a/cmds/statsd/tests/e2e/DimensionInCondition_e2e_test.cpp b/cmds/statsd/tests/e2e/DimensionInCondition_e2e_test.cpp index 275b58244e0f..4dffd13bef66 100644 --- a/cmds/statsd/tests/e2e/DimensionInCondition_e2e_test.cpp +++ b/cmds/statsd/tests/e2e/DimensionInCondition_e2e_test.cpp @@ -61,7 +61,7 @@ StatsdConfig CreateCountMetricWithNoLinkConfig() { CreateDimensions(android::util::SCREEN_BRIGHTNESS_CHANGED, {1 /* level */}); *metric->mutable_dimensions_in_condition() = CreateAttributionUidDimensions( android::util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); - metric->set_bucket(ONE_MINUTE); + metric->set_bucket(FIVE_MINUTES); return config; } @@ -142,8 +142,8 @@ TEST(DimensionInConditionE2eTest, TestCountMetricNoLink) { auto data = countMetrics.data(0); EXPECT_EQ(data.bucket_info_size(), 1); EXPECT_EQ(data.bucket_info(0).count(), 1); - EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs); - EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs); + EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs); EXPECT_EQ(data.dimensions_in_what().field(), android::util::SCREEN_BRIGHTNESS_CHANGED); EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1); EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1); @@ -153,8 +153,8 @@ TEST(DimensionInConditionE2eTest, TestCountMetricNoLink) { data = countMetrics.data(1); EXPECT_EQ(data.bucket_info_size(), 1); EXPECT_EQ(data.bucket_info(0).count(), 1); - EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs); - EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs); + EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs); EXPECT_EQ(data.dimensions_in_what().field(), android::util::SCREEN_BRIGHTNESS_CHANGED); EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1); EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1); @@ -165,8 +165,8 @@ TEST(DimensionInConditionE2eTest, TestCountMetricNoLink) { data = countMetrics.data(2); EXPECT_EQ(data.bucket_info_size(), 1); EXPECT_EQ(data.bucket_info(0).count(), 3); - EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs); - EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs); + EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs); EXPECT_EQ(data.dimensions_in_what().field(), android::util::SCREEN_BRIGHTNESS_CHANGED); EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1); EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1); @@ -177,11 +177,11 @@ TEST(DimensionInConditionE2eTest, TestCountMetricNoLink) { data = countMetrics.data(3); EXPECT_EQ(data.bucket_info_size(), 2); EXPECT_EQ(data.bucket_info(0).count(), 2); - EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs); - EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs); + EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs); EXPECT_EQ(data.bucket_info(1).count(), 1); - EXPECT_EQ(data.bucket_info(1).start_bucket_nanos(), bucketStartTimeNs + bucketSizeNs); - EXPECT_EQ(data.bucket_info(1).end_bucket_nanos(), bucketStartTimeNs + 2 * bucketSizeNs); + EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(), bucketStartTimeNs + 2 * bucketSizeNs); EXPECT_EQ(data.dimensions_in_what().field(), android::util::SCREEN_BRIGHTNESS_CHANGED); EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1); EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1); @@ -192,8 +192,8 @@ TEST(DimensionInConditionE2eTest, TestCountMetricNoLink) { data = countMetrics.data(4); EXPECT_EQ(data.bucket_info_size(), 1); EXPECT_EQ(data.bucket_info(0).count(), 2); - EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs + bucketSizeNs); - EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + 2 * bucketSizeNs); + EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + 2 * bucketSizeNs); EXPECT_EQ(data.dimensions_in_what().field(), android::util::SCREEN_BRIGHTNESS_CHANGED); EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1); EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1); @@ -203,8 +203,8 @@ TEST(DimensionInConditionE2eTest, TestCountMetricNoLink) { data = countMetrics.data(5); EXPECT_EQ(data.bucket_info_size(), 1); EXPECT_EQ(data.bucket_info(0).count(), 1); - EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs); - EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs); + EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs); EXPECT_EQ(data.dimensions_in_what().field(), android::util::SCREEN_BRIGHTNESS_CHANGED); EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1); EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1); @@ -215,8 +215,8 @@ TEST(DimensionInConditionE2eTest, TestCountMetricNoLink) { data = countMetrics.data(6); EXPECT_EQ(data.bucket_info_size(), 1); EXPECT_EQ(data.bucket_info(0).count(), 1); - EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs); - EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs); + EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs); EXPECT_EQ(data.dimensions_in_what().field(), android::util::SCREEN_BRIGHTNESS_CHANGED); EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1); EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1); @@ -252,7 +252,7 @@ StatsdConfig CreateCountMetricWithLinkConfig() { addPredicateToPredicateCombination(isSyncingPredicate, combinationPredicate); auto metric = config.add_count_metric(); - metric->set_bucket(ONE_MINUTE); + metric->set_bucket(FIVE_MINUTES); metric->set_id(StringToId("AppCrashMetric")); metric->set_what(appCrashMatcher.id()); metric->set_condition(combinationPredicate->id()); @@ -358,8 +358,8 @@ TEST(DimensionInConditionE2eTest, TestCountMetricWithLink) { EXPECT_FALSE(data.dimensions_in_condition().has_field()); EXPECT_EQ(data.bucket_info_size(), 1); EXPECT_EQ(data.bucket_info(0).count(), 1); - EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs); - EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs); + EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs); data = countMetrics.data(1); EXPECT_EQ(data.dimensions_in_what().field(), android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED); @@ -370,8 +370,8 @@ TEST(DimensionInConditionE2eTest, TestCountMetricWithLink) { android::util::SYNC_STATE_CHANGED, 111, "App1"); EXPECT_EQ(data.bucket_info_size(), 1); EXPECT_EQ(data.bucket_info(0).count(), 2); - EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs); - EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs); + EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs); data = countMetrics.data(2); EXPECT_EQ(data.dimensions_in_what().field(), android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED); @@ -381,8 +381,8 @@ TEST(DimensionInConditionE2eTest, TestCountMetricWithLink) { EXPECT_FALSE(data.dimensions_in_condition().has_field()); EXPECT_EQ(data.bucket_info_size(), 1); EXPECT_EQ(data.bucket_info(0).count(), 2); - EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs); - EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs); + EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs); data = countMetrics.data(3); EXPECT_EQ(data.dimensions_in_what().field(), android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED); @@ -393,11 +393,11 @@ TEST(DimensionInConditionE2eTest, TestCountMetricWithLink) { android::util::SYNC_STATE_CHANGED, 333, "App2"); EXPECT_EQ(data.bucket_info_size(), 2); EXPECT_EQ(data.bucket_info(0).count(), 1); - EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs); - EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs); + EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs); EXPECT_EQ(data.bucket_info(1).count(), 1); - EXPECT_EQ(data.bucket_info(1).start_bucket_nanos(), bucketStartTimeNs + bucketSizeNs); - EXPECT_EQ(data.bucket_info(1).end_bucket_nanos(), bucketStartTimeNs + 2 * bucketSizeNs); + EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(), bucketStartTimeNs + 2 * bucketSizeNs); data = countMetrics.data(4); EXPECT_EQ(data.dimensions_in_what().field(), android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED); @@ -407,8 +407,8 @@ TEST(DimensionInConditionE2eTest, TestCountMetricWithLink) { EXPECT_FALSE(data.dimensions_in_condition().has_field()); EXPECT_EQ(data.bucket_info_size(), 1); EXPECT_EQ(data.bucket_info(0).count(), 1); - EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs + bucketSizeNs); - EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + 2 * bucketSizeNs); + EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + 2 * bucketSizeNs); } namespace { @@ -441,7 +441,7 @@ StatsdConfig CreateDurationMetricConfigNoLink(DurationMetric::AggregationType ag addPredicateToPredicateCombination(isSyncingPredicate, combinationPredicate); auto metric = config.add_duration_metric(); - metric->set_bucket(ONE_MINUTE); + metric->set_bucket(FIVE_MINUTES); metric->set_id(StringToId("BatterySaverModeDurationMetric")); metric->set_what(inBatterySaverModePredicate.id()); metric->set_condition(combinationPredicate->id()); @@ -531,11 +531,11 @@ TEST(DimensionInConditionE2eTest, TestDurationMetricNoLink) { EXPECT_FALSE(data.dimensions_in_condition().has_field()); EXPECT_EQ(data.bucket_info_size(), 2); EXPECT_EQ(data.bucket_info(0).duration_nanos(), 9); - EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs); - EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs); + EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs); EXPECT_EQ(data.bucket_info(1).duration_nanos(), 30); - EXPECT_EQ(data.bucket_info(1).start_bucket_nanos(), bucketStartTimeNs + bucketSizeNs); - EXPECT_EQ(data.bucket_info(1).end_bucket_nanos(), bucketStartTimeNs + 2 * bucketSizeNs); + EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(), bucketStartTimeNs + 2 * bucketSizeNs); data = metrics.data(1); EXPECT_FALSE(data.dimensions_in_what().has_field()); @@ -543,11 +543,11 @@ TEST(DimensionInConditionE2eTest, TestDurationMetricNoLink) { android::util::SYNC_STATE_CHANGED, 111, "App1"); EXPECT_EQ(data.bucket_info_size(), 2); EXPECT_EQ(data.bucket_info(0).duration_nanos(), 500 - 201 + bucketSizeNs - 600); - EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs); - EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs); + EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs); EXPECT_EQ(data.bucket_info(1).duration_nanos(), 300); - EXPECT_EQ(data.bucket_info(1).start_bucket_nanos(), bucketStartTimeNs + bucketSizeNs); - EXPECT_EQ(data.bucket_info(1).end_bucket_nanos(), bucketStartTimeNs + 2 * bucketSizeNs); + EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(), bucketStartTimeNs + 2 * bucketSizeNs); data = metrics.data(2); EXPECT_FALSE(data.dimensions_in_what().has_field()); @@ -555,11 +555,11 @@ TEST(DimensionInConditionE2eTest, TestDurationMetricNoLink) { android::util::SYNC_STATE_CHANGED, 333, "App2"); EXPECT_EQ(data.bucket_info_size(), 2); EXPECT_EQ(data.bucket_info(0).duration_nanos(), 500 - 401 + bucketSizeNs - 600); - EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs); - EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs); + EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs); EXPECT_EQ(data.bucket_info(1).duration_nanos(), 700); - EXPECT_EQ(data.bucket_info(1).start_bucket_nanos(), bucketStartTimeNs + bucketSizeNs); - EXPECT_EQ(data.bucket_info(1).end_bucket_nanos(), bucketStartTimeNs + 2 * bucketSizeNs); + EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(), bucketStartTimeNs + 2 * bucketSizeNs); } } @@ -595,7 +595,7 @@ StatsdConfig CreateDurationMetricConfigWithLink(DurationMetric::AggregationType addPredicateToPredicateCombination(isSyncingPredicate, combinationPredicate); auto metric = config.add_duration_metric(); - metric->set_bucket(ONE_MINUTE); + metric->set_bucket(FIVE_MINUTES); metric->set_id(StringToId("AppInBackgroundMetric")); metric->set_what(isInBackgroundPredicate.id()); metric->set_condition(combinationPredicate->id()); @@ -693,8 +693,8 @@ TEST(DimensionInConditionE2eTest, TestDurationMetricWithLink) { EXPECT_FALSE(data.dimensions_in_condition().has_field()); EXPECT_EQ(data.bucket_info_size(), 1); EXPECT_EQ(data.bucket_info(0).duration_nanos(), 9); - EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs); - EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs); + EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs); data = metrics.data(1); EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1); @@ -703,11 +703,11 @@ TEST(DimensionInConditionE2eTest, TestDurationMetricWithLink) { android::util::SYNC_STATE_CHANGED, 111, "App1"); EXPECT_EQ(data.bucket_info_size(), 2); EXPECT_EQ(data.bucket_info(0).duration_nanos(), bucketSizeNs - 201); - EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs); - EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs); + EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs); EXPECT_EQ(data.bucket_info(1).duration_nanos(), 100); - EXPECT_EQ(data.bucket_info(1).start_bucket_nanos(), bucketStartTimeNs + bucketSizeNs); - EXPECT_EQ(data.bucket_info(1).end_bucket_nanos(), bucketStartTimeNs + 2 * bucketSizeNs); + EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(), bucketStartTimeNs + 2 * bucketSizeNs); data = metrics.data(2); EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1); @@ -716,11 +716,11 @@ TEST(DimensionInConditionE2eTest, TestDurationMetricWithLink) { android::util::SYNC_STATE_CHANGED, 333, "App2"); EXPECT_EQ(data.bucket_info_size(), 2); EXPECT_EQ(data.bucket_info(0).duration_nanos(), bucketSizeNs - 401); - EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs); - EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs); + EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs); EXPECT_EQ(data.bucket_info(1).duration_nanos(), 700); - EXPECT_EQ(data.bucket_info(1).start_bucket_nanos(), bucketStartTimeNs + bucketSizeNs); - EXPECT_EQ(data.bucket_info(1).end_bucket_nanos(), bucketStartTimeNs + 2 * bucketSizeNs); + EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(), bucketStartTimeNs + 2 * bucketSizeNs); } } @@ -730,4 +730,4 @@ GTEST_LOG_(INFO) << "This test does nothing.\n"; } // namespace statsd } // namespace os -} // namespace android
\ No newline at end of file +} // namespace android diff --git a/cmds/statsd/tests/e2e/GaugeMetric_e2e_test.cpp b/cmds/statsd/tests/e2e/GaugeMetric_e2e_test.cpp index 674d810a9ea9..3843e0a3c67d 100644 --- a/cmds/statsd/tests/e2e/GaugeMetric_e2e_test.cpp +++ b/cmds/statsd/tests/e2e/GaugeMetric_e2e_test.cpp @@ -53,7 +53,7 @@ StatsdConfig CreateStatsdConfigForPushedEvent() { fieldMatcher->add_child()->set_field(7); // activity_start_msec(int64) *gaugeMetric->mutable_dimensions_in_what() = CreateDimensions(android::util::APP_START_CHANGED, {1 /* uid field */ }); - gaugeMetric->set_bucket(ONE_MINUTE); + gaugeMetric->set_bucket(FIVE_MINUTES); auto links = gaugeMetric->add_links(); links->set_condition(isInBackgroundPredicate.id()); @@ -161,21 +161,21 @@ TEST(GaugeMetricE2eTest, TestMultipleFieldsForPushedEvent) { EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs); EXPECT_EQ(data.bucket_info(0).atom(0).app_start_changed().type(), AppStartChanged::HOT); EXPECT_EQ(data.bucket_info(0).atom(0).app_start_changed().activity_name(), "activity_name2"); - EXPECT_EQ(data.bucket_info(0).atom(0).app_start_changed().activity_start_msec(), 102L); + EXPECT_EQ(data.bucket_info(0).atom(0).app_start_changed().activity_start_millis(), 102L); EXPECT_EQ(data.bucket_info(1).atom_size(), 1); EXPECT_EQ(data.bucket_info(1).start_bucket_nanos(), bucketStartTimeNs + bucketSizeNs); EXPECT_EQ(data.bucket_info(1).end_bucket_nanos(), bucketStartTimeNs + 2 * bucketSizeNs); EXPECT_EQ(data.bucket_info(1).atom(0).app_start_changed().type(), AppStartChanged::WARM); EXPECT_EQ(data.bucket_info(1).atom(0).app_start_changed().activity_name(), "activity_name4"); - EXPECT_EQ(data.bucket_info(1).atom(0).app_start_changed().activity_start_msec(), 104L); + EXPECT_EQ(data.bucket_info(1).atom(0).app_start_changed().activity_start_millis(), 104L); EXPECT_EQ(data.bucket_info(2).atom_size(), 1); EXPECT_EQ(data.bucket_info(2).start_bucket_nanos(), bucketStartTimeNs + 2 * bucketSizeNs); EXPECT_EQ(data.bucket_info(2).end_bucket_nanos(), bucketStartTimeNs + 3 * bucketSizeNs); EXPECT_EQ(data.bucket_info(2).atom(0).app_start_changed().type(), AppStartChanged::COLD); EXPECT_EQ(data.bucket_info(2).atom(0).app_start_changed().activity_name(), "activity_name5"); - EXPECT_EQ(data.bucket_info(2).atom(0).app_start_changed().activity_start_msec(), 105L); + EXPECT_EQ(data.bucket_info(2).atom(0).app_start_changed().activity_start_millis(), 105L); data = gaugeMetrics.data(1); @@ -189,7 +189,7 @@ TEST(GaugeMetricE2eTest, TestMultipleFieldsForPushedEvent) { EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + 3 * bucketSizeNs); EXPECT_EQ(data.bucket_info(0).atom(0).app_start_changed().type(), AppStartChanged::COLD); EXPECT_EQ(data.bucket_info(0).atom(0).app_start_changed().activity_name(), "activity_name7"); - EXPECT_EQ(data.bucket_info(0).atom(0).app_start_changed().activity_start_msec(), 201L); + EXPECT_EQ(data.bucket_info(0).atom(0).app_start_changed().activity_start_millis(), 201L); } #else @@ -198,4 +198,4 @@ GTEST_LOG_(INFO) << "This test does nothing.\n"; } // namespace statsd } // namespace os -} // namespace android
\ No newline at end of file +} // namespace android diff --git a/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp b/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp index d00518147bfe..1b51780a59bc 100644 --- a/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp +++ b/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp @@ -71,7 +71,7 @@ StatsdConfig CreateStatsdConfig() { // The metric is dimensioning by uid only. *countMetric->mutable_dimensions_in_what() = CreateDimensions(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, {1}); - countMetric->set_bucket(ONE_MINUTE); + countMetric->set_bucket(FIVE_MINUTES); // Links between crash atom and condition of app is in syncing. auto links = countMetric->add_links(); @@ -337,4 +337,4 @@ GTEST_LOG_(INFO) << "This test does nothing.\n"; } // namespace statsd } // namespace os -} // namespace android
\ No newline at end of file +} // namespace android diff --git a/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp b/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp index 3b25694b6517..efdab9822984 100644 --- a/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp +++ b/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp @@ -56,7 +56,7 @@ StatsdConfig CreateStatsdConfig(DurationMetric::AggregationType aggregationType) *durationMetric->mutable_dimensions_in_what() = CreateAttributionUidDimensions( android::util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); - durationMetric->set_bucket(ONE_MINUTE); + durationMetric->set_bucket(FIVE_MINUTES); return config; } @@ -315,9 +315,9 @@ TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForMaxDuration3) android::util::WAKELOCK_STATE_CHANGED, 111); // The last wakelock holding spans 4 buckets. EXPECT_EQ((unsigned long long)data.bucket_info(1).duration_nanos(), 3 * bucketSizeNs); - EXPECT_EQ((unsigned long long)data.bucket_info(1).start_bucket_nanos(), + EXPECT_EQ((unsigned long long)data.bucket_info(1).start_bucket_elapsed_nanos(), bucketStartTimeNs + 5 * bucketSizeNs); - EXPECT_EQ((unsigned long long)data.bucket_info(1).end_bucket_nanos(), + EXPECT_EQ((unsigned long long)data.bucket_info(1).end_bucket_elapsed_nanos(), bucketStartTimeNs + 6 * bucketSizeNs); } @@ -327,4 +327,4 @@ GTEST_LOG_(INFO) << "This test does nothing.\n"; } // namespace statsd } // namespace os -} // namespace android
\ No newline at end of file +} // namespace android diff --git a/cmds/statsd/tests/guardrail/StatsdStats_test.cpp b/cmds/statsd/tests/guardrail/StatsdStats_test.cpp index a1343002405b..bd114439b83c 100644 --- a/cmds/statsd/tests/guardrail/StatsdStats_test.cpp +++ b/cmds/statsd/tests/guardrail/StatsdStats_test.cpp @@ -225,7 +225,7 @@ TEST(StatsdStatsTest, TestAtomLog) { bool dropboxAtomGood = false; for (const auto& atomStats : report.atom_stats()) { - if (atomStats.tag() == android::util::SENSOR_STATE_CHANGED && atomStats.count() == 2) { + if (atomStats.tag() == android::util::SENSOR_STATE_CHANGED && atomStats.count() == 3) { sensorAtomGood = true; } if (atomStats.tag() == android::util::DROPBOX_ERROR_CHANGED && atomStats.count() == 1) { diff --git a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp index 1e71b73ca9fa..20ddbe9f0e38 100644 --- a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp @@ -48,12 +48,15 @@ TEST(CountMetricProducerTest, TestNonDimensionalEvents) { metric.set_bucket(ONE_MINUTE); LogEvent event1(tagId, bucketStartTimeNs + 1); + event1.init(); LogEvent event2(tagId, bucketStartTimeNs + 2); + event2.init(); sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); 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); @@ -76,6 +79,8 @@ TEST(CountMetricProducerTest, TestNonDimensionalEvents) { // 1 matched event happens in bucket 2. LogEvent event3(tagId, bucketStartTimeNs + bucketSizeNs + 2); + event3.init(); + countProducer.onMatchedLogEvent(1 /*log matcher index*/, event3); countProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1); EXPECT_EQ(1UL, countProducer.mPastBuckets.size()); @@ -106,11 +111,15 @@ TEST(CountMetricProducerTest, TestEventsWithNonSlicedCondition) { metric.set_condition(StringToId("SCREEN_ON")); LogEvent event1(1, bucketStartTimeNs + 1); + event1.init(); + LogEvent event2(1, bucketStartTimeNs + 10); + event2.init(); 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); @@ -172,6 +181,7 @@ TEST(CountMetricProducerTest, TestEventsWithSlicedCondition) { CountMetricProducer countProducer(kConfigKey, metric, 1 /*condition tracker index*/, wizard, bucketStartTimeNs); + countProducer.setBucketSize(60 * NS_PER_SEC); countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1); countProducer.flushIfNeededLocked(bucketStartTimeNs + 1); @@ -210,6 +220,8 @@ TEST(CountMetricProducerTest, TestEventWithAppUpgrade) { sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); CountMetricProducer countProducer(kConfigKey, metric, -1 /* no condition */, wizard, bucketStartTimeNs); + countProducer.setBucketSize(60 * NS_PER_SEC); + sp<AnomalyTracker> anomalyTracker = countProducer.addAnomalyTracker(alert); EXPECT_TRUE(anomalyTracker != nullptr); @@ -267,6 +279,7 @@ TEST(CountMetricProducerTest, TestEventWithAppUpgradeInNextBucket) { sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); CountMetricProducer countProducer(kConfigKey, metric, -1 /* no condition */, wizard, bucketStartTimeNs); + countProducer.setBucketSize(60 * NS_PER_SEC); // Bucket is flushed yet. countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1); @@ -322,16 +335,25 @@ TEST(CountMetricProducerTest, TestAnomalyDetectionUnSliced) { sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); CountMetricProducer countProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, bucketStartTimeNs); + countProducer.setBucketSize(60 * NS_PER_SEC); + sp<AnomalyTracker> anomalyTracker = countProducer.addAnomalyTracker(alert); int tagId = 1; LogEvent event1(tagId, bucketStartTimeNs + 1); + event1.init(); LogEvent event2(tagId, bucketStartTimeNs + 2); + event2.init(); LogEvent event3(tagId, bucketStartTimeNs + 2 * bucketSizeNs + 1); + event3.init(); LogEvent event4(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 1); + event4.init(); LogEvent event5(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 2); + event5.init(); LogEvent event6(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 3); + event6.init(); LogEvent event7(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 2 * NS_PER_SEC); + event7.init(); // Two events in bucket #0. countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1); @@ -355,13 +377,13 @@ TEST(CountMetricProducerTest, TestAnomalyDetectionUnSliced) { EXPECT_EQ(3L, countProducer.mCurrentSlicedCounter->begin()->second); // Anomaly at event 6 is within refractory period. The alarm is at event 5 timestamp not event 6 EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), - event5.GetTimestampNs() / NS_PER_SEC + refPeriodSec); + event5.GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec); countProducer.onMatchedLogEvent(1 /*log matcher index*/, event7); EXPECT_EQ(1UL, countProducer.mCurrentSlicedCounter->size()); EXPECT_EQ(4L, countProducer.mCurrentSlicedCounter->begin()->second); EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), - event7.GetTimestampNs() / NS_PER_SEC + refPeriodSec); + event7.GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec); } } // namespace statsd diff --git a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp index 23e15f78a96c..79695967a6dd 100644 --- a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp @@ -51,12 +51,15 @@ TEST(DurationMetricTrackerTest, TestNoCondition) { int tagId = 1; LogEvent event1(tagId, bucketStartTimeNs + 1); + event1.init(); LogEvent event2(tagId, bucketStartTimeNs + bucketSizeNs + 2); + event2.init(); FieldMatcher dimensions; 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); @@ -86,14 +89,20 @@ TEST(DurationMetricTrackerTest, TestNonSlicedCondition) { int tagId = 1; LogEvent event1(tagId, bucketStartTimeNs + 1); + event1.init(); LogEvent event2(tagId, bucketStartTimeNs + 2); + event2.init(); LogEvent event3(tagId, bucketStartTimeNs + bucketSizeNs + 1); + event3.init(); LogEvent event4(tagId, bucketStartTimeNs + bucketSizeNs + 3); + event4.init(); FieldMatcher dimensions; DurationMetricProducer durationProducer( kConfigKey, metric, 0 /* condition index */, 1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs); + durationProducer.setBucketSize(60 * NS_PER_SEC); + EXPECT_FALSE(durationProducer.mCondition); EXPECT_FALSE(durationProducer.isConditionSliced()); @@ -143,8 +152,11 @@ TEST(DurationMetricTrackerTest, TestSumDurationWithUpgrade) { DurationMetricProducer durationProducer( kConfigKey, metric, -1 /* no condition */, 1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs); + durationProducer.setBucketSize(60 * NS_PER_SEC); - durationProducer.onMatchedLogEvent(1 /* start index*/, LogEvent(tagId, startTimeNs)); + LogEvent start_event(tagId, startTimeNs); + start_event.init(); + durationProducer.onMatchedLogEvent(1 /* start index*/, start_event); EXPECT_EQ(0UL, durationProducer.mPastBuckets.size()); EXPECT_EQ(bucketStartTimeNs, durationProducer.mCurrentBucketStartTimeNs); @@ -158,7 +170,9 @@ TEST(DurationMetricTrackerTest, TestSumDurationWithUpgrade) { EXPECT_EQ(eventUpgradeTimeNs, durationProducer.mCurrentBucketStartTimeNs); // We skip ahead one bucket, so we fill in the first two partial buckets and one full bucket. - durationProducer.onMatchedLogEvent(2 /* stop index*/, LogEvent(tagId, endTimeNs)); + LogEvent end_event(tagId, endTimeNs); + end_event.init(); + durationProducer.onMatchedLogEvent(2 /* stop index*/, end_event); buckets = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; EXPECT_EQ(3UL, buckets.size()); EXPECT_EQ(eventUpgradeTimeNs, buckets[1].mBucketStartNs); @@ -193,8 +207,11 @@ TEST(DurationMetricTrackerTest, TestSumDurationWithUpgradeInFollowingBucket) { DurationMetricProducer durationProducer( kConfigKey, metric, -1 /* no condition */, 1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs); + durationProducer.setBucketSize(60 * NS_PER_SEC); - durationProducer.onMatchedLogEvent(1 /* start index*/, LogEvent(tagId, startTimeNs)); + LogEvent start_event(tagId, startTimeNs); + start_event.init(); + durationProducer.onMatchedLogEvent(1 /* start index*/, start_event); EXPECT_EQ(0UL, durationProducer.mPastBuckets.size()); EXPECT_EQ(bucketStartTimeNs, durationProducer.mCurrentBucketStartTimeNs); @@ -211,7 +228,9 @@ TEST(DurationMetricTrackerTest, TestSumDurationWithUpgradeInFollowingBucket) { EXPECT_EQ(eventUpgradeTimeNs, durationProducer.mCurrentBucketStartTimeNs); // We skip ahead one bucket, so we fill in the first two partial buckets and one full bucket. - durationProducer.onMatchedLogEvent(2 /* stop index*/, LogEvent(tagId, endTimeNs)); + LogEvent end_event(tagId, endTimeNs); + end_event.init(); + durationProducer.onMatchedLogEvent(2 /* stop index*/, end_event); buckets = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; EXPECT_EQ(3UL, buckets.size()); EXPECT_EQ(eventUpgradeTimeNs, buckets[2].mBucketStartNs); @@ -242,13 +261,19 @@ TEST(DurationMetricTrackerTest, TestSumDurationAnomalyWithUpgrade) { DurationMetricProducer durationProducer( kConfigKey, metric, -1 /* no condition */, 1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs); + durationProducer.setBucketSize(60 * NS_PER_SEC); + sp<AnomalyTracker> anomalyTracker = durationProducer.addAnomalyTracker(alert); EXPECT_TRUE(anomalyTracker != nullptr); - durationProducer.onMatchedLogEvent(1 /* start index*/, LogEvent(tagId, startTimeNs)); + LogEvent start_event(tagId, startTimeNs); + start_event.init(); + durationProducer.onMatchedLogEvent(1 /* start index*/, start_event); durationProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1); // We skip ahead one bucket, so we fill in the first two partial buckets and one full bucket. - durationProducer.onMatchedLogEvent(2 /* stop index*/, LogEvent(tagId, endTimeNs)); + LogEvent end_event(tagId, endTimeNs); + end_event.init(); + durationProducer.onMatchedLogEvent(2 /* stop index*/, end_event); EXPECT_EQ(bucketStartTimeNs + bucketSizeNs - startTimeNs, (uint64_t)anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY)); @@ -275,8 +300,11 @@ TEST(DurationMetricTrackerTest, TestMaxDurationWithUpgrade) { DurationMetricProducer durationProducer( kConfigKey, metric, -1 /* no condition */, 1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs); + durationProducer.setBucketSize(60 * NS_PER_SEC); - durationProducer.onMatchedLogEvent(1 /* start index*/, LogEvent(tagId, startTimeNs)); + LogEvent start_event(tagId, startTimeNs); + start_event.init(); + durationProducer.onMatchedLogEvent(1 /* start index*/, start_event); EXPECT_EQ(0UL, durationProducer.mPastBuckets.size()); EXPECT_EQ(bucketStartTimeNs, durationProducer.mCurrentBucketStartTimeNs); @@ -285,7 +313,9 @@ TEST(DurationMetricTrackerTest, TestMaxDurationWithUpgrade) { EXPECT_EQ(eventUpgradeTimeNs, durationProducer.mCurrentBucketStartTimeNs); // We skip ahead one bucket, so we fill in the first two partial buckets and one full bucket. - durationProducer.onMatchedLogEvent(2 /* stop index*/, LogEvent(tagId, endTimeNs)); + LogEvent end_event(tagId, endTimeNs); + end_event.init(); + durationProducer.onMatchedLogEvent(2 /* stop index*/, end_event); EXPECT_EQ(0UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); durationProducer.flushIfNeededLocked(bucketStartTimeNs + 3 * bucketSizeNs + 1); @@ -318,8 +348,11 @@ TEST(DurationMetricTrackerTest, TestMaxDurationWithUpgradeInNextBucket) { DurationMetricProducer durationProducer( kConfigKey, metric, -1 /* no condition */, 1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs); + durationProducer.setBucketSize(60 * NS_PER_SEC); - durationProducer.onMatchedLogEvent(1 /* start index*/, LogEvent(tagId, startTimeNs)); + LogEvent start_event(tagId, startTimeNs); + start_event.init(); + durationProducer.onMatchedLogEvent(1 /* start index*/, start_event); EXPECT_EQ(0UL, durationProducer.mPastBuckets.size()); EXPECT_EQ(bucketStartTimeNs, durationProducer.mCurrentBucketStartTimeNs); @@ -328,7 +361,9 @@ TEST(DurationMetricTrackerTest, TestMaxDurationWithUpgradeInNextBucket) { EXPECT_EQ(eventUpgradeTimeNs, durationProducer.mCurrentBucketStartTimeNs); // Stop occurs in the same partial bucket as created for the app upgrade. - durationProducer.onMatchedLogEvent(2 /* stop index*/, LogEvent(tagId, endTimeNs)); + LogEvent end_event(tagId, endTimeNs); + end_event.init(); + durationProducer.onMatchedLogEvent(2 /* stop index*/, end_event); EXPECT_EQ(0UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); EXPECT_EQ(eventUpgradeTimeNs, durationProducer.mCurrentBucketStartTimeNs); diff --git a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp index 26f7c2669f15..0eb8ce2603bd 100644 --- a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp @@ -67,6 +67,7 @@ TEST(GaugeMetricProducerTest, TestNoCondition) { GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, tagId, bucketStartTimeNs, pullerManager); + gaugeProducer.setBucketSize(60 * NS_PER_SEC); vector<shared_ptr<LogEvent>> allData; allData.clear(); @@ -144,6 +145,7 @@ TEST(GaugeMetricProducerTest, TestPushedEventsWithUpgrade) { GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, -1 /* -1 means no pulling */, bucketStartTimeNs, pullerManager); + gaugeProducer.setBucketSize(60 * NS_PER_SEC); sp<AnomalyTracker> anomalyTracker = gaugeProducer.addAnomalyTracker(alert); EXPECT_TRUE(anomalyTracker != nullptr); @@ -225,6 +227,7 @@ TEST(GaugeMetricProducerTest, TestPulledWithUpgrade) { GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, tagId, bucketStartTimeNs, pullerManager); + gaugeProducer.setBucketSize(60 * NS_PER_SEC); vector<shared_ptr<LogEvent>> allData; shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 1); @@ -292,6 +295,7 @@ TEST(GaugeMetricProducerTest, TestWithCondition) { GaugeMetricProducer gaugeProducer(kConfigKey, metric, 1, wizard, tagId, bucketStartTimeNs, pullerManager); + gaugeProducer.setBucketSize(60 * NS_PER_SEC); gaugeProducer.onConditionChanged(true, bucketStartTimeNs + 8); EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); @@ -350,6 +354,7 @@ TEST(GaugeMetricProducerTest, TestAnomalyDetection) { gaugeFieldMatcher->add_child()->set_field(2); GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, tagId, bucketStartTimeNs, pullerManager); + gaugeProducer.setBucketSize(60 * NS_PER_SEC); Alert alert; alert.set_id(101); @@ -387,7 +392,7 @@ TEST(GaugeMetricProducerTest, TestAnomalyDetection) { .mFields->begin() ->mValue.int_value); EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), - event2->GetTimestampNs() / NS_PER_SEC + refPeriodSec); + event2->GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec); std::shared_ptr<LogEvent> event3 = std::make_shared<LogEvent>(tagId, bucketStartTimeNs + 2 * bucketSizeNs + 10); @@ -402,7 +407,7 @@ TEST(GaugeMetricProducerTest, TestAnomalyDetection) { .mFields->begin() ->mValue.int_value); EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), - event2->GetTimestampNs() / NS_PER_SEC + refPeriodSec); + event2->GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec); // The event4 does not have the gauge field. Thus the current bucket value is 0. std::shared_ptr<LogEvent> event4 = diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp index 325a372d8056..ce4fa3278493 100644 --- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp @@ -66,6 +66,7 @@ TEST(ValueMetricProducerTest, TestNonDimensionalEvents) { ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, tagId, bucketStartTimeNs, pullerManager); + valueProducer.setBucketSize(60 * NS_PER_SEC); vector<shared_ptr<LogEvent>> allData; allData.clear(); @@ -79,6 +80,8 @@ TEST(ValueMetricProducerTest, TestNonDimensionalEvents) { // has one slice EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second; + valueProducer.setBucketSize(60 * NS_PER_SEC); + // startUpdated:true tainted:0 sum:0 start:11 EXPECT_EQ(true, curInterval.startUpdated); EXPECT_EQ(0, curInterval.tainted); @@ -162,7 +165,7 @@ TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition) { ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, tagId, bucketStartTimeNs, pullerManager); - + valueProducer.setBucketSize(60 * NS_PER_SEC); valueProducer.onConditionChanged(true, bucketStartTimeNs + 8); // has one slice @@ -215,6 +218,7 @@ TEST(ValueMetricProducerTest, TestPushedEventsWithUpgrade) { make_shared<StrictMock<MockStatsPullerManager>>(); ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, -1, bucketStartTimeNs, pullerManager); + valueProducer.setBucketSize(60 * NS_PER_SEC); shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10); event1->write(1); @@ -269,6 +273,7 @@ TEST(ValueMetricProducerTest, TestPulledValueWithUpgrade) { })); ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, tagId, bucketStartTimeNs, pullerManager); + valueProducer.setBucketSize(60 * NS_PER_SEC); vector<shared_ptr<LogEvent>> allData; allData.clear(); @@ -311,6 +316,7 @@ TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition) { ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, -1, bucketStartTimeNs, pullerManager); + valueProducer.setBucketSize(60 * NS_PER_SEC); shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10); event1->write(1); @@ -357,6 +363,8 @@ TEST(ValueMetricProducerTest, TestAnomalyDetection) { sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, -1 /*not pulled*/, bucketStartTimeNs); + valueProducer.setBucketSize(60 * NS_PER_SEC); + sp<AnomalyTracker> anomalyTracker = valueProducer.addAnomalyTracker(alert); @@ -406,16 +414,16 @@ TEST(ValueMetricProducerTest, TestAnomalyDetection) { valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event4); // Anomaly at event 4 since Value sum == 131 > 130! EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), - event4->GetTimestampNs() / NS_PER_SEC + refPeriodSec); + event4->GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec); valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event5); // Event 5 is within 3 sec refractory period. Thus last alarm timestamp is still event4. EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), - event4->GetTimestampNs() / NS_PER_SEC + refPeriodSec); + event4->GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec); valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event6); // Anomaly at event 6 since Value sum == 160 > 130 and after refractory period. EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), - event6->GetTimestampNs() / NS_PER_SEC + refPeriodSec); + event6->GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec); } } // namespace statsd diff --git a/cmds/statsd/tests/statsd_test_util.cpp b/cmds/statsd/tests/statsd_test_util.cpp index d3a89617e921..b7acef75fafb 100644 --- a/cmds/statsd/tests/statsd_test_util.cpp +++ b/cmds/statsd/tests/statsd_test_util.cpp @@ -406,7 +406,7 @@ AttributionNode CreateAttribution(const int& uid, const string& tag) { void sortLogEventsByTimestamp(std::vector<std::unique_ptr<LogEvent>> *events) { std::sort(events->begin(), events->end(), [](const std::unique_ptr<LogEvent>& a, const std::unique_ptr<LogEvent>& b) { - return a->GetTimestampNs() < b->GetTimestampNs(); + return a->GetElapsedTimestampNs() < b->GetElapsedTimestampNs(); }); } diff --git a/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/DisplayProtoUtils.java b/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/DisplayProtoUtils.java index 03e5fef5d717..b6b16e40a4b2 100644 --- a/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/DisplayProtoUtils.java +++ b/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/DisplayProtoUtils.java @@ -32,9 +32,9 @@ public class DisplayProtoUtils { for (StatsLog.ConfigMetricsReport report : reports.getReportsList()) { sb.append("StatsLogReport size: ").append(report.getMetricsCount()).append("\n"); - sb.append("Last report time:").append(getDateStr(report.getLastReportNanos())). + sb.append("Last report time:").append(getDateStr(report.getLastReportElapsedNanos())). append("\n"); - sb.append("Current report time:").append(getDateStr(report.getCurrentReportNanos())). + sb.append("Current report time:").append(getDateStr(report.getCurrentReportElapsedNanos())). append("\n"); for (StatsLog.StatsLogReport log : report.getMetricsList()) { sb.append("\n\n"); @@ -109,8 +109,8 @@ public class DisplayProtoUtils { } for (StatsLog.DurationBucketInfo info : duration.getBucketInfoList()) { - sb.append("\t[").append(getDateStr(info.getStartBucketNanos())).append("-") - .append(getDateStr(info.getEndBucketNanos())).append("] -> ") + sb.append("\t[").append(getDateStr(info.getStartBucketElapsedNanos())).append("-") + .append(getDateStr(info.getEndBucketElapsedNanos())).append("] -> ") .append(info.getDurationNanos()).append(" ns\n"); } } @@ -121,7 +121,7 @@ public class DisplayProtoUtils { StatsLog.StatsLogReport.EventMetricDataWrapper eventMetricDataWrapper = log.getEventMetrics(); for (StatsLog.EventMetricData event : eventMetricDataWrapper.getDataList()) { - sb.append(getDateStr(event.getTimestampNanos())).append(": "); + sb.append(getDateStr(event.getElapsedTimestampNanos())).append(": "); sb.append(event.getAtom().getPushedCase().toString()).append("\n"); } } @@ -141,8 +141,8 @@ public class DisplayProtoUtils { } for (StatsLog.CountBucketInfo info : count.getBucketInfoList()) { - sb.append("\t[").append(getDateStr(info.getStartBucketNanos())).append("-") - .append(getDateStr(info.getEndBucketNanos())).append("] -> ") + sb.append("\t[").append(getDateStr(info.getStartBucketElapsedNanos())).append("-") + .append(getDateStr(info.getEndBucketElapsedNanos())).append("] -> ") .append(info.getCount()).append("\n"); } } diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/DisplayProtoUtils.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/DisplayProtoUtils.java index 87b82c261bf1..d55f3f31fd9f 100644 --- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/DisplayProtoUtils.java +++ b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/DisplayProtoUtils.java @@ -36,9 +36,9 @@ public class DisplayProtoUtils { int numMetrics = 0; for (StatsLog.ConfigMetricsReport report : reports.getReportsList()) { sb.append("StatsLogReport size: ").append(report.getMetricsCount()).append("\n"); - sb.append("Last report time:").append(getDateStr(report.getLastReportNanos())). + sb.append("Last report time:").append(getDateStr(report.getLastReportElapsedNanos())). append("\n"); - sb.append("Current report time:").append(getDateStr(report.getCurrentReportNanos())). + sb.append("Current report time:").append(getDateStr(report.getCurrentReportElapsedNanos())). append("\n"); for (StatsLog.StatsLogReport log : report.getMetricsList()) { numMetrics++; @@ -120,8 +120,8 @@ public class DisplayProtoUtils { } for (StatsLog.DurationBucketInfo info : duration.getBucketInfoList()) { - sb.append("\t[").append(getDateStr(info.getStartBucketNanos())).append("-") - .append(getDateStr(info.getEndBucketNanos())).append("] -> ") + sb.append("\t[").append(getDateStr(info.getStartBucketElapsedNanos())).append("-") + .append(getDateStr(info.getEndBucketElapsedNanos())).append("] -> ") .append(info.getDurationNanos()).append(" ns\n"); } } @@ -132,7 +132,7 @@ public class DisplayProtoUtils { StatsLog.StatsLogReport.EventMetricDataWrapper eventMetricDataWrapper = log.getEventMetrics(); for (StatsLog.EventMetricData event : eventMetricDataWrapper.getDataList()) { - sb.append(getDateStr(event.getTimestampNanos())).append(": "); + sb.append(getDateStr(event.getElapsedTimestampNanos())).append(": "); sb.append(event.getAtom().getPushedCase().toString()).append("\n"); } } @@ -152,8 +152,8 @@ public class DisplayProtoUtils { } for (StatsLog.CountBucketInfo info : count.getBucketInfoList()) { - sb.append("\t[").append(getDateStr(info.getStartBucketNanos())).append("-") - .append(getDateStr(info.getEndBucketNanos())).append("] -> ") + sb.append("\t[").append(getDateStr(info.getStartBucketElapsedNanos())).append("-") + .append(getDateStr(info.getEndBucketElapsedNanos())).append("] -> ") .append(info.getCount()).append("\n"); } } diff --git a/core/java/Android.bp b/core/java/Android.bp index f7c5c57a07e4..fb27f74211fb 100644 --- a/core/java/Android.bp +++ b/core/java/Android.bp @@ -7,33 +7,3 @@ filegroup { name: "IDropBoxManagerService.aidl", srcs: ["com/android/internal/os/IDropBoxManagerService.aidl"], } - -// only used by key_store_service -cc_library_shared { - name: "libkeystore_aidl", - srcs: ["android/security/IKeystoreService.aidl", - "android/security/IConfirmationPromptCallback.aidl"], - aidl: { - export_aidl_headers: true, - include_dirs: [ - "frameworks/base/core/java/", - "system/security/keystore/", - ], - }, - shared_libs: [ - "libbinder", - "libcutils", - "libhardware", - "libhidlbase", - "libhidltransport", - "libhwbinder", - "liblog", - "libkeystore_parcelables", - "libselinux", - "libutils", - ], - export_shared_lib_headers: [ - "libbinder", - "libkeystore_parcelables", - ], -} diff --git a/core/java/android/annotation/OWNERS b/core/java/android/annotation/OWNERS new file mode 100644 index 000000000000..d6bb71b50e34 --- /dev/null +++ b/core/java/android/annotation/OWNERS @@ -0,0 +1 @@ +tnorbye@google.com diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index bcd88fee6720..8a9efe876321 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -17,6 +17,7 @@ package android.app; import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS; + import static java.lang.Character.MIN_VALUE; import android.annotation.CallSuper; @@ -135,6 +136,7 @@ import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.List; @@ -1898,7 +1900,7 @@ public class Activity extends ContextThemeWrapper if (isFinishing()) { if (mAutoFillResetNeeded) { - getAutofillManager().onActivityFinished(); + getAutofillManager().onActivityFinishing(); } else if (mIntent != null && mIntent.hasExtra(AutofillManager.EXTRA_RESTORE_SESSION_TOKEN)) { // Activity was launched when user tapped a link in the Autofill Save UI - since @@ -7162,8 +7164,8 @@ public class Activity extends ContextThemeWrapper 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) @@ -7689,6 +7691,9 @@ public class Activity extends ContextThemeWrapper } } } + if (android.view.autofill.Helper.sVerbose) { + Log.v(TAG, "autofillClientGetViewVisibility(): " + Arrays.toString(visible)); + } return visible; } @@ -7751,7 +7756,6 @@ public class Activity extends ContextThemeWrapper * @param disable {@code true} to disable preview screenshots; {@code false} otherwise. * @hide */ - @SystemApi public void setDisablePreviewScreenshots(boolean disable) { try { ActivityManager.getService().setDisablePreviewScreenshots(mToken, disable); diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index ae47a684f6ab..03faeeeb91a1 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -44,6 +44,7 @@ import android.graphics.GraphicBuffer; 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 @@ public class ActivityManager { } /** + * 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 fee58274a5fc..d5430f05d65b 100644 --- a/core/java/android/app/ActivityOptions.java +++ b/core/java/android/app/ActivityOptions.java @@ -1106,6 +1106,11 @@ public class ActivityOptions { } /** @hide */ + public void setRemoteAnimationAdapter(RemoteAnimationAdapter remoteAnimationAdapter) { + mRemoteAnimationAdapter = remoteAnimationAdapter; + } + + /** @hide */ public static ActivityOptions fromBundle(Bundle bOptions) { return bOptions != null ? new ActivityOptions(bOptions) : null; } diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index 02be00268a45..f6e5f373ba46 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -68,6 +68,7 @@ import android.os.WorkSource; import android.service.voice.IVoiceInteractionSession; import android.view.IRecentsAnimationRunner; import android.view.RemoteAnimationDefinition; +import android.view.RemoteAnimationAdapter; import com.android.internal.app.IVoiceInteractor; import com.android.internal.os.IResultReceiver; import com.android.internal.policy.IKeyguardDismissCallback; @@ -423,6 +424,8 @@ interface IActivityManager { 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 @@ interface IActivityManager { * Registers remote animations for a specific activity. */ void registerRemoteAnimations(in IBinder token, in RemoteAnimationDefinition definition); + + /** + * Registers a remote animation to be run for all activity starts from a certain package during + * a short predefined amount of time. + */ + void registerRemoteAnimationForNextActivityStart(in String packageName, + in RemoteAnimationAdapter adapter); } diff --git a/core/java/android/app/admin/SecurityLog.java b/core/java/android/app/admin/SecurityLog.java index 69ec26c275a2..faaa0043cce8 100644 --- a/core/java/android/app/admin/SecurityLog.java +++ b/core/java/android/app/admin/SecurityLog.java @@ -319,6 +319,7 @@ public class SecurityLog { * {@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/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java index 0f1c249faf83..1312a2e6b623 100644 --- a/core/java/android/app/assist/AssistStructure.java +++ b/core/java/android/app/assist/AssistStructure.java @@ -1303,6 +1303,17 @@ public class AssistStructure implements Parcelable { } /** + * @hide + */ + public void setWebDomain(@Nullable String domain) { + if (domain == null) return; + + final Uri uri = Uri.parse(domain); + mWebScheme = uri.getScheme(); + mWebDomain = uri.getHost(); + } + + /** * Returns the scheme of the HTML document represented by this view. * * <p>Typically used when the view associated with the view is a container for an HTML @@ -1889,14 +1900,7 @@ public class AssistStructure implements Parcelable { @Override public void setWebDomain(@Nullable String domain) { - if (domain == null) { - mNode.mWebScheme = null; - mNode.mWebDomain = null; - return; - } - Uri uri = Uri.parse(domain); - mNode.mWebScheme = uri.getScheme(); - mNode.mWebDomain = uri.getHost(); + mNode.setWebDomain(domain); } @Override diff --git a/core/java/android/app/backup/OWNERS b/core/java/android/app/backup/OWNERS new file mode 100644 index 000000000000..1c9a43acfa65 --- /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 9a50a009ce34..7f8c50cd4ce5 100644 --- a/core/java/android/app/servertransaction/ActivityLifecycleItem.java +++ b/core/java/android/app/servertransaction/ActivityLifecycleItem.java @@ -91,4 +91,9 @@ public abstract class ActivityLifecycleItem extends ClientTransactionItem { pw.println(prefix + "target state:" + getTargetState()); pw.println(prefix + "description: " + mDescription); } + + @Override + public void recycle() { + setDescription(null); + } } diff --git a/core/java/android/app/servertransaction/DestroyActivityItem.java b/core/java/android/app/servertransaction/DestroyActivityItem.java index 48a79f79dae1..0edcf1884f01 100644 --- a/core/java/android/app/servertransaction/DestroyActivityItem.java +++ b/core/java/android/app/servertransaction/DestroyActivityItem.java @@ -65,6 +65,7 @@ public class DestroyActivityItem extends ActivityLifecycleItem { @Override public void recycle() { + super.recycle(); mFinished = false; mConfigChanges = 0; ObjectPool.recycle(this); diff --git a/core/java/android/app/servertransaction/PauseActivityItem.java b/core/java/android/app/servertransaction/PauseActivityItem.java index 70a4755f99af..91e73cd5b1cd 100644 --- a/core/java/android/app/servertransaction/PauseActivityItem.java +++ b/core/java/android/app/servertransaction/PauseActivityItem.java @@ -102,6 +102,7 @@ public class PauseActivityItem extends ActivityLifecycleItem { @Override public void recycle() { + super.recycle(); mFinished = false; mUserLeaving = false; mConfigChanges = 0; diff --git a/core/java/android/app/servertransaction/ResumeActivityItem.java b/core/java/android/app/servertransaction/ResumeActivityItem.java index ed90f2cb1013..af2fb713e1bc 100644 --- a/core/java/android/app/servertransaction/ResumeActivityItem.java +++ b/core/java/android/app/servertransaction/ResumeActivityItem.java @@ -101,6 +101,7 @@ public class ResumeActivityItem extends ActivityLifecycleItem { @Override public void recycle() { + super.recycle(); mProcState = ActivityManager.PROCESS_STATE_UNKNOWN; mUpdateProcState = false; mIsForward = false; diff --git a/core/java/android/app/servertransaction/StopActivityItem.java b/core/java/android/app/servertransaction/StopActivityItem.java index b814d1ae1392..f955a903d649 100644 --- a/core/java/android/app/servertransaction/StopActivityItem.java +++ b/core/java/android/app/servertransaction/StopActivityItem.java @@ -72,6 +72,7 @@ public class StopActivityItem extends ActivityLifecycleItem { @Override public void recycle() { + super.recycle(); mShowWindow = false; mConfigChanges = 0; ObjectPool.recycle(this); diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java index bc7823b00f49..1dc7549e763a 100644 --- a/core/java/android/bluetooth/BluetoothAdapter.java +++ b/core/java/android/bluetooth/BluetoothAdapter.java @@ -2307,6 +2307,9 @@ public final class BluetoothAdapter { } 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 @@ public final class BluetoothAdapter { 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 000000000000..647e0d033fb7 --- /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 0e2263f773b8..656188fbdfb8 100644 --- a/core/java/android/bluetooth/BluetoothProfile.java +++ b/core/java/android/bluetooth/BluetoothProfile.java @@ -165,12 +165,19 @@ public interface BluetoothProfile { 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 76cb3f5b548e..0a0d21498032 100644 --- a/core/java/android/bluetooth/BluetoothUuid.java +++ b/core/java/android/bluetooth/BluetoothUuid.java @@ -79,6 +79,9 @@ public final class BluetoothUuid { 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/database/sqlite/SQLiteOpenHelper.java b/core/java/android/database/sqlite/SQLiteOpenHelper.java index a2991e6e9cb6..64e9e5db7c4b 100644 --- a/core/java/android/database/sqlite/SQLiteOpenHelper.java +++ b/core/java/android/database/sqlite/SQLiteOpenHelper.java @@ -58,7 +58,7 @@ public abstract class SQLiteOpenHelper { private SQLiteDatabase mDatabase; private boolean mIsInitializing; - private final SQLiteDatabase.OpenParams.Builder mOpenParamsBuilder; + private SQLiteDatabase.OpenParams.Builder mOpenParamsBuilder; /** * Create a helper object to create, open, and/or manage a database. @@ -163,8 +163,7 @@ public abstract class SQLiteOpenHelper { mName = name; mNewVersion = version; mMinimumSupportedVersion = Math.max(0, minimumSupportedVersion); - mOpenParamsBuilder = openParamsBuilder; - mOpenParamsBuilder.addOpenFlags(SQLiteDatabase.CREATE_IF_NECESSARY); + setOpenParamsBuilder(openParamsBuilder); } /** @@ -230,6 +229,30 @@ public abstract class SQLiteOpenHelper { } /** + * Sets configuration parameters that are used for opening {@link SQLiteDatabase}. + * <p>Please note that {@link SQLiteDatabase#CREATE_IF_NECESSARY} flag will always be set when + * opening the database + * + * @param openParams configuration parameters that are used for opening {@link SQLiteDatabase}. + * @throws IllegalStateException if the database is already open + */ + public void setOpenParams(@NonNull SQLiteDatabase.OpenParams openParams) { + Preconditions.checkNotNull(openParams); + synchronized (this) { + if (mDatabase != null && mDatabase.isOpen()) { + throw new IllegalStateException( + "OpenParams cannot be set after opening the database"); + } + setOpenParamsBuilder(new SQLiteDatabase.OpenParams.Builder(openParams)); + } + } + + private void setOpenParamsBuilder(SQLiteDatabase.OpenParams.Builder openParamsBuilder) { + mOpenParamsBuilder = openParamsBuilder; + mOpenParamsBuilder.addOpenFlags(SQLiteDatabase.CREATE_IF_NECESSARY); + } + + /** * Sets the maximum number of milliseconds that SQLite connection is allowed to be idle * before it is closed and removed from the pool. * diff --git a/core/java/android/hardware/OWNERS b/core/java/android/hardware/OWNERS new file mode 100644 index 000000000000..b8fea556e4c5 --- /dev/null +++ b/core/java/android/hardware/OWNERS @@ -0,0 +1,7 @@ +# Camera +per-file *Camera* = cychen@google.com +per-file *Camera* = epeev@google.com +per-file *Camera* = etalvala@google.com +per-file *Camera* = shuzhenwang@google.com +per-file *Camera* = yinchiayeh@google.com +per-file *Camera* = zhijunhe@google.com diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java index 3f8eaa902cb2..8502fc413c05 100644 --- a/core/java/android/hardware/camera2/CameraCharacteristics.java +++ b/core/java/android/hardware/camera2/CameraCharacteristics.java @@ -341,7 +341,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri */ @SuppressWarnings({"unchecked"}) public List<CaptureRequest.Key<?>> getAvailablePhysicalCameraRequestKeys() { - if (mAvailableSessionKeys == null) { + if (mAvailablePhysicalRequestKeys == null) { Object crKey = CaptureRequest.Key.class; Class<CaptureRequest.Key<?>> crKeyTyped = (Class<CaptureRequest.Key<?>>)crKey; @@ -1790,11 +1790,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * The respective value of such request key can be obtained by calling * {@link CaptureRequest.Builder#getPhysicalCameraKey }. Capture requests that contain * individual physical device requests must be built via - * {@link android.hardware.camera2.CameraDevice#createCaptureRequest(int, Set)}. - * Such extended capture requests can be passed only to - * {@link CameraCaptureSession#capture } or {@link CameraCaptureSession#captureBurst } and - * not to {@link CameraCaptureSession#setRepeatingRequest } or - * {@link CameraCaptureSession#setRepeatingBurst }.</p> + * {@link android.hardware.camera2.CameraDevice#createCaptureRequest(int, Set)}.</p> * <p><b>Optional</b> - This value may be {@code null} on some devices.</p> * <p><b>Limited capability</b> - * Present on all camera devices that report being at least {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED HARDWARE_LEVEL_LIMITED} devices in the diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java index fd285aef864f..72db33f90c20 100644 --- a/core/java/android/hardware/camera2/CameraDevice.java +++ b/core/java/android/hardware/camera2/CameraDevice.java @@ -863,11 +863,8 @@ public abstract class CameraDevice implements AutoCloseable { * request for a specific physical camera. The settings are chosen * to be the best options for the specific logical camera device. If * additional physical camera ids are passed, then they will also use the - * same settings template. Requests containing individual physical camera - * settings can be passed only to {@link CameraCaptureSession#capture} or - * {@link CameraCaptureSession#captureBurst} and not to - * {@link CameraCaptureSession#setRepeatingRequest} or - * {@link CameraCaptureSession#setRepeatingBurst}</p> + * same settings template. Clients can further modify individual camera + * settings by calling {@link CaptureRequest.Builder#setPhysicalCameraKey}.</p> * * <p>Individual physical camera settings will only be honored for camera session * that was initialiazed with corresponding physical camera id output configuration @@ -896,8 +893,8 @@ public abstract class CameraDevice implements AutoCloseable { * @see #TEMPLATE_STILL_CAPTURE * @see #TEMPLATE_VIDEO_SNAPSHOT * @see #TEMPLATE_MANUAL - * @see CaptureRequest.Builder#setKey - * @see CaptureRequest.Builder#getKey + * @see CaptureRequest.Builder#setPhysicalCameraKey + * @see CaptureRequest.Builder#getPhysicalCameraKey */ @NonNull public CaptureRequest.Builder createCaptureRequest(@RequestTemplate int templateType, diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java index 732f6a519781..e558b7e29a20 100644 --- a/core/java/android/hardware/camera2/CameraMetadata.java +++ b/core/java/android/hardware/camera2/CameraMetadata.java @@ -829,19 +829,24 @@ public abstract class CameraMetadata<TKey> { * <li>{@link CameraCharacteristics#LENS_RADIAL_DISTORTION android.lens.radialDistortion}</li> * </ul> * </li> + * <li>The SENSOR_INFO_TIMESTAMP_SOURCE of the logical device and physical devices must be + * the same.</li> * <li>The logical camera device must be LIMITED or higher device.</li> * </ul> * <p>Both the logical camera device and its underlying physical devices support the * mandatory stream combinations required for their device levels.</p> * <p>Additionally, for each guaranteed stream combination, the logical camera supports:</p> * <ul> - * <li>Replacing one logical {@link android.graphics.ImageFormat#YUV_420_888 YUV_420_888} + * <li>For each guaranteed stream combination, the logical camera supports replacing one + * logical {@link android.graphics.ImageFormat#YUV_420_888 YUV_420_888} * or raw stream with two physical streams of the same size and format, each from a * separate physical camera, given that the size and format are supported by both * physical cameras.</li> - * <li>Adding two raw streams, each from one physical camera, if the logical camera doesn't - * advertise RAW capability, but the underlying physical cameras do. This is usually - * the case when the physical cameras have different sensor sizes.</li> + * <li>If the logical camera doesn't advertise RAW capability, but the underlying physical + * cameras do, the logical camera will support guaranteed stream combinations for RAW + * capability, except that the RAW streams will be physical streams, each from a separate + * physical camera. This is usually the case when the physical cameras have different + * sensor sizes.</li> * </ul> * <p>Using physical streams in place of a logical stream of the same size and format will * not slow down the frame rate of the capture, as long as the minimum frame duration diff --git a/core/java/android/hardware/camera2/OWNERS b/core/java/android/hardware/camera2/OWNERS new file mode 100644 index 000000000000..18acfee14555 --- /dev/null +++ b/core/java/android/hardware/camera2/OWNERS @@ -0,0 +1,6 @@ +cychen@google.com +epeev@google.com +etalvala@google.com +shuzhenwang@google.com +yinchiayeh@google.com +zhijunhe@google.com diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java index b205e2c7649d..a040a09cf469 100644 --- a/core/java/android/hardware/camera2/params/OutputConfiguration.java +++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java @@ -82,11 +82,9 @@ import java.util.List; * * </ul> * - * <p>Please note that surface sharing is currently only enabled for outputs that use the - * {@link ImageFormat#PRIVATE} format. This includes surface sources like - * {@link android.view.SurfaceView}, {@link android.media.MediaRecorder}, - * {@link android.graphics.SurfaceTexture} and {@link android.media.ImageReader}, configured using - * the aforementioned format.</p> + * <p> As of {@link android.os.Build.VERSION_CODES#P Android P}, all formats can be used for + * sharing, subject to device support. On prior API levels, only {@link ImageFormat#PRIVATE} + * format may be used.</p> * * @see CameraDevice#createCaptureSessionByOutputConfigurations * diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index 166342dd4e6d..7dde2ed70698 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -112,8 +112,14 @@ public class ConnectivityManager { * <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 @@ public class ConnectivityManager { * 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 @@ public class ConnectivityManager { * 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 @@ public class ConnectivityManager { /** * 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 @@ public class ConnectivityManager { /** * 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 @@ public class ConnectivityManager { 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 6a262e2c87ca..8599f47c6245 100644 --- a/core/java/android/net/IpSecConfig.java +++ b/core/java/android/net/IpSecConfig.java @@ -218,6 +218,25 @@ public final class IpSecConfig implements Parcelable { @VisibleForTesting public IpSecConfig() {} + /** Copy constructor */ + @VisibleForTesting + public IpSecConfig(IpSecConfig c) { + mMode = c.mMode; + mSourceAddress = c.mSourceAddress; + mDestinationAddress = c.mDestinationAddress; + mNetwork = c.mNetwork; + mSpiResourceId = c.mSpiResourceId; + mEncryption = c.mEncryption; + mAuthentication = c.mAuthentication; + mAuthenticatedEncryption = c.mAuthenticatedEncryption; + mEncapType = c.mEncapType; + mEncapSocketResourceId = c.mEncapSocketResourceId; + mEncapRemotePort = c.mEncapRemotePort; + mNattKeepaliveInterval = c.mNattKeepaliveInterval; + mMarkValue = c.mMarkValue; + mMarkMask = c.mMarkMask; + } + private IpSecConfig(Parcel in) { mMode = in.readInt(); mSourceAddress = in.readString(); diff --git a/core/java/android/net/IpSecTransform.java b/core/java/android/net/IpSecTransform.java index 38759a9183f2..60e96f943401 100644 --- a/core/java/android/net/IpSecTransform.java +++ b/core/java/android/net/IpSecTransform.java @@ -84,9 +84,11 @@ public final class IpSecTransform implements AutoCloseable { @Retention(RetentionPolicy.SOURCE) public @interface EncapType {} - private IpSecTransform(Context context, IpSecConfig config) { + /** @hide */ + @VisibleForTesting + public IpSecTransform(Context context, IpSecConfig config) { mContext = context; - mConfig = config; + mConfig = new IpSecConfig(config); mResourceId = INVALID_RESOURCE_ID; } @@ -143,6 +145,18 @@ public final class IpSecTransform implements AutoCloseable { } /** + * Equals method used for testing + * + * @hide + */ + @VisibleForTesting + public static boolean equals(IpSecTransform lhs, IpSecTransform rhs) { + if (lhs == null || rhs == null) return (lhs == rhs); + return IpSecConfig.equals(lhs.getConfig(), rhs.getConfig()) + && lhs.mResourceId == rhs.mResourceId; + } + + /** * Deactivate this {@code IpSecTransform} and free allocated resources. * * <p>Deactivating a transform while it is still applied to a socket will result in errors on diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java index 8e05cfa96625..bae373d7564b 100644 --- a/core/java/android/net/NetworkCapabilities.java +++ b/core/java/android/net/NetworkCapabilities.java @@ -116,6 +116,7 @@ public final class NetworkCapabilities implements Parcelable { NET_CAPABILITY_NOT_ROAMING, NET_CAPABILITY_FOREGROUND, NET_CAPABILITY_NOT_CONGESTED, + NET_CAPABILITY_NOT_SUSPENDED, }) public @interface NetCapability { } @@ -239,7 +240,6 @@ public final class NetworkCapabilities implements Parcelable { /** * 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 final class NetworkCapabilities implements Parcelable { */ 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 @@ public final class NetworkCapabilities implements Parcelable { 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 @@ public final class NetworkCapabilities implements Parcelable { 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 3a40cf4bf8a3..3cd37bf4fbdd 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 5d7cf1e3899f..7cd58e8b7c36 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -2240,12 +2240,16 @@ public abstract class BatteryStats implements Parcelable { 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/HwBinder.java b/core/java/android/os/HwBinder.java index cdee1101c27b..228fe7a3dae5 100644 --- a/core/java/android/os/HwBinder.java +++ b/core/java/android/os/HwBinder.java @@ -29,7 +29,13 @@ public abstract class HwBinder implements IHwBinder { private static final NativeAllocationRegistry sNativeRegistry; - /** @hide */ + /** + * Create and initialize a HwBinder object and the native objects + * used to allow this to participate in hwbinder transactions. + * + * @hide + */ + @SystemApi public HwBinder() { native_setup(); @@ -44,12 +50,28 @@ public abstract class HwBinder implements IHwBinder { int code, HwParcel request, HwParcel reply, int flags) throws RemoteException; - /** @hide */ + /** + * Process a hwbinder transaction. + * + * @param code interface specific code for interface. + * @param request parceled transaction + * @param reply object to parcel reply into + * @param flags transaction flags to be chosen by wire protocol + * + * @hide + */ + @SystemApi public abstract void onTransact( int code, HwParcel request, HwParcel reply, int flags) throws RemoteException; - /** @hide */ + /** + * Registers this service with the hwservicemanager. + * + * @param serviceName instance name of the service + * @hide + */ + @SystemApi public native final void registerService(String serviceName) throws RemoteException; diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl index 65e9473380ce..5e23932c48cc 100644 --- a/core/java/android/os/IUserManager.aidl +++ b/core/java/android/os/IUserManager.aidl @@ -71,6 +71,7 @@ interface IUserManager { Bundle getUserRestrictions(int userHandle); boolean hasBaseUserRestriction(String restrictionKey, int userHandle); boolean hasUserRestriction(in String restrictionKey, int userHandle); + boolean hasUserRestrictionOnAnyUser(in String restrictionKey); void setUserRestriction(String key, boolean value, int userHandle); void setApplicationRestrictions(in String packageName, in Bundle restrictions, int userHandle); diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index 7e7af1a12299..185620066454 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -1678,6 +1678,18 @@ public class UserManager { } /** + * @hide + * Returns whether any user on the device has the given user restriction set. + */ + public boolean hasUserRestrictionOnAnyUser(String restrictionKey) { + try { + return mService.hasUserRestrictionOnAnyUser(restrictionKey); + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } + } + + /** * Return the serial number for a user. This is a device-unique * number assigned to that user; if the user is deleted and then a new * user created, the new users will not be given the same serial number. diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 1223271ad4c7..c53d005606c4 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -3692,18 +3692,15 @@ public final class Settings { new SettingsValidators.InclusiveIntegerRangeValidator(0, 3); /** - * User-selected RTT mode + * User-selected RTT mode. When on, outgoing and incoming calls will be answered as RTT + * calls when supported by the device and carrier. Boolean value. * 0 = OFF - * 1 = FULL - * 2 = VCO - * 3 = HCO - * Uses the same constants as TTY (e.g. {@link android.telecom.TelecomManager#TTY_MODE_OFF}) - * @hide + * 1 = ON */ public static final String RTT_CALLING_MODE = "rtt_calling_mode"; /** @hide */ - public static final Validator RTT_CALLING_MODE_VALIDATOR = TTY_MODE_VALIDATOR; + public static final Validator RTT_CALLING_MODE_VALIDATOR = BOOLEAN_VALIDATOR; /** * Whether the sounds effects (key clicks, lid open ...) are enabled. The value is @@ -5385,6 +5382,17 @@ public final class Settings { "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 +5451,6 @@ public final class Settings { */ public static final String ENABLED_INPUT_METHODS = "enabled_input_methods"; - private static final Validator ENABLED_INPUT_METHODS_VALIDATOR = new Validator() { - @Override - public boolean validate(String value) { - if (value == null) { - return false; - } - String[] inputMethods = value.split(":"); - boolean valid = true; - for (String inputMethod : inputMethods) { - if (inputMethod.length() == 0) { - return false; - } - String[] subparts = inputMethod.split(";"); - for (String subpart : subparts) { - // allow either a non negative integer or a ComponentName - valid |= (NON_NEGATIVE_INTEGER_VALIDATOR.validate(subpart) - || COMPONENT_NAME_VALIDATOR.validate(subpart)); - } - if (!valid) { - return false; - } - } - return valid; - } - }; - /** * List of system input methods that are currently disabled. This is a string * containing the IDs of all disabled input methods, each ID separated @@ -7709,7 +7691,6 @@ public final class Settings { ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, ENABLED_ACCESSIBILITY_SERVICES, ENABLED_VR_LISTENERS, - ENABLED_INPUT_METHODS, TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES, TOUCH_EXPLORATION_ENABLED, ACCESSIBILITY_ENABLED, @@ -7815,7 +7796,6 @@ public final class Settings { VALIDATORS.put(ENABLED_ACCESSIBILITY_SERVICES, ENABLED_ACCESSIBILITY_SERVICES_VALIDATOR); VALIDATORS.put(ENABLED_VR_LISTENERS, ENABLED_VR_LISTENERS_VALIDATOR); - VALIDATORS.put(ENABLED_INPUT_METHODS, ENABLED_INPUT_METHODS_VALIDATOR); VALIDATORS.put(TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES, TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES_VALIDATOR); VALIDATORS.put(TOUCH_EXPLORATION_ENABLED, TOUCH_EXPLORATION_ENABLED_VALIDATOR); @@ -11382,7 +11362,8 @@ public final class Settings { "chained_battery_attribution_enabled"; /** - * The packages whitelisted to be run in autofill compatibility mode. + * The packages whitelisted to be run in autofill compatibility mode. The list + * of packages is ":" colon delimited. * * @hide */ @@ -12097,6 +12078,28 @@ public final class Settings { "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/IConfirmationPromptCallback.aidl b/core/java/android/security/IConfirmationPromptCallback.aidl deleted file mode 100644 index 96a1a04828b5..000000000000 --- a/core/java/android/security/IConfirmationPromptCallback.aidl +++ /dev/null @@ -1,27 +0,0 @@ -/** - * Copyright (c) 2017, The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.security; - -/** - * This must be kept manually in sync with system/security/keystore until AIDL - * can generate both Java and C++ bindings. - * - * @hide - */ -interface IConfirmationPromptCallback { - oneway void onConfirmationPromptCompleted(in int result, in byte[] dataThatWasConfirmed); -} diff --git a/core/java/android/security/IKeystoreService.aidl b/core/java/android/security/IKeystoreService.aidl deleted file mode 100644 index 738eb6865230..000000000000 --- a/core/java/android/security/IKeystoreService.aidl +++ /dev/null @@ -1,87 +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 android.security; - -import android.security.keymaster.ExportResult; -import android.security.keymaster.KeyCharacteristics; -import android.security.keymaster.KeymasterArguments; -import android.security.keymaster.KeymasterCertificateChain; -import android.security.keymaster.KeymasterBlob; -import android.security.keymaster.OperationResult; -import android.security.KeystoreArguments; - -/** - * This must be kept manually in sync with system/security/keystore until AIDL - * can generate both Java and C++ bindings. - * - * @hide - */ -interface IKeystoreService { - int getState(int userId); - byte[] get(String name, int uid); - int insert(String name, in byte[] item, int uid, int flags); - int del(String name, int uid); - int exist(String name, int uid); - String[] list(String namePrefix, int uid); - int reset(); - int onUserPasswordChanged(int userId, String newPassword); - int lock(int userId); - int unlock(int userId, String userPassword); - int isEmpty(int userId); - int generate(String name, int uid, int keyType, int keySize, int flags, - in KeystoreArguments args); - int import_key(String name, in byte[] data, int uid, int flags); - byte[] sign(String name, in byte[] data); - int verify(String name, in byte[] data, in byte[] signature); - byte[] get_pubkey(String name); - String grant(String name, int granteeUid); - int ungrant(String name, int granteeUid); - long getmtime(String name, int uid); - int is_hardware_backed(String string); - int clear_uid(long uid); - - // Keymaster 0.4 methods - int addRngEntropy(in byte[] data, int flags); - int generateKey(String alias, in KeymasterArguments arguments, in byte[] entropy, int uid, - int flags, out KeyCharacteristics characteristics); - int getKeyCharacteristics(String alias, in KeymasterBlob clientId, in KeymasterBlob appId, - int uid, out KeyCharacteristics characteristics); - int importKey(String alias, in KeymasterArguments arguments, int format, - in byte[] keyData, int uid, int flags, out KeyCharacteristics characteristics); - ExportResult exportKey(String alias, int format, in KeymasterBlob clientId, - in KeymasterBlob appId, int uid); - OperationResult begin(IBinder appToken, String alias, int purpose, boolean pruneable, - in KeymasterArguments params, in byte[] entropy, int uid); - OperationResult update(IBinder token, in KeymasterArguments params, in byte[] input); - OperationResult finish(IBinder token, in KeymasterArguments params, in byte[] signature, - in byte[] entropy); - int abort(IBinder handle); - boolean isOperationAuthorized(IBinder token); - int addAuthToken(in byte[] authToken); - int onUserAdded(int userId, int parentId); - int onUserRemoved(int userId); - int attestKey(String alias, in KeymasterArguments params, out KeymasterCertificateChain chain); - int attestDeviceIds(in KeymasterArguments params, out KeymasterCertificateChain chain); - int onDeviceOffBody(); - int importWrappedKey(in String wrappedKeyAlias, in byte[] wrappedKey, - in String wrappingKeyAlias, in byte[] maskingKey, in KeymasterArguments arguments, - in long rootSid, in long fingerprintSid, - out KeyCharacteristics characteristics); - int presentConfirmationPrompt(IBinder listener, String promptText, in byte[] extraData, - in String locale, in int uiOptionsAsFlags); - int cancelConfirmationPrompt(IBinder listener); -} diff --git a/core/java/android/security/KeystoreArguments.aidl b/core/java/android/security/KeystoreArguments.aidl deleted file mode 100644 index dc8ed50182ed..000000000000 --- a/core/java/android/security/KeystoreArguments.aidl +++ /dev/null @@ -1,20 +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 android.security; - -/* @hide */ -parcelable KeystoreArguments cpp_header "keystore/KeystoreArguments.h"; diff --git a/core/java/android/security/keymaster/ExportResult.aidl b/core/java/android/security/keymaster/ExportResult.aidl deleted file mode 100644 index 17486531a3f0..000000000000 --- a/core/java/android/security/keymaster/ExportResult.aidl +++ /dev/null @@ -1,20 +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 android.security.keymaster; - -/* @hide */ -parcelable ExportResult cpp_header "keystore/ExportResult.h"; diff --git a/core/java/android/security/keymaster/KeyCharacteristics.aidl b/core/java/android/security/keymaster/KeyCharacteristics.aidl deleted file mode 100644 index 32e75ad267b2..000000000000 --- a/core/java/android/security/keymaster/KeyCharacteristics.aidl +++ /dev/null @@ -1,20 +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 android.security.keymaster; - -/* @hide */ -parcelable KeyCharacteristics cpp_header "keystore/KeyCharacteristics.h"; diff --git a/core/java/android/security/keymaster/KeymasterBlob.aidl b/core/java/android/security/keymaster/KeymasterBlob.aidl deleted file mode 100644 index 5c5db9ec314b..000000000000 --- a/core/java/android/security/keymaster/KeymasterBlob.aidl +++ /dev/null @@ -1,20 +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 android.security.keymaster; - -/* @hide */ -parcelable KeymasterBlob cpp_header "keystore/KeymasterBlob.h"; diff --git a/core/java/android/security/keymaster/KeymasterCertificateChain.aidl b/core/java/android/security/keymaster/KeymasterCertificateChain.aidl deleted file mode 100644 index ddb5cae1a254..000000000000 --- a/core/java/android/security/keymaster/KeymasterCertificateChain.aidl +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.security.keymaster; - -/* @hide */ -parcelable KeymasterCertificateChain cpp_header "keystore/KeymasterCertificateChain.h"; diff --git a/core/java/android/security/keymaster/KeymasterDefs.java b/core/java/android/security/keymaster/KeymasterDefs.java index 1d1333504350..f4dcce1e7e58 100644 --- a/core/java/android/security/keymaster/KeymasterDefs.java +++ b/core/java/android/security/keymaster/KeymasterDefs.java @@ -75,6 +75,7 @@ public final class KeymasterDefs { public static final int KM_TAG_ALLOW_WHILE_ON_BODY = KM_BOOL | 506; public static final int KM_TAG_TRUSTED_USER_PRESENCE_REQUIRED = KM_BOOL | 507; public static final int KM_TAG_TRUSTED_CONFIRMATION_REQUIRED = KM_BOOL | 508; + public static final int KM_TAG_UNLOCKED_DEVICE_REQUIRED = KM_BOOL | 509; public static final int KM_TAG_ALL_APPLICATIONS = KM_BOOL | 600; public static final int KM_TAG_APPLICATION_ID = KM_BYTES | 601; @@ -216,6 +217,7 @@ public final class KeymasterDefs { public static final int KM_ERROR_MISSING_MIN_MAC_LENGTH = -58; public static final int KM_ERROR_UNSUPPORTED_MIN_MAC_LENGTH = -59; public static final int KM_ERROR_CANNOT_ATTEST_IDS = -66; + public static final int KM_ERROR_DEVICE_LOCKED = -72; public static final int KM_ERROR_UNIMPLEMENTED = -100; public static final int KM_ERROR_VERSION_MISMATCH = -101; public static final int KM_ERROR_UNKNOWN_ERROR = -1000; @@ -262,6 +264,7 @@ public final class KeymasterDefs { sErrorCodeToString.put(KM_ERROR_INVALID_MAC_LENGTH, "Invalid MAC or authentication tag length"); sErrorCodeToString.put(KM_ERROR_CANNOT_ATTEST_IDS, "Unable to attest device ids"); + sErrorCodeToString.put(KM_ERROR_DEVICE_LOCKED, "Device locked"); sErrorCodeToString.put(KM_ERROR_UNIMPLEMENTED, "Not implemented"); sErrorCodeToString.put(KM_ERROR_UNKNOWN_ERROR, "Unknown error"); } diff --git a/core/java/android/security/keymaster/OperationResult.aidl b/core/java/android/security/keymaster/OperationResult.aidl deleted file mode 100644 index db689d46521a..000000000000 --- a/core/java/android/security/keymaster/OperationResult.aidl +++ /dev/null @@ -1,20 +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 android.security.keymaster; - -/* @hide */ -parcelable OperationResult cpp_header "keystore/OperationResult.h"; diff --git a/core/java/android/security/keystore/OWNERS b/core/java/android/security/keystore/OWNERS new file mode 100644 index 000000000000..bb487fb52c9f --- /dev/null +++ b/core/java/android/security/keystore/OWNERS @@ -0,0 +1,4 @@ +aseemk@google.com +bozhu@google.com +dementyev@google.com +robertberry@google.com diff --git a/core/java/android/service/autofill/AutofillServiceInfo.java b/core/java/android/service/autofill/AutofillServiceInfo.java index 7383de738ad6..70c4ec07ee4e 100644 --- a/core/java/android/service/autofill/AutofillServiceInfo.java +++ b/core/java/android/service/autofill/AutofillServiceInfo.java @@ -32,12 +32,12 @@ import android.text.TextUtils; import android.util.ArrayMap; import android.util.AttributeSet; import android.util.Log; +import android.util.Pair; import android.util.Xml; import com.android.internal.R; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; - import com.android.internal.util.XmlUtils; import org.xmlpull.v1.XmlPullParser; @@ -45,7 +45,6 @@ import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; import java.io.PrintWriter; -import java.util.Map; /** * {@link ServiceInfo} and meta-data about an {@link AutofillService}. @@ -80,7 +79,7 @@ public final class AutofillServiceInfo { private final String mSettingsActivity; @Nullable - private final Map<String, Long> mCompatibilityPackages; + private final ArrayMap<String, Pair<Long, String>> mCompatibilityPackages; public AutofillServiceInfo(Context context, ComponentName comp, int userHandle) throws PackageManager.NameNotFoundException { @@ -118,7 +117,7 @@ public final class AutofillServiceInfo { } String settingsActivity = null; - Map<String, Long> compatibilityPackages = null; + ArrayMap<String, Pair<Long, String>> compatibilityPackages = null; try { final Resources resources = context.getPackageManager().getResourcesForApplication( @@ -154,9 +153,10 @@ public final class AutofillServiceInfo { mCompatibilityPackages = compatibilityPackages; } - private Map<String, Long> parseCompatibilityPackages(XmlPullParser parser, Resources resources) + private ArrayMap<String, Pair<Long, String>> parseCompatibilityPackages(XmlPullParser parser, + Resources resources) throws IOException, XmlPullParserException { - Map<String, Long> compatibilityPackages = null; + ArrayMap<String, Pair<Long, String>> compatibilityPackages = null; final int outerDepth = parser.getDepth(); int type; @@ -200,11 +200,13 @@ public final class AutofillServiceInfo { } else { maxVersionCode = Long.MAX_VALUE; } + final String urlBarResourceId = cpAttributes.getString( + R.styleable.AutofillService_CompatibilityPackage_urlBarResourceId); if (compatibilityPackages == null) { compatibilityPackages = new ArrayMap<>(); } - compatibilityPackages.put(name, maxVersionCode); + compatibilityPackages.put(name, new Pair<>(maxVersionCode, urlBarResourceId)); } finally { XmlUtils.skipCurrentTag(parser); if (cpAttributes != null) { @@ -226,16 +228,21 @@ public final class AutofillServiceInfo { return mSettingsActivity; } + public ArrayMap<String, Pair<Long, String>> getCompatibilityPackages() { + return mCompatibilityPackages; + } + + /** + * Gets the resource id of the URL bar for a package. Used in compat mode + */ + // TODO: return a list of strings instead @Nullable - public boolean isCompatibilityModeRequested(String packageName, long versionCode) { + public String getUrlBarResourceId(String packageName) { if (mCompatibilityPackages == null) { - return false; - } - final Long maxVersionCode = mCompatibilityPackages.get(packageName); - if (maxVersionCode == null) { - return false; + return null; } - return versionCode <= maxVersionCode; + final Pair<Long, String> pair = mCompatibilityPackages.get(packageName); + return pair == null ? null : pair.second; } @Override diff --git a/core/java/android/service/autofill/DateTransformation.java b/core/java/android/service/autofill/DateTransformation.java index 4e1425d86380..ec24a09a470f 100644 --- a/core/java/android/service/autofill/DateTransformation.java +++ b/core/java/android/service/autofill/DateTransformation.java @@ -20,6 +20,7 @@ import static android.view.autofill.Helper.sDebug; 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 android.widget.TextView; 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 0f7b540f8a24..4f797f46da2a 100644 --- a/core/java/android/service/autofill/DateValueSanitizer.java +++ b/core/java/android/service/autofill/DateValueSanitizer.java @@ -21,6 +21,7 @@ import static android.view.autofill.Helper.sDebug; 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 android.view.autofill.AutofillValue; 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 cd1efd68df6f..5bf56cb9c1b5 100644 --- a/core/java/android/service/autofill/FieldClassification.java +++ b/core/java/android/service/autofill/FieldClassification.java @@ -108,21 +108,21 @@ public final class FieldClassification { */ 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 final class FieldClassification { 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/FillContext.java b/core/java/android/service/autofill/FillContext.java index cda2f4a23c9a..535c00bc5319 100644 --- a/core/java/android/service/autofill/FillContext.java +++ b/core/java/android/service/autofill/FillContext.java @@ -177,30 +177,6 @@ public final class FillContext implements Parcelable { return foundNodes; } - /** - * Finds the {@link ViewNode} that has the requested {@code id}, if any. - * - * @hide - */ - @Nullable public ViewNode findViewNodeByAutofillId(@NonNull AutofillId id) { - final LinkedList<ViewNode> nodesToProcess = new LinkedList<>(); - final int numWindowNodes = mStructure.getWindowNodeCount(); - for (int i = 0; i < numWindowNodes; i++) { - nodesToProcess.add(mStructure.getWindowNodeAt(i).getRootViewNode()); - } - while (!nodesToProcess.isEmpty()) { - final ViewNode node = nodesToProcess.removeFirst(); - if (id.equals(node.getAutofillId())) { - return node; - } - for (int i = 0; i < node.getChildCount(); i++) { - nodesToProcess.addLast(node.getChildAt(i)); - } - } - - return null; - } - public static final Parcelable.Creator<FillContext> CREATOR = new Parcelable.Creator<FillContext>() { @Override diff --git a/core/java/android/service/autofill/UserData.java b/core/java/android/service/autofill/UserData.java index 6bab6aa82375..a1dd1f846515 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.os.Parcelable; 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 @@ public final class UserData implements Parcelable { 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 @@ public final class UserData implements Parcelable { } /** @hide */ - public String[] getRemoteIds() { - return mRemoteIds; + public String[] getCategoryIds() { + return mCategoryIds; } /** @hide */ @@ -106,11 +109,11 @@ public final class UserData implements Parcelable { 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 @@ public final class UserData implements Parcelable { 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 final class UserData implements Parcelable { */ 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 final class UserData implements Parcelable { */ public Builder setFieldClassificationAlgorithm(@Nullable String name, @Nullable Bundle args) { + throwIfDestroyed(); mAlgorithm = name; mAlgorithmArgs = args; return this; @@ -198,37 +218,58 @@ public final class UserData implements Parcelable { /** * 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 @@ public final class UserData implements Parcelable { 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 @@ public final class UserData implements Parcelable { @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 @@ public final class UserData implements Parcelable { // 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 @@ public final class UserData implements Parcelable { } /** + * 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 dbe415773899..6fa5312be5cc 100644 --- a/core/java/android/text/BoringLayout.java +++ b/core/java/android/text/BoringLayout.java @@ -322,12 +322,6 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback */ public static Metrics isBoring(CharSequence text, TextPaint paint, TextDirectionHeuristic textDir, Metrics metrics) { - return isBoring(text, null /* precomputed */, paint, textDir, metrics); - } - - /** @hide */ - public static Metrics isBoring(CharSequence text, PrecomputedText precomputed, TextPaint paint, - TextDirectionHeuristic textDir, Metrics metrics) { final int textLength = text.length(); if (hasAnyInterestingChars(text, textLength)) { return null; // There are some interesting characters. Not boring. @@ -350,17 +344,18 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback fm.reset(); } - if (precomputed != null) { + TextLine line = TextLine.obtain(); + line.set(paint, text, 0, textLength, Layout.DIR_LEFT_TO_RIGHT, + Layout.DIRS_ALL_LEFT_TO_RIGHT, false, null); + if (text instanceof MeasuredText) { + MeasuredText mt = (MeasuredText) text; // Reaching here means there is only one paragraph. - MeasuredParagraph mp = precomputed.getMeasuredParagraph(0); + MeasuredParagraph mp = mt.getMeasuredParagraph(0); fm.width = (int) Math.ceil(mp.getWidth(0, mp.getTextLength())); } else { - TextLine line = TextLine.obtain(); - line.set(paint, text, 0, textLength, Layout.DIR_LEFT_TO_RIGHT, - Layout.DIRS_ALL_LEFT_TO_RIGHT, false, null); fm.width = (int) Math.ceil(line.metrics(fm)); - TextLine.recycle(line); } + TextLine.recycle(line); return fm; } diff --git a/core/java/android/text/DynamicLayout.java b/core/java/android/text/DynamicLayout.java index 10444f045d6f..18431cacbfaf 100644 --- a/core/java/android/text/DynamicLayout.java +++ b/core/java/android/text/DynamicLayout.java @@ -363,7 +363,7 @@ public class DynamicLayout extends Layout { @JustificationMode int justificationMode, @Nullable TextUtils.TruncateAt ellipsize, @IntRange(from = 0) int ellipsizedWidth) { - super(createEllipsizer(ellipsize, display), null /* precomputed */, + super(createEllipsizer(ellipsize, display), paint, width, align, textDir, spacingmult, spacingadd); final Builder b = Builder.obtain(base, paint, width) diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java index d5d35904031c..aa97b2aba749 100644 --- a/core/java/android/text/Layout.java +++ b/core/java/android/text/Layout.java @@ -245,13 +245,6 @@ public abstract class Layout { protected Layout(CharSequence text, TextPaint paint, int width, Alignment align, TextDirectionHeuristic textDir, float spacingMult, float spacingAdd) { - this(text, null /* precomputed */, paint, width, align, textDir, spacingMult, spacingAdd); - } - - /** @hide */ - protected Layout(CharSequence text, PrecomputedText precomputed, TextPaint paint, - int width, Alignment align, TextDirectionHeuristic textDir, - float spacingMult, float spacingAdd) { if (width < 0) throw new IllegalArgumentException("Layout: " + width + " < 0"); @@ -266,7 +259,6 @@ public abstract class Layout { } mText = text; - mPrecomputed = precomputed; mPaint = paint; mWidth = width; mAlignment = align; @@ -570,7 +562,7 @@ public abstract class Layout { // XXX: assumes there's nothing additional to be done canvas.drawText(buf, start, end, x, lbaseline, paint); } else { - tl.set(paint, buf, mPrecomputed, start, end, dir, directions, hasTab, tabStops); + tl.set(paint, buf, start, end, dir, directions, hasTab, tabStops); if (justify) { tl.justify(right - left - indentWidth); } @@ -2272,7 +2264,6 @@ public abstract class Layout { } private CharSequence mText; - private PrecomputedText mPrecomputed; private TextPaint mPaint; private TextPaint mWorkPaint = new TextPaint(); private int mWidth; diff --git a/core/java/android/text/MeasuredText.java b/core/java/android/text/MeasuredText.java new file mode 100644 index 000000000000..bb7a9e0b7906 --- /dev/null +++ b/core/java/android/text/MeasuredText.java @@ -0,0 +1,427 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.text; + +import android.annotation.IntRange; +import android.annotation.NonNull; +import android.util.IntArray; + +import com.android.internal.util.ArrayUtils; +import com.android.internal.util.Preconditions; + +import java.util.ArrayList; + +/** + * A text which has already been measured. + */ +public class MeasuredText implements Spanned { + private static final char LINE_FEED = '\n'; + + // The original text. + private final @NonNull CharSequence mText; + + // The inclusive start offset of the measuring target. + private final @IntRange(from = 0) int mStart; + + // The exclusive end offset of the measuring target. + private final @IntRange(from = 0) int mEnd; + + // The TextPaint used for measurement. + private final @NonNull TextPaint mPaint; + + // The requested text direction. + private final @NonNull TextDirectionHeuristic mTextDir; + + // The measured paragraph texts. + private final @NonNull MeasuredParagraph[] mMeasuredParagraphs; + + // The sorted paragraph end offsets. + private final @NonNull int[] mParagraphBreakPoints; + + // The break strategy for this measured text. + private final @Layout.BreakStrategy int mBreakStrategy; + + // The hyphenation frequency for this measured text. + private final @Layout.HyphenationFrequency int mHyphenationFrequency; + + /** + * A Builder for MeasuredText + */ + public static final class Builder { + // Mandatory parameters. + private final @NonNull CharSequence mText; + private final @NonNull TextPaint mPaint; + + // Members to be updated by setters. + private @IntRange(from = 0) int mStart; + private @IntRange(from = 0) int mEnd; + private TextDirectionHeuristic mTextDir = TextDirectionHeuristics.FIRSTSTRONG_LTR; + private @Layout.BreakStrategy int mBreakStrategy = Layout.BREAK_STRATEGY_HIGH_QUALITY; + private @Layout.HyphenationFrequency int mHyphenationFrequency = + Layout.HYPHENATION_FREQUENCY_NORMAL; + + + /** + * Builder constructor + * + * @param text The text to be measured. + * @param paint The paint to be used for drawing. + */ + public Builder(@NonNull CharSequence text, @NonNull TextPaint paint) { + Preconditions.checkNotNull(text); + Preconditions.checkNotNull(paint); + + mText = text; + mPaint = paint; + mStart = 0; + mEnd = text.length(); + } + + /** + * Set the range of measuring target. + * + * @param start The measuring target start offset in the text. + * @param end The measuring target end offset in the text. + */ + public @NonNull Builder setRange(@IntRange(from = 0) int start, + @IntRange(from = 0) int end) { + Preconditions.checkArgumentInRange(start, 0, mText.length(), "start"); + Preconditions.checkArgumentInRange(end, 0, mText.length(), "end"); + Preconditions.checkArgument(start <= end, "The range is reversed."); + + mStart = start; + mEnd = end; + return this; + } + + /** + * Set the text direction heuristic + * + * The default value is {@link TextDirectionHeuristics#FIRSTSTRONG_LTR}. + * + * @param textDir The text direction heuristic for resolving bidi behavior. + * @return this builder, useful for chaining. + */ + public @NonNull Builder setTextDirection(@NonNull TextDirectionHeuristic textDir) { + Preconditions.checkNotNull(textDir); + mTextDir = textDir; + return this; + } + + /** + * Set the break strategy + * + * The default value is {@link Layout#BREAK_STRATEGY_HIGH_QUALITY}. + * + * @param breakStrategy The break strategy. + * @return this builder, useful for chaining. + */ + public @NonNull Builder setBreakStrategy(@Layout.BreakStrategy int breakStrategy) { + mBreakStrategy = breakStrategy; + return this; + } + + /** + * Set the hyphenation frequency + * + * The default value is {@link Layout#HYPHENATION_FREQUENCY_NORMAL}. + * + * @param hyphenationFrequency The hyphenation frequency. + * @return this builder, useful for chaining. + */ + public @NonNull Builder setHyphenationFrequency( + @Layout.HyphenationFrequency int hyphenationFrequency) { + mHyphenationFrequency = hyphenationFrequency; + return this; + } + + /** + * Build the measured text + * + * @return the measured text. + */ + public @NonNull MeasuredText build() { + return build(true /* build full layout result */); + } + + /** @hide */ + public @NonNull MeasuredText build(boolean computeLayout) { + final boolean needHyphenation = mBreakStrategy != Layout.BREAK_STRATEGY_SIMPLE + && mHyphenationFrequency != Layout.HYPHENATION_FREQUENCY_NONE; + + final IntArray paragraphEnds = new IntArray(); + final ArrayList<MeasuredParagraph> measuredTexts = new ArrayList<>(); + + int paraEnd = 0; + for (int paraStart = mStart; paraStart < mEnd; paraStart = paraEnd) { + paraEnd = TextUtils.indexOf(mText, LINE_FEED, paraStart, mEnd); + if (paraEnd < 0) { + // No LINE_FEED(U+000A) character found. Use end of the text as the paragraph + // end. + paraEnd = mEnd; + } else { + paraEnd++; // Includes LINE_FEED(U+000A) to the prev paragraph. + } + + paragraphEnds.add(paraEnd); + measuredTexts.add(MeasuredParagraph.buildForStaticLayout( + mPaint, mText, paraStart, paraEnd, mTextDir, needHyphenation, + computeLayout, null /* no recycle */)); + } + + return new MeasuredText(mText, mStart, mEnd, mPaint, mTextDir, mBreakStrategy, + mHyphenationFrequency, measuredTexts.toArray( + new MeasuredParagraph[measuredTexts.size()]), + paragraphEnds.toArray()); + } + }; + + // Use MeasuredText.Builder instead. + private MeasuredText(@NonNull CharSequence text, + @IntRange(from = 0) int start, + @IntRange(from = 0) int end, + @NonNull TextPaint paint, + @NonNull TextDirectionHeuristic textDir, + @Layout.BreakStrategy int breakStrategy, + @Layout.HyphenationFrequency int frequency, + @NonNull MeasuredParagraph[] measuredTexts, + @NonNull int[] paragraphBreakPoints) { + mText = text; + mStart = start; + mEnd = end; + // Copy the paint so that we can keep the reference of typeface in native layout result. + mPaint = new TextPaint(paint); + mMeasuredParagraphs = measuredTexts; + mParagraphBreakPoints = paragraphBreakPoints; + mTextDir = textDir; + mBreakStrategy = breakStrategy; + mHyphenationFrequency = frequency; + } + + /** + * Return the underlying text. + */ + public @NonNull CharSequence getText() { + return mText; + } + + /** + * Returns the inclusive start offset of measured region. + */ + public @IntRange(from = 0) int getStart() { + return mStart; + } + + /** + * Returns the exclusive end offset of measured region. + */ + public @IntRange(from = 0) int getEnd() { + return mEnd; + } + + /** + * Returns the text direction associated with char sequence. + */ + public @NonNull TextDirectionHeuristic getTextDir() { + return mTextDir; + } + + /** + * Returns the paint used to measure this text. + */ + public @NonNull TextPaint getPaint() { + return mPaint; + } + + /** + * Returns the length of the paragraph of this text. + */ + public @IntRange(from = 0) int getParagraphCount() { + return mParagraphBreakPoints.length; + } + + /** + * Returns the paragraph start offset of the text. + */ + public @IntRange(from = 0) int getParagraphStart(@IntRange(from = 0) int paraIndex) { + Preconditions.checkArgumentInRange(paraIndex, 0, getParagraphCount(), "paraIndex"); + return paraIndex == 0 ? mStart : mParagraphBreakPoints[paraIndex - 1]; + } + + /** + * Returns the paragraph end offset of the text. + */ + public @IntRange(from = 0) int getParagraphEnd(@IntRange(from = 0) int paraIndex) { + Preconditions.checkArgumentInRange(paraIndex, 0, getParagraphCount(), "paraIndex"); + return mParagraphBreakPoints[paraIndex]; + } + + /** @hide */ + public @NonNull MeasuredParagraph getMeasuredParagraph(@IntRange(from = 0) int paraIndex) { + return mMeasuredParagraphs[paraIndex]; + } + + /** + * Returns the break strategy for this text. + */ + public @Layout.BreakStrategy int getBreakStrategy() { + return mBreakStrategy; + } + + /** + * Returns the hyphenation frequency for this text. + */ + public @Layout.HyphenationFrequency int getHyphenationFrequency() { + return mHyphenationFrequency; + } + + /** + * Returns true if the given TextPaint gives the same result of text layout for this text. + * @hide + */ + public boolean canUseMeasuredResult(@NonNull TextPaint paint) { + return mPaint.getTextSize() == paint.getTextSize() + && mPaint.getTextSkewX() == paint.getTextSkewX() + && mPaint.getTextScaleX() == paint.getTextScaleX() + && mPaint.getLetterSpacing() == paint.getLetterSpacing() + && mPaint.getWordSpacing() == paint.getWordSpacing() + && mPaint.getFlags() == paint.getFlags() // Maybe not all flag affects text layout. + && mPaint.getTextLocales() == paint.getTextLocales() // need to be equals? + && mPaint.getFontVariationSettings() == paint.getFontVariationSettings() + && mPaint.getTypeface() == paint.getTypeface() + && TextUtils.equals(mPaint.getFontFeatureSettings(), paint.getFontFeatureSettings()); + } + + /** @hide */ + public int findParaIndex(@IntRange(from = 0) int pos) { + // TODO: Maybe good to remove paragraph concept from MeasuredText and add substring layout + // support to StaticLayout. + for (int i = 0; i < mParagraphBreakPoints.length; ++i) { + if (pos < mParagraphBreakPoints[i]) { + return i; + } + } + throw new IndexOutOfBoundsException( + "pos must be less than " + mParagraphBreakPoints[mParagraphBreakPoints.length - 1] + + ", gave " + pos); + } + + /** @hide */ + public float getWidth(@IntRange(from = 0) int start, @IntRange(from = 0) int end) { + final int paraIndex = findParaIndex(start); + final int paraStart = getParagraphStart(paraIndex); + final int paraEnd = getParagraphEnd(paraIndex); + if (start < paraStart || paraEnd < end) { + throw new RuntimeException("Cannot measured across the paragraph:" + + "para: (" + paraStart + ", " + paraEnd + "), " + + "request: (" + start + ", " + end + ")"); + } + return getMeasuredParagraph(paraIndex).getWidth(start - paraStart, end - paraStart); + } + + /** + * Returns the size of native MeasuredText memory usage + * + * Note that this may not be aculate. Must be used only for testing purposes. + * @hide + */ + public int getMemoryUsage() { + int r = 0; + for (int i = 0; i < getParagraphCount(); ++i) { + r += getMeasuredParagraph(i).getMemoryUsage(); + } + return r; + } + + /////////////////////////////////////////////////////////////////////////////////////////////// + // Spanned overrides + // + // Just proxy for underlying mText if appropriate. + + @Override + public <T> T[] getSpans(int start, int end, Class<T> type) { + if (mText instanceof Spanned) { + return ((Spanned) mText).getSpans(start, end, type); + } else { + return ArrayUtils.emptyArray(type); + } + } + + @Override + public int getSpanStart(Object tag) { + if (mText instanceof Spanned) { + return ((Spanned) mText).getSpanStart(tag); + } else { + return -1; + } + } + + @Override + public int getSpanEnd(Object tag) { + if (mText instanceof Spanned) { + return ((Spanned) mText).getSpanEnd(tag); + } else { + return -1; + } + } + + @Override + public int getSpanFlags(Object tag) { + if (mText instanceof Spanned) { + return ((Spanned) mText).getSpanFlags(tag); + } else { + return 0; + } + } + + @Override + public int nextSpanTransition(int start, int limit, Class type) { + if (mText instanceof Spanned) { + return ((Spanned) mText).nextSpanTransition(start, limit, type); + } else { + return mText.length(); + } + } + + /////////////////////////////////////////////////////////////////////////////////////////////// + // CharSequence overrides. + // + // Just proxy for underlying mText. + + @Override + public int length() { + return mText.length(); + } + + @Override + public char charAt(int index) { + // TODO: Should this be index + mStart ? + return mText.charAt(index); + } + + @Override + public CharSequence subSequence(int start, int end) { + // TODO: return MeasuredText. + // TODO: Should this be index + mStart, end + mStart ? + return mText.subSequence(start, end); + } + + @Override + public String toString() { + return mText.toString(); + } +} diff --git a/core/java/android/text/PrecomputedText.java b/core/java/android/text/PrecomputedText.java deleted file mode 100644 index 39fc2bd52db4..000000000000 --- a/core/java/android/text/PrecomputedText.java +++ /dev/null @@ -1,412 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.text; - -import android.annotation.IntRange; -import android.annotation.NonNull; -import android.util.IntArray; - -import com.android.internal.util.Preconditions; - -import java.util.ArrayList; - -/** - * A text which has the character metrics data - * - * This text holds a part of the text layout result. You can accelerate - * {@link android.widget.TextView} or {@link StaticLayout} by using this text. - * - * <pre> - * Example of background measurement. - * <code> - * void asyncSetText(final TextView textView, final String expensiveLongString, Handler handler) { - * final PrecomputedText.Params params = textView.getTextParams(); - * handler.post(() -> { - * final PrecomputedText precomputedText - * = PrecomputedText.create(expensiveLongString, params); - * textView.post(() -> { - * textView.setPrecomputedTextOrThrow(precomputedText); - * }); - * }); - * } - * </code> - * </pre> - * - * Note that the {@link PrecomputedText} created from different parameters of the target - * {@link android.widget.TextView} will be rejected internally and compute the text layout again - * with the current {@link android.widget.TextView} parameters. - */ -public class PrecomputedText { - private static final char LINE_FEED = '\n'; - - /** - * The information required for building {@link PrecomputedText}. - * - * Contains information required for precomputing text measurement metadata, so it can be done - * in isolation of a {@link android.widget.TextView} or {@link StaticLayout}, when final layout - * constraints are not known. - */ - public static class Params { - // The TextPaint used for measurement. - private final @NonNull TextPaint mPaint; - - // The requested text direction. - private final @NonNull TextDirectionHeuristic mTextDir; - - // The break strategy for this measured text. - private final @Layout.BreakStrategy int mBreakStrategy; - - // The hyphenation frequency for this measured text. - private final @Layout.HyphenationFrequency int mHyphenationFrequency; - - /** - * A builder for creating {@link Params}. - */ - public static class Builder { - // The TextPaint used for measurement. - private final @NonNull TextPaint mPaint; - - // The requested text direction. - private TextDirectionHeuristic mTextDir = TextDirectionHeuristics.FIRSTSTRONG_LTR; - - // The break strategy for this measured text. - private @Layout.BreakStrategy int mBreakStrategy = Layout.BREAK_STRATEGY_HIGH_QUALITY; - - // The hyphenation frequency for this measured text. - private @Layout.HyphenationFrequency int mHyphenationFrequency = - Layout.HYPHENATION_FREQUENCY_NORMAL; - - /** - * Builder constructor - * - * @param paint The paint to be used for drawing - */ - public Builder(@NonNull TextPaint paint) { - mPaint = paint; - } - - /** - * Set the line break strategy - * - * The default value is {@link Layout#BREAK_STRATEGY_HIGH_QUALITY}. - * - * @param strategy The break strategy - * @return this builder, useful for chaining - */ - public Builder setBreakStrategy(@Layout.BreakStrategy int strategy) { - mBreakStrategy = strategy; - return this; - } - - /** - * Set the hyphenation frequency - * - * The default value is {@link Layout#HYPHENATION_FREQUENCY_NORMAL}. - * - * @param frequency The hyphenation frequency - * @return this builder, useful for chaining - */ - public Builder setHyphenationFrequency(@Layout.HyphenationFrequency int frequency) { - mHyphenationFrequency = frequency; - return this; - } - - /** - * Set the text direction heuristic - * - * The default value is {@link TextDirectionHeuristics#FIRSTSTRONG_LTR}. - * - * @param textDir The text direction heuristic for resolving bidi behavior - * @return this builder, useful for chaining - */ - public Builder setTextDirection(@NonNull TextDirectionHeuristic textDir) { - mTextDir = textDir; - return this; - } - - /** - * Build the {@link Params} - * - * @return the layout parameter - */ - public @NonNull Params build() { - return new Params(mPaint, mTextDir, mBreakStrategy, mHyphenationFrequency); - } - } - - // Use Builder instead. - /** @hide */ - public Params(@NonNull TextPaint paint, @NonNull TextDirectionHeuristic textDir, - @Layout.BreakStrategy int strategy, @Layout.HyphenationFrequency int frequency) { - mPaint = paint; - mTextDir = textDir; - mBreakStrategy = strategy; - mHyphenationFrequency = frequency; - } - - /** - * Returns the {@link TextPaint} for this text - * - * @return A {@link TextPaint} - */ - public @NonNull TextPaint getTextPaint() { - return mPaint; - } - - /** - * Returns the {@link TextDirectionHeuristic} for this text - * - * @return A {@link TextDirectionHeuristic} - */ - public @NonNull TextDirectionHeuristic getTextDirection() { - return mTextDir; - } - - /** - * Returns the break strategy for this text - * - * @return A line break strategy - */ - public @Layout.BreakStrategy int getBreakStrategy() { - return mBreakStrategy; - } - - /** - * Returns the hyphenation frequency for this text - * - * @return A hyphenation frequency - */ - public @Layout.HyphenationFrequency int getHyphenationFrequency() { - return mHyphenationFrequency; - } - - private boolean sameTextMetricsInternal(@NonNull TextPaint paint, - @NonNull TextDirectionHeuristic textDir, @Layout.BreakStrategy int strategy, - @Layout.HyphenationFrequency int frequency) { - return mTextDir == textDir - && mBreakStrategy == strategy - && mHyphenationFrequency == frequency - && mPaint.equalsForTextMeasurement(paint); - } - - /** - * Check if the same text layout. - * - * @return true if this and the given param result in the same text layout - */ - public boolean sameTextMetrics(@NonNull Params param) { - return sameTextMetricsInternal(param.mPaint, param.mTextDir, param.mBreakStrategy, - param.mHyphenationFrequency); - } - }; - - // The original text. - private final @NonNull CharSequence mText; - - // The inclusive start offset of the measuring target. - private final @IntRange(from = 0) int mStart; - - // The exclusive end offset of the measuring target. - private final @IntRange(from = 0) int mEnd; - - private final @NonNull Params mParams; - - // The measured paragraph texts. - private final @NonNull MeasuredParagraph[] mMeasuredParagraphs; - - // The sorted paragraph end offsets. - private final @NonNull int[] mParagraphBreakPoints; - - /** - * Create a new {@link PrecomputedText} which will pre-compute text measurement and glyph - * positioning information. - * <p> - * This can be expensive, so computing this on a background thread before your text will be - * presented can save work on the UI thread. - * </p> - * - * @param text The text to be measured - * @param param Parameters that define how text will be precomputed - * @return A {@link PrecomputedText} - */ - public static PrecomputedText create(@NonNull CharSequence text, @NonNull Params param) { - return createInternal(text, param, 0, text.length(), true /* compute full Layout */); - } - - - /** @hide */ - public static PrecomputedText createWidthOnly(@NonNull CharSequence text, @NonNull Params param, - @IntRange(from = 0) int start, @IntRange(from = 0) int end) { - return createInternal(text, param, start, end, false /* compute width only */); - } - - private static PrecomputedText createInternal(@NonNull CharSequence text, @NonNull Params param, - @IntRange(from = 0) int start, @IntRange(from = 0) int end, boolean computeLayout) { - Preconditions.checkNotNull(text); - Preconditions.checkNotNull(param); - final boolean needHyphenation = param.getBreakStrategy() != Layout.BREAK_STRATEGY_SIMPLE - && param.getHyphenationFrequency() != Layout.HYPHENATION_FREQUENCY_NONE; - - final IntArray paragraphEnds = new IntArray(); - final ArrayList<MeasuredParagraph> measuredTexts = new ArrayList<>(); - - int paraEnd = 0; - for (int paraStart = start; paraStart < end; paraStart = paraEnd) { - paraEnd = TextUtils.indexOf(text, LINE_FEED, paraStart, end); - if (paraEnd < 0) { - // No LINE_FEED(U+000A) character found. Use end of the text as the paragraph - // end. - paraEnd = end; - } else { - paraEnd++; // Includes LINE_FEED(U+000A) to the prev paragraph. - } - - paragraphEnds.add(paraEnd); - measuredTexts.add(MeasuredParagraph.buildForStaticLayout( - param.getTextPaint(), text, paraStart, paraEnd, param.getTextDirection(), - needHyphenation, computeLayout, null /* no recycle */)); - } - - return new PrecomputedText(text, start, end, param, - measuredTexts.toArray(new MeasuredParagraph[measuredTexts.size()]), - paragraphEnds.toArray()); - } - - // Use PrecomputedText.create instead. - private PrecomputedText(@NonNull CharSequence text, @IntRange(from = 0) int start, - @IntRange(from = 0) int end, @NonNull Params param, - @NonNull MeasuredParagraph[] measuredTexts, @NonNull int[] paragraphBreakPoints) { - mText = TextUtils.stringOrSpannedString(text); - mStart = start; - mEnd = end; - mParams = param; - mMeasuredParagraphs = measuredTexts; - mParagraphBreakPoints = paragraphBreakPoints; - } - - /** - * Return the underlying text. - */ - public @NonNull CharSequence getText() { - return mText; - } - - /** - * Returns the inclusive start offset of measured region. - * @hide - */ - public @IntRange(from = 0) int getStart() { - return mStart; - } - - /** - * Returns the exclusive end offset of measured region. - * @hide - */ - public @IntRange(from = 0) int getEnd() { - return mEnd; - } - - /** - * Returns the layout parameters used to measure this text. - */ - public @NonNull Params getParams() { - return mParams; - } - - /** - * Returns the length of the paragraph of this text. - */ - public @IntRange(from = 0) int getParagraphCount() { - return mParagraphBreakPoints.length; - } - - /** - * Returns the paragraph start offset of the text. - */ - public @IntRange(from = 0) int getParagraphStart(@IntRange(from = 0) int paraIndex) { - Preconditions.checkArgumentInRange(paraIndex, 0, getParagraphCount(), "paraIndex"); - return paraIndex == 0 ? mStart : mParagraphBreakPoints[paraIndex - 1]; - } - - /** - * Returns the paragraph end offset of the text. - */ - public @IntRange(from = 0) int getParagraphEnd(@IntRange(from = 0) int paraIndex) { - Preconditions.checkArgumentInRange(paraIndex, 0, getParagraphCount(), "paraIndex"); - return mParagraphBreakPoints[paraIndex]; - } - - /** @hide */ - public @NonNull MeasuredParagraph getMeasuredParagraph(@IntRange(from = 0) int paraIndex) { - return mMeasuredParagraphs[paraIndex]; - } - - /** - * Returns true if the given TextPaint gives the same result of text layout for this text. - * @hide - */ - public boolean canUseMeasuredResult(@IntRange(from = 0) int start, @IntRange(from = 0) int end, - @NonNull TextDirectionHeuristic textDir, @NonNull TextPaint paint, - @Layout.BreakStrategy int strategy, @Layout.HyphenationFrequency int frequency) { - final TextPaint mtPaint = mParams.getTextPaint(); - return mStart == start - && mEnd == end - && mParams.sameTextMetricsInternal(paint, textDir, strategy, frequency); - } - - /** @hide */ - public int findParaIndex(@IntRange(from = 0) int pos) { - // TODO: Maybe good to remove paragraph concept from PrecomputedText and add substring - // layout support to StaticLayout. - for (int i = 0; i < mParagraphBreakPoints.length; ++i) { - if (pos < mParagraphBreakPoints[i]) { - return i; - } - } - throw new IndexOutOfBoundsException( - "pos must be less than " + mParagraphBreakPoints[mParagraphBreakPoints.length - 1] - + ", gave " + pos); - } - - /** @hide */ - public float getWidth(@IntRange(from = 0) int start, @IntRange(from = 0) int end) { - final int paraIndex = findParaIndex(start); - final int paraStart = getParagraphStart(paraIndex); - final int paraEnd = getParagraphEnd(paraIndex); - if (start < paraStart || paraEnd < end) { - throw new RuntimeException("Cannot measured across the paragraph:" - + "para: (" + paraStart + ", " + paraEnd + "), " - + "request: (" + start + ", " + end + ")"); - } - return getMeasuredParagraph(paraIndex).getWidth(start - paraStart, end - paraStart); - } - - /** - * Returns the size of native PrecomputedText memory usage - * - * Note that this is not guaranteed to be accurate. Must be used only for testing purposes. - * @hide - */ - public int getMemoryUsage() { - int r = 0; - for (int i = 0; i < getParagraphCount(); ++i) { - r += getMeasuredParagraph(i).getMemoryUsage(); - } - return r; - } -} diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java index 5869802e74e6..e62f4216f33a 100644 --- a/core/java/android/text/StaticLayout.java +++ b/core/java/android/text/StaticLayout.java @@ -78,23 +78,6 @@ public class StaticLayout extends Layout { /** * Obtain a builder for constructing StaticLayout objects. * - * @param source The precomputed text. - * @param start The index of the start of the text - * @param end The index + 1 of the end of the text - * @param paint The base paint used for layout - * @param width The width in pixels - * @return a builder object used for constructing the StaticLayout - */ - @NonNull - public static Builder obtain(@NonNull PrecomputedText source, @IntRange(from = 0) int start, - @IntRange(from = 0) int end, @NonNull TextPaint paint, - @IntRange(from = 0) int width) { - return obtain(source.getText(), source, start, end, paint, width); - } - - /** - * Obtain a builder for constructing StaticLayout objects. - * * @param source The text to be laid out, optionally with spans * @param start The index of the start of the text * @param end The index + 1 of the end of the text @@ -106,12 +89,6 @@ public class StaticLayout extends Layout { public static Builder obtain(@NonNull CharSequence source, @IntRange(from = 0) int start, @IntRange(from = 0) int end, @NonNull TextPaint paint, @IntRange(from = 0) int width) { - return obtain(source, null, start, end, paint, width); - } - - private static Builder obtain(@NonNull CharSequence source, @Nullable PrecomputedText text, - @IntRange(from = 0) int start, @IntRange(from = 0) int end, - @NonNull TextPaint paint, @IntRange(from = 0) int width) { Builder b = sPool.acquire(); if (b == null) { b = new Builder(); @@ -119,7 +96,6 @@ public class StaticLayout extends Layout { // set default initial values b.mText = source; - b.mPrecomputed = text; b.mStart = start; b.mEnd = end; b.mPaint = paint; @@ -452,7 +428,6 @@ public class StaticLayout extends Layout { } private CharSequence mText; - private PrecomputedText mPrecomputed; private int mStart; private int mEnd; private TextPaint mPaint; @@ -515,7 +490,7 @@ public class StaticLayout extends Layout { float spacingmult, float spacingadd, boolean includepad, TextUtils.TruncateAt ellipsize, int ellipsizedWidth) { - this(source, null /* precomputed */, bufstart, bufend, paint, outerwidth, align, + this(source, bufstart, bufend, paint, outerwidth, align, TextDirectionHeuristics.FIRSTSTRONG_LTR, spacingmult, spacingadd, includepad, ellipsize, ellipsizedWidth, Integer.MAX_VALUE); } @@ -525,16 +500,7 @@ public class StaticLayout extends Layout { * @deprecated Use {@link Builder} instead. */ @Deprecated - public StaticLayout(CharSequence source, int bufstart, int bufend, TextPaint paint, - int outerwidth, Alignment align, TextDirectionHeuristic textDir, - float spacingmult, float spacingadd, boolean includepad, - TextUtils.TruncateAt ellipsize, int ellipsizedWidth, int maxLines) { - this(source, null /* precomputed */, bufstart, bufend, paint, outerwidth, align, textDir, - spacingmult, spacingadd, includepad, ellipsize, ellipsizedWidth, maxLines); - } - - /** @hide */ - public StaticLayout(CharSequence source, PrecomputedText precomputed, int bufstart, int bufend, + public StaticLayout(CharSequence source, int bufstart, int bufend, TextPaint paint, int outerwidth, Alignment align, TextDirectionHeuristic textDir, float spacingmult, float spacingadd, @@ -545,7 +511,6 @@ public class StaticLayout extends Layout { : (source instanceof Spanned) ? new SpannedEllipsizer(source) : new Ellipsizer(source), - (ellipsize == null) ? precomputed : null, paint, outerwidth, align, textDir, spacingmult, spacingadd); Builder b = Builder.obtain(source, bufstart, bufend, paint, outerwidth) @@ -686,20 +651,43 @@ public class StaticLayout extends Layout { b.mJustificationMode != Layout.JUSTIFICATION_MODE_NONE, indents, mLeftPaddings, mRightPaddings); - PrecomputedText measured = null; - final Spanned spanned = (b.mText instanceof Spanned) ? (Spanned) b.mText : null; - if (b.mPrecomputed != null) { - if (b.mPrecomputed.canUseMeasuredResult(bufStart, bufEnd, textDir, paint, - b.mBreakStrategy, b.mHyphenationFrequency)) { - measured = b.mPrecomputed; + MeasuredText measured = null; + final Spanned spanned; + final boolean canUseMeasuredText; + if (source instanceof MeasuredText) { + measured = (MeasuredText) source; + + if (bufStart != measured.getStart() || bufEnd != measured.getEnd()) { + // The buffer position has changed. Re-measure here. + canUseMeasuredText = false; + } else if (b.mBreakStrategy != measured.getBreakStrategy() + || b.mHyphenationFrequency != measured.getHyphenationFrequency()) { + // The computed hyphenation pieces may not be able to used. Re-measure it. + canUseMeasuredText = false; + } else { + // We can use measured information. + canUseMeasuredText = true; } - } - if (measured == null) { - final PrecomputedText.Params param = new PrecomputedText.Params(paint, textDir, - b.mBreakStrategy, b.mHyphenationFrequency); - measured = PrecomputedText.createWidthOnly(source, param, bufStart, bufEnd); + } else { + canUseMeasuredText = false; } + if (!canUseMeasuredText) { + measured = new MeasuredText.Builder(source, paint) + .setRange(bufStart, bufEnd) + .setTextDirection(textDir) + .setBreakStrategy(b.mBreakStrategy) + .setHyphenationFrequency(b.mHyphenationFrequency) + .build(false /* full layout is not necessary for line breaking */); + spanned = (source instanceof Spanned) ? (Spanned) source : null; + } else { + final CharSequence original = measured.getText(); + spanned = (original instanceof Spanned) ? (Spanned) original : null; + // Overwrite with the one when measured. + // TODO: Give an option for developer not to overwrite and measure again here? + textDir = measured.getTextDir(); + paint = measured.getPaint(); + } try { for (int paraIndex = 0; paraIndex < measured.getParagraphCount(); paraIndex++) { diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java index be5bb4d1809a..55367dcce47e 100644 --- a/core/java/android/text/TextLine.java +++ b/core/java/android/text/TextLine.java @@ -16,7 +16,6 @@ package android.text; -import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.graphics.Canvas; @@ -61,7 +60,7 @@ public class TextLine { private char[] mChars; private boolean mCharsValid; private Spanned mSpanned; - private PrecomputedText mComputed; + private MeasuredText mMeasured; // Additional width of whitespace for justification. This value is per whitespace, thus // the line width will increase by mAddedWidth x (number of stretchable whitespaces). @@ -120,7 +119,7 @@ public class TextLine { tl.mSpanned = null; tl.mTabs = null; tl.mChars = null; - tl.mComputed = null; + tl.mMeasured = null; tl.mMetricAffectingSpanSpanSet.recycle(); tl.mCharacterStyleSpanSet.recycle(); @@ -150,31 +149,10 @@ public class TextLine { * @param tabStops the tabStops. Can be null. */ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) - public void set(TextPaint paint, CharSequence text, int start, - int limit, int dir, Directions directions, boolean hasTabs, TabStops tabStops) { - set(paint, text, null, start, limit, dir, directions, hasTabs, tabStops); - } - - /** - * Initializes a TextLine and prepares it for use. - * - * @param paint the base paint for the line - * @param text the text, can be Styled - * @param precomputed the precomputed text - * @param start the start of the line relative to the text - * @param limit the limit of the line relative to the text - * @param dir the paragraph direction of this line - * @param directions the directions information of this line - * @param hasTabs true if the line might contain tabs - * @param tabStops the tabStops. - */ - public void set(@NonNull TextPaint paint, @NonNull CharSequence text, - @Nullable PrecomputedText precomputed, @IntRange(from = 0) int start, - @IntRange(from = 0) int limit, int dir, @NonNull Directions directions, boolean hasTabs, - @Nullable TabStops tabStops) { + public void set(TextPaint paint, CharSequence text, int start, int limit, int dir, + Directions directions, boolean hasTabs, TabStops tabStops) { mPaint = paint; mText = text; - mComputed = precomputed; mStart = start; mLen = limit - start; mDir = dir; @@ -192,6 +170,14 @@ public class TextLine { hasReplacement = mReplacementSpanSpanSet.numberOfSpans > 0; } + mMeasured = null; + if (text instanceof MeasuredText) { + MeasuredText mt = (MeasuredText) text; + if (mt.canUseMeasuredResult(paint)) { + mMeasured = mt; + } + } + mCharsValid = hasReplacement || hasTabs || directions != Layout.DIRS_ALL_LEFT_TO_RIGHT; if (mCharsValid) { @@ -760,12 +746,12 @@ public class TextLine { return wp.getRunAdvance(mChars, start, end, contextStart, contextEnd, runIsRtl, offset); } else { final int delta = mStart; - if (mComputed == null) { + if (mMeasured == null) { // TODO: Enable measured getRunAdvance for ReplacementSpan and RTL text. return wp.getRunAdvance(mText, delta + start, delta + end, delta + contextStart, delta + contextEnd, runIsRtl, delta + offset); } else { - return mComputed.getWidth(start + delta, end + delta); + return mMeasured.getWidth(start + delta, end + delta); } } } diff --git a/core/java/android/util/ExceptionUtils.java b/core/java/android/util/ExceptionUtils.java index da7387fcae70..1a397b39ef3c 100644 --- a/core/java/android/util/ExceptionUtils.java +++ b/core/java/android/util/ExceptionUtils.java @@ -86,4 +86,16 @@ public class ExceptionUtils { 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 000000000000..86ed1229b28e --- /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/util/StatsLog.java b/core/java/android/util/StatsLog.java index 4805318385d5..3350f3e164bc 100644 --- a/core/java/android/util/StatsLog.java +++ b/core/java/android/util/StatsLog.java @@ -34,7 +34,7 @@ public final class StatsLog extends StatsLogInternal { */ public static boolean logStart(int label) { if (label >= 0 && label < 16) { - StatsLog.write(APP_HOOK, label, APP_HOOK__STATE__START); + StatsLog.write(APP_BREADCRUMB_REPORTED, label, APP_BREADCRUMB_REPORTED__STATE__START); return true; } return false; @@ -48,7 +48,7 @@ public final class StatsLog extends StatsLogInternal { */ public static boolean logStop(int label) { if (label >= 0 && label < 16) { - StatsLog.write(APP_HOOK, label, APP_HOOK__STATE__STOP); + StatsLog.write(APP_BREADCRUMB_REPORTED, label, APP_BREADCRUMB_REPORTED__STATE__STOP); return true; } return false; @@ -62,7 +62,8 @@ public final class StatsLog extends StatsLogInternal { */ public static boolean logEvent(int label) { if (label >= 0 && label < 16) { - StatsLog.write(APP_HOOK, label, APP_HOOK__STATE__UNSPECIFIED); + StatsLog.write(APP_BREADCRUMB_REPORTED, label, + APP_BREADCRUMB_REPORTED__STATE__UNSPECIFIED); return true; } return false; diff --git a/core/java/android/view/PointerIcon.java b/core/java/android/view/PointerIcon.java index 3fd469630db0..8cb46b704c18 100644 --- a/core/java/android/view/PointerIcon.java +++ b/core/java/android/view/PointerIcon.java @@ -23,6 +23,10 @@ import android.content.res.Resources; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Rect; +import android.graphics.RectF; import android.graphics.drawable.AnimationDrawable; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; @@ -396,6 +400,33 @@ public final class PointerIcon implements Parcelable { return true; } + /** + * Get the Bitmap from the Drawable. + * + * If the Bitmap needed to be scaled up to account for density, BitmapDrawable + * handles this at draw time. But this class doesn't actually draw the Bitmap; + * it is just a holder for native code to access its SkBitmap. So this needs to + * get a version that is scaled to account for density. + */ + private Bitmap getBitmapFromDrawable(BitmapDrawable bitmapDrawable) { + Bitmap bitmap = bitmapDrawable.getBitmap(); + final int scaledWidth = bitmapDrawable.getIntrinsicWidth(); + final int scaledHeight = bitmapDrawable.getIntrinsicHeight(); + if (scaledWidth == bitmap.getWidth() && scaledHeight == bitmap.getHeight()) { + return bitmap; + } + + Rect src = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight()); + RectF dst = new RectF(0, 0, scaledWidth, scaledHeight); + + Bitmap scaled = Bitmap.createBitmap(scaledWidth, scaledHeight, bitmap.getConfig()); + Canvas canvas = new Canvas(scaled); + Paint paint = new Paint(); + paint.setFilterBitmap(true); + canvas.drawBitmap(bitmap, src, dst, paint); + return scaled; + } + private void loadResource(Context context, Resources resources, @XmlRes int resourceId) { final XmlResourceParser parser = resources.getXml(resourceId); final int bitmapRes; @@ -452,7 +483,8 @@ public final class PointerIcon implements Parcelable { + "is different. All frames should have the exact same size and " + "share the same hotspot."); } - mBitmapFrames[i - 1] = ((BitmapDrawable)drawableFrame).getBitmap(); + BitmapDrawable bitmapDrawableFrame = (BitmapDrawable) drawableFrame; + mBitmapFrames[i - 1] = getBitmapFromDrawable(bitmapDrawableFrame); } } } @@ -461,7 +493,8 @@ public final class PointerIcon implements Parcelable { + "refer to a bitmap drawable."); } - final Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap(); + BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable; + final Bitmap bitmap = getBitmapFromDrawable(bitmapDrawable); validateHotSpot(bitmap, hotSpotX, hotSpotY); // Set the properties now that we have successfully loaded the icon. mBitmap = bitmap; diff --git a/core/java/android/view/RecordingCanvas.java b/core/java/android/view/RecordingCanvas.java index fc7d828de12e..fbb862be54ef 100644 --- a/core/java/android/view/RecordingCanvas.java +++ b/core/java/android/view/RecordingCanvas.java @@ -34,7 +34,7 @@ import android.graphics.Rect; import android.graphics.RectF; import android.graphics.TemporaryBuffer; import android.text.GraphicsOperations; -import android.text.PrecomputedText; +import android.text.MeasuredText; import android.text.SpannableString; import android.text.SpannedString; import android.text.TextUtils; @@ -507,8 +507,8 @@ public class RecordingCanvas extends Canvas { TextUtils.getChars(text, contextStart, contextEnd, buf, 0); long measuredTextPtr = 0; int measuredTextOffset = 0; - if (text instanceof PrecomputedText) { - PrecomputedText mt = (PrecomputedText) text; + if (text instanceof MeasuredText) { + MeasuredText mt = (MeasuredText) text; int paraIndex = mt.findParaIndex(start); if (end <= mt.getParagraphEnd(paraIndex)) { // Only support if the target is in the same paragraph. @@ -641,7 +641,7 @@ public class RecordingCanvas extends Canvas { @FastNative private static native void nDrawTextRun(long nativeCanvas, char[] text, int start, int count, int contextStart, int contextCount, float x, float y, boolean isRtl, long nativePaint, - long nativePrecomputedText, int measuredTextOffset); + long nativeMeasuredText, int measuredTextOffset); @FastNative private static native void nDrawTextOnPath(long nativeCanvas, char[] text, int index, int count, diff --git a/core/java/android/view/RenderNodeAnimator.java b/core/java/android/view/RenderNodeAnimator.java index c4a716011765..d26a2f643ed4 100644 --- a/core/java/android/view/RenderNodeAnimator.java +++ b/core/java/android/view/RenderNodeAnimator.java @@ -158,7 +158,7 @@ public class RenderNodeAnimator extends Animator { } private void applyInterpolator() { - if (mInterpolator == null) return; + if (mInterpolator == null || mNativePtr == null) return; long ni; if (isNativeInterpolator(mInterpolator)) { diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java index 370c97e37262..e50d40ef098f 100644 --- a/core/java/android/view/ThreadedRenderer.java +++ b/core/java/android/view/ThreadedRenderer.java @@ -331,6 +331,7 @@ public final class ThreadedRenderer { private static final int FLAG_DUMP_FRAMESTATS = 1 << 0; private static final int FLAG_DUMP_RESET = 1 << 1; + private static final int FLAG_DUMP_ALL = FLAG_DUMP_FRAMESTATS; @IntDef(flag = true, prefix = { "FLAG_DUMP_" }, value = { FLAG_DUMP_FRAMESTATS, @@ -636,7 +637,10 @@ public final class ThreadedRenderer { */ void dumpGfxInfo(PrintWriter pw, FileDescriptor fd, String[] args) { pw.flush(); - int flags = 0; + // If there's no arguments, eg 'dumpsys gfxinfo', then dump everything. + // If there's a targetted package, eg 'dumpsys gfxinfo com.android.systemui', then only + // dump the summary information + int flags = (args == null || args.length == 0) ? FLAG_DUMP_ALL : 0; for (int i = 0; i < args.length; i++) { switch (args[i]) { case "framestats": @@ -645,6 +649,9 @@ public final class ThreadedRenderer { case "reset": flags |= FLAG_DUMP_RESET; break; + case "-a": // magic option passed when dumping a bugreport. + flags = FLAG_DUMP_ALL; + break; } } nDumpProfileInfo(mNativeProxy, fd, flags); diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 2af246758812..790e227071a9 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -7272,7 +7272,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, // becomes true where it should issue notifyViewEntered(). afm.notifyViewEntered(this); } - } else if (!isFocused()) { + } else if (!enter && !isFocused()) { afm.notifyViewExited(this); } } @@ -8807,6 +8807,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, /** * Computes whether this virtual autofill view is visible to the user. * + * <p><b>Note: </b>By default it returns {@code true}, but views providing a virtual hierarchy + * view must override it. + * * @return Whether the view is visible on the screen. */ public boolean isVisibleToUserForAutofill(int virtualId) { @@ -8819,7 +8822,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } } } - return false; + return true; } /** diff --git a/core/java/android/view/animation/AnimationUtils.java b/core/java/android/view/animation/AnimationUtils.java index 990fbdb019d6..29f8442b2b46 100644 --- a/core/java/android/view/animation/AnimationUtils.java +++ b/core/java/android/view/animation/AnimationUtils.java @@ -18,6 +18,7 @@ package android.view.animation; import android.annotation.AnimRes; import android.annotation.InterpolatorRes; +import android.annotation.TestApi; import android.content.Context; import android.content.res.Resources; import android.content.res.Resources.NotFoundException; @@ -58,14 +59,43 @@ public class AnimationUtils { } }; - /** @hide */ + /** + * Locks AnimationUtils{@link #currentAnimationTimeMillis()} to a fixed value for the current + * thread. This is used by {@link android.view.Choreographer} to ensure that all accesses + * during a vsync update are synchronized to the timestamp of the vsync. + * + * It is also exposed to tests to allow for rapid, flake-free headless testing. + * + * Must be followed by a call to {@link #unlockAnimationClock()} to allow time to + * progress. Failing to do this will result in stuck animations, scrolls, and flings. + * + * Note that time is not allowed to "rewind" and must perpetually flow forward. So the + * lock may fail if the time is in the past from a previously returned value, however + * time will be frozen for the duration of the lock. The clock is a thread-local, so + * ensure that {@link #lockAnimationClock(long)}, {@link #unlockAnimationClock()}, and + * {@link #currentAnimationTimeMillis()} are all called on the same thread. + * + * This is also not reference counted in any way. Any call to {@link #unlockAnimationClock()} + * will unlock the clock for everyone on the same thread. It is therefore recommended + * for tests to use their own thread to ensure that there is no collision with any existing + * {@link android.view.Choreographer} instance. + * + * @hide + * */ + @TestApi public static void lockAnimationClock(long vsyncMillis) { AnimationState state = sAnimationState.get(); state.animationClockLocked = true; state.currentVsyncTimeMillis = vsyncMillis; } - /** @hide */ + /** + * Frees the time lock set in place by {@link #lockAnimationClock(long)}. Must be called + * to allow the animation clock to self-update. + * + * @hide + */ + @TestApi public static void unlockAnimationClock() { sAnimationState.get().animationClockLocked = false; } diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java index 8b64bad8fe62..95c12fb8c1db 100644 --- a/core/java/android/view/autofill/AutofillManager.java +++ b/core/java/android/view/autofill/AutofillManager.java @@ -356,6 +356,13 @@ public final class AutofillManager { @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 @@ public final class AutofillManager { } @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 @@ public final class AutofillManager { /** 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 @@ public final class AutofillManager { } 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 @@ public final class AutofillManager { // Update focus on existing session. updateSessionLocked(id, null, value, ACTION_VIEW_ENTERED, flags); } + addEnteredIdLocked(id); } } return callback; @@ -900,8 +920,9 @@ public final class AutofillManager { @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 @@ public final class AutofillManager { } 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 @@ public final class AutofillManager { // 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 @@ public final class AutofillManager { } 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 @@ public final class AutofillManager { } synchronized (mLock) { if (!mEnabled || !isActiveLocked()) { + if (sVerbose) { + Log.v(TAG, "notifyValueChanged(" + view.getAutofillId() + ":" + virtualId + + "): ignoring on state " + getStateAsStringLocked()); + } return; } @@ -1032,7 +1064,6 @@ public final class AutofillManager { } } - /** * Called when a {@link View} is clicked. Currently only used by views that should trigger save. * @@ -1058,16 +1089,16 @@ public final class AutofillManager { * * @hide */ - public void onActivityFinished() { + public void onActivityFinishing() { if (!hasAutofillFeature()) { return; } synchronized (mLock) { if (mSaveOnFinish) { - if (sDebug) Log.d(TAG, "Committing session on finish() as requested by service"); + if (sDebug) Log.d(TAG, "onActivityFinishing(): calling commitLocked()"); commitLocked(); } else { - if (sDebug) Log.d(TAG, "Cancelling session on finish() as requested by service"); + if (sDebug) Log.d(TAG, "onActivityFinishing(): calling cancelLocked()"); cancelLocked(); } } @@ -1088,6 +1119,7 @@ public final class AutofillManager { if (!hasAutofillFeature()) { return; } + if (sVerbose) Log.v(TAG, "commit() called by app"); synchronized (mLock) { commitLocked(); } @@ -1392,7 +1424,8 @@ public final class AutofillManager { 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) { @@ -1407,7 +1440,8 @@ public final class AutofillManager { mSessionId = mService.startSession(client.autofillClientGetActivityToken(), mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(), - mCallback != null, flags, client.autofillClientGetComponentName()); + mCallback != null, flags, client.autofillClientGetComponentName(), + isCompatibilityModeEnabledLocked()); if (mSessionId != NO_SESSION) { mState = STATE_ACTIVE; } @@ -1429,7 +1463,7 @@ public final class AutofillManager { throw e.rethrowFromSystemServer(); } - resetSessionLocked(); + resetSessionLocked(/* resetEnteredIds= */ true); } @GuardedBy("mLock") @@ -1444,22 +1478,25 @@ public final class AutofillManager { 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); } @@ -1474,7 +1511,7 @@ public final class AutofillManager { client.autofillClientGetActivityToken(), mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(), mCallback != null, flags, client.autofillClientGetComponentName(), - mSessionId, action); + mSessionId, action, isCompatibilityModeEnabledLocked()); if (newId != mSessionId) { if (sDebug) Log.d(TAG, "Session restarted: " + mSessionId + "=>" + newId); mSessionId = newId; @@ -1629,7 +1666,7 @@ public final class AutofillManager { 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 @@ -1825,7 +1862,7 @@ public final class AutofillManager { private void setSessionFinished(int newState) { synchronized (mLock) { if (sVerbose) Log.v(TAG, "setSessionFinished(): from " + mState + " to " + newState); - resetSessionLocked(); + resetSessionLocked(/* resetEnteredIds= */ false); mState = newState; } } @@ -1953,6 +1990,7 @@ public final class AutofillManager { 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( @@ -2295,6 +2333,7 @@ public final class AutofillManager { final boolean[] isVisible; if (client.autofillClientIsVisibleForAutofill()) { + if (sVerbose) Log.v(TAG, "client is visible, check tracked ids"); isVisible = client.autofillClientGetViewVisibility(trackedIds); } else { // All false @@ -2314,7 +2353,7 @@ public final class AutofillManager { } if (sVerbose) { - Log.v(TAG, "TrackedViews(trackedIds=" + trackedIds + "): " + Log.v(TAG, "TrackedViews(trackedIds=" + Arrays.toString(trackedIds) + "): " + " mVisibleTrackedIds=" + mVisibleTrackedIds + " mInvisibleTrackedIds=" + mInvisibleTrackedIds); } @@ -2420,6 +2459,9 @@ public final class AutofillManager { } if (mVisibleTrackedIds == null) { + if (sVerbose) { + Log.v(TAG, "onVisibleForAutofillChangedLocked(): no more visible ids"); + } finishSessionLocked(); } } diff --git a/core/java/android/view/autofill/AutofillPopupWindow.java b/core/java/android/view/autofill/AutofillPopupWindow.java index e80fdd93542c..1da998d01ba3 100644 --- a/core/java/android/view/autofill/AutofillPopupWindow.java +++ b/core/java/android/view/autofill/AutofillPopupWindow.java @@ -46,6 +46,7 @@ public class AutofillPopupWindow extends PopupWindow { private final WindowPresenter mWindowPresenter; private WindowManager.LayoutParams mWindowLayoutParams; + private boolean mFullScreen; private final View.OnAttachStateChangeListener mOnAttachStateChangeListener = new View.OnAttachStateChangeListener() { @@ -104,12 +105,17 @@ public class AutofillPopupWindow extends PopupWindow { */ 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 @@ public class AutofillPopupWindow extends PopupWindow { } @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/view/autofill/IAutoFillManager.aidl b/core/java/android/view/autofill/IAutoFillManager.aidl index 001854716715..56f79abf6c19 100644 --- a/core/java/android/view/autofill/IAutoFillManager.aidl +++ b/core/java/android/view/autofill/IAutoFillManager.aidl @@ -38,7 +38,7 @@ interface IAutoFillManager { void removeClient(in IAutoFillManagerClient client, int userId); int startSession(IBinder activityToken, in IBinder appCallback, in AutofillId autoFillId, in Rect bounds, in AutofillValue value, int userId, boolean hasCallback, int flags, - in ComponentName componentName); + in ComponentName componentName, boolean compatMode); FillEventHistory getFillEventHistory(); boolean restoreSession(int sessionId, in IBinder activityToken, in IBinder appCallback); void updateSession(int sessionId, in AutofillId id, in Rect bounds, @@ -46,7 +46,7 @@ interface IAutoFillManager { int updateOrRestartSession(IBinder activityToken, in IBinder appCallback, in AutofillId autoFillId, in Rect bounds, in AutofillValue value, int userId, boolean hasCallback, int flags, in ComponentName componentName, int sessionId, - int action); + int action, boolean compatMode); void finishSession(int sessionId, int userId); void cancelSession(int sessionId, int userId); void setAuthenticationResult(in Bundle data, int sessionId, int authenticationId, int userId); diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java index e91db1390582..7217def3cf08 100644 --- a/core/java/android/widget/PopupWindow.java +++ b/core/java/android/widget/PopupWindow.java @@ -1583,7 +1583,7 @@ public class PopupWindow { * * @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 f6e771a9605e..5710db3ce8e0 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -80,8 +80,8 @@ import android.text.GraphicsOperations; import android.text.InputFilter; import android.text.InputType; import android.text.Layout; +import android.text.MeasuredText; import android.text.ParcelableSpan; -import android.text.PrecomputedText; import android.text.Selection; import android.text.SpanWatcher; import android.text.Spannable; @@ -637,7 +637,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private CharSequence mText; private CharSequence mTransformed; private BufferType mBufferType = BufferType.NORMAL; - private PrecomputedText mPrecomputed; private CharSequence mHint; private Layout mHintLayout; @@ -4086,80 +4085,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } /** - * Gets the parameters for text layout precomputation, for use with {@link PrecomputedText} - * - * @return A current {@link PrecomputedText.Params} - */ - public @NonNull PrecomputedText.Params getTextMetricsParams() { - return new PrecomputedText.Params(new TextPaint(mTextPaint), getTextDirectionHeuristic(), - mBreakStrategy, mHyphenationFrequency); - } - - /** - * Apply the text layout parameter. - */ - public void setTextMetricsParams(@NonNull PrecomputedText.Params params) { - mTextPaint.set(params.getTextPaint()); - mTextDir = params.getTextDirection(); - mBreakStrategy = params.getBreakStrategy(); - mHyphenationFrequency = params.getHyphenationFrequency(); - if (mLayout != null) { - nullLayouts(); - requestLayout(); - invalidate(); - } - } - - /** - * Sets the precomputed text. - * - * If the parameters for the precomputed text is different from current text view parameters, - * apply the parameteres to the text view too. - * - * @param text A precomputed text. - */ - public void setPrecomputedTextAndParams(@NonNull PrecomputedText text) { - Preconditions.checkNotNull(text); - final PrecomputedText.Params params = text.getParams(); - if (!params.sameTextMetrics(getTextMetricsParams())) { - setTextMetricsParams(params); - } - setText(text.getText()); - if (mTransformed != text.getText()) { - // setText modified given text for some reasons, selection, transformation, etc. - // Can't use computed result. - return; - } else { - mPrecomputed = text; - } - } - - /** - * Sets the precomputed text. - * - * If the parameters for the precomputed text is different from current text view parameters, - * throws {@link IllegalArgumentException}. - * - * @param text A precomputed text. - */ - public void setPrecomputedTextOrThrow(@NonNull PrecomputedText text) { - Preconditions.checkNotNull(text); - final PrecomputedText.Params params = text.getParams(); - if (!params.sameTextMetrics(getTextMetricsParams())) { - throw new IllegalArgumentException( - "The precomputed configuration is different from this TextView."); - } - setText(text.getText()); - if (mTransformed != text.getText()) { - // setText modified given text for some reasons, selection, transformation, etc. - // Can't use computed result. - // TODO: Do we throw an exception here too? - } else { - mPrecomputed = text; - } - } - - /** * Set justification mode. The default value is {@link Layout#JUSTIFICATION_MODE_NONE}. If the * last line is too short for justification, the last line will be displayed with the * alignment set by {@link android.view.View#setTextAlignment}. @@ -5594,7 +5519,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private void setText(CharSequence text, BufferType type, boolean notifyBefore, int oldlen) { - mPrecomputed = null; mTextSetFromXmlOrResourceId = false; if (text == null) { text = ""; @@ -5653,7 +5577,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (imm != null) imm.restartInput(this); } else if (type == BufferType.SPANNABLE || mMovement != null) { text = mSpannableFactory.newSpannable(text); - } else if (!(text instanceof CharWrapper)) { + } else if (!(text instanceof MeasuredText || text instanceof CharWrapper)) { text = TextUtils.stringOrSpannedString(text); } @@ -8320,8 +8244,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener result = builder.build(); } else { if (boring == UNKNOWN_BORING) { - boring = BoringLayout.isBoring(mTransformed, mPrecomputed, mTextPaint, mTextDir, - mBoring); + boring = BoringLayout.isBoring(mTransformed, mTextPaint, mTextDir, mBoring); if (boring != null) { mBoring = boring; } @@ -8359,15 +8282,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } } if (result == null) { - StaticLayout.Builder builder; - if (mPrecomputed != null) { - builder = StaticLayout.Builder.obtain(mPrecomputed, 0, - mPrecomputed.getText().length(), mTextPaint, wantWidth); - } else { - builder = StaticLayout.Builder.obtain(mTransformed, 0, mTransformed.length(), - mTextPaint, wantWidth); - } - builder.setAlignment(alignment) + StaticLayout.Builder builder = StaticLayout.Builder.obtain(mTransformed, + 0, mTransformed.length(), mTextPaint, wantWidth) + .setAlignment(alignment) .setTextDirection(mTextDir) .setLineSpacing(mSpacingAdd, mSpacingMult) .setIncludePad(mIncludePad) @@ -8494,8 +8411,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } if (des < 0) { - boring = BoringLayout.isBoring(mTransformed, mPrecomputed, mTextPaint, mTextDir, - mBoring); + boring = BoringLayout.isBoring(mTransformed, mTextPaint, mTextDir, mBoring); if (boring != null) { mBoring = boring; } @@ -11780,9 +11696,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } /** - * Returns the current {@link TextDirectionHeuristic} - * - * @return A {@link TextDirectionHeuristic}. * @hide */ protected TextDirectionHeuristic getTextDirectionHeuristic() { diff --git a/core/java/com/android/internal/colorextraction/drawable/GradientDrawable.java b/core/java/com/android/internal/colorextraction/drawable/GradientDrawable.java index 500c028ed4c6..bf151c39271a 100644 --- a/core/java/com/android/internal/colorextraction/drawable/GradientDrawable.java +++ b/core/java/com/android/internal/colorextraction/drawable/GradientDrawable.java @@ -57,6 +57,8 @@ public class GradientDrawable extends Drawable { private int mMainColor; private int mSecondaryColor; private ValueAnimator mColorAnimation; + private int mMainColorTo; + private int mSecondaryColorTo; public GradientDrawable(@NonNull Context context) { mDensity = context.getResources().getDisplayMetrics().density; @@ -76,7 +78,7 @@ public class GradientDrawable extends Drawable { } public void setColors(int mainColor, int secondaryColor, boolean animated) { - if (mainColor == mMainColor && secondaryColor == mSecondaryColor) { + if (mainColor == mMainColorTo && secondaryColor == mSecondaryColorTo) { return; } @@ -84,6 +86,9 @@ public class GradientDrawable extends Drawable { mColorAnimation.cancel(); } + mMainColorTo = mainColor; + mSecondaryColorTo = mainColor; + if (animated) { final int mainFrom = mMainColor; final int secFrom = mSecondaryColor; diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index eb58b0919385..4c5991ef8afb 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -187,7 +187,7 @@ public class BatteryStatsImpl extends BatteryStats { public final AtomicFile mCheckinFile; public final AtomicFile mDailyFile; - static final int MSG_UPDATE_WAKELOCKS = 1; + static final int MSG_REPORT_CPU_UPDATE_NEEDED = 1; static final int MSG_REPORT_POWER_CHANGE = 2; static final int MSG_REPORT_CHARGING = 3; static final long DELAY_UPDATE_WAKELOCKS = 5*1000; @@ -273,10 +273,7 @@ public class BatteryStatsImpl extends BatteryStats { public void handleMessage(Message msg) { BatteryCallback cb = mCallback; switch (msg.what) { - case MSG_UPDATE_WAKELOCKS: - synchronized (BatteryStatsImpl.this) { - updateCpuTimeLocked(); - } + case MSG_REPORT_CPU_UPDATE_NEEDED: if (cb != null) { cb.batteryNeedsCpuUpdate(); } @@ -302,6 +299,10 @@ public class BatteryStatsImpl extends BatteryStats { } } + public void postBatteryNeedsCpuUpdateMsg() { + mHandler.sendEmptyMessage(MSG_REPORT_CPU_UPDATE_NEEDED); + } + /** * Update per-freq cpu times for all the uids in {@link #mPendingUids}. */ @@ -487,6 +488,10 @@ public class BatteryStatsImpl extends BatteryStats { Future<?> scheduleReadProcStateCpuTimes(boolean onBattery, boolean onBatteryScreenOff); Future<?> scheduleCopyFromAllUidsCpuTimes(boolean onBattery, boolean onBatteryScreenOff); Future<?> scheduleCpuSyncDueToSettingChange(); + Future<?> scheduleCpuSyncDueToScreenStateChange(boolean onBattery, + boolean onBatteryScreenOff); + Future<?> scheduleCpuSyncDueToWakelockChange(long delayMillis); + void cancelCpuSyncDueToWakelockChange(); } public Handler mHandler; @@ -1453,12 +1458,10 @@ public class BatteryStatsImpl extends BatteryStats { long mCount; long mLoadedCount; long mUnpluggedCount; - long mPluggedCount; LongSamplingCounter(TimeBase timeBase, Parcel in) { mTimeBase = timeBase; - mPluggedCount = in.readLong(); - mCount = mPluggedCount; + mCount = in.readLong(); mLoadedCount = in.readLong(); mUnpluggedCount = in.readLong(); timeBase.add(this); @@ -1477,16 +1480,15 @@ public class BatteryStatsImpl extends BatteryStats { @Override public void onTimeStarted(long elapsedRealtime, long baseUptime, long baseRealtime) { - mUnpluggedCount = mPluggedCount; + mUnpluggedCount = mCount; } @Override public void onTimeStopped(long elapsedRealtime, long baseUptime, long baseRealtime) { - mPluggedCount = mCount; } public long getCountLocked(int which) { - long val = mTimeBase.isRunning() ? mCount : mPluggedCount; + long val = mCount; if (which == STATS_SINCE_UNPLUGGED) { val -= mUnpluggedCount; } else if (which != STATS_SINCE_CHARGED) { @@ -1499,12 +1501,15 @@ public class BatteryStatsImpl extends BatteryStats { public void logState(Printer pw, String prefix) { pw.println(prefix + "mCount=" + mCount + " mLoadedCount=" + mLoadedCount - + " mUnpluggedCount=" + mUnpluggedCount - + " mPluggedCount=" + mPluggedCount); + + " mUnpluggedCount=" + mUnpluggedCount); } void addCountLocked(long count) { - if (mTimeBase.isRunning()) { + addCountLocked(count, mTimeBase.isRunning()); + } + + void addCountLocked(long count, boolean isRunning) { + if (isRunning) { mCount += count; } } @@ -1514,7 +1519,7 @@ public class BatteryStatsImpl extends BatteryStats { */ void reset(boolean detachIfReset) { mCount = 0; - mLoadedCount = mPluggedCount = mUnpluggedCount = 0; + mLoadedCount = mUnpluggedCount = 0; if (detachIfReset) { detach(); } @@ -1531,7 +1536,7 @@ public class BatteryStatsImpl extends BatteryStats { void readSummaryFromParcelLocked(Parcel in) { mLoadedCount = in.readLong(); mCount = mLoadedCount; - mUnpluggedCount = mPluggedCount = mLoadedCount; + mUnpluggedCount = mLoadedCount; } } @@ -3852,9 +3857,6 @@ public class BatteryStatsImpl extends BatteryStats { + Display.stateToString(screenState) + " and battery is " + (unplugged ? "on" : "off")); } - updateCpuTimeLocked(); - mExternalSync.scheduleCopyFromAllUidsCpuTimes(mOnBatteryTimeBase.isRunning(), - mOnBatteryScreenOffTimeBase.isRunning()); mOnBatteryTimeBase.setRunning(unplugged, uptime, realtime); if (updateOnBatteryTimeBase) { @@ -4143,15 +4145,11 @@ public class BatteryStatsImpl extends BatteryStats { } private void requestWakelockCpuUpdate() { - if (!mHandler.hasMessages(MSG_UPDATE_WAKELOCKS)) { - Message m = mHandler.obtainMessage(MSG_UPDATE_WAKELOCKS); - mHandler.sendMessageDelayed(m, DELAY_UPDATE_WAKELOCKS); - } + mExternalSync.scheduleCpuSyncDueToWakelockChange(DELAY_UPDATE_WAKELOCKS); } private void requestImmediateCpuUpdate() { - mHandler.removeMessages(MSG_UPDATE_WAKELOCKS); - mHandler.sendEmptyMessage(MSG_UPDATE_WAKELOCKS); + mExternalSync.scheduleCpuSyncDueToWakelockChange(0 /* delayMillis */); } public void setRecordAllHistoryLocked(boolean enabled) { @@ -4554,7 +4552,7 @@ public class BatteryStatsImpl extends BatteryStats { } public boolean startAddingCpuLocked() { - mHandler.removeMessages(MSG_UPDATE_WAKELOCKS); + mExternalSync.cancelCpuSyncDueToWakelockChange(); return mOnBatteryInternal; } @@ -4807,6 +4805,8 @@ public class BatteryStatsImpl extends BatteryStats { + Display.stateToString(state)); addHistoryRecordLocked(elapsedRealtime, uptime); } + mExternalSync.scheduleCpuSyncDueToScreenStateChange( + mOnBatteryTimeBase.isRunning(), mOnBatteryScreenOffTimeBase.isRunning()); if (isScreenOn(state)) { updateTimeBasesLocked(mOnBatteryTimeBase.isRunning(), state, mClocks.uptimeMillis() * 1000, elapsedRealtime * 1000); @@ -5286,6 +5286,18 @@ public class BatteryStatsImpl extends BatteryStats { 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; @@ -9196,8 +9208,14 @@ public class BatteryStatsImpl extends BatteryStats { } public void addCpuTimeLocked(int utime, int stime) { - mUserTime += utime; - mSystemTime += stime; + addCpuTimeLocked(utime, stime, mBsi.mOnBatteryTimeBase.isRunning()); + } + + public void addCpuTimeLocked(int utime, int stime, boolean isRunning) { + if (isRunning) { + mUserTime += utime; + mSystemTime += stime; + } } public void addForegroundTimeLocked(long ttime) { @@ -11761,13 +11779,24 @@ public class BatteryStatsImpl extends BatteryStats { } } + public boolean isOnBatteryLocked() { + return mOnBatteryTimeBase.isRunning(); + } + + public boolean isOnBatteryScreenOffLocked() { + return mOnBatteryScreenOffTimeBase.isRunning(); + } + /** * Read and distribute CPU usage across apps. If their are partial wakelocks being held * and we are on battery with screen off, we give more of the cpu time to those apps holding * wakelocks. If the screen is on, we just assign the actual cpu time an app used. + * It's possible this will be invoked after the internal battery/screen states are updated, so + * passing the appropriate battery/screen states to try attribute the cpu times to correct + * buckets. */ @GuardedBy("this") - public void updateCpuTimeLocked() { + public void updateCpuTimeLocked(boolean onBattery, boolean onBatteryScreenOff) { if (mPowerProfile == null) { return; } @@ -11784,7 +11813,7 @@ public class BatteryStatsImpl extends BatteryStats { // usually holding the wakelock on behalf of an app. // And Only distribute cpu power to wakelocks if the screen is off and we're on battery. ArrayList<StopwatchTimer> partialTimersToConsider = null; - if (mOnBatteryScreenOffTimeBase.isRunning()) { + if (onBatteryScreenOff) { partialTimersToConsider = new ArrayList<>(); for (int i = mPartialTimers.size() - 1; i >= 0; --i) { final StopwatchTimer timer = mPartialTimers.get(i); @@ -11802,7 +11831,7 @@ public class BatteryStatsImpl extends BatteryStats { // When the battery is not on, we don't attribute the cpu times to any timers but we still // need to take the snapshots. - if (!mOnBatteryInternal) { + if (!onBattery) { mKernelUidCpuTimeReader.readDelta(null); mKernelUidCpuFreqTimeReader.readDelta(null); if (mConstants.TRACK_CPU_ACTIVE_CLUSTER_TIME) { @@ -11818,16 +11847,16 @@ public class BatteryStatsImpl extends BatteryStats { mUserInfoProvider.refreshUserIds(); final SparseLongArray updatedUids = mKernelUidCpuFreqTimeReader.perClusterTimesAvailable() ? null : new SparseLongArray(); - readKernelUidCpuTimesLocked(partialTimersToConsider, updatedUids); + readKernelUidCpuTimesLocked(partialTimersToConsider, updatedUids, onBattery); // updatedUids=null means /proc/uid_time_in_state provides snapshots of per-cluster cpu // freqs, so no need to approximate these values. if (updatedUids != null) { - updateClusterSpeedTimes(updatedUids); + updateClusterSpeedTimes(updatedUids, onBattery); } - readKernelUidCpuFreqTimesLocked(partialTimersToConsider); + readKernelUidCpuFreqTimesLocked(partialTimersToConsider, onBattery, onBatteryScreenOff); if (mConstants.TRACK_CPU_ACTIVE_CLUSTER_TIME) { - readKernelUidCpuActiveTimesLocked(); - readKernelUidCpuClusterTimesLocked(); + readKernelUidCpuActiveTimesLocked(onBattery); + readKernelUidCpuClusterTimesLocked(onBattery); } } @@ -11867,7 +11896,7 @@ public class BatteryStatsImpl extends BatteryStats { * @param updatedUids The uids for which times spent at different frequencies are calculated. */ @VisibleForTesting - public void updateClusterSpeedTimes(@NonNull SparseLongArray updatedUids) { + public void updateClusterSpeedTimes(@NonNull SparseLongArray updatedUids, boolean onBattery) { long totalCpuClustersTimeMs = 0; // Read the time spent for each cluster at various cpu frequencies. final long[][] clusterSpeedTimesMs = new long[mKernelCpuSpeedReaders.length][]; @@ -11909,7 +11938,7 @@ public class BatteryStatsImpl extends BatteryStats { } cpuSpeeds[speed].addCountLocked(appCpuTimeUs * clusterSpeedTimesMs[cluster][speed] - / totalCpuClustersTimeMs); + / totalCpuClustersTimeMs, onBattery); } } } @@ -11926,7 +11955,7 @@ public class BatteryStatsImpl extends BatteryStats { */ @VisibleForTesting public void readKernelUidCpuTimesLocked(@Nullable ArrayList<StopwatchTimer> partialTimers, - @Nullable SparseLongArray updatedUids) { + @Nullable SparseLongArray updatedUids, boolean onBattery) { mTempTotalCpuUserTimeUs = mTempTotalCpuSystemTimeUs = 0; final int numWakelocks = partialTimers == null ? 0 : partialTimers.size(); final long startTimeMs = mClocks.uptimeMillis(); @@ -11977,8 +12006,8 @@ public class BatteryStatsImpl extends BatteryStats { Slog.d(TAG, sb.toString()); } - u.mUserCpuTime.addCountLocked(userTimeUs); - u.mSystemCpuTime.addCountLocked(systemTimeUs); + u.mUserCpuTime.addCountLocked(userTimeUs, onBattery); + u.mSystemCpuTime.addCountLocked(systemTimeUs, onBattery); if (updatedUids != null) { updatedUids.put(u.getUid(), userTimeUs + systemTimeUs); } @@ -12010,15 +12039,15 @@ public class BatteryStatsImpl extends BatteryStats { Slog.d(TAG, sb.toString()); } - timer.mUid.mUserCpuTime.addCountLocked(userTimeUs); - timer.mUid.mSystemCpuTime.addCountLocked(systemTimeUs); + timer.mUid.mUserCpuTime.addCountLocked(userTimeUs, onBattery); + timer.mUid.mSystemCpuTime.addCountLocked(systemTimeUs, onBattery); if (updatedUids != null) { final int uid = timer.mUid.getUid(); updatedUids.put(uid, updatedUids.get(uid, 0) + userTimeUs + systemTimeUs); } final Uid.Proc proc = timer.mUid.getProcessStatsLocked("*wakelock*"); - proc.addCpuTimeLocked(userTimeUs / 1000, systemTimeUs / 1000); + proc.addCpuTimeLocked(userTimeUs / 1000, systemTimeUs / 1000, onBattery); mTempTotalCpuUserTimeUs -= userTimeUs; mTempTotalCpuSystemTimeUs -= systemTimeUs; @@ -12033,7 +12062,8 @@ public class BatteryStatsImpl extends BatteryStats { * @param partialTimers The wakelock holders among which the cpu freq times will be distributed. */ @VisibleForTesting - public void readKernelUidCpuFreqTimesLocked(@Nullable ArrayList<StopwatchTimer> partialTimers) { + public void readKernelUidCpuFreqTimesLocked(@Nullable ArrayList<StopwatchTimer> partialTimers, + boolean onBattery, boolean onBatteryScreenOff) { final boolean perClusterTimesAvailable = mKernelUidCpuFreqTimeReader.perClusterTimesAvailable(); final int numWakelocks = partialTimers == null ? 0 : partialTimers.size(); @@ -12056,13 +12086,13 @@ public class BatteryStatsImpl extends BatteryStats { if (u.mCpuFreqTimeMs == null || u.mCpuFreqTimeMs.getSize() != cpuFreqTimeMs.length) { u.mCpuFreqTimeMs = new LongSamplingCounterArray(mOnBatteryTimeBase); } - u.mCpuFreqTimeMs.addCountLocked(cpuFreqTimeMs); + u.mCpuFreqTimeMs.addCountLocked(cpuFreqTimeMs, onBattery); if (u.mScreenOffCpuFreqTimeMs == null || u.mScreenOffCpuFreqTimeMs.getSize() != cpuFreqTimeMs.length) { u.mScreenOffCpuFreqTimeMs = new LongSamplingCounterArray( mOnBatteryScreenOffTimeBase); } - u.mScreenOffCpuFreqTimeMs.addCountLocked(cpuFreqTimeMs); + u.mScreenOffCpuFreqTimeMs.addCountLocked(cpuFreqTimeMs, onBatteryScreenOff); if (perClusterTimesAvailable) { if (u.mCpuClusterSpeedTimesUs == null || @@ -12098,7 +12128,7 @@ public class BatteryStatsImpl extends BatteryStats { } else { appAllocationUs = cpuFreqTimeMs[freqIndex] * 1000; } - cpuTimesUs[speed].addCountLocked(appAllocationUs); + cpuTimesUs[speed].addCountLocked(appAllocationUs, onBattery); freqIndex++; } } @@ -12132,7 +12162,7 @@ public class BatteryStatsImpl extends BatteryStats { } final long allocationUs = mWakeLockAllocationsUs[cluster][speed] / (numWakelocks - i); - cpuTimeUs[speed].addCountLocked(allocationUs); + cpuTimeUs[speed].addCountLocked(allocationUs, onBattery); mWakeLockAllocationsUs[cluster][speed] -= allocationUs; } } @@ -12145,7 +12175,7 @@ public class BatteryStatsImpl extends BatteryStats { * counters. */ @VisibleForTesting - public void readKernelUidCpuActiveTimesLocked() { + public void readKernelUidCpuActiveTimesLocked(boolean onBattery) { final long startTimeMs = mClocks.uptimeMillis(); mKernelUidCpuActiveTimeReader.readDelta((uid, cpuActiveTimesUs) -> { uid = mapUid(uid); @@ -12160,7 +12190,7 @@ public class BatteryStatsImpl extends BatteryStats { return; } final Uid u = getUidStatsLocked(uid); - u.mCpuActiveTimeMs.addCountLocked(cpuActiveTimesUs); + u.mCpuActiveTimeMs.addCountLocked(cpuActiveTimesUs, onBattery); }); final long elapsedTimeMs = mClocks.uptimeMillis() - startTimeMs; @@ -12174,7 +12204,7 @@ public class BatteryStatsImpl extends BatteryStats { * counters. */ @VisibleForTesting - public void readKernelUidCpuClusterTimesLocked() { + public void readKernelUidCpuClusterTimesLocked(boolean onBattery) { final long startTimeMs = mClocks.uptimeMillis(); mKernelUidCpuClusterTimeReader.readDelta((uid, cpuClusterTimesUs) -> { uid = mapUid(uid); @@ -12189,7 +12219,7 @@ public class BatteryStatsImpl extends BatteryStats { return; } final Uid u = getUidStatsLocked(uid); - u.mCpuClusterTimesMs.addCountLocked(cpuClusterTimesUs); + u.mCpuClusterTimesMs.addCountLocked(cpuClusterTimesUs, onBattery); }); final long elapsedTimeMs = mClocks.uptimeMillis() - startTimeMs; @@ -12399,9 +12429,7 @@ public class BatteryStatsImpl extends BatteryStats { reportChangesToStatsLog(mHaveBatteryLevel ? mHistoryCur : null, status, plugType, level, temp); - final boolean onBattery = - plugType == BATTERY_PLUGGED_NONE && - status != BatteryManager.BATTERY_STATUS_UNKNOWN; + final boolean onBattery = isOnBattery(plugType, status); final long uptime = mClocks.uptimeMillis(); final long elapsedRealtime = mClocks.elapsedRealtime(); if (!mHaveBatteryLevel) { @@ -12591,6 +12619,10 @@ public class BatteryStatsImpl extends BatteryStats { mMaxLearnedBatteryCapacity = Math.max(mMaxLearnedBatteryCapacity, chargeFullUAh); } + public static boolean isOnBattery(int plugType, int status) { + return plugType == BATTERY_PLUGGED_NONE && status != BatteryManager.BATTERY_STATUS_UNKNOWN; + } + // Inform StatsLog of setBatteryState changes. // If this is the first reporting, pass in recentPast == null. private void reportChangesToStatsLog(HistoryItem recentPast, diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java index 9c8997657474..30d81a7216a3 100644 --- a/core/java/com/android/internal/os/ZygoteInit.java +++ b/core/java/com/android/internal/os/ZygoteInit.java @@ -576,7 +576,8 @@ public class ZygoteInit { 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 1916c11e9c9d..f44a1d122f39 100644 --- a/core/java/com/android/internal/print/DumpUtils.java +++ b/core/java/com/android/internal/print/DumpUtils.java @@ -213,10 +213,9 @@ public class DumpUtils { 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 000000000000..21d750c59a4e --- /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 000000000000..ce79049a195d --- /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 2c05d0b976fc..482d028a67f3 100644 --- a/core/jni/android/graphics/Paint.cpp +++ b/core/jni/android/graphics/Paint.cpp @@ -1008,23 +1008,6 @@ namespace PaintGlue { return paint->getLooper() && paint->getLooper()->asABlurShadow(nullptr); } - static jboolean equalsForTextMeasurement(jlong lPaint, jlong rPaint) { - if (lPaint == rPaint) { - return true; - } - Paint* leftPaint = reinterpret_cast<Paint*>(lPaint); - Paint* rightPaint = reinterpret_cast<Paint*>(rPaint); - - const Typeface* leftTypeface = Typeface::resolveDefault(leftPaint->getAndroidTypeface()); - const Typeface* rightTypeface = Typeface::resolveDefault(rightPaint->getAndroidTypeface()); - minikin::MinikinPaint leftMinikinPaint - = MinikinUtils::prepareMinikinPaint(leftPaint, leftTypeface); - minikin::MinikinPaint rightMinikinPaint - = MinikinUtils::prepareMinikinPaint(rightPaint, rightTypeface); - - return leftMinikinPaint == rightMinikinPaint; - } - }; // namespace PaintGlue static const JNINativeMethod methods[] = { @@ -1124,8 +1107,7 @@ static const JNINativeMethod methods[] = { {"nGetStrikeThruPosition","(J)F", (void*) PaintGlue::getStrikeThruPosition}, {"nGetStrikeThruThickness","(J)F", (void*) PaintGlue::getStrikeThruThickness}, {"nSetShadowLayer", "(JFFFI)V", (void*)PaintGlue::setShadowLayer}, - {"nHasShadowLayer", "(J)Z", (void*)PaintGlue::hasShadowLayer}, - {"nEqualsForTextMeasurement", "(JJ)Z", (void*)PaintGlue::equalsForTextMeasurement}, + {"nHasShadowLayer", "(J)Z", (void*)PaintGlue::hasShadowLayer} }; int register_android_graphics_Paint(JNIEnv* env) { diff --git a/core/proto/android/net/OWNERS b/core/proto/android/net/OWNERS new file mode 100644 index 000000000000..a845dcbec661 --- /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 8481ffc112d4..46f4901d8a29 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; + // frequency time pre cpu, unit in jiffy. + message Stats { + option (android.msg_privacy).dest = DEST_AUTOMATIC; - optional string cpu_name = 1; + optional string cpu_name = 1; - message TimeInState { - option (android.msg_privacy).dest = DEST_AUTOMATIC; + message TimeInState { + option (android.msg_privacy).dest = DEST_AUTOMATIC; - optional int32 state_khz = 1; // cpu frequency - optional int64 time_jiffy = 2; // number of jiffies + 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 ca4360238c6b..ce69fc9d4037 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 @@ package android.os; * * 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 698f394385ab..9a53b89ffe30 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/service/usb.proto"; import "frameworks/base/core/proto/android/util/event_log_tags.proto"; import "frameworks/base/core/proto/android/util/log.proto"; import "frameworks/base/libs/incident/proto/android/os/header.proto"; +import "frameworks/base/libs/incident/proto/android/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 @@ message IncidentProto { ]; // 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 c296dab5a1ef..5021a06b1769 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 b8f618b5d4c2..f5d77d637733 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 @@ package android.os; * * 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; -} - -// Next tag: 5 -message MigrateTypeProto { - option (android.msg_privacy).dest = DEST_AUTOMATIC; + optional int32 node = 1; - optional int32 node = 1; + optional string zone = 2; - optional string zone = 2; + optional string type = 3; - optional string type = 3; + // 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; - // order level starts from 0 for 4KB to page_block_order defined above, e.g. 10 for 4096KB - repeated int32 free_pages_count = 4; -} - -// Next tag: 9 -message BlockProto { - option (android.msg_privacy).dest = DEST_AUTOMATIC; + // Next tag: 9 + message Block { + option (android.msg_privacy).dest = DEST_AUTOMATIC; - optional int32 node = 1; + optional int32 node = 1; - optional string zone = 2; + optional string zone = 2; - optional int32 unmovable = 3; + optional int32 unmovable = 3; - optional int32 reclaimable = 4; + optional int32 reclaimable = 4; - optional int32 movable = 5; + optional int32 movable = 5; - optional int32 cma = 6; + optional int32 cma = 6; - optional int32 reserve = 7; + 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 204a5af70947..ff7515e61471 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; - // Summary - optional SummaryProto summary = 2; -} - -// Next Tag: 11 -message ProcessProto { - 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; + // ID of the process + optional int32 pid = 1; - // proportional set size, unit KB - optional int64 pss = 4; + // virtual set size, unit KB + optional int64 vss = 2; - // unique set size, unit KB - optional int64 uss = 5; + // resident set size, unit KB + optional int64 rss = 3; - // swap size, unit KB - optional int64 swap = 6; + // proportional set size, unit KB + optional int64 pss = 4; - // proportional swap size, unit KB - optional int64 pswap = 7; + // unique set size, unit KB + optional int64 uss = 5; - // unique swap size, unit KB - optional int64 uswap = 8; + // swap size, unit KB + optional int64 swap = 6; - // zswap size, unit KB - optional int64 zswap = 9; + // proportional swap size, unit KB + optional int64 pswap = 7; - // process command - optional string cmdline = 10; -} + // unique swap size, unit KB + optional int64 uswap = 8; -// Next Tag: 3 -message SummaryProto { - option (android.msg_privacy).dest = DEST_AUTOMATIC; + // zswap size, unit KB + optional int64 zswap = 9; - optional ProcessProto total = 1; + // process command + optional string cmdline = 10; + } + repeated Process processes = 1; - optional ZramProto zram = 2; + // Summary + // Next Tag: 3 + message Summary { + option (android.msg_privacy).dest = DEST_AUTOMATIC; - optional RamProto ram = 3; -} + optional Process total = 1; -// TODO: sync on how to use these values -message ZramProto { - option (android.msg_privacy).dest = DEST_AUTOMATIC; + // TODO: sync on how to use these values + message Zram { + option (android.msg_privacy).dest = DEST_AUTOMATIC; - optional string raw_text = 1; -} + optional string raw_text = 1; + } + optional Zram zram = 2; -message RamProto { - option (android.msg_privacy).dest = DEST_AUTOMATIC; + message Ram { + 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 9cce7274d000..0ab92d7686c7 100644 --- a/core/proto/android/os/ps.proto +++ b/core/proto/android/os/ps.proto @@ -22,7 +22,7 @@ option java_multiple_files = true; 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 02fc4da4d220..6b92aa8e8ffa 100644 --- a/core/proto/android/providers/settings.proto +++ b/core/proto/android/providers/settings.proto @@ -422,6 +422,8 @@ message GlobalSettingsProto { 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 6f3c25fa8194..3a527b59d074 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_settings_24dp.xml b/core/res/res/drawable/ic_settings_24dp.xml index fc75f04ff46b..c70b122358b8 100644 --- a/core/res/res/drawable/ic_settings_24dp.xml +++ b/core/res/res/drawable/ic_settings_24dp.xml @@ -16,9 +16,9 @@ Copyright (C) 2015 The Android Open Source Project <vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" - android:viewportWidth="48.0" - android:viewportHeight="48.0"> + android:viewportWidth="24.0" + android:viewportHeight="24.0"> <path - android:fillColor="#FF000000" - android:pathData="M38.86 25.95c.08,-.64.14,-1.29.14,-1.95s-.06,-1.31,-.14,-1.95l4.23,-3.31c.38,-.3.49,-.84.24,-1.28l-4,-6.93c-.25,-.43,-.77,-.61,-1.22,-.43l-4.98 2.01c-1.03,-.79,-2.16,-1.46,-3.38,-1.97L29 4.84c-.09,-.47,-.5,-.84,-1,-.84h-8c-.5 0,-.91.37,-.99.84l-.75 5.3c-1.22.51,-2.35 1.17,-3.38 1.97L9.9 10.1c-.45,-.17,-.97 0,-1.22.43l-4 6.93c-.25.43,-.14.97.24 1.28l4.22 3.31C9.06 22.69 9 23.34 9 24s.06 1.31.14 1.95l-4.22 3.31c-.38.3,-.49.84,-.24 1.28l4 6.93c.25.43.77.61 1.22.43l4.98,-2.01c1.03.79 2.16 1.46 3.38 1.97l.75 5.3c.08.47.49.84.99.84h8c.5 0 .91,-.37.99,-.84l.75,-5.3c1.22,-.51 2.35,-1.17 3.38,-1.97l4.98 2.01c.45.17.97 0 1.22,-.43l4,-6.93c.25,-.43.14,-.97,-.24,-1.28l-4.22,-3.31zM24 31c-3.87 0,-7,-3.13,-7,-7s3.13,-7 7,-7 7 3.13 7 7,-3.13 7,-7 7z"/> + android:pathData="M19.4,13.0c0.0,-0.3 0.1,-0.6 0.1,-1.0s0.0,-0.7 -0.1,-1.0l2.1,-1.7c0.2,-0.2 0.2,-0.4 0.1,-0.6l-2.0,-3.5C19.5,5.1 19.3,5.0 19.0,5.1l-2.5,1.0c-0.5,-0.4 -1.1,-0.7 -1.7,-1.0l-0.4,-2.6C14.5,2.2 14.2,2.0 14.0,2.0l-4.0,0.0C9.8,2.0 9.5,2.2 9.5,2.4L9.1,5.1C8.5,5.3 8.0,5.7 7.4,6.1L5.0,5.1C4.7,5.0 4.5,5.1 4.3,5.3l-2.0,3.5C2.2,8.9 2.3,9.2 2.5,9.4L4.6,11.0c0.0,0.3 -0.1,0.6 -0.1,1.0s0.0,0.7 0.1,1.0l-2.1,1.7c-0.2,0.2 -0.2,0.4 -0.1,0.6l2.0,3.5C4.5,18.9 4.7,19.0 5.0,18.9l2.5,-1.0c0.5,0.4 1.1,0.7 1.7,1.0l0.4,2.6c0.0,0.2 0.2,0.4 0.5,0.4l4.0,0.0c0.2,0.0 0.5,-0.2 0.5,-0.4l0.4,-2.6c0.6,-0.3 1.2,-0.6 1.7,-1.0l2.5,1.0c0.2,0.1 0.5,0.0 0.6,-0.2l2.0,-3.5c0.1,-0.2 0.1,-0.5 -0.1,-0.6L19.4,13.0zM12.0,15.5c-1.9,0.0 -3.5,-1.6 -3.5,-3.5s1.6,-3.5 3.5,-3.5s3.5,1.6 3.5,3.5S13.9,15.5 12.0,15.5z" + android:fillColor="#FF000000" /> </vector> 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 000000000000..29f1f431c199 --- /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 000000000000..07298c182269 --- /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 9a3d7361118c..8774334f11be 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 007fd045ced7..05896b18cfb8 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 4c25225dd50c..aa5625124f29 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/attrs.xml b/core/res/res/values/attrs.xml index 005d07dc008a..d26567e9a3f5 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -7918,6 +7918,10 @@ android.content.pm.PackageInfo#getLongVersionCode()} for the target package. --> <attr name="maxLongVersionCode" format="string" /> + <!-- The resource id of view that contains the URL bar of the HTML page being loaded. + Typically used when compatibility mode is used in a browser. + --> + <attr name="urlBarResourceId" format="string" /> </declare-styleable> <!-- =============================== --> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index cf7925baa578..091129858736 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -999,7 +999,7 @@ <integer name="config_shortPressOnSleepBehavior">0</integer> <!-- Time to wait while a button is pressed before triggering a very long press. --> - <integer name="config_veryLongPressTimeout">6000</integer> + <integer name="config_veryLongPressTimeout">3500</integer> <!-- Package name for default keyguard appwidget [DO NOT TRANSLATE] --> <string name="widget_default_package_name" translatable="false"></string> @@ -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> @@ -2913,8 +2917,8 @@ <item name="config_pictureInPictureAspectRatioLimitForMinSize" format="float" type="dimen">1.777778</item> <!-- The default gravity for the picture-in-picture window. - Currently, this maps to Gravity.BOTTOM | Gravity.RIGHT --> - <integer name="config_defaultPictureInPictureGravity">0x55</integer> + Currently, this maps to Gravity.TOP | Gravity.RIGHT --> + <integer name="config_defaultPictureInPictureGravity">0x35</integer> <!-- The minimum aspect ratio (width/height) that is supported for picture-in-picture. Any ratio smaller than this is considered too tall and thin to be usable. Currently, this @@ -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 0411c6ed8833..cfaab6ac2eec 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/public.xml b/core/res/res/values/public.xml index 93f22f25d7d3..82fefef5fef6 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -2869,6 +2869,7 @@ <public name="outlineSpotShadowColor" /> <public name="outlineAmbientShadowColor" /> <public name="maxLongVersionCode" /> + <public name="urlBarResourceId" /> </public-group> <public-group type="style" first-id="0x010302e0"> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 0efb6f91fce0..837113d4845e 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 5a9dc7fa92be..9995642ba455 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 9c3d8ebfa505..f6a11bd0352a 100644 --- a/core/res/res/values/themes.xml +++ b/core/res/res/values/themes.xml @@ -451,8 +451,8 @@ please see themes_device_defaults.xml. <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 dc8ed9efaa76..67c975496fbf 100644 --- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java +++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java @@ -402,6 +402,8 @@ public class SettingsBackupTest { 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 @@ public class SettingsBackupTest { 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 @@ public class SettingsBackupTest { Settings.Secure.DOZE_ALWAYS_ON, Settings.Secure.DOZE_PULSE_ON_LONG_PRESS, Settings.Secure.EMERGENCY_ASSISTANCE_APPLICATION, + Settings.Secure.ENABLED_INPUT_METHODS, // Intentionally removed in P Settings.Secure.ENABLED_NOTIFICATION_ASSISTANT, Settings.Secure.ENABLED_NOTIFICATION_LISTENERS, Settings.Secure.ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES, diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsCpuTimesTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsCpuTimesTest.java index 32053e30e886..cb049b780fa8 100644 --- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsCpuTimesTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsCpuTimesTest.java @@ -114,7 +114,7 @@ public class BatteryStatsCpuTimesTest { when(mKernelUidCpuFreqTimeReader.readFreqs(mPowerProfile)).thenReturn(freqs); // RUN - mBatteryStatsImpl.updateCpuTimeLocked(); + mBatteryStatsImpl.updateCpuTimeLocked(false, false); // VERIFY assertArrayEquals("Unexpected cpu freqs", freqs, mBatteryStatsImpl.getCpuFreqs()); @@ -134,7 +134,7 @@ public class BatteryStatsCpuTimesTest { mBatteryStatsImpl.setOnBatteryInternal(true); // RUN - mBatteryStatsImpl.updateCpuTimeLocked(); + mBatteryStatsImpl.updateCpuTimeLocked(true, false); // VERIFY verify(mUserInfoProvider).refreshUserIds(); @@ -213,7 +213,7 @@ public class BatteryStatsCpuTimesTest { } // RUN - mBatteryStatsImpl.updateClusterSpeedTimes(updatedUids); + mBatteryStatsImpl.updateClusterSpeedTimes(updatedUids, true); // VERIFY int totalClustersTimeMs = 0; @@ -261,7 +261,7 @@ public class BatteryStatsCpuTimesTest { // RUN final SparseLongArray updatedUids = new SparseLongArray(); - mBatteryStatsImpl.readKernelUidCpuTimesLocked(null, updatedUids); + mBatteryStatsImpl.readKernelUidCpuTimesLocked(null, updatedUids, true); // VERIFY for (int i = 0; i < testUids.length; ++i) { @@ -294,7 +294,7 @@ public class BatteryStatsCpuTimesTest { }).when(mKernelUidCpuTimeReader).readDelta(any(KernelUidCpuTimeReader.Callback.class)); // RUN - mBatteryStatsImpl.readKernelUidCpuTimesLocked(null, null); + mBatteryStatsImpl.readKernelUidCpuTimesLocked(null, null, true); // VERIFY for (int i = 0; i < testUids.length; ++i) { @@ -333,7 +333,7 @@ public class BatteryStatsCpuTimesTest { }).when(mKernelUidCpuTimeReader).readDelta(any(KernelUidCpuTimeReader.Callback.class)); // RUN - mBatteryStatsImpl.readKernelUidCpuTimesLocked(null, null); + mBatteryStatsImpl.readKernelUidCpuTimesLocked(null, null, true); // VERIFY for (int i = 0; i < testUids.length; ++i) { @@ -368,7 +368,7 @@ public class BatteryStatsCpuTimesTest { }).when(mKernelUidCpuTimeReader).readDelta(any(KernelUidCpuTimeReader.Callback.class)); // RUN - mBatteryStatsImpl.readKernelUidCpuTimesLocked(null, null); + mBatteryStatsImpl.readKernelUidCpuTimesLocked(null, null, true); // VERIFY for (int i = 0; i < testUids.length; ++i) { @@ -423,7 +423,7 @@ public class BatteryStatsCpuTimesTest { }).when(mKernelUidCpuTimeReader).readDelta(any(KernelUidCpuTimeReader.Callback.class)); // RUN - mBatteryStatsImpl.readKernelUidCpuTimesLocked(null, null); + mBatteryStatsImpl.readKernelUidCpuTimesLocked(null, null, true); // VERIFY for (int i = 0; i < testUids.length; ++i) { @@ -470,7 +470,7 @@ public class BatteryStatsCpuTimesTest { // RUN final SparseLongArray updatedUids = new SparseLongArray(); - mBatteryStatsImpl.readKernelUidCpuTimesLocked(partialTimers, updatedUids); + mBatteryStatsImpl.readKernelUidCpuTimesLocked(partialTimers, updatedUids, true); // VERIFY long totalUserTimeUs = 0; @@ -549,7 +549,7 @@ public class BatteryStatsCpuTimesTest { any(KernelUidCpuFreqTimeReader.Callback.class)); // RUN - mBatteryStatsImpl.readKernelUidCpuFreqTimesLocked(null); + mBatteryStatsImpl.readKernelUidCpuFreqTimesLocked(null, true, false); // VERIFY for (int i = 0; i < testUids.length; ++i) { @@ -582,7 +582,7 @@ public class BatteryStatsCpuTimesTest { any(KernelUidCpuFreqTimeReader.Callback.class)); // RUN - mBatteryStatsImpl.readKernelUidCpuFreqTimesLocked(null); + mBatteryStatsImpl.readKernelUidCpuFreqTimesLocked(null, true, true); // VERIFY for (int i = 0; i < testUids.length; ++i) { @@ -633,7 +633,7 @@ public class BatteryStatsCpuTimesTest { when(mKernelUidCpuFreqTimeReader.perClusterTimesAvailable()).thenReturn(true); // RUN - mBatteryStatsImpl.readKernelUidCpuFreqTimesLocked(null); + mBatteryStatsImpl.readKernelUidCpuFreqTimesLocked(null, true, false); // VERIFY for (int i = 0; i < testUids.length; ++i) { @@ -676,7 +676,7 @@ public class BatteryStatsCpuTimesTest { any(KernelUidCpuFreqTimeReader.Callback.class)); // RUN - mBatteryStatsImpl.readKernelUidCpuFreqTimesLocked(null); + mBatteryStatsImpl.readKernelUidCpuFreqTimesLocked(null, true, true); // VERIFY for (int i = 0; i < testUids.length; ++i) { @@ -743,7 +743,7 @@ public class BatteryStatsCpuTimesTest { when(mKernelUidCpuFreqTimeReader.perClusterTimesAvailable()).thenReturn(true); // RUN - mBatteryStatsImpl.readKernelUidCpuFreqTimesLocked(partialTimers); + mBatteryStatsImpl.readKernelUidCpuFreqTimesLocked(partialTimers, true, false); // VERIFY final long[][] expectedWakeLockUidTimesUs = new long[clusterFreqs.length][]; @@ -832,7 +832,7 @@ public class BatteryStatsCpuTimesTest { any(KernelUidCpuFreqTimeReader.Callback.class)); // RUN - mBatteryStatsImpl.readKernelUidCpuFreqTimesLocked(null); + mBatteryStatsImpl.readKernelUidCpuFreqTimesLocked(null, true, false); // VERIFY for (int i = 0; i < testUids.length; ++i) { @@ -865,7 +865,7 @@ public class BatteryStatsCpuTimesTest { any(KernelUidCpuFreqTimeReader.Callback.class)); // RUN - mBatteryStatsImpl.readKernelUidCpuFreqTimesLocked(null); + mBatteryStatsImpl.readKernelUidCpuFreqTimesLocked(null, true, true); // VERIFY for (int i = 0; i < testUids.length; ++i) { @@ -909,7 +909,7 @@ public class BatteryStatsCpuTimesTest { any(KernelUidCpuFreqTimeReader.Callback.class)); // RUN - mBatteryStatsImpl.readKernelUidCpuFreqTimesLocked(null); + mBatteryStatsImpl.readKernelUidCpuFreqTimesLocked(null, true, false); // VERIFY for (int i = 0; i < testUids.length; ++i) { @@ -949,7 +949,7 @@ public class BatteryStatsCpuTimesTest { any(KernelUidCpuFreqTimeReader.Callback.class)); // RUN - mBatteryStatsImpl.readKernelUidCpuFreqTimesLocked(null); + mBatteryStatsImpl.readKernelUidCpuFreqTimesLocked(null, true, false); // VERIFY for (int i = 0; i < testUids.length; ++i) { @@ -1006,7 +1006,7 @@ public class BatteryStatsCpuTimesTest { any(KernelUidCpuFreqTimeReader.Callback.class)); // RUN - mBatteryStatsImpl.readKernelUidCpuFreqTimesLocked(null); + mBatteryStatsImpl.readKernelUidCpuFreqTimesLocked(null, true, false); // VERIFY for (int i = 0; i < testUids.length; ++i) { @@ -1047,7 +1047,7 @@ public class BatteryStatsCpuTimesTest { any(KernelUidCpuActiveTimeReader.Callback.class)); // RUN - mBatteryStatsImpl.readKernelUidCpuActiveTimesLocked(); + mBatteryStatsImpl.readKernelUidCpuActiveTimesLocked(true); // VERIFY for (int i = 0; i < testUids.length; ++i) { @@ -1073,7 +1073,7 @@ public class BatteryStatsCpuTimesTest { any(KernelUidCpuActiveTimeReader.Callback.class)); // RUN - mBatteryStatsImpl.readKernelUidCpuActiveTimesLocked(); + mBatteryStatsImpl.readKernelUidCpuActiveTimesLocked(true); // VERIFY for (int i = 0; i < testUids.length; ++i) { @@ -1112,7 +1112,7 @@ public class BatteryStatsCpuTimesTest { any(KernelUidCpuClusterTimeReader.Callback.class)); // RUN - mBatteryStatsImpl.readKernelUidCpuClusterTimesLocked(); + mBatteryStatsImpl.readKernelUidCpuClusterTimesLocked(true); // VERIFY for (int i = 0; i < testUids.length; ++i) { @@ -1142,7 +1142,7 @@ public class BatteryStatsCpuTimesTest { any(KernelUidCpuClusterTimeReader.Callback.class)); // RUN - mBatteryStatsImpl.readKernelUidCpuClusterTimesLocked(); + mBatteryStatsImpl.readKernelUidCpuClusterTimesLocked(true); // VERIFY for (int i = 0; i < testUids.length; ++i) { diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java index 82ac9da575d7..01ddc15efb47 100644 --- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java @@ -161,9 +161,7 @@ public class BatteryStatsNoteTest extends TestCase { actualRunTimeUs = uid.getProcessStateTime(BatteryStats.Uid.PROCESS_STATE_FOREGROUND_SERVICE, elapsedTimeUs, STATS_SINCE_CHARGED); - expectedRunTimeMs = stateRuntimeMap.get( - ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE) - + stateRuntimeMap.get(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE); + expectedRunTimeMs = stateRuntimeMap.get(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE); assertEquals(expectedRunTimeMs * 1000, actualRunTimeUs); actualRunTimeUs = uid.getProcessStateTime(BatteryStats.Uid.PROCESS_STATE_TOP_SLEEPING, @@ -173,7 +171,8 @@ public class BatteryStatsNoteTest extends TestCase { actualRunTimeUs = uid.getProcessStateTime(BatteryStats.Uid.PROCESS_STATE_FOREGROUND, elapsedTimeUs, STATS_SINCE_CHARGED); - expectedRunTimeMs = stateRuntimeMap.get(ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND); + expectedRunTimeMs = stateRuntimeMap.get(ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) + + stateRuntimeMap.get(ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE); assertEquals(expectedRunTimeMs * 1000, actualRunTimeUs); actualRunTimeUs = uid.getProcessStateTime(BatteryStats.Uid.PROCESS_STATE_BACKGROUND, diff --git a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java index 7b239f05c0d3..cb05253c6f57 100644 --- a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java +++ b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java @@ -171,6 +171,20 @@ public class MockBatteryStatsImpl extends BatteryStatsImpl { return null; } + @Override + public Future<?> scheduleCpuSyncDueToScreenStateChange( + boolean onBattery, boolean onBatteryScreenOff) { + return null; + } + + @Override + public Future<?> scheduleCpuSyncDueToWakelockChange(long delayMillis) { + return null; + } + + @Override + public void cancelCpuSyncDueToWakelockChange() { + } } } diff --git a/core/tests/featureflagtests/OWNERS b/core/tests/featureflagtests/OWNERS new file mode 100644 index 000000000000..1a8fd2b62f9a --- /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/core/tests/hosttests/test-apps/SharedUid/32/jni/Android.mk b/core/tests/hosttests/test-apps/SharedUid/32/jni/Android.mk index 994131a22ab3..9b9e8113f234 100644 --- a/core/tests/hosttests/test-apps/SharedUid/32/jni/Android.mk +++ b/core/tests/hosttests/test-apps/SharedUid/32/jni/Android.mk @@ -29,13 +29,10 @@ LOCAL_MODULE_TAGS := tests LOCAL_SRC_FILES:= \ native.cpp -# All of the shard libraries we link against. -LOCAL_SHARED_LIBRARIES := liblog +LOCAL_LDLIBS := -llog LOCAL_CFLAGS += -Wall -Wextra -Werror -# Also need the JNI headers. -LOCAL_C_INCLUDES += \ - $(JNI_H_INCLUDE) +LOCAL_SDK_VERSION := current include $(BUILD_SHARED_LIBRARY) diff --git a/core/tests/hosttests/test-apps/SharedUid/32/jni/native.cpp b/core/tests/hosttests/test-apps/SharedUid/32/jni/native.cpp index 99cf587af2a6..fe32454aa10d 100644 --- a/core/tests/hosttests/test-apps/SharedUid/32/jni/native.cpp +++ b/core/tests/hosttests/test-apps/SharedUid/32/jni/native.cpp @@ -15,12 +15,15 @@ */ #define LOG_TAG "pmtest32 native.cpp" -#include <utils/Log.h> +#include <android/log.h> #include <stdio.h> #include "jni.h" +#define ALOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__) +#define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) + static jint add(JNIEnv */* env */, jobject /* thiz */, jint a, jint b) { int result = a + b; diff --git a/core/tests/hosttests/test-apps/SharedUid/64/jni/Android.mk b/core/tests/hosttests/test-apps/SharedUid/64/jni/Android.mk index 6c2679b30bfb..600a5d148740 100644 --- a/core/tests/hosttests/test-apps/SharedUid/64/jni/Android.mk +++ b/core/tests/hosttests/test-apps/SharedUid/64/jni/Android.mk @@ -30,14 +30,10 @@ LOCAL_MODULE_TAGS := tests LOCAL_SRC_FILES:= \ native.cpp -# All of the shared libraries we link against. -LOCAL_SHARED_LIBRARIES := \ - libutils liblog +LOCAL_LDLIBS := -llog LOCAL_CFLAGS += -Wall -Wextra -Werror -# Also need the JNI headers. -LOCAL_C_INCLUDES += \ - $(JNI_H_INCLUDE) +LOCAL_SDK_VERSION := current include $(BUILD_SHARED_LIBRARY) diff --git a/core/tests/hosttests/test-apps/SharedUid/64/jni/native.cpp b/core/tests/hosttests/test-apps/SharedUid/64/jni/native.cpp index 0b6d7501dcae..ad9e7469d1b8 100644 --- a/core/tests/hosttests/test-apps/SharedUid/64/jni/native.cpp +++ b/core/tests/hosttests/test-apps/SharedUid/64/jni/native.cpp @@ -15,12 +15,15 @@ */ #define LOG_TAG "pmtest64 native.cpp" -#include <utils/Log.h> +#include <android/log.h> #include <stdio.h> #include "jni.h" +#define ALOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__) +#define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) + static jint add(JNIEnv */* env */, jobject /* thiz */, jint a, jint b) { int result = a + b; diff --git a/core/tests/hosttests/test-apps/SharedUid/dual/jni/Android.mk b/core/tests/hosttests/test-apps/SharedUid/dual/jni/Android.mk index d668f29456c8..8e9ac6b5de95 100644 --- a/core/tests/hosttests/test-apps/SharedUid/dual/jni/Android.mk +++ b/core/tests/hosttests/test-apps/SharedUid/dual/jni/Android.mk @@ -29,14 +29,10 @@ LOCAL_MODULE_TAGS := tests LOCAL_SRC_FILES:= \ native.cpp -# All of the shard libraries we link against. LOCAL_LDLIBS = -llog -LOCAL_SHARED_LIBRARIES := liblog LOCAL_CFLAGS += -Wall -Wextra -Werror -# Also need the JNI headers. -LOCAL_C_INCLUDES += \ - $(JNI_H_INCLUDE) +LOCAL_SDK_VERSION := current include $(BUILD_SHARED_LIBRARY) diff --git a/core/tests/hosttests/test-apps/SharedUid/dual/jni/native.cpp b/core/tests/hosttests/test-apps/SharedUid/dual/jni/native.cpp index 3947e21a77bd..5c5088f40a94 100644 --- a/core/tests/hosttests/test-apps/SharedUid/dual/jni/native.cpp +++ b/core/tests/hosttests/test-apps/SharedUid/dual/jni/native.cpp @@ -15,12 +15,15 @@ */ #define LOG_TAG "pmtestdual native.cpp" -#include <utils/Log.h> +#include <android/log.h> #include <stdio.h> #include "jni.h" +#define ALOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__) +#define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) + static jint add(JNIEnv */* env */, jobject /* thiz */, jint a, jint b) { int result = a + b; diff --git a/graphics/java/android/graphics/BaseCanvas.java b/graphics/java/android/graphics/BaseCanvas.java index 07df0454362c..eacb727099ed 100644 --- a/graphics/java/android/graphics/BaseCanvas.java +++ b/graphics/java/android/graphics/BaseCanvas.java @@ -22,7 +22,7 @@ import android.annotation.Nullable; import android.annotation.Size; import android.graphics.Canvas.VertexMode; import android.text.GraphicsOperations; -import android.text.PrecomputedText; +import android.text.MeasuredText; import android.text.SpannableString; import android.text.SpannedString; import android.text.TextUtils; @@ -487,8 +487,8 @@ public abstract class BaseCanvas { TextUtils.getChars(text, contextStart, contextEnd, buf, 0); long measuredTextPtr = 0; int measuredTextOffset = 0; - if (text instanceof PrecomputedText) { - PrecomputedText mt = (PrecomputedText) text; + if (text instanceof MeasuredText) { + MeasuredText mt = (MeasuredText) text; int paraIndex = mt.findParaIndex(start); if (end <= mt.getParagraphEnd(paraIndex)) { // Only suppor the same paragraph. @@ -647,7 +647,7 @@ public abstract class BaseCanvas { private static native void nDrawTextRun(long nativeCanvas, char[] text, int start, int count, int contextStart, int contextCount, float x, float y, boolean isRtl, long nativePaint, - long nativePrecomputedText, int measuredTextOffset); + long nativeMeasuredText, int measuredTextOffset); private static native void nDrawTextOnPath(long nativeCanvas, char[] text, int index, int count, long nativePath, float hOffset, float vOffset, int bidiFlags, long nativePaint); diff --git a/graphics/java/android/graphics/ImageDecoder.java b/graphics/java/android/graphics/ImageDecoder.java index ee7abc5bd254..acefead785c4 100644 --- a/graphics/java/android/graphics/ImageDecoder.java +++ b/graphics/java/android/graphics/ImageDecoder.java @@ -444,6 +444,7 @@ public final class ImageDecoder implements AutoCloseable { private boolean mPreferRamOverQuality = false; private boolean mAsAlphaMask = false; private Rect mCropRect; + private Rect mOutPaddingRect; private Source mSource; private PostProcessor mPostProcessor; @@ -782,6 +783,18 @@ public final class ImageDecoder implements AutoCloseable { } /** + * Set a Rect for retrieving nine patch padding. + * + * If the image is a nine patch, this Rect will be set to the padding + * rectangle during decode. Otherwise it will not be modified. + * + * @hide + */ + public void setOutPaddingRect(@NonNull Rect outPadding) { + mOutPaddingRect = outPadding; + } + + /** * Specify whether the {@link Bitmap} should be mutable. * * <p>By default, a {@link Bitmap} created will be immutable, but that can @@ -892,7 +905,6 @@ public final class ImageDecoder implements AutoCloseable { postProcessPtr, mDesiredWidth, mDesiredHeight, mCropRect, mMutable, mAllocator, mRequireUnpremultiplied, mPreferRamOverQuality, mAsAlphaMask); - } private void callHeaderDecoded(@Nullable OnHeaderDecodedListener listener, @@ -965,7 +977,10 @@ public final class ImageDecoder implements AutoCloseable { if (np != null && NinePatch.isNinePatchChunk(np)) { Rect opticalInsets = new Rect(); bm.getOpticalInsets(opticalInsets); - Rect padding = new Rect(); + Rect padding = decoder.mOutPaddingRect; + if (padding == null) { + padding = new Rect(); + } nGetPadding(decoder.mNativePtr, padding); return new NinePatchDrawable(res, bm, np, padding, opticalInsets, null); @@ -1008,6 +1023,15 @@ public final class ImageDecoder implements AutoCloseable { final int srcDensity = computeDensity(src, decoder); Bitmap bm = decoder.decodeBitmap(); bm.setDensity(srcDensity); + + Rect padding = decoder.mOutPaddingRect; + if (padding != null) { + byte[] np = bm.getNinePatchChunk(); + if (np != null && NinePatch.isNinePatchChunk(np)) { + nGetPadding(decoder.mNativePtr, padding); + } + } + return bm; } } diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java index 42dac38affba..ed147e9e2ec7 100644 --- a/graphics/java/android/graphics/Paint.java +++ b/graphics/java/android/graphics/Paint.java @@ -2835,16 +2835,6 @@ public class Paint { return result; } - /** - * Returns true of the passed {@link Paint} will have the same effect on text measurement - * - * @param other A {@link Paint} object. - * @return true if the other {@link Paint} has the same effect on text measurement. - */ - public boolean equalsForTextMeasurement(@NonNull Paint other) { - return nEqualsForTextMeasurement(mNativePaint, other.mNativePaint); - } - // regular JNI private static native long nGetNativeFinalizer(); private static native long nInit(); @@ -3012,6 +3002,4 @@ public class Paint { private static native float nGetStrikeThruThickness(long paintPtr); @CriticalNative private static native void nSetTextSize(long paintPtr, float textSize); - @CriticalNative - private static native boolean nEqualsForTextMeasurement(long leftPaintPtr, long rightPaintPtr); } diff --git a/graphics/java/android/graphics/drawable/BitmapDrawable.java b/graphics/java/android/graphics/drawable/BitmapDrawable.java index 7ad062a6f6f9..44b783bb6e63 100644 --- a/graphics/java/android/graphics/drawable/BitmapDrawable.java +++ b/graphics/java/android/graphics/drawable/BitmapDrawable.java @@ -27,6 +27,7 @@ import android.graphics.BitmapFactory; import android.graphics.BitmapShader; import android.graphics.Canvas; import android.graphics.ColorFilter; +import android.graphics.ImageDecoder; import android.graphics.Insets; import android.graphics.Matrix; import android.graphics.Outline; @@ -49,6 +50,7 @@ import com.android.internal.R; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; +import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; @@ -111,7 +113,7 @@ public class BitmapDrawable extends Drawable { */ @Deprecated public BitmapDrawable() { - mBitmapState = new BitmapState((Bitmap) null); + init(new BitmapState((Bitmap) null), null); } /** @@ -124,8 +126,7 @@ public class BitmapDrawable extends Drawable { @SuppressWarnings("unused") @Deprecated public BitmapDrawable(Resources res) { - mBitmapState = new BitmapState((Bitmap) null); - mBitmapState.mTargetDensity = mTargetDensity; + init(new BitmapState((Bitmap) null), res); } /** @@ -135,7 +136,7 @@ public class BitmapDrawable extends Drawable { */ @Deprecated public BitmapDrawable(Bitmap bitmap) { - this(new BitmapState(bitmap), null); + init(new BitmapState(bitmap), null); } /** @@ -143,8 +144,7 @@ public class BitmapDrawable extends Drawable { * the display metrics of the resources. */ public BitmapDrawable(Resources res, Bitmap bitmap) { - this(new BitmapState(bitmap), res); - mBitmapState.mTargetDensity = mTargetDensity; + init(new BitmapState(bitmap), res); } /** @@ -154,10 +154,7 @@ public class BitmapDrawable extends Drawable { */ @Deprecated public BitmapDrawable(String filepath) { - this(new BitmapState(BitmapFactory.decodeFile(filepath)), null); - if (mBitmapState.mBitmap == null) { - android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + filepath); - } + this(null, filepath); } /** @@ -165,10 +162,21 @@ public class BitmapDrawable extends Drawable { */ @SuppressWarnings({ "unused", "ChainingConstructorIgnoresParameter" }) public BitmapDrawable(Resources res, String filepath) { - this(new BitmapState(BitmapFactory.decodeFile(filepath)), null); - mBitmapState.mTargetDensity = mTargetDensity; - if (mBitmapState.mBitmap == null) { - android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + filepath); + Bitmap bitmap = null; + try (FileInputStream stream = new FileInputStream(filepath)) { + bitmap = ImageDecoder.decodeBitmap(ImageDecoder.createSource(res, stream), + (decoder, info, src) -> { + decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE); + }); + } catch (Exception e) { + /* do nothing. This matches the behavior of BitmapFactory.decodeFile() + If the exception happened on decode, mBitmapState.mBitmap will be null. + */ + } finally { + init(new BitmapState(bitmap), res); + if (mBitmapState.mBitmap == null) { + android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + filepath); + } } } @@ -179,10 +187,7 @@ public class BitmapDrawable extends Drawable { */ @Deprecated public BitmapDrawable(java.io.InputStream is) { - this(new BitmapState(BitmapFactory.decodeStream(is)), null); - if (mBitmapState.mBitmap == null) { - android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + is); - } + this(null, is); } /** @@ -190,10 +195,21 @@ public class BitmapDrawable extends Drawable { */ @SuppressWarnings({ "unused", "ChainingConstructorIgnoresParameter" }) public BitmapDrawable(Resources res, java.io.InputStream is) { - this(new BitmapState(BitmapFactory.decodeStream(is)), null); - mBitmapState.mTargetDensity = mTargetDensity; - if (mBitmapState.mBitmap == null) { - android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + is); + Bitmap bitmap = null; + try { + bitmap = ImageDecoder.decodeBitmap(ImageDecoder.createSource(res, is), + (decoder, info, src) -> { + decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE); + }); + } catch (Exception e) { + /* do nothing. This matches the behavior of BitmapFactory.decodeStream() + If the exception happened on decode, mBitmapState.mBitmap will be null. + */ + } finally { + init(new BitmapState(bitmap), res); + if (mBitmapState.mBitmap == null) { + android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + is); + } } } @@ -812,9 +828,19 @@ public class BitmapDrawable extends Drawable { } } + int density = Bitmap.DENSITY_NONE; + if (value.density == TypedValue.DENSITY_DEFAULT) { + density = DisplayMetrics.DENSITY_DEFAULT; + } else if (value.density != TypedValue.DENSITY_NONE) { + density = value.density; + } + Bitmap bitmap = null; try (InputStream is = r.openRawResource(srcResId, value)) { - bitmap = BitmapFactory.decodeResourceStream(r, value, is, null, null); + ImageDecoder.Source source = ImageDecoder.createSource(r, is, density); + bitmap = ImageDecoder.decodeBitmap(source, (decoder, info, src) -> { + decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE); + }); } catch (Exception e) { // Do nothing and pick up the error below. } @@ -1013,14 +1039,21 @@ public class BitmapDrawable extends Drawable { } } + private BitmapDrawable(BitmapState state, Resources res) { + init(state, res); + } + /** - * The one constructor to rule them all. This is called by all public + * The one helper to rule them all. This is called by all public & private * constructors to set the state and initialize local properties. */ - private BitmapDrawable(BitmapState state, Resources res) { + private void init(BitmapState state, Resources res) { mBitmapState = state; - updateLocalState(res); + + if (mBitmapState != null && res != null) { + mBitmapState.mTargetDensity = mTargetDensity; + } } /** diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java index 05533d787aa1..8af2fd8bbb5e 100644 --- a/graphics/java/android/graphics/drawable/Drawable.java +++ b/graphics/java/android/graphics/drawable/Drawable.java @@ -37,6 +37,7 @@ import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.ColorFilter; +import android.graphics.ImageDecoder; import android.graphics.Insets; import android.graphics.NinePatch; import android.graphics.Outline; @@ -50,11 +51,13 @@ import android.graphics.Xfermode; import android.os.Trace; import android.util.AttributeSet; import android.util.DisplayMetrics; +import android.util.Log; import android.util.StateSet; import android.util.TypedValue; import android.util.Xml; import android.view.View; +import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.lang.ref.WeakReference; @@ -1179,6 +1182,10 @@ public abstract class Drawable { return null; } + if (opts == null) { + return getBitmapDrawable(res, value, is); + } + /* ugh. The decodeStream contract is that we have already allocated the pad rect, but if the bitmap does not had a ninepatch chunk, then the pad will be ignored. If we could change this to lazily @@ -1194,7 +1201,6 @@ public abstract class Drawable { // an application in compatibility mode, without scaling those down // to the compatibility density only to have them scaled back up when // drawn to the screen. - if (opts == null) opts = new BitmapFactory.Options(); opts.inScreenDensity = Drawable.resolveDensity(res, 0); Bitmap bm = BitmapFactory.decodeResourceStream(res, value, is, pad, opts); if (bm != null) { @@ -1211,6 +1217,33 @@ public abstract class Drawable { return null; } + private static Drawable getBitmapDrawable(Resources res, TypedValue value, InputStream is) { + try { + ImageDecoder.Source source = null; + if (value != null) { + int density = Bitmap.DENSITY_NONE; + if (value.density == TypedValue.DENSITY_DEFAULT) { + density = DisplayMetrics.DENSITY_DEFAULT; + } else if (value.density != TypedValue.DENSITY_NONE) { + density = value.density; + } + source = ImageDecoder.createSource(res, is, density); + } else { + source = ImageDecoder.createSource(res, is); + } + + return ImageDecoder.decodeDrawable(source, (decoder, info, src) -> { + decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE); + }); + } catch (IOException e) { + /* do nothing. + If the exception happened on decode, the drawable will be null. + */ + Log.e("Drawable", "Unable to decode stream: " + e); + } + return null; + } + /** * Create a drawable from an XML document. For more information on how to * create resources in XML, see @@ -1310,11 +1343,10 @@ public abstract class Drawable { } Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, pathName); - try { - Bitmap bm = BitmapFactory.decodeFile(pathName); - if (bm != null) { - return drawableFromBitmap(null, bm, null, null, null, pathName); - } + try (FileInputStream stream = new FileInputStream(pathName)) { + return getBitmapDrawable(null, null, stream); + } catch(IOException e) { + // Do nothing; we will just return null if the FileInputStream had an error } finally { Trace.traceEnd(Trace.TRACE_TAG_RESOURCES); } diff --git a/graphics/java/android/graphics/drawable/NinePatchDrawable.java b/graphics/java/android/graphics/drawable/NinePatchDrawable.java index 17900204fa22..66f2a3173eae 100644 --- a/graphics/java/android/graphics/drawable/NinePatchDrawable.java +++ b/graphics/java/android/graphics/drawable/NinePatchDrawable.java @@ -24,9 +24,9 @@ import android.content.res.Resources; import android.content.res.Resources.Theme; import android.content.res.TypedArray; import android.graphics.Bitmap; -import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.ColorFilter; +import android.graphics.ImageDecoder; import android.graphics.Insets; import android.graphics.NinePatch; import android.graphics.Outline; @@ -211,7 +211,8 @@ public class NinePatchDrawable extends Drawable { restoreAlpha = -1; } - final boolean needsDensityScaling = canvas.getDensity() == 0; + final boolean needsDensityScaling = canvas.getDensity() == 0 + && Bitmap.DENSITY_NONE != state.mNinePatch.getDensity(); if (needsDensityScaling) { restoreToCount = restoreToCount >= 0 ? restoreToCount : canvas.save(); @@ -421,10 +422,6 @@ public class NinePatchDrawable extends Drawable { final int srcResId = a.getResourceId(R.styleable.NinePatchDrawable_src, 0); if (srcResId != 0) { - final BitmapFactory.Options options = new BitmapFactory.Options(); - options.inDither = !state.mDither; - options.inScreenDensity = r.getDisplayMetrics().noncompatDensityDpi; - final Rect padding = new Rect(); final Rect opticalInsets = new Rect(); Bitmap bitmap = null; @@ -433,7 +430,17 @@ public class NinePatchDrawable extends Drawable { final TypedValue value = new TypedValue(); final InputStream is = r.openRawResource(srcResId, value); - bitmap = BitmapFactory.decodeResourceStream(r, value, is, padding, options); + int density = Bitmap.DENSITY_NONE; + if (value.density == TypedValue.DENSITY_DEFAULT) { + density = DisplayMetrics.DENSITY_DEFAULT; + } else if (value.density != TypedValue.DENSITY_NONE) { + density = value.density; + } + ImageDecoder.Source source = ImageDecoder.createSource(r, is, density); + bitmap = ImageDecoder.decodeBitmap(source, (decoder, info, src) -> { + decoder.setOutPaddingRect(padding); + decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE); + }); is.close(); } catch (IOException e) { @@ -660,8 +667,9 @@ public class NinePatchDrawable extends Drawable { return; } - final int sourceDensity = ninePatch.getDensity(); final int targetDensity = mTargetDensity; + final int sourceDensity = ninePatch.getDensity() == Bitmap.DENSITY_NONE ? + targetDensity : ninePatch.getDensity(); final Insets sourceOpticalInsets = mNinePatchState.mOpticalInsets; if (sourceOpticalInsets != Insets.NONE) { diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java index ded427eb244a..e2aba0401036 100644 --- a/keystore/java/android/security/KeyStore.java +++ b/keystore/java/android/security/KeyStore.java @@ -545,7 +545,9 @@ public class KeyStore { try { args = args != null ? args : new KeymasterArguments(); entropy = entropy != null ? entropy : new byte[0]; - return mBinder.begin(getToken(), alias, purpose, pruneable, args, entropy, uid); + OperationResult res = mBinder.begin(getToken(), alias, purpose, pruneable, args, entropy, uid); + // This result is -26 (KEY_USER_NOT_AUTHENTICATED) but why?? + return res; } catch (RemoteException e) { Log.w(TAG, "Cannot connect to keystore", e); return null; @@ -563,7 +565,8 @@ public class KeyStore { try { arguments = arguments != null ? arguments : new KeymasterArguments(); input = input != null ? input : new byte[0]; - return mBinder.update(token, arguments, input); + OperationResult res = mBinder.update(token, arguments, input); + return res; } catch (RemoteException e) { Log.w(TAG, "Cannot connect to keystore", e); return null; @@ -618,9 +621,9 @@ public class KeyStore { * @return {@code KeyStore.NO_ERROR} on success, otherwise an error value corresponding to * a {@code KeymasterDefs.KM_ERROR_} value or {@code KeyStore} ResponseCode. */ - public int addAuthToken(byte[] authToken) { + public int addAuthToken(byte[] authToken, int userId) { try { - return mBinder.addAuthToken(authToken); + return mBinder.addAuthToken(authToken, userId); } catch (RemoteException e) { Log.w(TAG, "Cannot connect to keystore", e); return SYSTEM_ERROR; @@ -832,14 +835,14 @@ public class KeyStore { public InvalidKeyException getInvalidKeyException( String keystoreKeyAlias, int uid, KeyStoreException e) { switch (e.getErrorCode()) { - case LOCKED: + case LOCKED: // 2 return new UserNotAuthenticatedException(); - case KeymasterDefs.KM_ERROR_KEY_EXPIRED: + case KeymasterDefs.KM_ERROR_KEY_EXPIRED: // -25 return new KeyExpiredException(); - case KeymasterDefs.KM_ERROR_KEY_NOT_YET_VALID: + case KeymasterDefs.KM_ERROR_KEY_NOT_YET_VALID: // -2 return new KeyNotYetValidException(); - case KeymasterDefs.KM_ERROR_KEY_USER_NOT_AUTHENTICATED: - case OP_AUTH_NEEDED: + case KeymasterDefs.KM_ERROR_KEY_USER_NOT_AUTHENTICATED: // -26 + case OP_AUTH_NEEDED: // 15 { // We now need to determine whether the key/operation can become usable if user // authentication is performed, or whether it can never become usable again. @@ -879,7 +882,7 @@ public class KeyStore { // None of the key's SIDs can ever be authenticated return new KeyPermanentlyInvalidatedException(); } - case UNINITIALIZED: + case UNINITIALIZED: // 3 return new KeyPermanentlyInvalidatedException(); default: return new InvalidKeyException("Keystore operation failed", e); diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java index 09b3b9b523b4..419eb24e1cc1 100644 --- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java +++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java @@ -243,13 +243,7 @@ public abstract class AndroidKeyStoreKeyGeneratorSpi extends KeyGeneratorSpi { // Check that user authentication related parameters are acceptable. This method // will throw an IllegalStateException if there are issues (e.g., secure lock screen // not set up). - KeymasterUtils.addUserAuthArgs(new KeymasterArguments(), - spec.isUserAuthenticationRequired(), - spec.getUserAuthenticationValidityDurationSeconds(), - spec.isUserAuthenticationValidWhileOnBody(), - spec.isInvalidatedByBiometricEnrollment(), - GateKeeper.INVALID_SECURE_USER_ID /* boundToSpecificSecureUserId */, - spec.isUserConfirmationRequired()); + KeymasterUtils.addUserAuthArgs(new KeymasterArguments(), spec); } catch (IllegalStateException | IllegalArgumentException e) { throw new InvalidAlgorithmParameterException(e); } @@ -285,16 +279,7 @@ public abstract class AndroidKeyStoreKeyGeneratorSpi extends KeyGeneratorSpi { args.addEnums(KeymasterDefs.KM_TAG_BLOCK_MODE, mKeymasterBlockModes); args.addEnums(KeymasterDefs.KM_TAG_PADDING, mKeymasterPaddings); args.addEnums(KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigests); - KeymasterUtils.addUserAuthArgs(args, - spec.isUserAuthenticationRequired(), - spec.getUserAuthenticationValidityDurationSeconds(), - spec.isUserAuthenticationValidWhileOnBody(), - spec.isInvalidatedByBiometricEnrollment(), - GateKeeper.INVALID_SECURE_USER_ID /* boundToSpecificSecureUserId */, - spec.isUserConfirmationRequired()); - if (spec.isTrustedUserPresenceRequired()) { - args.addBoolean(KeymasterDefs.KM_TAG_TRUSTED_USER_PRESENCE_REQUIRED); - } + KeymasterUtils.addUserAuthArgs(args, spec); KeymasterUtils.addMinMacLengthAuthorizationIfNecessary( args, mKeymasterAlgorithm, diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java index e33e3cd4e92b..d68a33de2c61 100644 --- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java +++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java @@ -344,13 +344,7 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato // Check that user authentication related parameters are acceptable. This method // will throw an IllegalStateException if there are issues (e.g., secure lock screen // not set up). - KeymasterUtils.addUserAuthArgs(new KeymasterArguments(), - mSpec.isUserAuthenticationRequired(), - mSpec.getUserAuthenticationValidityDurationSeconds(), - mSpec.isUserAuthenticationValidWhileOnBody(), - mSpec.isInvalidatedByBiometricEnrollment(), - GateKeeper.INVALID_SECURE_USER_ID /* boundToSpecificSecureUserId */, - mSpec.isUserConfirmationRequired()); + KeymasterUtils.addUserAuthArgs(new KeymasterArguments(), mSpec); } catch (IllegalArgumentException | IllegalStateException e) { throw new InvalidAlgorithmParameterException(e); } @@ -541,13 +535,7 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato args.addEnums(KeymasterDefs.KM_TAG_PADDING, mKeymasterSignaturePaddings); args.addEnums(KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigests); - KeymasterUtils.addUserAuthArgs(args, - mSpec.isUserAuthenticationRequired(), - mSpec.getUserAuthenticationValidityDurationSeconds(), - mSpec.isUserAuthenticationValidWhileOnBody(), - mSpec.isInvalidatedByBiometricEnrollment(), - GateKeeper.INVALID_SECURE_USER_ID /* boundToSpecificSecureUserId */, - mSpec.isUserConfirmationRequired()); + KeymasterUtils.addUserAuthArgs(args, mSpec); args.addDateIfNotNull(KeymasterDefs.KM_TAG_ACTIVE_DATETIME, mSpec.getKeyValidityStart()); args.addDateIfNotNull(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME, mSpec.getKeyValidityForOriginationEnd()); diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java index 05cc74a0bec9..fc86ca0443b0 100644 --- a/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java +++ b/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java @@ -497,13 +497,7 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi { importArgs.addEnums(KeymasterDefs.KM_TAG_PADDING, keymasterEncryptionPaddings); importArgs.addEnums(KeymasterDefs.KM_TAG_PADDING, KeyProperties.SignaturePadding.allToKeymaster(spec.getSignaturePaddings())); - KeymasterUtils.addUserAuthArgs(importArgs, - spec.isUserAuthenticationRequired(), - spec.getUserAuthenticationValidityDurationSeconds(), - spec.isUserAuthenticationValidWhileOnBody(), - spec.isInvalidatedByBiometricEnrollment(), - spec.getBoundToSpecificSecureUserId(), - spec.isUserConfirmationRequired()); + KeymasterUtils.addUserAuthArgs(importArgs, spec); importArgs.addDateIfNotNull(KeymasterDefs.KM_TAG_ACTIVE_DATETIME, spec.getKeyValidityStart()); importArgs.addDateIfNotNull(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME, @@ -700,13 +694,7 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi { int[] keymasterPaddings = KeyProperties.EncryptionPadding.allToKeymaster( params.getEncryptionPaddings()); args.addEnums(KeymasterDefs.KM_TAG_PADDING, keymasterPaddings); - KeymasterUtils.addUserAuthArgs(args, - params.isUserAuthenticationRequired(), - params.getUserAuthenticationValidityDurationSeconds(), - params.isUserAuthenticationValidWhileOnBody(), - params.isInvalidatedByBiometricEnrollment(), - params.getBoundToSpecificSecureUserId(), - params.isUserConfirmationRequired()); + KeymasterUtils.addUserAuthArgs(args, params); KeymasterUtils.addMinMacLengthAuthorizationIfNecessary( args, keymasterAlgorithm, diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java index da23c70f58bb..d0814c6f2f93 100644 --- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java +++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java @@ -21,6 +21,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.app.KeyguardManager; import android.hardware.fingerprint.FingerprintManager; +import android.security.GateKeeper; import android.security.KeyStore; import android.text.TextUtils; @@ -232,7 +233,7 @@ import javax.security.auth.x500.X500Principal; * key = (SecretKey) keyStore.getKey("key2", null); * }</pre> */ -public final class KeyGenParameterSpec implements AlgorithmParameterSpec { +public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAuthArgs { private static final X500Principal DEFAULT_CERT_SUBJECT = new X500Principal("CN=fake"); private static final BigInteger DEFAULT_CERT_SERIAL_NUMBER = new BigInteger("1"); @@ -265,6 +266,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec { private final boolean mInvalidatedByBiometricEnrollment; private final boolean mIsStrongBoxBacked; private final boolean mUserConfirmationRequired; + private final boolean mUnlockedDeviceRequired; /** * @hide should be built with Builder @@ -295,7 +297,8 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec { boolean userAuthenticationValidWhileOnBody, boolean invalidatedByBiometricEnrollment, boolean isStrongBoxBacked, - boolean userConfirmationRequired) { + boolean userConfirmationRequired, + boolean unlockedDeviceRequired) { if (TextUtils.isEmpty(keyStoreAlias)) { throw new IllegalArgumentException("keyStoreAlias must not be empty"); } @@ -344,6 +347,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec { mInvalidatedByBiometricEnrollment = invalidatedByBiometricEnrollment; mIsStrongBoxBacked = isStrongBoxBacked; mUserConfirmationRequired = userConfirmationRequired; + mUnlockedDeviceRequired = unlockedDeviceRequired; } /** @@ -669,6 +673,22 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec { } /** + * Returns {@code true} if the key cannot be used unless the device screen is unlocked. + * + * @see Builder#SetUnlockedDeviceRequired(boolean) + */ + public boolean isUnlockedDeviceRequired() { + return mUnlockedDeviceRequired; + } + + /** + * @hide + */ + public long getBoundToSpecificSecureUserId() { + return GateKeeper.INVALID_SECURE_USER_ID; + } + + /** * Builder of {@link KeyGenParameterSpec} instances. */ public final static class Builder { @@ -699,6 +719,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec { private boolean mInvalidatedByBiometricEnrollment = true; private boolean mIsStrongBoxBacked = false; private boolean mUserConfirmationRequired; + private boolean mUnlockedDeviceRequired = false; /** * Creates a new instance of the {@code Builder}. @@ -1267,6 +1288,18 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec { } /** + * Sets whether the keystore requires the screen to be unlocked before allowing decryption + * using this key. If this is set to {@code true}, any attempt to decrypt using this key + * while the screen is locked will fail. A locked device requires a PIN, password, + * fingerprint, or other trusted factor to access. + */ + @NonNull + public Builder setUnlockedDeviceRequired(boolean unlockedDeviceRequired) { + mUnlockedDeviceRequired = unlockedDeviceRequired; + return this; + } + + /** * Builds an instance of {@code KeyGenParameterSpec}. */ @NonNull @@ -1297,7 +1330,8 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec { mUserAuthenticationValidWhileOnBody, mInvalidatedByBiometricEnrollment, mIsStrongBoxBacked, - mUserConfirmationRequired); + mUserConfirmationRequired, + mUnlockedDeviceRequired); } } } diff --git a/keystore/java/android/security/keystore/KeyProtection.java b/keystore/java/android/security/keystore/KeyProtection.java index b5b328192f21..7f8259b89962 100644 --- a/keystore/java/android/security/keystore/KeyProtection.java +++ b/keystore/java/android/security/keystore/KeyProtection.java @@ -212,7 +212,7 @@ import javax.crypto.Mac; * ... * }</pre> */ -public final class KeyProtection implements ProtectionParameter { +public final class KeyProtection implements ProtectionParameter, UserAuthArgs { private final Date mKeyValidityStart; private final Date mKeyValidityForOriginationEnd; private final Date mKeyValidityForConsumptionEnd; @@ -229,6 +229,8 @@ public final class KeyProtection implements ProtectionParameter { private final long mBoundToSecureUserId; private final boolean mCriticalToDeviceEncryption; private final boolean mUserConfirmationRequired; + private final boolean mTrustedUserPresenceRequired; + private final boolean mUnlockedDeviceRequired; private KeyProtection( Date keyValidityStart, @@ -242,11 +244,13 @@ public final class KeyProtection implements ProtectionParameter { boolean randomizedEncryptionRequired, boolean userAuthenticationRequired, int userAuthenticationValidityDurationSeconds, + boolean trustedUserPresenceRequired, boolean userAuthenticationValidWhileOnBody, boolean invalidatedByBiometricEnrollment, long boundToSecureUserId, boolean criticalToDeviceEncryption, - boolean userConfirmationRequired) { + boolean userConfirmationRequired, + boolean unlockedDeviceRequired) { mKeyValidityStart = Utils.cloneIfNotNull(keyValidityStart); mKeyValidityForOriginationEnd = Utils.cloneIfNotNull(keyValidityForOriginationEnd); mKeyValidityForConsumptionEnd = Utils.cloneIfNotNull(keyValidityForConsumptionEnd); @@ -265,6 +269,8 @@ public final class KeyProtection implements ProtectionParameter { mBoundToSecureUserId = boundToSecureUserId; mCriticalToDeviceEncryption = criticalToDeviceEncryption; mUserConfirmationRequired = userConfirmationRequired; + mTrustedUserPresenceRequired = trustedUserPresenceRequired; + mUnlockedDeviceRequired = unlockedDeviceRequired; } /** @@ -437,6 +443,14 @@ public final class KeyProtection implements ProtectionParameter { } /** + * Returns {@code true} if the key is authorized to be used only if a test of user presence has + * been performed between the {@code Signature.initSign()} and {@code Signature.sign()} calls. + */ + public boolean isTrustedUserPresenceRequired() { + return mTrustedUserPresenceRequired; + } + + /** * Returns {@code true} if the key will be de-authorized when the device is removed from the * user's body. This option has no effect on keys that don't have an authentication validity * duration, and has no effect if the device lacks an on-body sensor. @@ -494,6 +508,15 @@ public final class KeyProtection implements ProtectionParameter { } /** + * Returns {@code true} if the key cannot be used unless the device screen is unlocked. + * + * @see Builder#SetRequireDeviceUnlocked(boolean) + */ + public boolean isUnlockedDeviceRequired() { + return mUnlockedDeviceRequired; + } + + /** * Builder of {@link KeyProtection} instances. */ public final static class Builder { @@ -512,6 +535,9 @@ public final class KeyProtection implements ProtectionParameter { private boolean mUserAuthenticationValidWhileOnBody; private boolean mInvalidatedByBiometricEnrollment = true; private boolean mUserConfirmationRequired; + private boolean mTrustedUserPresenceRequired = false; + private boolean mUnlockedDeviceRequired = false; + private long mBoundToSecureUserId = GateKeeper.INVALID_SECURE_USER_ID; private boolean mCriticalToDeviceEncryption = false; @@ -811,6 +837,16 @@ public final class KeyProtection implements ProtectionParameter { } /** + * Sets whether a test of user presence is required to be performed between the + * {@code Signature.initSign()} and {@code Signature.sign()} method calls. + */ + @NonNull + public Builder setTrustedUserPresenceRequired(boolean required) { + mTrustedUserPresenceRequired = required; + return this; + } + + /** * Sets whether the key will remain authorized only until the device is removed from the * user's body up to the limit of the authentication validity period (see * {@link #setUserAuthenticationValidityDurationSeconds} and @@ -892,6 +928,18 @@ public final class KeyProtection implements ProtectionParameter { } /** + * Sets whether the keystore requires the screen to be unlocked before allowing decryption + * using this key. If this is set to {@code true}, any attempt to decrypt using this key + * while the screen is locked will fail. A locked device requires a PIN, password, + * fingerprint, or other trusted factor to access. + */ + @NonNull + public Builder setUnlockedDeviceRequired(boolean unlockedDeviceRequired) { + mUnlockedDeviceRequired = unlockedDeviceRequired; + return this; + } + + /** * Builds an instance of {@link KeyProtection}. * * @throws IllegalArgumentException if a required field is missing @@ -910,11 +958,13 @@ public final class KeyProtection implements ProtectionParameter { mRandomizedEncryptionRequired, mUserAuthenticationRequired, mUserAuthenticationValidityDurationSeconds, + mTrustedUserPresenceRequired, mUserAuthenticationValidWhileOnBody, mInvalidatedByBiometricEnrollment, mBoundToSecureUserId, mCriticalToDeviceEncryption, - mUserConfirmationRequired); + mUserConfirmationRequired, + mUnlockedDeviceRequired); } } } diff --git a/keystore/java/android/security/keystore/KeymasterUtils.java b/keystore/java/android/security/keystore/KeymasterUtils.java index 4e28601f17a1..5bd0e7406ff9 100644 --- a/keystore/java/android/security/keystore/KeymasterUtils.java +++ b/keystore/java/android/security/keystore/KeymasterUtils.java @@ -18,6 +18,7 @@ package android.security.keystore; import android.util.Log; import android.hardware.fingerprint.FingerprintManager; +import android.os.UserHandle; import android.security.GateKeeper; import android.security.KeyStore; import android.security.keymaster.KeymasterArguments; @@ -101,22 +102,27 @@ public abstract class KeymasterUtils { * require user authentication. */ public static void addUserAuthArgs(KeymasterArguments args, - boolean userAuthenticationRequired, - int userAuthenticationValidityDurationSeconds, - boolean userAuthenticationValidWhileOnBody, - boolean invalidatedByBiometricEnrollment, - long boundToSpecificSecureUserId, - boolean userConfirmationRequired) { - if (userConfirmationRequired) { + UserAuthArgs spec) { + if (spec.isTrustedUserPresenceRequired()) { + args.addBoolean(KeymasterDefs.KM_TAG_TRUSTED_USER_PRESENCE_REQUIRED); + } + + if (spec.isUserConfirmationRequired()) { args.addBoolean(KeymasterDefs.KM_TAG_TRUSTED_CONFIRMATION_REQUIRED); } - if (!userAuthenticationRequired) { + if (spec.isUnlockedDeviceRequired()) { + args.addBoolean(KeymasterDefs.KM_TAG_UNLOCKED_DEVICE_REQUIRED); + // Once keymaster is properly ignoring this tag, it should be added to every auth list + args.addUnsignedInt(KeymasterDefs.KM_TAG_USER_ID, UserHandle.getCallingUserId()); + } + + if (!spec.isUserAuthenticationRequired()) { args.addBoolean(KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED); return; } - if (userAuthenticationValidityDurationSeconds == -1) { + if (spec.getUserAuthenticationValidityDurationSeconds() == -1) { // Every use of this key needs to be authorized by the user. This currently means // fingerprint-only auth. FingerprintManager fingerprintManager = @@ -132,9 +138,9 @@ public abstract class KeymasterUtils { } long sid; - if (boundToSpecificSecureUserId != GateKeeper.INVALID_SECURE_USER_ID) { - sid = boundToSpecificSecureUserId; - } else if (invalidatedByBiometricEnrollment) { + if (spec.getBoundToSpecificSecureUserId() != GateKeeper.INVALID_SECURE_USER_ID) { + sid = spec.getBoundToSpecificSecureUserId(); + } else if (spec.isInvalidatedByBiometricEnrollment()) { // The fingerprint-only SID will change on fingerprint enrollment or removal of all, // enrolled fingerprints, invalidating the key. sid = fingerprintOnlySid; @@ -147,14 +153,14 @@ public abstract class KeymasterUtils { args.addUnsignedLong( KeymasterDefs.KM_TAG_USER_SECURE_ID, KeymasterArguments.toUint64(sid)); args.addEnum(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, KeymasterDefs.HW_AUTH_FINGERPRINT); - if (userAuthenticationValidWhileOnBody) { + if (spec.isUserAuthenticationValidWhileOnBody()) { throw new ProviderException("Key validity extension while device is on-body is not " + "supported for keys requiring fingerprint authentication"); } } else { long sid; - if (boundToSpecificSecureUserId != GateKeeper.INVALID_SECURE_USER_ID) { - sid = boundToSpecificSecureUserId; + if (spec.getBoundToSpecificSecureUserId() != GateKeeper.INVALID_SECURE_USER_ID) { + sid = spec.getBoundToSpecificSecureUserId(); } else { // The key is authorized for use for the specified amount of time after the user has // authenticated. Whatever unlocks the secure lock screen should authorize this key. @@ -165,8 +171,8 @@ public abstract class KeymasterUtils { args.addEnum(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, KeymasterDefs.HW_AUTH_PASSWORD | KeymasterDefs.HW_AUTH_FINGERPRINT); args.addUnsignedInt(KeymasterDefs.KM_TAG_AUTH_TIMEOUT, - userAuthenticationValidityDurationSeconds); - if (userAuthenticationValidWhileOnBody) { + spec.getUserAuthenticationValidityDurationSeconds()); + if (spec.isUserAuthenticationValidWhileOnBody()) { args.addBoolean(KeymasterDefs.KM_TAG_ALLOW_WHILE_ON_BODY); } } diff --git a/keystore/java/android/security/keystore/UserAuthArgs.java b/keystore/java/android/security/keystore/UserAuthArgs.java new file mode 100644 index 000000000000..3a7017ecaa88 --- /dev/null +++ b/keystore/java/android/security/keystore/UserAuthArgs.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.security.keystore; + +/** + * @hide + * + * This is an interface to encapsulate the user authentication arguments that + * are passed to KeymasterUtils.addUserAuthArgs. Classes that represent + * authorization characteristics for new or imported keys can implement this + * interface to be passed to that method. + */ +public interface UserAuthArgs { + + boolean isUserAuthenticationRequired(); + int getUserAuthenticationValidityDurationSeconds(); + boolean isUserAuthenticationValidWhileOnBody(); + boolean isInvalidatedByBiometricEnrollment(); + boolean isTrustedUserPresenceRequired(); + boolean isUnlockedDeviceRequired(); + boolean isUserConfirmationRequired(); + long getBoundToSpecificSecureUserId(); + +} diff --git a/libs/incident/Android.mk b/libs/incident/Android.mk index b63400f14609..08c834699f40 100644 --- a/libs/incident/Android.mk +++ b/libs/incident/Android.mk @@ -33,6 +33,7 @@ LOCAL_SRC_FILES := \ ../../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 000000000000..a1e89b0dbf98 --- /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/media/OWNERS b/media/OWNERS new file mode 100644 index 000000000000..8f405e98545b --- /dev/null +++ b/media/OWNERS @@ -0,0 +1,4 @@ +elaurent@google.com +etalvala@google.com +lajos@google.com +marcone@google.com diff --git a/native/android/OWNERS b/native/android/OWNERS new file mode 100644 index 000000000000..11d4be43571e --- /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/CarrierDefaultApp/OWNERS b/packages/CarrierDefaultApp/OWNERS new file mode 100644 index 000000000000..7057ce6cb6fe --- /dev/null +++ b/packages/CarrierDefaultApp/OWNERS @@ -0,0 +1,12 @@ +tgunn@google.com +breadley@google.com +hallliu@google.com +rgreenwalt@google.com +mpq@google.com +amitmahajan@google.com +fionaxu@google.com +jackyu@google.com +jminjie@google.com +satk@google.com +shuoq@google.com +refuhoo@google.com
\ No newline at end of file diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java index 44f68eca49a3..d73a5d73e5bf 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 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat 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 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat @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 7c2e55f35cde..cf73aacb9b55 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 @@ public final class SelectPrinterActivity extends Activity implements 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 000000000000..e14c99b61e2f --- /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 e6f4ec6e8979..c78f4541384a 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 1f67dfb568a6..9947dec14d48 100644 --- a/packages/SettingsLib/src/com/android/settingslib/Utils.java +++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java @@ -1,6 +1,7 @@ package com.android.settingslib; import android.annotation.ColorInt; +import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.pm.PackageInfo; @@ -17,10 +18,13 @@ import android.graphics.drawable.Drawable; import android.location.LocationManager; import android.net.ConnectivityManager; import android.os.BatteryManager; +import android.os.SystemProperties; import android.os.UserHandle; import android.os.UserManager; import android.print.PrintManager; import android.provider.Settings; + +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.UserIcons; import com.android.settingslib.drawable.UserIconDrawable; import com.android.settingslib.wrapper.LocationManagerWrapper; @@ -30,6 +34,9 @@ public class Utils { private static final String CURRENT_MODE_KEY = "CURRENT_MODE"; private static final String NEW_MODE_KEY = "NEW_MODE"; + @VisibleForTesting + static final String STORAGE_MANAGER_SHOW_OPT_IN_PROPERTY = + "ro.storage_manager.show_opt_in"; private static Signature[] sSystemSignature; private static String sPermissionControllerPackageName; @@ -37,11 +44,11 @@ public class Utils { private static String sSharedSystemSharedLibPackageName; static final int[] WIFI_PIE = { - com.android.internal.R.drawable.ic_wifi_signal_0, - com.android.internal.R.drawable.ic_wifi_signal_1, - com.android.internal.R.drawable.ic_wifi_signal_2, - com.android.internal.R.drawable.ic_wifi_signal_3, - com.android.internal.R.drawable.ic_wifi_signal_4 + com.android.internal.R.drawable.ic_wifi_signal_0, + com.android.internal.R.drawable.ic_wifi_signal_1, + com.android.internal.R.drawable.ic_wifi_signal_2, + com.android.internal.R.drawable.ic_wifi_signal_3, + com.android.internal.R.drawable.ic_wifi_signal_4 }; public static void updateLocationEnabled(Context context, boolean enabled, int userId, @@ -262,7 +269,7 @@ public class Utils { */ public static boolean isSystemPackage(Resources resources, PackageManager pm, PackageInfo pkg) { if (sSystemSignature == null) { - sSystemSignature = new Signature[]{ getSystemSignature(pm) }; + sSystemSignature = new Signature[]{getSystemSignature(pm)}; } if (sPermissionControllerPackageName == null) { sPermissionControllerPackageName = pm.getPermissionControllerPackageName(); @@ -274,7 +281,7 @@ public class Utils { sSharedSystemSharedLibPackageName = pm.getSharedSystemSharedLibraryPackageName(); } return (sSystemSignature[0] != null - && sSystemSignature[0].equals(getFirstSignature(pkg))) + && sSystemSignature[0].equals(getFirstSignature(pkg))) || pkg.packageName.equals(sPermissionControllerPackageName) || pkg.packageName.equals(sServicesSystemSharedLibPackageName) || pkg.packageName.equals(sSharedSystemSharedLibPackageName) @@ -312,7 +319,6 @@ public class Utils { * Returns the Wifi icon resource for a given RSSI level. * * @param level The number of bars to show (0-4) - * * @throws IllegalArgumentException if an invalid RSSI level is given. */ public static int getWifiIconResource(int level) { @@ -342,4 +348,19 @@ public class Utils { return !context.getSystemService(ConnectivityManager.class) .isNetworkSupported(ConnectivityManager.TYPE_MOBILE); } + + /** Returns if the automatic storage management feature is turned on or not. **/ + public static boolean isStorageManagerEnabled(Context context) { + boolean isDefaultOn; + try { + // Turn off by default if the opt-in was shown. + isDefaultOn = !SystemProperties.getBoolean(STORAGE_MANAGER_SHOW_OPT_IN_PROPERTY, true); + } catch (Resources.NotFoundException e) { + isDefaultOn = false; + } + return Settings.Secure.getInt(context.getContentResolver(), + Settings.Secure.AUTOMATIC_STORAGE_MANAGER_ENABLED, + isDefaultOn ? 1 : 0) + != 0; + } } diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java new file mode 100644 index 000000000000..8f9e4635bb4b --- /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 991d9221c796..34a099cb7ea0 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.BluetoothA2dpSink; 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 @@ public class LocalBluetoothProfileManager { 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 @@ public class LocalBluetoothProfileManager { //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 @@ public class LocalBluetoothProfileManager { "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 @@ public class LocalBluetoothProfileManager { 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 @@ public class LocalBluetoothProfileManager { 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/notification/EnableZenModeDialog.java b/packages/SettingsLib/src/com/android/settingslib/notification/EnableZenModeDialog.java index 1a54d6a3396b..b98f27ea62d0 100644 --- a/packages/SettingsLib/src/com/android/settingslib/notification/EnableZenModeDialog.java +++ b/packages/SettingsLib/src/com/android/settingslib/notification/EnableZenModeDialog.java @@ -76,6 +76,8 @@ public class EnableZenModeDialog { protected Uri mForeverId; private int mBucketIndex = -1; + @VisibleForTesting + protected NotificationManager mNotificationManager; private AlarmManager mAlarmManager; private int mUserId; private boolean mAttached; @@ -98,7 +100,7 @@ public class EnableZenModeDialog { } public Dialog createDialog() { - NotificationManager noMan = (NotificationManager) mContext. + mNotificationManager = (NotificationManager) mContext. getSystemService(Context.NOTIFICATION_SERVICE); mForeverId = Condition.newId(mContext).appendPath("forever").build(); mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE); @@ -131,7 +133,8 @@ public class EnableZenModeDialog { Slog.d(TAG, "Invalid manual condition: " + tag.condition); } // always triggers priority-only dnd with chosen condition - noMan.setZenMode(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS, + mNotificationManager.setZenMode( + Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS, getRealConditionId(tag.condition), TAG); } }); @@ -465,7 +468,16 @@ public class EnableZenModeDialog { mZenAlarmWarning.setVisibility(warningText == null ? View.GONE : View.VISIBLE); } - private String computeAlarmWarningText(Condition condition) { + @VisibleForTesting + protected String computeAlarmWarningText(Condition condition) { + boolean allowAlarms = (mNotificationManager.getNotificationPolicy().priorityCategories + & NotificationManager.Policy.PRIORITY_CATEGORY_ALARMS) != 0; + + // don't show alarm warning if alarms are allowed to bypass dnd + if (allowAlarms) { + return null; + } + final long now = System.currentTimeMillis(); final long nextAlarm = getNextAlarm(); if (nextAlarm < now) { @@ -483,14 +495,19 @@ public class EnableZenModeDialog { if (warningRes == 0) { return null; } + + return mContext.getResources().getString(warningRes, getTime(nextAlarm, now)); + } + + @VisibleForTesting + protected String getTime(long nextAlarm, long now) { final boolean soon = (nextAlarm - now) < 24 * 60 * 60 * 1000; final boolean is24 = DateFormat.is24HourFormat(mContext, ActivityManager.getCurrentUser()); final String skeleton = soon ? (is24 ? "Hm" : "hma") : (is24 ? "EEEHm" : "EEEhma"); final String pattern = DateFormat.getBestDateTimePattern(Locale.getDefault(), skeleton); final CharSequence formattedTime = DateFormat.format(pattern, nextAlarm); final int templateRes = soon ? R.string.alarm_template : R.string.alarm_template_far; - final String template = mContext.getResources().getString(templateRes, formattedTime); - return mContext.getResources().getString(warningRes, template); + return mContext.getResources().getString(templateRes, formattedTime); } // used as the view tag on condition rows diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java index f69944006a87..b380ac53ddad 100644 --- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java @@ -17,6 +17,7 @@ package com.android.settingslib.wifi; import android.annotation.IntDef; +import android.annotation.MainThread; import android.annotation.Nullable; import android.app.AppGlobals; import android.content.Context; @@ -42,6 +43,8 @@ import android.net.wifi.WifiManager; import android.net.wifi.WifiNetworkScoreCache; import android.net.wifi.hotspot2.PasspointConfiguration; import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; import android.os.Parcelable; import android.os.RemoteException; import android.os.ServiceManager; @@ -57,6 +60,7 @@ import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.android.settingslib.R; +import com.android.settingslib.utils.ThreadUtils; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -68,7 +72,14 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; - +/** + * Represents a selectable Wifi Network for use in various wifi selection menus backed by + * {@link WifiTracker}. + * + * <p>An AccessPoint, which would be more fittingly named "WifiNetwork", is an aggregation of + * {@link ScanResult ScanResults} along with pertinent metadata (e.g. current connection info, + * network scores) required to successfully render the network to the user. + */ public class AccessPoint implements Comparable<AccessPoint> { static final String TAG = "SettingsLib.AccessPoint"; @@ -288,11 +299,6 @@ public class AccessPoint implements Comparable<AccessPoint> { mId = sLastId.incrementAndGet(); } - AccessPoint(Context context, AccessPoint other) { - mContext = context; - copyFrom(other); - } - AccessPoint(Context context, Collection<ScanResult> results) { mContext = context; @@ -346,33 +352,6 @@ public class AccessPoint implements Comparable<AccessPoint> { } /** - * Copy accesspoint information. NOTE: We do not copy tag information because that is never - * set on the internal copy. - */ - void copyFrom(AccessPoint that) { - this.ssid = that.ssid; - this.bssid = that.bssid; - this.security = that.security; - this.mKey = that.mKey; - this.networkId = that.networkId; - this.pskType = that.pskType; - this.mConfig = that.mConfig; //TODO: Watch out, this object is mutated. - this.mRssi = that.mRssi; - this.mInfo = that.mInfo; - this.mNetworkInfo = that.mNetworkInfo; - this.mScanResults.clear(); - this.mScanResults.addAll(that.mScanResults); - this.mScoredNetworkCache.clear(); - this.mScoredNetworkCache.putAll(that.mScoredNetworkCache); - this.mId = that.mId; - this.mSpeed = that.mSpeed; - this.mIsScoredNetworkMetered = that.mIsScoredNetworkMetered; - this.mIsCarrierAp = that.mIsCarrierAp; - this.mCarrierApEapType = that.mCarrierApEapType; - this.mCarrierName = that.mCarrierName; - } - - /** * Returns a negative integer, zero, or a positive integer if this AccessPoint is less than, * equal to, or greater than the other AccessPoint. * @@ -467,7 +446,7 @@ public class AccessPoint implements Comparable<AccessPoint> { } builder.append(",metered=").append(isMetered()); - if (WifiTracker.sVerboseLogging) { + if (isVerboseLoggingEnabled()) { builder.append(",rssi=").append(mRssi); builder.append(",scan cache size=").append(mScanResults.size()); } @@ -546,7 +525,7 @@ public class AccessPoint implements Comparable<AccessPoint> { mSpeed = generateAverageSpeedForSsid(); boolean changed = oldSpeed != mSpeed; - if(WifiTracker.sVerboseLogging && changed) { + if(isVerboseLoggingEnabled() && changed) { Log.i(TAG, String.format("%s: Set speed to %d", ssid, mSpeed)); } return changed; @@ -577,7 +556,7 @@ public class AccessPoint implements Comparable<AccessPoint> { } } int speed = count == 0 ? Speed.NONE : totalSpeed / count; - if (WifiTracker.sVerboseLogging) { + if (isVerboseLoggingEnabled()) { Log.i(TAG, String.format("%s generated fallback speed is: %d", getSsidStr(), speed)); } return roundToClosestSpeedEnum(speed); @@ -913,7 +892,7 @@ public class AccessPoint implements Comparable<AccessPoint> { } } - if (WifiTracker.sVerboseLogging) { + if (isVerboseLoggingEnabled()) { summary.append(WifiUtils.buildLoggingSummary(this, config)); } @@ -1070,12 +1049,12 @@ public class AccessPoint implements Comparable<AccessPoint> { // Only update labels on visible rssi changes updateSpeed(); if (mAccessPointListener != null) { - mAccessPointListener.onLevelChanged(this); + ThreadUtils.postOnMainThread(() -> mAccessPointListener.onLevelChanged(this)); } } if (mAccessPointListener != null) { - mAccessPointListener.onAccessPointChanged(this); + ThreadUtils.postOnMainThread(() -> mAccessPointListener.onAccessPointChanged(this)); } if (!scanResults.isEmpty()) { @@ -1123,10 +1102,10 @@ public class AccessPoint implements Comparable<AccessPoint> { mNetworkInfo = null; } if (updated && mAccessPointListener != null) { - mAccessPointListener.onAccessPointChanged(this); + ThreadUtils.postOnMainThread(() -> mAccessPointListener.onAccessPointChanged(this)); if (oldLevel != getLevel() /* current level */) { - mAccessPointListener.onLevelChanged(this); + ThreadUtils.postOnMainThread(() -> mAccessPointListener.onLevelChanged(this)); } } @@ -1137,7 +1116,7 @@ public class AccessPoint implements Comparable<AccessPoint> { mConfig = config; networkId = config != null ? config.networkId : WifiConfiguration.INVALID_NETWORK_ID; if (mAccessPointListener != null) { - mAccessPointListener.onAccessPointChanged(this); + ThreadUtils.postOnMainThread(() -> mAccessPointListener.onAccessPointChanged(this)); } } @@ -1333,8 +1312,44 @@ public class AccessPoint implements Comparable<AccessPoint> { return string; } + /** + * Callbacks relaying changes to the AccessPoint representation. + * + * <p>All methods are invoked on the Main Thread. + */ public interface AccessPointListener { - void onAccessPointChanged(AccessPoint accessPoint); - void onLevelChanged(AccessPoint accessPoint); + /** + * Indicates a change to the externally visible state of the AccessPoint trigger by an + * update of ScanResults, saved configuration state, connection state, or score + * (labels/metered) state. + * + * <p>Clients should refresh their view of the AccessPoint to match the updated state when + * this is invoked. Overall this method is extraneous if clients are listening to + * {@link WifiTracker.WifiListener#onAccessPointsChanged()} callbacks. + * + * <p>Examples of changes include signal strength, connection state, speed label, and + * generally anything that would impact the summary string. + * + * @param accessPoint The accessPoint object the listener was registered on which has + * changed + */ + @MainThread void onAccessPointChanged(AccessPoint accessPoint); + + /** + * Indicates the "wifi pie signal level" has changed, retrieved via calls to + * {@link AccessPoint#getLevel()}. + * + * <p>This call is a subset of {@link #onAccessPointChanged(AccessPoint)} , hence is also + * extraneous if the client is already reacting to that or the + * {@link WifiTracker.WifiListener#onAccessPointsChanged()} callbacks. + * + * @param accessPoint The accessPoint object the listener was registered on whose level has + * changed + */ + @MainThread void onLevelChanged(AccessPoint accessPoint); + } + + private static boolean isVerboseLoggingEnabled() { + return WifiTracker.sVerboseLogging || Log.isLoggable(TAG, Log.VERBOSE); } } diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java index fac585e06306..ae544dd6dbe8 100644 --- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java @@ -15,6 +15,7 @@ */ package com.android.settingslib.wifi; +import android.annotation.AnyThread; import android.annotation.MainThread; import android.content.BroadcastReceiver; import android.content.Context; @@ -48,8 +49,6 @@ import android.text.format.DateUtils; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Log; -import android.util.SparseArray; -import android.util.SparseIntArray; import android.widget.Toast; import com.android.settingslib.R; @@ -58,6 +57,7 @@ import com.android.settingslib.core.lifecycle.LifecycleObserver; import com.android.settingslib.core.lifecycle.events.OnDestroy; import com.android.settingslib.core.lifecycle.events.OnStart; import com.android.settingslib.core.lifecycle.events.OnStop; +import com.android.settingslib.utils.ThreadUtils; import java.io.PrintWriter; import java.util.ArrayList; @@ -88,8 +88,17 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro return Log.isLoggable(TAG, Log.DEBUG); } - /** verbose logging flag. this flag is set thru developer debugging options - * and used so as to assist with in-the-field WiFi connectivity debugging */ + private static boolean isVerboseLoggingEnabled() { + return WifiTracker.sVerboseLogging || Log.isLoggable(TAG, Log.VERBOSE); + } + + /** + * Verbose logging flag set thru developer debugging options and used so as to assist with + * in-the-field WiFi connectivity debugging. + * + * <p>{@link #isVerboseLoggingEnabled()} should be read rather than referencing this value + * directly, to ensure adb TAG level verbose settings are respected. + */ public static boolean sVerboseLogging; // TODO: Allow control of this? @@ -104,7 +113,6 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro private final NetworkRequest mNetworkRequest; private final AtomicBoolean mConnected = new AtomicBoolean(false); private final WifiListener mListener; - @VisibleForTesting MainHandler mMainHandler; @VisibleForTesting WorkHandler mWorkHandler; private HandlerThread mWorkThread; @@ -113,35 +121,17 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro @GuardedBy("mLock") private boolean mRegistered; - /** - * The externally visible access point list. - * - * Updated using main handler. Clone of this collection is returned from - * {@link #getAccessPoints()} - */ - private final List<AccessPoint> mAccessPoints = new ArrayList<>(); - - /** - * The internal list of access points, synchronized on itself. - * - * Never exposed outside this class. - */ + /** The list of AccessPoints, aggregated visible ScanResults with metadata. */ @GuardedBy("mLock") private final List<AccessPoint> mInternalAccessPoints = new ArrayList<>(); /** * Synchronization lock for managing concurrency between main and worker threads. * - * <p>This lock should be held for all background work. - * TODO(b/37674366): Remove the worker thread so synchronization is no longer necessary. + * <p>This lock should be held for all modifications to {@link #mInternalAccessPoints}. */ private final Object mLock = new Object(); - //visible to both worker and main thread. - @GuardedBy("mLock") - private final AccessPointListenerAdapter mAccessPointListenerAdapter - = new AccessPointListenerAdapter(); - private final HashMap<String, Integer> mSeenBssids = new HashMap<>(); // TODO(sghuman): Change this to be keyed on AccessPoint.getKey @@ -161,6 +151,12 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro @VisibleForTesting Scanner mScanner; + /** + * Tracks whether fresh scan results have been received since scanning start. + * + * <p>If this variable is false, we will not evict the scan result cache or invoke callbacks + * so that we do not update the UI with stale data / clear out existing UI elements prematurely. + */ @GuardedBy("mLock") private boolean mStaleScanResults = true; @@ -209,12 +205,11 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro NetworkScoreManager networkScoreManager, IntentFilter filter) { mContext = context; - mMainHandler = new MainHandler(Looper.getMainLooper()); mWifiManager = wifiManager; mListener = new WifiListenerWrapper(wifiListener); mConnectivityManager = connectivityManager; - // check if verbose logging has been turned on or off + // check if verbose logging developer option has been turned on or off sVerboseLogging = (mWifiManager.getVerboseLoggingLevel() > 0); mFilter = filter; @@ -226,6 +221,7 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro mNetworkScoreManager = networkScoreManager; + // TODO(sghuman): Remove this and create less hacky solution for testing final HandlerThread workThread = new HandlerThread(TAG + "{" + Integer.toHexString(System.identityHashCode(this)) + "}", Process.THREAD_PRIORITY_BACKGROUND); @@ -238,6 +234,8 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro * @param workThread substitute Handler thread, for testing purposes only */ @VisibleForTesting + // TODO(sghuman): Remove this method, this needs to happen in a factory method and be passed in + // during construction void setWorkThread(HandlerThread workThread) { mWorkThread = workThread; mWorkHandler = new WorkHandler(workThread.getLooper()); @@ -270,32 +268,29 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro mLastNetworkInfo = mConnectivityManager.getNetworkInfo(mWifiManager.getCurrentNetwork()); final List<ScanResult> newScanResults = mWifiManager.getScanResults(); - if (sVerboseLogging) { + if (isVerboseLoggingEnabled()) { Log.i(TAG, "Fetched scan results: " + newScanResults); } List<WifiConfiguration> configs = mWifiManager.getConfiguredNetworks(); mInternalAccessPoints.clear(); updateAccessPointsLocked(newScanResults, configs); - - // Synchronously copy access points - mMainHandler.removeMessages(MainHandler.MSG_ACCESS_POINT_CHANGED); - mMainHandler.handleMessage( - Message.obtain(mMainHandler, MainHandler.MSG_ACCESS_POINT_CHANGED)); - if (sVerboseLogging) { - Log.i(TAG, "force update - external access point list:\n" + mAccessPoints); - } } } /** * Temporarily stop scanning for wifi networks. + * + * <p>Sets {@link #mStaleScanResults} to true. */ - public void pauseScanning() { + private void pauseScanning() { if (mScanner != null) { mScanner.pause(); mScanner = null; } + synchronized (mLock) { + mStaleScanResults = true; + } } /** @@ -387,11 +382,9 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro mRegistered = false; } unregisterScoreCache(); - pauseScanning(); + pauseScanning(); // and set mStaleScanResults mWorkHandler.removePendingMessages(); - mMainHandler.removePendingMessages(); - mStaleScanResults = true; } } @@ -409,12 +402,19 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro } /** - * Gets the current list of access points. Should be called from main thread, otherwise - * expect inconsistencies + * Gets the current list of access points. + * + * <p>This method is can be called on an abitrary thread by clients, but is normally called on + * the UI Thread by the rendering App. */ - @MainThread + @AnyThread public List<AccessPoint> getAccessPoints() { - return new ArrayList<>(mAccessPoints); + // TODO(sghuman): Investigate how to eliminate or reduce the need for locking now that we + // have transitioned to a single worker thread model. + + synchronized (mLock) { + return new ArrayList<>(mInternalAccessPoints); + } } public WifiManager getManager() { @@ -447,6 +447,8 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro } private void handleResume() { + // TODO(sghuman): Investigate removing this and replacing it with a cache eviction call + // instead. mScanResultCache.clear(); mSeenBssids.clear(); } @@ -509,7 +511,7 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro private void updateAccessPoints() { List<WifiConfiguration> configs = mWifiManager.getConfiguredNetworks(); final List<ScanResult> newScanResults = mWifiManager.getScanResults(); - if (sVerboseLogging) { + if (isVerboseLoggingEnabled()) { Log.i(TAG, "Fetched scan results: " + newScanResults); } @@ -524,11 +526,15 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro * Update the internal list of access points. * * <p>Do not call directly (except for forceUpdate), use {@link #updateAccessPoints()} which - * respects {@link #mStaleScanResults}. + * acquires the lock first. */ @GuardedBy("mLock") private void updateAccessPointsLocked(final List<ScanResult> newScanResults, List<WifiConfiguration> configs) { + // TODO(sghuman): Reduce the synchronization time by only holding the lock when + // modifying lists exposed to operations on the MainThread (getAccessPoints, stopTracking, + // startTracking, etc). + WifiConfiguration connectionConfig = null; if (mLastInfo != null) { connectionConfig = getWifiConfigurationForNetworkId( @@ -634,7 +640,7 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro mInternalAccessPoints.clear(); mInternalAccessPoints.addAll(accessPoints); - mMainHandler.sendEmptyMessage(MainHandler.MSG_ACCESS_POINT_CHANGED); + conditionallyNotifyListeners(); } @VisibleForTesting @@ -650,7 +656,6 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro } } final AccessPoint accessPoint = new AccessPoint(mContext, scanResults); - accessPoint.setListener(mAccessPointListenerAdapter); return accessPoint; } @@ -661,16 +666,17 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro if (cache.get(i).matches(config)) { AccessPoint ret = cache.remove(i); ret.loadConfig(config); + return ret; } } final AccessPoint accessPoint = new AccessPoint(mContext, config); - accessPoint.setListener(mAccessPointListenerAdapter); return accessPoint; } private void updateNetworkInfo(NetworkInfo networkInfo) { - /* sticky broadcasts can call this when wifi is disabled */ + + /* Sticky broadcasts can call this when wifi is disabled */ if (!mWifiManager.isWifiEnabled()) { clearAccessPointsAndConditionallyUpdate(); return; @@ -681,6 +687,10 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro if (DBG()) { Log.d(TAG, "mLastNetworkInfo set: " + mLastNetworkInfo); } + + if(networkInfo.isConnected() != mConnected.getAndSet(networkInfo.isConnected())) { + mListener.onConnectedChanged(); + } } WifiConfiguration connectionConfig = null; @@ -711,18 +721,25 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro } } - if (reorder) Collections.sort(mInternalAccessPoints); - if (updated) mMainHandler.sendEmptyMessage(MainHandler.MSG_ACCESS_POINT_CHANGED); + if (reorder) { + Collections.sort(mInternalAccessPoints); + } + if (updated) { + conditionallyNotifyListeners(); + } } } + /** + * Clears the access point list and conditionally invokes + * {@link WifiListener#onAccessPointsChanged()} if required (i.e. the list was not already + * empty). + */ private void clearAccessPointsAndConditionallyUpdate() { synchronized (mLock) { if (!mInternalAccessPoints.isEmpty()) { mInternalAccessPoints.clear(); - if (!mMainHandler.hasMessages(MainHandler.MSG_ACCESS_POINT_CHANGED)) { - mMainHandler.sendEmptyMessage(MainHandler.MSG_ACCESS_POINT_CHANGED); - } + mListener.onAccessPointsChanged(); } } } @@ -745,27 +762,26 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro } if (updated) { Collections.sort(mInternalAccessPoints); - mMainHandler.sendEmptyMessage(MainHandler.MSG_ACCESS_POINT_CHANGED); + conditionallyNotifyListeners(); } } } - private void updateWifiState(int state) { - mWorkHandler.obtainMessage(WorkHandler.MSG_UPDATE_WIFI_STATE, state, 0).sendToTarget(); - if (!mWifiManager.isWifiEnabled()) { - clearAccessPointsAndConditionallyUpdate(); - } - } - @VisibleForTesting final BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { + // No work should be performed in this Receiver, instead all operations should be passed + // off to the WorkHandler to avoid concurrent modification exceptions. + String action = intent.getAction(); if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) { - updateWifiState(intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, - WifiManager.WIFI_STATE_UNKNOWN)); + mWorkHandler.obtainMessage( + WorkHandler.MSG_UPDATE_WIFI_STATE, + intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, + WifiManager.WIFI_STATE_UNKNOWN), + 0).sendToTarget(); } else if (WifiManager.SCAN_RESULTS_AVAILABLE_ACTION.equals(action)) { mWorkHandler .obtainMessage( @@ -778,12 +794,6 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro mWorkHandler.sendEmptyMessage(WorkHandler.MSG_UPDATE_ACCESS_POINTS); } else if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(action)) { NetworkInfo info = intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO); - - if(mConnected.get() != info.isConnected()) { - mConnected.set(info.isConnected()); - mMainHandler.sendEmptyMessage(MainHandler.MSG_CONNECTED_CHANGED); - } - mWorkHandler.obtainMessage(WorkHandler.MSG_UPDATE_NETWORK_INFO, info) .sendToTarget(); mWorkHandler.sendEmptyMessage(WorkHandler.MSG_UPDATE_ACCESS_POINTS); @@ -808,68 +818,8 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro } @VisibleForTesting - final class MainHandler extends Handler { - @VisibleForTesting static final int MSG_CONNECTED_CHANGED = 0; - @VisibleForTesting static final int MSG_WIFI_STATE_CHANGED = 1; - @VisibleForTesting static final int MSG_ACCESS_POINT_CHANGED = 2; - private static final int MSG_RESUME_SCANNING = 3; - private static final int MSG_PAUSE_SCANNING = 4; - - public MainHandler(Looper looper) { - super(looper); - } - - @Override - public void handleMessage(Message msg) { - if (mListener == null) { - return; - } - switch (msg.what) { - case MSG_CONNECTED_CHANGED: - mListener.onConnectedChanged(); - break; - case MSG_WIFI_STATE_CHANGED: - mListener.onWifiStateChanged(msg.arg1); - break; - case MSG_ACCESS_POINT_CHANGED: - // Only notify listeners of changes if we have fresh scan results, otherwise the - // UI will be updated with stale results. We want to copy the APs regardless, - // for instances where forceUpdate was invoked by the caller. - if (mStaleScanResults) { - copyAndNotifyListeners(false /*notifyListeners*/); - } else { - copyAndNotifyListeners(true /*notifyListeners*/); - mListener.onAccessPointsChanged(); - } - break; - case MSG_RESUME_SCANNING: - if (mScanner != null) { - mScanner.resume(); - } - break; - case MSG_PAUSE_SCANNING: - if (mScanner != null) { - mScanner.pause(); - } - synchronized (mLock) { - mStaleScanResults = true; - } - break; - } - } - - void removePendingMessages() { - removeMessages(MSG_ACCESS_POINT_CHANGED); - removeMessages(MSG_CONNECTED_CHANGED); - removeMessages(MSG_WIFI_STATE_CHANGED); - removeMessages(MSG_PAUSE_SCANNING); - removeMessages(MSG_RESUME_SCANNING); - } - } - - @VisibleForTesting final class WorkHandler extends Handler { - private static final int MSG_UPDATE_ACCESS_POINTS = 0; + @VisibleForTesting static final int MSG_UPDATE_ACCESS_POINTS = 0; private static final int MSG_UPDATE_NETWORK_INFO = 1; private static final int MSG_RESUME = 2; private static final int MSG_UPDATE_WIFI_STATE = 3; @@ -882,6 +832,8 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro @Override public void handleMessage(Message msg) { + // TODO(sghuman): Clean up synchronization to only be used when modifying collections + // exposed to the MainThread (through onStart, onStop, forceUpdate). synchronized (mLock) { processMessage(msg); } @@ -911,6 +863,7 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro mScanner.resume(); } } else { + clearAccessPointsAndConditionallyUpdate(); mLastInfo = null; mLastNetworkInfo = null; if (mScanner != null) { @@ -920,8 +873,7 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro mStaleScanResults = true; } } - mMainHandler.obtainMessage(MainHandler.MSG_WIFI_STATE_CHANGED, msg.arg1, 0) - .sendToTarget(); + mListener.onWifiStateChanged(msg.arg1); break; } } @@ -1010,16 +962,26 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro @Override public void onWifiStateChanged(int state) { + if (isVerboseLoggingEnabled()) { + Log.i(TAG, + String.format("Invoking onWifiStateChanged callback with state %d", state)); + } mHandler.post(() -> mDelegatee.onWifiStateChanged(state)); } @Override public void onConnectedChanged() { + if (isVerboseLoggingEnabled()) { + Log.i(TAG, "Invoking onConnectedChanged callback"); + } mHandler.post(() -> mDelegatee.onConnectedChanged()); } @Override public void onAccessPointsChanged() { + if (isVerboseLoggingEnabled()) { + Log.i(TAG, "Invoking onAccessPointsChanged callback"); + } mHandler.post(() -> mDelegatee.onAccessPointsChanged()); } } @@ -1041,101 +1003,27 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro void onWifiStateChanged(int state); /** - * Called when the connection state of wifi has changed and isConnected - * should be called to get the updated state. + * Called when the connection state of wifi has changed and + * {@link WifiTracker#isConnected()} should be called to get the updated state. */ void onConnectedChanged(); /** * Called to indicate the list of AccessPoints has been updated and - * getAccessPoints should be called to get the latest information. + * {@link WifiTracker#getAccessPoints()} should be called to get the updated list. */ void onAccessPointsChanged(); } /** - * Helps capture notifications that were generated during AccessPoint modification. Used later - * on by {@link #copyAndNotifyListeners(boolean)} to send notifications. + * Invokes {@link WifiListenerWrapper#onAccessPointsChanged()} if {@link #mStaleScanResults} + * is false. */ - private static class AccessPointListenerAdapter implements AccessPoint.AccessPointListener { - static final int AP_CHANGED = 1; - static final int LEVEL_CHANGED = 2; - - final SparseIntArray mPendingNotifications = new SparseIntArray(); - - @Override - public void onAccessPointChanged(AccessPoint accessPoint) { - int type = mPendingNotifications.get(accessPoint.mId); - mPendingNotifications.put(accessPoint.mId, type | AP_CHANGED); - } - - @Override - public void onLevelChanged(AccessPoint accessPoint) { - int type = mPendingNotifications.get(accessPoint.mId); - mPendingNotifications.put(accessPoint.mId, type | LEVEL_CHANGED); - } - } - - /** - * Responsible for copying access points from {@link #mInternalAccessPoints} and notifying - * accesspoint listeners. - * - * @param notifyListeners if true, accesspoint listeners are notified, otherwise notifications - * dropped. - */ - @MainThread - private void copyAndNotifyListeners(boolean notifyListeners) { - // Need to watch out for memory allocations on main thread. - SparseArray<AccessPoint> oldAccessPoints = new SparseArray<>(); - SparseIntArray notificationMap = null; - List<AccessPoint> updatedAccessPoints = new ArrayList<>(); - - for (AccessPoint accessPoint : mAccessPoints) { - oldAccessPoints.put(accessPoint.mId, accessPoint); - } - - synchronized (mLock) { - if (DBG()) { - Log.d(TAG, "Starting to copy AP items on the MainHandler. Internal APs: " - + mInternalAccessPoints); - } - - if (notifyListeners) { - notificationMap = mAccessPointListenerAdapter.mPendingNotifications.clone(); - } - - mAccessPointListenerAdapter.mPendingNotifications.clear(); - - for (AccessPoint internalAccessPoint : mInternalAccessPoints) { - AccessPoint accessPoint = oldAccessPoints.get(internalAccessPoint.mId); - if (accessPoint == null) { - accessPoint = new AccessPoint(mContext, internalAccessPoint); - } else { - accessPoint.copyFrom(internalAccessPoint); - } - updatedAccessPoints.add(accessPoint); - } + private void conditionallyNotifyListeners() { + if (mStaleScanResults) { + return; } - mAccessPoints.clear(); - mAccessPoints.addAll(updatedAccessPoints); - - if (notificationMap != null && notificationMap.size() > 0) { - for (AccessPoint accessPoint : updatedAccessPoints) { - int notificationType = notificationMap.get(accessPoint.mId); - AccessPoint.AccessPointListener listener = accessPoint.mAccessPointListener; - if (notificationType == 0 || listener == null) { - continue; - } - - if ((notificationType & AccessPointListenerAdapter.AP_CHANGED) != 0) { - listener.onAccessPointChanged(accessPoint); - } - - if ((notificationType & AccessPointListenerAdapter.LEVEL_CHANGED) != 0) { - listener.onLevelChanged(accessPoint); - } - } - } + ThreadUtils.postOnMainThread(() -> mListener.onAccessPointsChanged()); } } diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java index 144031108662..54c02a22e79f 100644 --- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java +++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java @@ -113,38 +113,6 @@ public class AccessPointTest { } @Test - public void testCopyAccessPoint_dataShouldMatch() { - WifiConfiguration configuration = createWifiConfiguration(); - configuration.meteredHint = true; - - NetworkInfo networkInfo = - new NetworkInfo(ConnectivityManager.TYPE_WIFI, 2, "WIFI", "WIFI_SUBTYPE"); - AccessPoint originalAccessPoint = new AccessPoint(mContext, configuration); - WifiInfo wifiInfo = new WifiInfo(); - wifiInfo.setSSID(WifiSsid.createFromAsciiEncoded(configuration.SSID)); - wifiInfo.setBSSID(configuration.BSSID); - originalAccessPoint.update(configuration, wifiInfo, networkInfo); - AccessPoint copy = new AccessPoint(mContext, originalAccessPoint); - - assertThat(originalAccessPoint.getSsid().toString()).isEqualTo(copy.getSsid().toString()); - assertThat(originalAccessPoint.getBssid()).isEqualTo(copy.getBssid()); - assertThat(originalAccessPoint.getConfig()).isEqualTo(copy.getConfig()); - assertThat(originalAccessPoint.getSecurity()).isEqualTo(copy.getSecurity()); - assertThat(originalAccessPoint.isMetered()).isEqualTo(copy.isMetered()); - assertThat(originalAccessPoint.compareTo(copy) == 0).isTrue(); - } - - @Test - public void testThatCopyAccessPoint_scanCacheShouldMatch() { - AccessPoint original = createAccessPointWithScanResultCache(); - assertThat(original.getRssi()).isEqualTo(4); - AccessPoint copy = new AccessPoint(mContext, createWifiConfiguration()); - assertThat(copy.getRssi()).isEqualTo(AccessPoint.UNREACHABLE_RSSI); - copy.copyFrom(original); - assertThat(original.getRssi()).isEqualTo(copy.getRssi()); - } - - @Test public void testCompareTo_GivesActiveBeforeInactive() { AccessPoint activeAp = new TestAccessPointBuilder(mContext).setActive(true).build(); AccessPoint inactiveAp = new TestAccessPointBuilder(mContext).setActive(false).build(); diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java index 6be4936413b7..0c49bb66a40e 100644 --- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java +++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java @@ -356,19 +356,14 @@ public class WifiTrackerTest { private void waitForHandlersToProcessCurrentlyEnqueuedMessages(WifiTracker tracker) throws InterruptedException { + // TODO(sghuman): This should no longer be necessary in a single work handler model + CountDownLatch workerLatch = new CountDownLatch(1); tracker.mWorkHandler.post(() -> { workerLatch.countDown(); }); assertTrue("Latch timed out while waiting for WorkerHandler", workerLatch.await(LATCH_TIMEOUT, TimeUnit.MILLISECONDS)); - - CountDownLatch mainLatch = new CountDownLatch(1); - tracker.mMainHandler.post(() -> { - mainLatch.countDown(); - }); - assertTrue("Latch timed out while waiting for MainHandler", - mainLatch.await(LATCH_TIMEOUT, TimeUnit.MILLISECONDS)); } private void switchToNetwork2(WifiTracker tracker) throws InterruptedException { @@ -390,38 +385,6 @@ public class WifiTrackerTest { } @Test - public void testAccessPointListenerSetWhenLookingUpUsingScanResults() { - ScanResult scanResult = new ScanResult(); - scanResult.level = 123; - scanResult.BSSID = "bssid-" + 111; - scanResult.timestamp = SystemClock.elapsedRealtime() * 1000; - scanResult.capabilities = ""; - - WifiTracker tracker = new WifiTracker( - InstrumentationRegistry.getTargetContext(), null, true, true); - - AccessPoint result = tracker.getCachedOrCreate( - Collections.singletonList(scanResult), new ArrayList<AccessPoint>()); - assertTrue(result.mAccessPointListener != null); - } - - @Test - public void testAccessPointListenerSetWhenLookingUpUsingWifiConfiguration() { - WifiConfiguration configuration = new WifiConfiguration(); - configuration.SSID = "test123"; - configuration.BSSID="bssid"; - configuration.networkId = 123; - configuration.allowedKeyManagement = new BitSet(); - configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK); - - WifiTracker tracker = new WifiTracker( - InstrumentationRegistry.getTargetContext(), null, true, true); - - AccessPoint result = tracker.getCachedOrCreate(configuration, new ArrayList<AccessPoint>()); - assertTrue(result.mAccessPointListener != null); - } - - @Test public void startAndStopTrackingShouldRegisterAndUnregisterScoreCache() throws InterruptedException { WifiTracker tracker = createMockedWifiTracker(); @@ -534,7 +497,6 @@ public class WifiTrackerTest { waitForHandlersToProcessCurrentlyEnqueuedMessages(tracker); } - @FlakyTest @Test public void scoreCacheUpdateScoresShouldChangeSortOrder() throws InterruptedException { WifiTracker tracker = createTrackerWithImmediateBroadcastsAndInjectInitialScanResults(); @@ -634,9 +596,9 @@ public class WifiTrackerTest { public void scoresShouldBeRequestedForNewScanResultOnly() throws InterruptedException { // Scores can be requested together or serially depending on how the scan results are // processed. - mRequestScoresLatch = new CountDownLatch(2); + mRequestScoresLatch = new CountDownLatch(1); WifiTracker tracker = createTrackerWithImmediateBroadcastsAndInjectInitialScanResults(); - mRequestScoresLatch.await(LATCH_TIMEOUT, TimeUnit.MILLISECONDS); + assertTrue(mRequestScoresLatch.await(LATCH_TIMEOUT, TimeUnit.MILLISECONDS)); mRequestedKeys.clear(); String ssid = "ssid3"; @@ -770,7 +732,7 @@ public class WifiTrackerTest { CountDownLatch ready = new CountDownLatch(1); CountDownLatch latch = new CountDownLatch(1); CountDownLatch lock = new CountDownLatch(1); - tracker.mMainHandler.post(() -> { + tracker.mWorkHandler.post(() -> { try { ready.countDown(); lock.await(); @@ -781,12 +743,7 @@ public class WifiTrackerTest { }); // Enqueue messages - tracker.mMainHandler.sendEmptyMessage( - WifiTracker.MainHandler.MSG_ACCESS_POINT_CHANGED); - tracker.mMainHandler.sendEmptyMessage( - WifiTracker.MainHandler.MSG_CONNECTED_CHANGED); - tracker.mMainHandler.sendEmptyMessage( - WifiTracker.MainHandler.MSG_WIFI_STATE_CHANGED); + tracker.mWorkHandler.sendEmptyMessage(WifiTracker.WorkHandler.MSG_UPDATE_ACCESS_POINTS); try { ready.await(); // Make sure we have entered the first message handler @@ -800,12 +757,9 @@ public class WifiTrackerTest { lock.countDown(); assertTrue("Latch timed out", latch.await(LATCH_TIMEOUT, TimeUnit.MILLISECONDS)); - assertThat(tracker.mMainHandler.hasMessages( - WifiTracker.MainHandler.MSG_ACCESS_POINT_CHANGED)).isFalse(); - assertThat(tracker.mMainHandler.hasMessages( - WifiTracker.MainHandler.MSG_CONNECTED_CHANGED)).isFalse(); - assertThat(tracker.mMainHandler.hasMessages( - WifiTracker.MainHandler.MSG_WIFI_STATE_CHANGED)).isFalse(); + assertThat(tracker.mWorkHandler.hasMessages( + WifiTracker.WorkHandler.MSG_UPDATE_ACCESS_POINTS)).isFalse(); + waitForHandlersToProcessCurrentlyEnqueuedMessages(tracker); verifyNoMoreInteractions(mockWifiListener); } @@ -862,7 +816,7 @@ public class WifiTrackerTest { mAccessPointsChangedLatch = new CountDownLatch(1); tracker.mReceiver.onReceive(mContext, new Intent(WifiManager.WIFI_STATE_CHANGED_ACTION)); - mAccessPointsChangedLatch.await(LATCH_TIMEOUT, TimeUnit.MILLISECONDS); + assertTrue(mAccessPointsChangedLatch.await(LATCH_TIMEOUT, TimeUnit.MILLISECONDS)); waitForHandlersToProcessCurrentlyEnqueuedMessages(tracker); assertThat(tracker.getAccessPoints()).isEmpty(); diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java index 12d3106cfe61..706d0c0f51e5 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java @@ -16,6 +16,9 @@ package com.android.settingslib; import static android.Manifest.permission.WRITE_SECURE_SETTINGS; + +import static com.android.settingslib.Utils.STORAGE_MANAGER_SHOW_OPT_IN_PROPERTY; + import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.Matchers.eq; @@ -30,6 +33,7 @@ import android.content.Context; import android.content.Intent; import android.content.res.Resources; import android.location.LocationManager; +import android.os.SystemProperties; import android.os.UserHandle; import android.provider.Settings; import android.provider.Settings.Secure; @@ -136,7 +140,7 @@ public class UtilsTest { } @Test - public void testStorageManagerDaysToRetainUsesResources() { + public void testGetDefaultStorageManagerDaysToRetain_storageManagerDaysToRetainUsesResources() { Resources resources = mock(Resources.class); when(resources.getInteger( eq( @@ -149,6 +153,12 @@ public class UtilsTest { assertThat(Utils.getDefaultStorageManagerDaysToRetain(resources)).isEqualTo(60); } + @Test + public void testIsStorageManagerEnabled_UsesSystemProperties() { + SystemProperties.set(STORAGE_MANAGER_SHOW_OPT_IN_PROPERTY, "false"); + assertThat(Utils.isStorageManagerEnabled(mContext)).isTrue(); + } + private static ArgumentMatcher<Intent> actionMatches(String expected) { return intent -> TextUtils.equals(expected, intent.getAction()); } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/EnableZenModeDialogTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/EnableZenModeDialogTest.java index 9b5da4ae1f95..ccd2f538c731 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/EnableZenModeDialogTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/EnableZenModeDialogTest.java @@ -17,15 +17,22 @@ package com.android.settingslib.notification; import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertNotNull; +import static junit.framework.Assert.assertNull; import static junit.framework.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; import android.app.Fragment; +import android.app.NotificationManager; import android.content.Context; +import android.content.res.Resources; import android.net.Uri; import android.service.notification.Condition; import android.view.LayoutInflater; @@ -46,7 +53,11 @@ public class EnableZenModeDialogTest { @Mock private Context mContext; @Mock + private Resources mResources; + @Mock private Fragment mFragment; + @Mock + private NotificationManager mNotificationManager; private Context mShadowContext; private LayoutInflater mLayoutInflater; @@ -58,6 +69,7 @@ public class EnableZenModeDialogTest { MockitoAnnotations.initMocks(this); mShadowContext = RuntimeEnvironment.application; when(mContext.getApplicationContext()).thenReturn(mContext); + when(mContext.getResources()).thenReturn(mResources); when(mFragment.getContext()).thenReturn(mShadowContext); mLayoutInflater = LayoutInflater.from(mShadowContext); @@ -67,6 +79,10 @@ public class EnableZenModeDialogTest { mController.mForeverId = Condition.newId(mContext).appendPath("forever").build(); when(mContext.getString(com.android.internal.R.string.zen_mode_forever)) .thenReturn("testSummary"); + NotificationManager.Policy alarmsEnabledPolicy = new NotificationManager.Policy( + NotificationManager.Policy.PRIORITY_CATEGORY_ALARMS, 0, 0, 0); + doReturn(alarmsEnabledPolicy).when(mNotificationManager).getNotificationPolicy(); + mController.mNotificationManager = mNotificationManager; mController.getContentView(); // these methods use static calls to ZenModeConfig which would normally fail in robotests, @@ -141,4 +157,38 @@ public class EnableZenModeDialogTest { assertFalse(mController.getConditionTagAt( EnableZenModeDialog.COUNTDOWN_ALARM_CONDITION_INDEX).rb.isChecked()); } + + @Test + public void testNoAlarmWarning() { + // setup alarm + long now = System.currentTimeMillis(); + doReturn(now + 100000).when(mController).getNextAlarm(); + doReturn("").when(mController).getTime(anyLong(), anyLong()); + + // allow alarms + when(mNotificationManager.getNotificationPolicy()).thenReturn( + new NotificationManager.Policy( + NotificationManager.Policy.PRIORITY_CATEGORY_ALARMS, 0, 0, 0)); + + // alarm warning should be null + assertNull(mController.computeAlarmWarningText(null)); + } + + @Test + public void testAlarmWarning() { + // setup alarm + long now = System.currentTimeMillis(); + doReturn(now + 1000000).when(mController).getNextAlarm(); + doReturn("").when(mController).getTime(anyLong(), anyLong()); + + // don't allow alarms to bypass dnd + when(mNotificationManager.getNotificationPolicy()).thenReturn( + new NotificationManager.Policy(0, 0, 0, 0)); + + // return a string if mResources is asked to retrieve a string + when(mResources.getString(anyInt(), anyString())).thenReturn(""); + + // alarm warning should NOT be null + assertNotNull(mController.computeAlarmWarningText(null)); + } }
\ No newline at end of file diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java index 91957e1ff05c..ad422d8011d7 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java @@ -76,11 +76,10 @@ public class SettingsHelper { */ private static final ArraySet<String> sBroadcastOnRestore; static { - sBroadcastOnRestore = new ArraySet<String>(5); + sBroadcastOnRestore = new ArraySet<String>(4); sBroadcastOnRestore.add(Settings.Secure.ENABLED_NOTIFICATION_LISTENERS); sBroadcastOnRestore.add(Settings.Secure.ENABLED_VR_LISTENERS); sBroadcastOnRestore.add(Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES); - sBroadcastOnRestore.add(Settings.Secure.ENABLED_INPUT_METHODS); sBroadcastOnRestore.add(Settings.Global.BLUETOOTH_ON); } diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java index 769b7e9a13c5..1e8c523a9e34 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 @@ class SettingsProtoDumpUtil { 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 85a579d2808d..87ea38202e2d 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java @@ -1591,6 +1591,7 @@ public class SettingsProvider extends ContentProvider { private boolean isGlobalOrSecureSettingRestrictedForUser(String setting, int userId, String value, int callingUid) { String restriction; + boolean checkAllUser = false; switch (setting) { case Settings.Secure.LOCATION_MODE: // Note LOCATION_MODE will be converted into LOCATION_PROVIDERS_ALLOWED @@ -1656,6 +1657,12 @@ public class SettingsProvider extends ContentProvider { restriction = UserManager.DISALLOW_AMBIENT_DISPLAY; break; + case Global.LOCATION_GLOBAL_KILL_SWITCH: + if ("0".equals(value)) return false; + restriction = UserManager.DISALLOW_CONFIG_LOCATION; + checkAllUser = true; + break; + default: if (setting != null && setting.startsWith(Settings.Global.DATA_ROAMING)) { if ("0".equals(value)) return false; @@ -1665,7 +1672,11 @@ public class SettingsProvider extends ContentProvider { return false; } - return mUserManager.hasUserRestriction(restriction, UserHandle.of(userId)); + if (checkAllUser) { + return mUserManager.hasUserRestrictionOnAnyUser(restriction); + } else { + return mUserManager.hasUserRestriction(restriction, UserHandle.of(userId)); + } } private int resolveOwningUserIdForSecureSettingLocked(int userId, String setting) { diff --git a/packages/SimAppDialog/Android.mk b/packages/SimAppDialog/Android.mk new file mode 100644 index 000000000000..00a2e60b29f4 --- /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/core/java/android/security/keymaster/KeymasterArguments.aidl b/packages/SimAppDialog/AndroidManifest.xml index 44d9f0954781..873f6c5bac54 100644 --- a/core/java/android/security/keymaster/KeymasterArguments.aidl +++ b/packages/SimAppDialog/AndroidManifest.xml @@ -1,5 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- /* - * Copyright (C) 2015 The Android Open Source Project + * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,8 +15,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -package android.security.keymaster; - -/* @hide */ -parcelable KeymasterArguments cpp_header "keystore/KeymasterArguments.h"; +--> +<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 000000000000..85896e8a2687 --- /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 000000000000..53eee7437c16 --- /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 000000000000..0462a9365c90 --- /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 000000000000..10dcb77a6584 --- /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 000000000000..0c3930dfdf08 --- /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 000000000000..9e9b80d39ed7 --- /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/OWNERS b/packages/SystemUI/OWNERS index af6dd77224c4..7c97ca6103aa 100644 --- a/packages/SystemUI/OWNERS +++ b/packages/SystemUI/OWNERS @@ -2,19 +2,23 @@ set noparent dsandler@google.com +adamcohen@google.com asc@google.com ashaikh@google.com beverlyt@google.com cinek@google.com cwren@google.com +dupin@google.com evanlaird@google.com jmonk@google.com jaggies@google.com jjaggi@google.com juliacr@google.com -dupin@google.com madym@google.com +ngmatthew@google.com roosa@google.com shahrk@google.com +sunnygoyal@google.com +twickham@google.com winsonc@google.com 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 000000000000..bdc44b38a176 --- /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 000000000000..61d937b90d04 --- /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 000000000000..4651dcb3a229 --- /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/drawable/ic_lock_lockdown.xml b/packages/SystemUI/res/drawable/ic_lock_lockdown.xml new file mode 100644 index 000000000000..b517fc89e094 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_lock_lockdown.xml @@ -0,0 +1,25 @@ +<!-- +Copyright (C) 2018 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24.0dp" + android:height="24.0dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + + <path + android:fillColor="#757575" + android:pathData="M18.0,8.0l-1.0,0.0L17.0,6.0c0.0,-2.8 -2.2,-5.0 -5.0,-5.0C9.2,1.0 7.0,3.2 7.0,6.0l0.0,2.0L6.0,8.0c-1.1,0.0 -2.0,0.9 -2.0,2.0l0.0,10.0c0.0,1.1 0.9,2.0 2.0,2.0l12.0,0.0c1.1,0.0 2.0,-0.9 2.0,-2.0L20.0,10.0C20.0,8.9 19.1,8.0 18.0,8.0zM12.0,17.0c-1.1,0.0 -2.0,-0.9 -2.0,-2.0s0.9,-2.0 2.0,-2.0c1.1,0.0 2.0,0.9 2.0,2.0S13.1,17.0 12.0,17.0zM15.1,8.0L8.9,8.0L8.9,6.0c0.0,-1.7 1.4,-3.1 3.1,-3.1c1.7,0.0 3.1,1.4 3.1,3.1L15.1,8.0z"/> +</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 000000000000..f432d366e926 --- /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 000000000000..866b5a5b0ec7 --- /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 999dbac3b8bc..4666c604dc9c 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 767764694fae..4062eb8068fa 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 000000000000..99ab8021b43b --- /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/qs_footer_impl.xml b/packages/SystemUI/res/layout/qs_footer_impl.xml index 97472a49187a..cf88adee7a62 100644 --- a/packages/SystemUI/res/layout/qs_footer_impl.xml +++ b/packages/SystemUI/res/layout/qs_footer_impl.xml @@ -71,23 +71,19 @@ android:singleLine="true" /> </LinearLayout> - <FrameLayout + <View + android:id="@+id/qs_drag_handle_view" android:layout_width="24dp" - android:layout_height="match_parent" > - <View - android:id="@+id/qs_drag_handle_view" - android:layout_width="match_parent" - android:layout_height="4dp" - android:layout_marginTop="28dp" - android:background="@drawable/qs_footer_drag_handle" /> - </FrameLayout> + android:layout_height="4dp" + android:layout_gravity="center" + android:background="@drawable/qs_footer_drag_handle" /> - <LinearLayout + <com.android.keyguard.AlphaOptimizedLinearLayout android:id="@+id/qs_footer_actions_container" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" - android:gravity="end" > + android:gravity="center_vertical|end" > <com.android.systemui.statusbar.phone.MultiUserSwitch android:id="@+id/multi_user_switch" android:layout_width="48dp" @@ -113,7 +109,7 @@ android:clipToPadding="false" android:contentDescription="@string/accessibility_quick_settings_edit" android:focusable="true" - android:padding="16dp" + android:padding="15dp" android:src="@drawable/ic_mode_edit" android:tint="?android:attr/colorForeground"/> @@ -131,6 +127,7 @@ android:layout_height="match_parent" android:background="@drawable/ripple_drawable" android:contentDescription="@string/accessibility_quick_settings_settings" + android:padding="15dp" android:src="@drawable/ic_settings_16dp" android:tint="?android:attr/colorForeground"/> @@ -145,7 +142,7 @@ android:visibility="invisible"/> </com.android.systemui.statusbar.AlphaOptimizedFrameLayout> - </LinearLayout> + </com.android.keyguard.AlphaOptimizedLinearLayout> </LinearLayout> </com.android.systemui.qs.QSFooterImpl> 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 000000000000..43197c400139 --- /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 a5e37d529bee..13ca11401c03 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/arrays_tv.xml b/packages/SystemUI/res/values/arrays_tv.xml index 7541b0e8c084..9197bb51e1a6 100644 --- a/packages/SystemUI/res/values/arrays_tv.xml +++ b/packages/SystemUI/res/values/arrays_tv.xml @@ -30,7 +30,7 @@ <item>com.google.android.apps.mediashell/.settings.CastSettingsActivity</item> <item>com.google.android.katniss.setting/.SpeechSettingsActivity</item> <item>com.google.android.katniss.setting/.SearchSettingsActivity</item> - <item>com.google.android.gsf.notouch/.UsageDiagnosticsSettingActivity</item> + <item>com.google.android.tungsten.setupwraith/.settings.usage.UsageDiagnosticsSettingActivity</item> <item>com.google.android.tvlauncher/.notifications.NotificationsSidePanelActivity</item> </string-array> </resources> diff --git a/packages/SystemUI/res/values/attrs_car.xml b/packages/SystemUI/res/values/attrs_car.xml new file mode 100644 index 000000000000..b1097c39363c --- /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 9c8dcb1b975a..db829f25802e 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 a62f38b946ee..bc828ff8efb4 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -230,7 +230,7 @@ <!-- The height of the quick settings footer that holds the user switcher, settings icon, etc. --> - <dimen name="qs_footer_height">48dp</dimen> + <dimen name="qs_footer_height">56dp</dimen> <!-- The padding between the notifications and the quick settings container --> <dimen name="qs_notification_padding">@dimen/notification_side_paddings</dimen> @@ -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,11 +334,14 @@ <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> <!-- Difference between drag handle margin in QQS and expanded QS --> - <dimen name="qs_footer_drag_handle_offset">6dp</dimen> + <dimen name="qs_footer_drag_handle_offset">10dp</dimen> <dimen name="qs_notif_collapsed_space">64dp</dimen> @@ -513,7 +517,7 @@ <dimen name="multi_user_avatar_keyguard_size">22dp</dimen> <!-- The width of user avatar when expanded --> - <dimen name="multi_user_avatar_expanded_size">16dp</dimen> + <dimen name="multi_user_avatar_expanded_size">18dp</dimen> <!-- The font size of the time when collapsed in QS --> <dimen name="qs_time_collapsed_size">14sp</dimen> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 8c59e75315a1..86cab22a2756 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 4cf817e02fff..b8319a8e8822 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl @@ -44,4 +44,9 @@ interface ISystemUiProxy { * Specifies the text to be shown for onboarding the new swipe-up gesture to access recents. */ void setRecentsOnboardingText(CharSequence text); + + /** + * Enables/disables launcher/overview interaction features {@link InteractionType}. + */ + void setInteractionState(int flags); } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/NavigationBarCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/NavigationBarCompat.java index f622d4a3338c..171918682099 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/NavigationBarCompat.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/NavigationBarCompat.java @@ -28,4 +28,33 @@ public class NavigationBarCompat { public static final int HIT_TARGET_NONE = 0; public static final int HIT_TARGET_BACK = 1; public static final int HIT_TARGET_HOME = 2; + + @Retention(RetentionPolicy.SOURCE) + @IntDef({FLAG_DISABLE_SWIPE_UP, + FLAG_DISABLE_QUICK_SCRUB, + FLAG_SHOW_OVERVIEW_BUTTON, + FLAG_HIDE_BACK_BUTTON + }) + public @interface InteractionType {} + + /** + * Interaction type: whether the gesture to swipe up from the navigation bar will trigger + * launcher to show overview + */ + + public static final int FLAG_DISABLE_SWIPE_UP = 0x1; + /** + * Interaction type: enable quick scrub and switch interaction on the home button + */ + public static final int FLAG_DISABLE_QUICK_SCRUB = 0x2; + + /** + * Interaction type: show/hide the overview button while this service is connected to launcher + */ + public static final int FLAG_SHOW_OVERVIEW_BUTTON = 0x4; + + /** + * Interaction type: show/hide the back button while this service is connected to launcher + */ + public static final int FLAG_HIDE_BACK_BUTTON = 0x8; } diff --git a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java index d0128efe2c44..1185f45469df 100644 --- a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java +++ b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java @@ -47,6 +47,8 @@ import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; +import static com.android.systemui.shared.system.NavigationBarCompat.InteractionType; + /** * Class to send information from overview to launcher with a binder. */ @@ -67,6 +69,7 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis private IOverviewProxy mOverviewProxy; private int mConnectionBackoffAttempts; private CharSequence mOnboardingText; + private @InteractionType int mInteractionFlags; private ISystemUiProxy mSysUiProxy = new ISystemUiProxy.Stub() { @@ -108,6 +111,22 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis public void setRecentsOnboardingText(CharSequence text) { mOnboardingText = text; } + + public void setInteractionState(@InteractionType int flags) { + long token = Binder.clearCallingIdentity(); + try { + if (mInteractionFlags != flags) { + mInteractionFlags = flags; + mHandler.post(() -> { + for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) { + mConnectionCallbacks.get(i).onInteractionFlagsChanged(flags); + } + }); + } + } finally { + Binder.restoreCallingIdentity(token); + } + } }; private final BroadcastReceiver mLauncherAddedReceiver = new BroadcastReceiver() { @@ -230,6 +249,10 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis return mOnboardingText; } + public int getInteractionFlags() { + return mInteractionFlags; + } + private void disconnectFromLauncherService() { if (mOverviewProxy != null) { mOverviewProxy.asBinder().unlinkToDeath(mOverviewServiceDeathRcpt, 0); @@ -263,5 +286,6 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis public interface OverviewProxyListener { default void onConnectionChanged(boolean isConnected) {} default void onRecentsAnimationStarted() {} + default void onInteractionFlagsChanged(@InteractionType int flags) {} } } diff --git a/packages/SystemUI/src/com/android/systemui/Prefs.java b/packages/SystemUI/src/com/android/systemui/Prefs.java index adb4e33d1a19..8b577400357d 100644 --- a/packages/SystemUI/src/com/android/systemui/Prefs.java +++ b/packages/SystemUI/src/com/android/systemui/Prefs.java @@ -47,6 +47,7 @@ public final class Prefs { 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 @@ public final class Prefs { 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/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java index 1aea5e7f4a80..259bff28d458 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java @@ -688,7 +688,7 @@ class GlobalActionsDialog implements DialogInterface.OnDismissListener, } private Action getLockdownAction() { - return new SinglePressAction(R.drawable.ic_lock_lock, + return new SinglePressAction(com.android.systemui.R.drawable.ic_lock_lockdown, R.string.global_action_lockdown) { @Override diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java index 222c6e8274f5..fccd9ceb5c97 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 class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha 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 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha 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 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha 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 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha } 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 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha @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 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha @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/QSFooterImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java index fe3ffb926305..b9919a3f5c12 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java @@ -174,7 +174,8 @@ public class QSFooterImpl extends FrameLayout implements QSFooter, .addFloat(mDivider, "alpha", 0, 1) .addFloat(mCarrierText, "alpha", 0, 1) .addFloat(mActionsContainer, "alpha", 0, 1) - .addFloat(mDragHandle, "translationY", 0, -mDragHandleExpandOffset) + .addFloat(mDragHandle, "translationY", mDragHandleExpandOffset, 0) + .addFloat(mDragHandle, "alpha", 1, 0) .build(); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java index f7c388db0840..5640be55d2d2 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 @@ package com.android.systemui.qs; 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 @@ import java.util.Collection; 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 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne private BrightnessController mBrightnessController; protected QSTileHost mHost; + protected QSTooltipView mTooltipView; protected QSSecurityFooter mFooter; private boolean mGridContentVisible = true; @@ -94,6 +97,9 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne 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 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne 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 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne @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 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne @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 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne return mBrightnessView; } + View getHeaderView() { + return mTooltipView; + } + public void setCallback(QSDetail.Callback callback) { mCallback = callback; } @@ -266,11 +289,27 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne 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 000000000000..d1f9741ba6c2 --- /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 83148558ea1d..1b4b7dfb310f 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 @@ public class QuickQSPanel extends QSPanel { @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 65135ab142d7..9fa7bebf6ee0 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 @@ public class TileLayout extends ViewGroup implements QSTileLayout { 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 37f2528205f7..6263efa2c711 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 @@ public class TouchAnimator { 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 7259282935a0..016cbd6f6675 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.internal.logging.MetricsLogger; 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; 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 abstract class QSTileImpl<TState extends State> implements QSTile { 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 000000000000..53101a5bd61f --- /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 000000000000..e8c9a5e5693a --- /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 64c52ed6d29f..000000000000 --- 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 e5a311d099d5..1d9ef616d98d 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 @@ import com.android.systemui.statusbar.phone.NavigationBarView; * 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 @@ class CarNavigationBarView extends NavigationBarView { @Override public void onFinishInflate() { mNavButtons = findViewById(R.id.nav_buttons); - mLightsOutButtons = findViewById(R.id.lights_out); - } - public void addButton(CarNavigationButton button, CarNavigationButton lightsOutButton){ - mNavButtons.addView(button); - mLightsOutButtons.addView(lightsOutButton); + mNotificationsButton = findViewById(R.id.notifications); + mNotificationsButton.setOnClickListener(this::onNotificationsClick); } - @Override - public void setDisabledFlags(int disabledFlags, boolean force) { - // TODO: Populate. + void setStatusBar(CarStatusBar carStatusBar) { + mCarStatusBar = carStatusBar; } - @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 2de358f1c292..0cdaec1432c7 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); - } + 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); + } - public void setResources(Drawable icon) { - mIcon.setImageDrawable(icon); + 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); + } } - 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); - } + /** + * @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 3dfb9130af2e..c15a01330534 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 @@ package com.android.systemui.statusbar.car; 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.classifier.FalsingManager; 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 @@ public class CarStatusBar extends StatusBar implements private TaskStackListenerImpl mTaskStackListener; - private CarNavigationBarController mController; private FullscreenUserSwitcher mFullscreenUserSwitcher; private CarBatteryController mCarBatteryController; @@ -78,15 +71,23 @@ public class CarStatusBar extends StatusBar implements 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 @@ public class CarStatusBar extends StatusBar implements mNavigationBarView = null; } + if (mLeftNavigationBarWindow != null) { + mWindowManager.removeViewImmediate(mLeftNavigationBarWindow); + mLeftNavigationBarView = null; + } + + if (mRightNavigationBarWindow != null) { + mWindowManager.removeViewImmediate(mRightNavigationBarWindow); + mRightNavigationBarView = null; + } + super.destroy(); } @@ -153,10 +164,36 @@ public class CarStatusBar extends StatusBar implements @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 @@ public class CarStatusBar extends StatusBar implements 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 @@ public class CarStatusBar extends StatusBar implements 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 @@ public class CarStatusBar extends StatusBar implements } 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 @@ public class CarStatusBar extends StatusBar implements } } - @Override - public NavigationBarView getNavigationBarView() { - return mNavigationBarView; - } @Override public View getNavigationBarWindow() { @@ -269,24 +365,6 @@ public class CarStatusBar extends StatusBar implements } } - 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 class CarStatusBar extends StatusBar implements 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 @@ public class CarStatusBar extends StatusBar implements // 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 @@ public class CarStatusBar extends StatusBar implements @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 11d20b221051..ef44ad17e1c7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java @@ -77,11 +77,11 @@ public class ActivityLaunchAnimator { mStatusBar = statusBar; } - public ActivityOptions getLaunchAnimation( - ExpandableNotificationRow sourceNofitication) { - AnimationRunner animationRunner = new AnimationRunner(sourceNofitication); - return ActivityOptions.makeRemoteAnimation( - new RemoteAnimationAdapter(animationRunner, 1000 /* Duration */, 0 /* delay */)); + public RemoteAnimationAdapter getLaunchAnimation( + ExpandableNotificationRow sourceNotification) { + AnimationRunner animationRunner = new AnimationRunner(sourceNotification); + return new RemoteAnimationAdapter(animationRunner, ANIMATION_DURATION, + 0 /* statusBarTransitionDelay */); } public boolean isAnimationPending() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java index 72938c25b753..79c605e4ed23 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java @@ -19,6 +19,7 @@ import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SHOWN; import static android.app.StatusBarManager.WINDOW_STATE_SHOWING; import static android.app.StatusBarManager.windowStateToString; +import static com.android.systemui.shared.system.NavigationBarCompat.InteractionType; import static com.android.systemui.statusbar.phone.BarTransitions.MODE_SEMI_TRANSPARENT; import static com.android.systemui.statusbar.phone.StatusBar.DEBUG_WINDOW_STATE; import static com.android.systemui.statusbar.phone.StatusBar.dumpBarTransitions; @@ -71,7 +72,6 @@ import android.view.WindowManagerGlobal; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener; -import android.widget.Button; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; @@ -165,6 +165,11 @@ public class NavigationBarFragment extends Fragment implements Callbacks { public void onRecentsAnimationStarted() { mNavigationBarView.setRecentsAnimationStarted(true); } + + @Override + public void onInteractionFlagsChanged(@InteractionType int flags) { + mNavigationBarView.updateStates(); + } }; // ----- Fragment Lifecycle Callbacks ----- diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java index 63f2cebb61cc..8970ea0c110b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java @@ -127,7 +127,7 @@ public class NavigationBarGestureHelper implements TunerService.Tunable, Gesture private boolean proxyMotionEvents(MotionEvent event) { final IOverviewProxy overviewProxy = mOverviewProxyService.getProxy(); - if (overviewProxy != null) { + if (overviewProxy != null && mNavigationBarView.isQuickStepSwipeUpEnabled()) { mNavigationBarView.requestUnbufferedDispatch(event); event.transform(mTransformGlobalMatrix); try { @@ -192,7 +192,7 @@ public class NavigationBarGestureHelper implements TunerService.Tunable, Gesture } public void onDraw(Canvas canvas) { - if (mOverviewProxyService.getProxy() != null) { + if (mNavigationBarView.isQuickScrubEnabled()) { mQuickScrubController.onDraw(canvas); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java index 53dc814726a2..0dea6b8970e7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java @@ -40,6 +40,7 @@ import android.graphics.drawable.AnimatedVectorDrawable; import android.os.Handler; import android.os.Message; import android.os.RemoteException; +import android.os.SystemProperties; import android.support.annotation.ColorInt; import android.util.AttributeSet; import android.util.Log; @@ -76,6 +77,11 @@ import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.function.Consumer; +import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_DISABLE_QUICK_SCRUB; +import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_DISABLE_SWIPE_UP; +import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_HIDE_BACK_BUTTON; +import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_SHOW_OVERVIEW_BUTTON; + public class NavigationBarView extends FrameLayout implements PluginListener<NavGesture> { final static boolean DEBUG = false; final static String TAG = "StatusBar/NavBarView"; @@ -372,6 +378,19 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav return getRecentsButton().getVisibility() == View.VISIBLE; } + public boolean isQuickStepSwipeUpEnabled() { + return mOverviewProxyService.getProxy() != null + && ((mOverviewProxyService.getInteractionFlags() + & FLAG_DISABLE_SWIPE_UP) == 0); + } + + public boolean isQuickScrubEnabled() { + return SystemProperties.getBoolean("persist.quickstep.scrub.enabled", true) + && mOverviewProxyService.getProxy() != null && !isRecentsButtonVisible() + && ((mOverviewProxyService.getInteractionFlags() + & FLAG_DISABLE_QUICK_SCRUB) == 0); + } + private void updateCarModeIcons(Context ctx) { mBackCarModeIcon = getDrawable(ctx, R.drawable.ic_sysbar_back_carmode, R.drawable.ic_sysbar_back_carmode); @@ -391,20 +410,20 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav } if (oldConfig.densityDpi != newConfig.densityDpi || oldConfig.getLayoutDirection() != newConfig.getLayoutDirection()) { - final boolean proxyAvailable = mOverviewProxyService.getProxy() != null; - mBackIcon = proxyAvailable + final boolean quickStepEnabled = isQuickStepSwipeUpEnabled() || isQuickScrubEnabled(); + mBackIcon = quickStepEnabled ? getDrawable(ctx, R.drawable.ic_sysbar_back_quick_step, R.drawable.ic_sysbar_back_quick_step_dark) : getDrawable(ctx, R.drawable.ic_sysbar_back, R.drawable.ic_sysbar_back_dark); mBackLandIcon = mBackIcon; - mBackAltIcon = proxyAvailable + mBackAltIcon = quickStepEnabled ? getDrawable(ctx, R.drawable.ic_sysbar_back_ime_quick_step, R.drawable.ic_sysbar_back_ime_quick_step_dark) : getDrawable(ctx, R.drawable.ic_sysbar_back_ime, R.drawable.ic_sysbar_back_ime_dark); mBackAltLandIcon = mBackAltIcon; - mHomeDefaultIcon = proxyAvailable + mHomeDefaultIcon = quickStepEnabled ? getDrawable(ctx, R.drawable.ic_sysbar_home_quick_step, R.drawable.ic_sysbar_home_quick_step_dark) : getDrawable(ctx, R.drawable.ic_sysbar_home, R.drawable.ic_sysbar_home_dark); @@ -555,9 +574,14 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav disableBack = false; disableRecent = false; } + if (mOverviewProxyService.getProxy() != null) { - // When overview is connected to the launcher service, disable the recents button - disableRecent = true; + // When overview is connected to the launcher service, disable the recents button by + // default unless overwritten by interaction flags. Similar with the back button but + // shown by default. + final int flags = mOverviewProxyService.getInteractionFlags(); + disableRecent |= (flags & FLAG_SHOW_OVERVIEW_BUTTON) == 0; + disableBack |= (flags & FLAG_HIDE_BACK_BUTTON) != 0; } ViewGroup navButtons = (ViewGroup) getCurrentView().findViewById(R.id.nav_buttons); @@ -635,6 +659,11 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav updateSlippery(); } + public void updateStates() { + updateSlippery(); + setDisabledFlags(mDisabledFlags, true); + } + private void updateSlippery() { setSlippery(mOverviewProxyService.getProxy() != null && mPanelView.isFullyExpanded()); } @@ -794,9 +823,8 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav } public void onOverviewProxyConnectionChanged(boolean isConnected) { - setSlippery(!isConnected); - setDisabledFlags(mDisabledFlags, true); - setUpSwipeUpOnboarding(isConnected); + updateStates(); + setUpSwipeUpOnboarding(isQuickStepSwipeUpEnabled()); updateIcons(getContext(), Configuration.EMPTY, mConfiguration); setNavigationIconHints(mNavigationIconHints, true); } @@ -881,6 +909,11 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav Log.d(TAG, "reorient(): rot=" + mCurrentRotation); } + // Resolve layout direction if not resolved since components changing layout direction such + // as changing languages will recreate this view and the direction will be resolved later + if (!isLayoutDirectionResolved()) { + resolveLayoutDirection(); + } updateTaskSwitchHelper(); 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 6444cc816663..747a551defe6 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 @@ public class PhoneStatusBarPolicy implements Callback, Callbacks, 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 @@ public class PhoneStatusBarPolicy implements Callback, Callbacks, .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 bb2f59718ffb..0bf01b0cb949 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickScrubController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickScrubController.java @@ -29,15 +29,12 @@ import android.graphics.Paint; import android.graphics.Rect; import android.os.Handler; import android.os.RemoteException; -import android.os.SystemProperties; import android.util.Log; import android.util.Slog; -import android.view.Display; import android.view.GestureDetector; import android.view.MotionEvent; import android.view.View; import android.view.ViewConfiguration; -import android.view.WindowManager; import android.view.WindowManagerGlobal; import android.view.animation.DecelerateInterpolator; import android.view.animation.Interpolator; @@ -89,6 +86,7 @@ public class QuickScrubController extends GestureDetector.SimpleOnGestureListene private int mLightTrackColor; private int mDarkTrackColor; private float mDarkIntensity; + private View mHomeButtonView; private final Handler mHandler = new Handler(); private final Interpolator mQuickScrubEndInterpolator = new DecelerateInterpolator(); @@ -114,11 +112,10 @@ public class QuickScrubController extends GestureDetector.SimpleOnGestureListene if (!mQuickScrubActive) { pos = mDragPositive ? Math.min((int) mTranslation, pos) : Math.max((int) mTranslation, pos); } - final View homeView = mNavigationBarView.getHomeButton().getCurrentView(); if (mIsVertical) { - homeView.setTranslationY(pos); + mHomeButtonView.setTranslationY(pos); } else { - homeView.setTranslationX(pos); + mHomeButtonView.setTranslationX(pos); } }; @@ -126,6 +123,7 @@ public class QuickScrubController extends GestureDetector.SimpleOnGestureListene @Override public void onAnimationEnd(Animator animation) { mNavigationBarView.getHomeButton().setClickable(true); + mHomeButtonView = null; mQuickScrubActive = false; mTranslation = 0; } @@ -137,8 +135,9 @@ public class QuickScrubController extends GestureDetector.SimpleOnGestureListene new GestureDetector.SimpleOnGestureListener() { @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velX, float velY) { - if (!isQuickScrubEnabled() || mQuickScrubActive || !mAllowQuickSwitch || - mNavigationBarView.getDownHitTarget() != HIT_TARGET_HOME) { + if (!mNavigationBarView.isQuickScrubEnabled() || mQuickScrubActive + || !mAllowQuickSwitch + || mNavigationBarView.getDownHitTarget() != HIT_TARGET_HOME) { return false; } float velocityX = mIsRTL ? -velX : velX; @@ -195,9 +194,8 @@ public class QuickScrubController extends GestureDetector.SimpleOnGestureListene */ @Override public boolean onInterceptTouchEvent(MotionEvent event) { - final IOverviewProxy overviewProxy = mOverviewEventSender.getProxy(); final ButtonDispatcher homeButton = mNavigationBarView.getHomeButton(); - if (overviewProxy == null) { + if (!mNavigationBarView.isQuickScrubEnabled()) { homeButton.setDelayTouchFeedback(false); return false; } @@ -226,7 +224,8 @@ public class QuickScrubController extends GestureDetector.SimpleOnGestureListene case MotionEvent.ACTION_DOWN: { int x = (int) event.getX(); int y = (int) event.getY(); - if (isQuickScrubEnabled() + mHomeButtonView = homeButton.getCurrentView(); + if (mNavigationBarView.isQuickScrubEnabled() && mNavigationBarView.getDownHitTarget() == HIT_TARGET_HOME) { mTouchDownX = x; mTouchDownY = y; @@ -294,7 +293,7 @@ public class QuickScrubController extends GestureDetector.SimpleOnGestureListene : Utilities.clamp(offset - mDownOffset, 0, trackSize); if (mQuickScrubActive) { try { - overviewProxy.onQuickScrubProgress(scrubFraction); + mOverviewEventSender.getProxy().onQuickScrubProgress(scrubFraction); if (DEBUG_OVERVIEW_PROXY) { Log.d(TAG_OPS, "Quick Scrub Progress:" + scrubFraction); } @@ -305,9 +304,9 @@ public class QuickScrubController extends GestureDetector.SimpleOnGestureListene mTranslation /= SWITCH_STICKINESS; } if (mIsVertical) { - homeButton.getCurrentView().setTranslationY(mTranslation); + mHomeButtonView.setTranslationY(mTranslation); } else { - homeButton.getCurrentView().setTranslationX(mTranslation); + mHomeButtonView.setTranslationX(mTranslation); } } } @@ -315,7 +314,7 @@ public class QuickScrubController extends GestureDetector.SimpleOnGestureListene } case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: - endQuickScrub(); + endQuickScrub(true /* animate */); break; } return mDraggingActive || mQuickScrubActive; @@ -357,6 +356,11 @@ public class QuickScrubController extends GestureDetector.SimpleOnGestureListene @Override public void setBarState(boolean isVertical, boolean isRTL) { + final boolean changed = (mIsVertical != isVertical) || (mIsRTL != isRTL); + if (changed) { + // End quickscrub if the state changes mid-transition + endQuickScrub(false /* animate */); + } mIsVertical = isVertical; mIsRTL = isRTL; try { @@ -370,10 +374,6 @@ public class QuickScrubController extends GestureDetector.SimpleOnGestureListene } } - boolean isQuickScrubEnabled() { - return SystemProperties.getBoolean("persist.quickstep.scrub.enabled", true); - } - private void startQuickScrub() { if (!mQuickScrubActive) { mQuickScrubActive = true; @@ -392,7 +392,7 @@ public class QuickScrubController extends GestureDetector.SimpleOnGestureListene } } - private void endQuickScrub() { + private void endQuickScrub(boolean animate) { mHandler.removeCallbacks(mLongPressRunnable); if (mDraggingActive || mQuickScrubActive) { mButtonAnimator.setIntValues((int) mTranslation, 0); @@ -406,6 +406,9 @@ public class QuickScrubController extends GestureDetector.SimpleOnGestureListene } catch (RemoteException e) { Log.e(TAG, "Failed to send end of quick scrub.", e); } + if (!animate) { + mQuickScrubEndAnimator.end(); + } } mDraggingActive = false; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java index 94ebc1bac2b0..2b5085389804 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java @@ -138,8 +138,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, protected float mScrimBehindAlphaKeyguard = SCRIM_BEHIND_ALPHA_KEYGUARD; protected float mScrimBehindAlphaUnlocking = SCRIM_BEHIND_ALPHA_UNLOCKING; - // Assuming the shade is expanded during initialization - private float mExpansionFraction = 1f; + private float mFraction; private boolean mDarkenWhileDragging; protected boolean mAnimateChange; @@ -253,7 +252,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, mCurrentBehindTint = state.getBehindTint(); mCurrentInFrontAlpha = state.getFrontAlpha(); mCurrentBehindAlpha = state.getBehindAlpha(); - applyExpansionToAlpha(); // Cancel blanking transitions that were pending before we requested a new state if (mPendingFrameCallback != null) { @@ -365,50 +363,45 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, * @param fraction From 0 to 1 where 0 means collapse and 1 expanded. */ public void setPanelExpansion(float fraction) { - if (mExpansionFraction != fraction) { - mExpansionFraction = fraction; - - if (!(mState == ScrimState.UNLOCKED || mState == ScrimState.KEYGUARD)) { - return; - } - - applyExpansionToAlpha(); + if (mFraction != fraction) { + mFraction = fraction; + + if (mState == ScrimState.UNLOCKED) { + // Darken scrim as you pull down the shade when unlocked + float behindFraction = getInterpolatedFraction(); + behindFraction = (float) Math.pow(behindFraction, 0.8f); + mCurrentBehindAlpha = behindFraction * mScrimBehindAlphaKeyguard; + mCurrentInFrontAlpha = 0; + } else if (mState == ScrimState.KEYGUARD) { + if (mUpdatePending) { + return; + } - if (mUpdatePending) { + // Either darken of make the scrim transparent when you + // pull down the shade + float interpolatedFract = getInterpolatedFraction(); + if (mDarkenWhileDragging) { + mCurrentBehindAlpha = MathUtils.lerp(mScrimBehindAlphaUnlocking, + mScrimBehindAlphaKeyguard, interpolatedFract); + mCurrentInFrontAlpha = (1f - interpolatedFract) * SCRIM_IN_FRONT_ALPHA_LOCKED; + } else { + mCurrentBehindAlpha = MathUtils.lerp(0 /* start */, mScrimBehindAlphaKeyguard, + interpolatedFract); + mCurrentInFrontAlpha = 0; + } + } else { return; } if (mPinnedHeadsUpCount != 0) { updateHeadsUpScrim(false); } + updateScrim(false /* animate */, mScrimInFront, mCurrentInFrontAlpha); updateScrim(false /* animate */, mScrimBehind, mCurrentBehindAlpha); } } - private void applyExpansionToAlpha() { - if (mState == ScrimState.UNLOCKED) { - // Darken scrim as you pull down the shade when unlocked - float behindFraction = getInterpolatedFraction(); - behindFraction = (float) Math.pow(behindFraction, 0.8f); - mCurrentBehindAlpha = behindFraction * mScrimBehindAlphaKeyguard; - mCurrentInFrontAlpha = 0; - } else if (mState == ScrimState.KEYGUARD) { - // Either darken of make the scrim transparent when you - // pull down the shade - float interpolatedFract = getInterpolatedFraction(); - if (mDarkenWhileDragging) { - mCurrentBehindAlpha = MathUtils.lerp(mScrimBehindAlphaUnlocking, - mScrimBehindAlphaKeyguard, interpolatedFract); - mCurrentInFrontAlpha = (1f - interpolatedFract) * SCRIM_IN_FRONT_ALPHA_LOCKED; - } else { - mCurrentBehindAlpha = MathUtils.lerp(0 /* start */, mScrimBehindAlphaKeyguard, - interpolatedFract); - mCurrentInFrontAlpha = 0; - } - } - } - /** * Keyguard and shade scrim opacity varies according to how many notifications are visible. * @param notificationCount Number of visible notifications. @@ -504,7 +497,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, } private float getInterpolatedFraction() { - float frac = mExpansionFraction; + float frac = mFraction; // let's start this 20% of the way down the screen frac = frac * 1.2f - 0.2f; if (frac <= 0) { @@ -834,7 +827,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, } else { alpha = 1.0f - mTopHeadsUpDragAmount; } - float expandFactor = (1.0f - mExpansionFraction); + float expandFactor = (1.0f - mFraction); expandFactor = Math.max(expandFactor, 0.0f); return alpha * expandFactor; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index 3b63d6c39146..24920cba21f5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -111,6 +111,7 @@ import android.view.IWindowManager; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.MotionEvent; +import android.view.RemoteAnimationAdapter; import android.view.ThreadedRenderer; import android.view.View; import android.view.ViewGroup; @@ -2849,7 +2850,7 @@ public class StatusBar extends SystemUI implements DemoMode, Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); int result = ActivityManager.START_CANCELED; ActivityOptions options = new ActivityOptions(getActivityOptions( - null /* sourceNotification */)); + null /* remoteAnimation */)); options.setDisallowEnterPictureInPictureWhileLaunching( disallowEnterPictureInPictureWhileLaunching); if (intent == KeyguardBottomAreaView.INSECURE_CAMERA_INTENT) { @@ -5001,11 +5002,15 @@ public class StatusBar extends SystemUI implements DemoMode, fillInIntent = new Intent().putExtra(Notification.EXTRA_REMOTE_INPUT_DRAFT, remoteInputText.toString()); } + RemoteAnimationAdapter adapter = mActivityLaunchAnimator.getLaunchAnimation( + row); try { + ActivityManager.getService().registerRemoteAnimationForNextActivityStart( + intent.getCreatorPackage(), adapter); launchResult = intent.sendAndReturnResult(mContext, 0, fillInIntent, null, - null, null, getActivityOptions(row)); + null, null, getActivityOptions(adapter)); mActivityLaunchAnimator.setLaunchResult(launchResult); - } catch (PendingIntent.CanceledException e) { + } catch (RemoteException | PendingIntent.CanceledException e) { // the stack trace isn't very helpful here. // Just log the exception message. Log.w(TAG, "Sending contentIntent failed: " + e); @@ -5165,7 +5170,8 @@ public class StatusBar extends SystemUI implements DemoMode, AsyncTask.execute(() -> { int launchResult = TaskStackBuilder.create(mContext) .addNextIntentWithParentStack(intent) - .startActivities(getActivityOptions(row), + .startActivities(getActivityOptions( + mActivityLaunchAnimator.getLaunchAnimation(row)), new UserHandle(UserHandle.getUserId(appUid))); mActivityLaunchAnimator.setLaunchResult(launchResult); if (shouldCollapse()) { @@ -5300,7 +5306,7 @@ public class StatusBar extends SystemUI implements DemoMode, } try { intent.send(null, 0, null, null, null, null, getActivityOptions( - null /* sourceNotification */)); + null /* animationAdapter */)); } catch (PendingIntent.CanceledException e) { // the stack trace isn't very helpful here. // Just log the exception message. @@ -5328,10 +5334,10 @@ public class StatusBar extends SystemUI implements DemoMode, return true; } - protected Bundle getActivityOptions(ExpandableNotificationRow sourceNotification) { + protected Bundle getActivityOptions(@Nullable RemoteAnimationAdapter animationAdapter) { ActivityOptions options; - if (sourceNotification != null) { - options = mActivityLaunchAnimator.getLaunchAnimation(sourceNotification); + if (animationAdapter != null) { + options = ActivityOptions.makeRemoteAnimation(animationAdapter); } else { options = ActivityOptions.makeBasic(); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java index 1c9c7949a971..676463407f3f 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 static java.lang.Thread.sleep; 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 @@ public class QSTileImplTest extends SysuiTestCase { 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 47027935d8d7..43e16dbeaeed 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java @@ -180,7 +180,6 @@ public class ScrimControllerTest extends SysuiTestCase { @Test public void transitionToUnlocked() { - mScrimController.setPanelExpansion(0f); mScrimController.transitionTo(ScrimState.UNLOCKED); mScrimController.finishAnimationsImmediately(); // Front scrim should be transparent @@ -198,7 +197,6 @@ public class ScrimControllerTest extends SysuiTestCase { public void transitionToUnlockedFromAod() { // Simulate unlock with fingerprint mScrimController.transitionTo(ScrimState.AOD); - mScrimController.setPanelExpansion(0f); mScrimController.finishAnimationsImmediately(); mScrimController.transitionTo(ScrimState.UNLOCKED); // Immediately tinted after the transition starts @@ -326,23 +324,6 @@ public class ScrimControllerTest extends SysuiTestCase { verify(mAlarmManager).cancel(any(AlarmManager.OnAlarmListener.class)); } - @Test - public void testConservesExpansionOpacityAfterTransition() { - mScrimController.transitionTo(ScrimState.UNLOCKED); - mScrimController.setPanelExpansion(0.5f); - mScrimController.finishAnimationsImmediately(); - - final float expandedAlpha = mScrimBehind.getViewAlpha(); - - mScrimController.transitionTo(ScrimState.BRIGHTNESS_MIRROR); - mScrimController.finishAnimationsImmediately(); - mScrimController.transitionTo(ScrimState.UNLOCKED); - mScrimController.finishAnimationsImmediately(); - - Assert.assertEquals("Scrim expansion opacity wasn't conserved when transitioning back", - expandedAlpha, mScrimBehind.getViewAlpha(), 0.01f); - } - private void assertScrimTint(ScrimView scrimView, boolean tinted) { final boolean viewIsTinted = scrimView.getTint() != Color.TRANSPARENT; final String name = scrimView == mScrimInFront ? "front" : "back"; diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto index ae5e133fa02f..abf1de578682 100644 --- a/proto/src/metrics_constants.proto +++ b/proto/src/metrics_constants.proto @@ -5213,6 +5213,67 @@ message MetricsEvent { // OS: P ACTION_OUTPUT_CHOOSER_DISCONNECT = 1297; + // OPEN: TV Settings > Home theater control + // OS: P + SETTINGS_TV_HOME_THEATER_CONTROL_CATEGORY = 1298; + + // OPEN: TV Settings > TV Inputs (Inputs & Devices) + // OS: P + SETTINGS_TV_INPUTS_CATEGORY = 1299; + + // OPEN: TV Settings > Device + // OS: P + SETTINGS_TV_DEVICE_CATEGORY = 1300; + + // OPEN: TV Settings > Network > Proxy settings + // OS: P + DIALOG_TV_NETWORK_PROXY = 1301; + + // 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; + + // ACTION: Settings > Display > Night Light + // SUBTYPE: com.android.server.display.ColorDisplayService.AutoMode value + // CATEGORY: SETTINGS + // OS: P + ACTION_NIGHT_DISPLAY_AUTO_MODE_CHANGED = 1309; + + // ACTION: Settings > Display > Night Light + // CATEGORY: SETTINGS + // SUBTYPE: 0 is starting time, 1 is ending time + // OS: P + ACTION_NIGHT_DISPLAY_AUTO_MODE_CUSTOM_TIME_CHANGED = 1310; + + // FIELD: Current mode corresponding to a QS tile + // CATEGORY: QUICK SETTINGS + // OS: P + FIELD_QS_MODE = 1311; + // ---- End P Constants, all P constants go above this line ---- // Add new aosp constants above this line. // END OF AOSP CONSTANTS diff --git a/rs/OWNERS b/rs/OWNERS new file mode 100644 index 000000000000..61853d3d40cf --- /dev/null +++ b/rs/OWNERS @@ -0,0 +1,5 @@ +butlermichael@google.com +dgross@google.com +jeanluc@google.com +miaowang@google.com +yangni@google.com diff --git a/services/accessibility/java/com/android/server/accessibility/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/MagnificationController.java index c36bb6d2e096..5b5d18f4aaf7 100644 --- a/services/accessibility/java/com/android/server/accessibility/MagnificationController.java +++ b/services/accessibility/java/com/android/server/accessibility/MagnificationController.java @@ -446,8 +446,10 @@ class MagnificationController implements Handler.Callback { mMagnificationRegion.getBounds(viewport); final MagnificationSpec spec = mCurrentMagnificationSpec; final float oldScale = spec.scale; - final float oldCenterX = (viewport.width() / 2.0f - spec.offsetX) / oldScale; - final float oldCenterY = (viewport.height() / 2.0f - spec.offsetY) / oldScale; + final float oldCenterX + = (viewport.width() / 2.0f - spec.offsetX + viewport.left) / oldScale; + final float oldCenterY + = (viewport.height() / 2.0f - spec.offsetY + viewport.top) / oldScale; final float normPivotX = (pivotX - spec.offsetX) / oldScale; final float normPivotY = (pivotY - spec.offsetY) / oldScale; final float offsetX = (oldCenterX - normPivotX) * (oldScale / scale); diff --git a/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java index 52ab85c480b3..6c6dd5b8bd67 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 @@ class MagnificationGestureHandler extends BaseEventStreamTransformation { 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 @@ class MagnificationGestureHandler extends BaseEventStreamTransformation { @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 @@ class MagnificationGestureHandler extends BaseEventStreamTransformation { 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 @@ class MagnificationGestureHandler extends BaseEventStreamTransformation { // 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 @@ class MagnificationGestureHandler extends BaseEventStreamTransformation { /** -> {@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 @@ class MagnificationGestureHandler extends BaseEventStreamTransformation { } } - 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 d5a722bba723..4b3abeadaea1 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java +++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java @@ -24,7 +24,6 @@ import static com.android.server.autofill.Helper.sDebug; import static com.android.server.autofill.Helper.sPartitionMaxCount; import static com.android.server.autofill.Helper.sVerbose; -import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; @@ -56,7 +55,12 @@ import android.os.UserManagerInternal; import android.provider.Settings; import android.service.autofill.FillEventHistory; import android.service.autofill.UserData; +import android.text.TextUtils; +import android.text.TextUtils.SimpleStringSplitter; +import android.util.ArrayMap; +import android.util.ArraySet; import android.util.LocalLog; +import android.util.Pair; import android.util.Slog; import android.util.SparseArray; import android.util.SparseBooleanArray; @@ -84,6 +88,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Objects; +import java.util.Set; /** * Entry point service for autofill management. @@ -98,6 +103,8 @@ public final class AutofillManagerService extends SystemService { static final String RECEIVER_BUNDLE_EXTRA_SESSIONS = "sessions"; + private static final char COMPAT_PACKAGE_DELIMITER = ':'; + private final Context mContext; private final AutoFillUI mUi; @@ -123,6 +130,9 @@ public final class AutofillManagerService extends SystemService { private final LocalLog mUiLatencyHistory = new LocalLog(20); private final LocalLog mWtfHistory = new LocalLog(50); + private final AutofillCompatState mAutofillCompatState = new AutofillCompatState(); + private final LocalService mLocalService = new LocalService(); + private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { @@ -271,7 +281,7 @@ public final class AutofillManagerService extends SystemService { @Override public void onStart() { publishBinderService(AUTOFILL_MANAGER_SERVICE, new AutoFillManagerServiceStub()); - publishLocalService(AutofillManagerInternal.class, new LocalService()); + publishLocalService(AutofillManagerInternal.class, mLocalService); } @Override @@ -317,6 +327,11 @@ public final class AutofillManagerService extends SystemService { mUiLatencyHistory, mWtfHistory, resolvedUserId, mUi, mDisabledUsers.get(resolvedUserId)); mServicesCache.put(userId, service); + final ArrayMap<String, Pair<Long, String>> compatPackages = + service.getCompatibilityPackagesLocked(); + if (compatPackages != null) { + addCompatibilityModeRequests(compatPackages, userId); + } } return service; } @@ -482,6 +497,7 @@ public final class AutofillManagerService extends SystemService { if (service != null) { mServicesCache.delete(userId); service.destroyLocked(); + mAutofillCompatState.removeCompatibilityModeRequests(userId); } } @@ -498,18 +514,60 @@ public final class AutofillManagerService extends SystemService { */ @GuardedBy("mLock") private void updateCachedServiceLocked(int userId, boolean disabled) { - AutofillManagerServiceImpl service = getServiceForUserLocked(userId); + AutofillManagerServiceImpl service = peekServiceForUserLocked(userId); if (service != null) { service.destroySessionsLocked(); service.updateLocked(disabled); if (!service.isEnabledLocked()) { removeCachedServiceLocked(userId); + } else { + final ArrayMap<String, Pair<Long, String>> compatPackages = + service.getCompatibilityPackagesLocked(); + if (compatPackages != null) { + addCompatibilityModeRequests(compatPackages, userId); + } } } } - private final class LocalService extends AutofillManagerInternal { + private void addCompatibilityModeRequests( + @NonNull ArrayMap<String, Pair<Long, String>> compatPackages, int userId) { + final Set<String> whiteListedPackages = Build.IS_ENG ? null + : getWhitelistedCompatModePackages(); + final int compatPackageCount = compatPackages.size(); + for (int i = 0; i < compatPackageCount; i++) { + final String packageName = compatPackages.keyAt(i); + if (!Build.IS_ENG && (whiteListedPackages == null + || !whiteListedPackages.contains(packageName))) { + Slog.w(TAG, "Ignoring not whitelisted compat package " + packageName); + continue; + } + final Long maxVersionCode = compatPackages.valueAt(i).first; + if (maxVersionCode != null) { + mAutofillCompatState.addCompatibilityModeRequest(packageName, + maxVersionCode, userId); + } + } + } + private @Nullable Set<String> getWhitelistedCompatModePackages() { + final String compatPackagesSetting = Settings.Global.getString( + mContext.getContentResolver(), + Settings.Global.AUTOFILL_COMPAT_ALLOWED_PACKAGES); + if (TextUtils.isEmpty(compatPackagesSetting)) { + return null; + } + final Set<String> compatPackages = new ArraySet<>(); + final SimpleStringSplitter splitter = new SimpleStringSplitter( + COMPAT_PACKAGE_DELIMITER); + splitter.setString(compatPackagesSetting); + while (splitter.hasNext()) { + compatPackages.add(splitter.next()); + } + return compatPackages; + } + + private final class LocalService extends AutofillManagerInternal { @Override public void onBackKeyPressed() { if (sDebug) Slog.d(TAG, "onBackKeyPressed()"); @@ -519,13 +577,59 @@ public final class AutofillManagerService extends SystemService { @Override public boolean isCompatibilityModeRequested(@NonNull String packageName, long versionCode, @UserIdInt int userId) { + return mAutofillCompatState.isCompatibilityModeRequested( + packageName, versionCode, userId); + } + } + + private static class AutofillCompatState { + private final Object mLock = new Object(); + + @GuardedBy("mLock") + private SparseArray<ArrayMap<String, Long>> mUserSpecs; + + boolean isCompatibilityModeRequested(@NonNull String packageName, + long versionCode, @UserIdInt int userId) { synchronized (mLock) { - final AutofillManagerServiceImpl service = getServiceForUserLocked(userId); - if (service != null) { - return service.isCompatibilityModeRequestedLocked(packageName, versionCode); + if (mUserSpecs == null) { + return false; + } + final ArrayMap<String, Long> userSpec = mUserSpecs.get(userId); + if (userSpec == null) { + return false; + } + final Long maxVersionCode = userSpec.get(packageName); + if (maxVersionCode == null) { + return false; + } + return versionCode <= maxVersionCode; + } + } + + void addCompatibilityModeRequest(@NonNull String packageName, + long versionCode, @UserIdInt int userId) { + synchronized (mLock) { + if (mUserSpecs == null) { + mUserSpecs = new SparseArray<>(); + } + ArrayMap<String, Long> userSpec = mUserSpecs.get(userId); + if (userSpec == null) { + userSpec = new ArrayMap<>(); + mUserSpecs.put(userId, userSpec); + } + userSpec.put(packageName, versionCode); + } + } + + void removeCompatibilityModeRequests(@UserIdInt int userId) { + synchronized (mLock) { + if (mUserSpecs != null) { + mUserSpecs.remove(userId); + if (mUserSpecs.size() <= 0) { + mUserSpecs = null; + } } } - return false; } } @@ -580,7 +684,7 @@ public final class AutofillManagerService extends SystemService { @Override public int startSession(IBinder activityToken, IBinder appCallback, AutofillId autofillId, Rect bounds, AutofillValue value, int userId, boolean hasCallback, int flags, - ComponentName componentName) { + ComponentName componentName, boolean compatMode) { activityToken = Preconditions.checkNotNull(activityToken, "activityToken"); appCallback = Preconditions.checkNotNull(appCallback, "appCallback"); @@ -599,7 +703,7 @@ public final class AutofillManagerService extends SystemService { synchronized (mLock) { final AutofillManagerServiceImpl service = getServiceForUserLocked(userId); return service.startSessionLocked(activityToken, getCallingUid(), appCallback, - autofillId, bounds, value, hasCallback, flags, componentName); + autofillId, bounds, value, hasCallback, flags, componentName, compatMode); } } @@ -770,7 +874,7 @@ public final class AutofillManagerService extends SystemService { public int updateOrRestartSession(IBinder activityToken, IBinder appCallback, AutofillId autoFillId, Rect bounds, AutofillValue value, int userId, boolean hasCallback, int flags, ComponentName componentName, int sessionId, - int action) { + int action, boolean compatMode) { boolean restart = false; synchronized (mLock) { final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId); @@ -783,7 +887,7 @@ public final class AutofillManagerService extends SystemService { } if (restart) { return startSession(activityToken, appCallback, autoFillId, bounds, value, userId, - hasCallback, flags, componentName); + hasCallback, flags, componentName, compatMode); } // Nothing changed... diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java index 2dcc6da0be32..712ea36be6ca 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java +++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java @@ -64,6 +64,7 @@ import android.util.ArrayMap; import android.util.ArraySet; import android.util.DebugUtils; import android.util.LocalLog; +import android.util.Pair; import android.util.Slog; import android.util.SparseArray; import android.util.TimeUtils; @@ -220,6 +221,17 @@ final class AutofillManagerServiceImpl { return mInfo.getServiceInfo().applicationInfo.uid; } + + @GuardedBy("mLock") + @Nullable + String getUrlBarResourceIdForCompatModeLocked(@NonNull String packageName) { + if (mInfo == null) { + Slog.w(TAG, "getUrlBarResourceIdForCompatModeLocked(): no mInfo"); + return null; + } + return mInfo.getUrlBarResourceId(packageName); + } + @Nullable String getServicePackageName() { final ComponentName serviceComponent = getServiceComponentName(); @@ -345,7 +357,7 @@ final class AutofillManagerServiceImpl { int startSessionLocked(@NonNull IBinder activityToken, int uid, @NonNull IBinder appCallbackToken, @NonNull AutofillId autofillId, @NonNull Rect virtualBounds, @Nullable AutofillValue value, boolean hasCallback, - int flags, @NonNull ComponentName componentName) { + int flags, @NonNull ComponentName componentName, boolean compatMode) { if (!isEnabledLocked()) { return 0; } @@ -375,7 +387,7 @@ final class AutofillManagerServiceImpl { pruneAbandonedSessionsLocked(); final Session newSession = createSessionByTokenLocked(activityToken, uid, appCallbackToken, - hasCallback, componentName, flags); + hasCallback, componentName, compatMode, flags); if (newSession == null) { return NO_SESSION; } @@ -481,7 +493,7 @@ final class AutofillManagerServiceImpl { @GuardedBy("mLock") private Session createSessionByTokenLocked(@NonNull IBinder activityToken, int uid, @NonNull IBinder appCallbackToken, boolean hasCallback, - @NonNull ComponentName componentName, int flags) { + @NonNull ComponentName componentName, boolean compatMode, int flags) { // use random ids so that one app cannot know that another app creates sessions int sessionId; int tries = 0; @@ -499,7 +511,8 @@ final class AutofillManagerServiceImpl { final Session newSession = new Session(this, mUi, mContext, mHandlerCaller, mUserId, mLock, sessionId, uid, activityToken, appCallbackToken, hasCallback, mUiLatencyHistory, - mWtfHistory, mInfo.getServiceInfo().getComponentName(), componentName, flags); + mWtfHistory, mInfo.getServiceInfo().getComponentName(), componentName, compatMode, + flags); mSessions.put(newSession.id, newSession); return newSession; @@ -857,7 +870,7 @@ final class AutofillManagerServiceImpl { } 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)); @@ -894,7 +907,10 @@ final class AutofillManagerServiceImpl { pw.print(prefix); pw.print("Disabled: "); pw.println(mDisabled); pw.print(prefix); pw.print("Field classification enabled: "); pw.println(isFieldClassificationEnabledLocked()); - pw.print(prefix); pw.print("Compat pkgs: "); pw.println(getWhitelistedCompatModePackages()); + final ArrayMap<String, Pair<Long, String>> compatPkgs = getCompatibilityPackagesLocked(); + if (compatPkgs != null) { + pw.print(prefix); pw.print("Compat pkgs: "); pw.println(compatPkgs.keySet()); + } pw.print(prefix); pw.print("Setup complete: "); pw.println(mSetupComplete); pw.print(prefix); pw.print("Last prune: "); pw.println(mLastPrune); @@ -1018,23 +1034,11 @@ final class AutofillManagerServiceImpl { } @GuardedBy("mLock") - boolean isCompatibilityModeRequestedLocked(@NonNull String packageName, - long versionCode) { - if (mInfo == null || !mInfo.isCompatibilityModeRequested(packageName, versionCode)) { - return false; + @Nullable ArrayMap<String, Pair<Long, String>> getCompatibilityPackagesLocked() { + if (mInfo != null) { + return mInfo.getCompatibilityPackages(); } - if (!Build.IS_ENG) { - // TODO: Build a map and watch for settings changes (this is called on app start) - final String whiteListedPackages = getWhitelistedCompatModePackages(); - return whiteListedPackages != null && whiteListedPackages.contains(packageName); - } - return true; - } - - private String getWhitelistedCompatModePackages() { - return Settings.Global.getString( - mContext.getContentResolver(), - Settings.Global.AUTOFILL_COMPAT_ALLOWED_PACKAGES); + return null; } private void sendStateToClients(boolean resetClient) { diff --git a/services/autofill/java/com/android/server/autofill/Helper.java b/services/autofill/java/com/android/server/autofill/Helper.java index 02a62e10e111..5ef467d44e6a 100644 --- a/services/autofill/java/com/android/server/autofill/Helper.java +++ b/services/autofill/java/com/android/server/autofill/Helper.java @@ -18,11 +18,14 @@ package com.android.server.autofill; import android.annotation.NonNull; import android.annotation.Nullable; +import android.app.assist.AssistStructure; +import android.app.assist.AssistStructure.ViewNode; import android.metrics.LogMaker; import android.os.Bundle; import android.service.autofill.Dataset; import android.util.ArrayMap; import android.util.ArraySet; +import android.util.Slog; import android.view.autofill.AutofillId; import android.view.autofill.AutofillValue; @@ -31,11 +34,14 @@ import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; +import java.util.LinkedList; import java.util.Objects; import java.util.Set; public final class Helper { + private static final String TAG = "AutofillHelper"; + /** * Defines a logging flag that can be dynamically changed at runtime using * {@code cmd autofill set log_level debug}. @@ -121,4 +127,61 @@ public final class Helper { pw.print(text.length()); pw.println("_chars"); } } + + /** + * Finds the {@link ViewNode} that has the requested {@code autofillId}, if any. + */ + @Nullable + public static ViewNode findViewNodeByAutofillId(@NonNull AssistStructure structure, + @NonNull AutofillId autofillId) { + return findViewNode(structure, (node) -> { + return autofillId.equals(node.getAutofillId()); + }); + } + + private static ViewNode findViewNode(@NonNull AssistStructure structure, + @NonNull ViewNodeFilter filter) { + final LinkedList<ViewNode> nodesToProcess = new LinkedList<>(); + final int numWindowNodes = structure.getWindowNodeCount(); + for (int i = 0; i < numWindowNodes; i++) { + nodesToProcess.add(structure.getWindowNodeAt(i).getRootViewNode()); + } + while (!nodesToProcess.isEmpty()) { + final ViewNode node = nodesToProcess.removeFirst(); + if (filter.matches(node)) { + return node; + } + for (int i = 0; i < node.getChildCount(); i++) { + nodesToProcess.addLast(node.getChildAt(i)); + } + } + + return null; + } + + /** + * Sanitize the {@code webDomain} property of the URL bar node on compat mode. + */ + public static void sanitizeUrlBar(@NonNull AssistStructure structure, + @NonNull String urlBarId) { + final ViewNode urlBarNode = findViewNode(structure, (node) -> { + return urlBarId.equals(node.getIdEntry()); + }); + if (urlBarNode != null) { + final String domain = urlBarNode.getText().toString(); + if (domain.isEmpty()) { + if (sDebug) Slog.d(TAG, "sanitizeUrlBar(): empty on " + urlBarId); + return; + } + urlBarNode.setWebDomain(domain); + if (sDebug) { + Slog.d(TAG, "sanitizeUrlBar(): id=" + urlBarId + ", domain=" + + urlBarNode.getWebDomain()); + } + } + } + + private interface ViewNodeFilter { + boolean matches(ViewNode node); + } } diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java index 4a247049d395..d0475810ee66 100644 --- a/services/autofill/java/com/android/server/autofill/Session.java +++ b/services/autofill/java/com/android/server/autofill/Session.java @@ -140,6 +140,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState /** Component that's being auto-filled */ @NonNull private final ComponentName mComponentName; + /** Whether the app being autofilled is running in compat mode. */ + private final boolean mCompatMode; + @GuardedBy("mLock") private final ArrayMap<AutofillId, ViewState> mViewStates = new ArrayMap<>(); @@ -263,6 +266,15 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState componentNameFromApp == null ? "null" : componentNameFromApp.flattenToShortString())); } + if (mCompatMode) { + // Sanitize URL bar, if needed + final String urlBarId = mService.getUrlBarResourceIdForCompatModeLocked( + mComponentName.getPackageName()); + if (sDebug) Slog.d(TAG, "url_bar in compat mode: " + urlBarId); + if (urlBarId != null) { + Helper.sanitizeUrlBar(structure, urlBarId); + } + } structure.sanitizeForParceling(true); // Flags used to start the session. @@ -476,7 +488,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @NonNull IBinder client, boolean hasCallback, @NonNull LocalLog uiLatencyHistory, @NonNull LocalLog wtfHistory, @NonNull ComponentName serviceComponentName, @NonNull ComponentName componentName, - int flags) { + boolean compatMode, int flags) { id = sessionId; mFlags = flags; this.uid = uid; @@ -491,6 +503,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mUiLatencyHistory = uiLatencyHistory; mWtfHistory = wtfHistory; mComponentName = componentName; + mCompatMode = compatMode; mClient = IAutoFillManagerClient.Stub.asInterface(client); mMetricsLogger.write(newLogMaker(MetricsEvent.AUTOFILL_SESSION_STARTED) @@ -1161,12 +1174,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @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; @@ -1183,12 +1196,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState 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 @@ -1208,32 +1221,53 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } 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); - } - if (matches == null) { - matches = new ArrayList<>(userValues.length); + + autofillId); } - 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; @@ -1537,7 +1571,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final int numContexts = mContexts.size(); for (int i = numContexts - 1; i >= 0; i--) { final FillContext context = mContexts.get(i); - final ViewNode node = context.findViewNodeByAutofillId(id); + final ViewNode node = Helper.findViewNodeByAutofillId(context.getStructure(), id); if (node != null) { final AutofillValue value = node.getAutofillValue(); if (sDebug) { @@ -1561,7 +1595,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState for (int i = numContexts - 1; i >= 0; i--) { final FillContext context = mContexts.get(i); - final ViewNode node = context.findViewNodeByAutofillId(id); + final ViewNode node = Helper.findViewNodeByAutofillId(context.getStructure(), id); if (node != null && node.getAutofillOptions() != null) { return node.getAutofillOptions(); } @@ -2288,6 +2322,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState pw.print(prefix); pw.print("mHasCallback: "); pw.println(mHasCallback); pw.print(prefix); pw.print("mClientState: "); pw.println( Helper.bundleToString(mClientState)); + pw.print(prefix); pw.print("mCompatMode: "); pw.println(mCompatMode); pw.print(prefix); pw.print("mSelectedDatasetIds: "); pw.println(mSelectedDatasetIds); mRemoteFillService.dump(prefix, pw); } 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 8240e4b758da..c023efbc3dc6 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.app.PendingIntent; 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.MotionEvent; 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 @@ final class FillUi { 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 @@ final class FillUi { 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 @@ final class FillUi { } } + 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 @@ final class FillUi { 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 @@ final class FillUi { // 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 class FillUi { 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 @@ final class FillUi { 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 000000000000..1c9a43acfa65 --- /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 7e179e5d24f8..501ff293b535 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.ApplicationInfo; 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 @@ public class TransportManager { @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 @@ public class TransportManager { 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 40419323ce0a..96e7d2f53610 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 static com.android.server.backup.transport.TransportUtils.formatMessage; 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 class TransportClientManager { 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 efad7d5e16a1..f633003e4f07 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_INTERNET; 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 @@ public class ConnectivityService extends IConnectivityManager.Stub 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 @@ public class ConnectivityService extends IConnectivityManager.Stub 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 @@ public class ConnectivityService extends IConnectivityManager.Stub 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 @@ public class ConnectivityService extends IConnectivityManager.Stub 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 @@ public class ConnectivityService extends IConnectivityManager.Stub 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 @@ public class ConnectivityService extends IConnectivityManager.Stub } 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 @@ public class ConnectivityService extends IConnectivityManager.Stub 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 @@ public class ConnectivityService extends IConnectivityManager.Stub 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 @@ public class ConnectivityService extends IConnectivityManager.Stub 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 @@ public class ConnectivityService extends IConnectivityManager.Stub } 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 bdeb23163e7e..7cd007bd0b8f 100644 --- a/services/core/java/com/android/server/InputMethodManagerService.java +++ b/services/core/java/com/android/server/InputMethodManagerService.java @@ -120,7 +120,6 @@ import android.service.vr.IVrManager; import android.service.vr.IVrStateCallbacks; import android.text.TextUtils; import android.text.style.SuggestionSpan; -import android.util.ArrayMap; import android.util.ArraySet; import android.util.AtomicFile; import android.util.EventLog; @@ -183,7 +182,6 @@ import java.util.concurrent.atomic.AtomicInteger; public class InputMethodManagerService extends IInputMethodManager.Stub implements ServiceConnection, Handler.Callback { static final boolean DEBUG = false; - static final boolean DEBUG_RESTORE = DEBUG || false; static final String TAG = "InputMethodManagerService"; @Retention(SOURCE) @@ -911,15 +909,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub || Intent.ACTION_USER_REMOVED.equals(action)) { updateCurrentProfileIds(); return; - } else if (Intent.ACTION_SETTING_RESTORED.equals(action)) { - final String name = intent.getStringExtra(Intent.EXTRA_SETTING_NAME); - if (Settings.Secure.ENABLED_INPUT_METHODS.equals(name)) { - final String prevValue = intent.getStringExtra( - Intent.EXTRA_SETTING_PREVIOUS_VALUE); - final String newValue = intent.getStringExtra( - Intent.EXTRA_SETTING_NEW_VALUE); - restoreEnabledInputMethods(mContext, prevValue, newValue); - } } else if (Intent.ACTION_LOCALE_CHANGED.equals(action)) { onActionLocaleChanged(); } else if (ACTION_SHOW_INPUT_METHOD_PICKER.equals(action)) { @@ -984,44 +973,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } } - // Apply the results of a restore operation to the set of enabled IMEs. Note that this - // does not attempt to validate on the fly with any installed device policy, so must only - // be run in the context of initial device setup. - // - // TODO: Move this method to InputMethodUtils with adding unit tests. - static void restoreEnabledInputMethods(Context context, String prevValue, String newValue) { - if (DEBUG_RESTORE) { - Slog.i(TAG, "Restoring enabled input methods:"); - Slog.i(TAG, "prev=" + prevValue); - Slog.i(TAG, " new=" + newValue); - } - // 'new' is the just-restored state, 'prev' is what was in settings prior to the restore - ArrayMap<String, ArraySet<String>> prevMap = - InputMethodUtils.parseInputMethodsAndSubtypesString(prevValue); - ArrayMap<String, ArraySet<String>> newMap = - InputMethodUtils.parseInputMethodsAndSubtypesString(newValue); - - // Merge the restored ime+subtype enabled states into the live state - for (ArrayMap.Entry<String, ArraySet<String>> entry : newMap.entrySet()) { - final String imeId = entry.getKey(); - ArraySet<String> prevSubtypes = prevMap.get(imeId); - if (prevSubtypes == null) { - prevSubtypes = new ArraySet<>(2); - prevMap.put(imeId, prevSubtypes); - } - prevSubtypes.addAll(entry.getValue()); - } - - final String mergedImesAndSubtypesString = - InputMethodUtils.buildInputMethodsAndSubtypesString(prevMap); - if (DEBUG_RESTORE) { - Slog.i(TAG, "Merged IME string:"); - Slog.i(TAG, " " + mergedImesAndSubtypesString); - } - Settings.Secure.putString(context.getContentResolver(), - Settings.Secure.ENABLED_INPUT_METHODS, mergedImesAndSubtypesString); - } - final class MyPackageMonitor extends PackageMonitor { /** * Package names that are known to contain {@link InputMethodService}. @@ -1577,7 +1528,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub broadcastFilter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); broadcastFilter.addAction(Intent.ACTION_USER_ADDED); broadcastFilter.addAction(Intent.ACTION_USER_REMOVED); - broadcastFilter.addAction(Intent.ACTION_SETTING_RESTORED); broadcastFilter.addAction(Intent.ACTION_LOCALE_CHANGED); broadcastFilter.addAction(ACTION_SHOW_INPUT_METHOD_PICKER); mContext.registerReceiver(new ImmsBroadcastReceiver(), broadcastFilter); diff --git a/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java index 87e8121a474d..836791649748 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 @@ class ActivityManagerDebugConfig { 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 e8b78394f901..5f8a5abf9564 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -380,6 +380,7 @@ import android.util.proto.ProtoUtils; import android.view.Gravity; import android.view.IRecentsAnimationRunner; import android.view.LayoutInflater; +import android.view.RemoteAnimationAdapter; import android.view.RemoteAnimationDefinition; import android.view.View; import android.view.WindowManager; @@ -579,6 +580,10 @@ public class ActivityManagerService extends IActivityManager.Stub // 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub } /** + * 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 @@ public class ActivityManagerService extends IActivityManager.Stub throw new IllegalArgumentException("Invalid task, not in foreground"); } - // When a task is locked, dismiss the pinned stack if it exists - mStackSupervisor.removeStacksInWindowingModes(WINDOWING_MODE_PINNED); - // {@code isSystemCaller} is used to distinguish whether this request is initiated by the // system or a specific app. // * System-initiated requests will only start the pinned mode (screen pinning) @@ -11374,6 +11454,9 @@ public class ActivityManagerService extends IActivityManager.Stub final int callingUid = Binder.getCallingUid(); long ident = Binder.clearCallingIdentity(); try { + // When a task is locked, dismiss the pinned stack if it exists + mStackSupervisor.removeStacksInWindowingModes(WINDOWING_MODE_PINNED); + mLockTaskController.startLockTaskMode(task, isSystemCaller, callingUid); } finally { Binder.restoreCallingIdentity(ident); @@ -12782,6 +12865,12 @@ public class ActivityManagerService extends IActivityManager.Stub @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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub } } } + + @Override + public void registerRemoteAnimationForNextActivityStart(String packageName, + RemoteAnimationAdapter adapter) throws RemoteException { + enforceCallingPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS, + "registerRemoteAnimationForNextActivityStart"); + synchronized (this) { + final long origId = Binder.clearCallingIdentity(); + try { + mActivityStartController.registerRemoteAnimationForNextActivityStart(packageName, + adapter); + } finally { + Binder.restoreCallingIdentity(origId); + } + } + } } diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java index 24a77c7693d0..fa0df562b6c6 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 @@ final class ActivityManagerShellCommand extends ShellCommand { 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 @@ final class ActivityManagerShellCommand extends ShellCommand { 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 9838851a56cf..ddba349dbe86 100644 --- a/services/core/java/com/android/server/am/ActivityRecord.java +++ b/services/core/java/com/android/server/am/ActivityRecord.java @@ -380,8 +380,9 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo } String getLifecycleDescription(String reason) { - return "component:" + intent.getComponent().flattenToShortString() + ", state=" + state - + ", reason=" + reason + ", time=" + System.currentTimeMillis(); + return "name= " + this + ", component=" + intent.getComponent().flattenToShortString() + + ", package=" + packageName + ", state=" + state + ", reason=" + reason + ", time=" + + System.currentTimeMillis(); } void dump(PrintWriter pw, String prefix) { @@ -2583,7 +2584,8 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo if (andResume) { lifecycleItem = ResumeActivityItem.obtain(service.isNextTransitionForward()); } else { - lifecycleItem = PauseActivityItem.obtain(); + lifecycleItem = PauseActivityItem.obtain() + .setDescription(getLifecycleDescription("relaunchActivityLocked")); } final ClientTransaction transaction = ClientTransaction.obtain(app.thread, appToken); transaction.addCallback(callbackItem); diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java index 055a1aa4bbca..812de88729de 100644 --- a/services/core/java/com/android/server/am/ActivityStack.java +++ b/services/core/java/com/android/server/am/ActivityStack.java @@ -1444,7 +1444,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai mService.mLifecycleManager.scheduleTransaction(prev.app.thread, prev.appToken, PauseActivityItem.obtain(prev.finishing, userLeaving, - prev.configChangeFlags, pauseImmediately)); + prev.configChangeFlags, pauseImmediately).setDescription( + prev.getLifecycleDescription("startPausingLocked"))); } catch (Exception e) { // Ignore exception, if process died other code will cleanup. Slog.w(TAG, "Exception thrown during pause", e); @@ -1524,7 +1525,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai if (r.finishing) { if (DEBUG_PAUSE) Slog.v(TAG, "Executing finish of failed to pause activity: " + r); - finishCurrentActivityLocked(r, FINISH_AFTER_VISIBLE, false); + finishCurrentActivityLocked(r, FINISH_AFTER_VISIBLE, false, + "activityPausedLocked"); } } } @@ -1541,7 +1543,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai prev.state = ActivityState.PAUSED; if (prev.finishing) { if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Executing finish of activity: " + prev); - prev = finishCurrentActivityLocked(prev, FINISH_AFTER_VISIBLE, false); + prev = finishCurrentActivityLocked(prev, FINISH_AFTER_VISIBLE, false, + "completedPausedLocked"); } else if (prev.app != null) { if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Enqueue pending stop if needed: " + prev + " wasStopping=" + wasStopping + " visible=" + prev.visible); @@ -3673,8 +3676,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai final int finishMode = (r.visible || r.nowVisible) ? FINISH_AFTER_VISIBLE : FINISH_AFTER_PAUSE; - final boolean removedActivity = finishCurrentActivityLocked(r, finishMode, oomAdj) - == null; + final boolean removedActivity = finishCurrentActivityLocked(r, finishMode, oomAdj, + "finishActivityLocked") == null; // The following code is an optimization. When the last non-task overlay activity // is removed from the task, we remove the entire task from the stack. However, @@ -3715,7 +3718,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai static final int FINISH_AFTER_PAUSE = 1; static final int FINISH_AFTER_VISIBLE = 2; - final ActivityRecord finishCurrentActivityLocked(ActivityRecord r, int mode, boolean oomAdj) { + final ActivityRecord finishCurrentActivityLocked(ActivityRecord r, int mode, boolean oomAdj, + String reason) { // First things first: if this activity is currently visible, // and the resumed activity is not yet visible, then hold off on // finishing until the resumed one becomes visible. @@ -3758,7 +3762,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai || prevState == STOPPED || prevState == ActivityState.INITIALIZING) { r.makeFinishingLocked(); - boolean activityRemoved = destroyActivityLocked(r, true, "finish-imm"); + boolean activityRemoved = destroyActivityLocked(r, true, "finish-imm:" + reason); if (finishingActivityInNonFocusedStack) { // Finishing activity that was in paused state and it was in not currently focused @@ -3794,7 +3798,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai continue; } Slog.d(TAG, "finishAllActivitiesLocked: finishing " + r + " immediately"); - finishCurrentActivityLocked(r, FINISH_IMMEDIATELY, false); + finishCurrentActivityLocked(r, FINISH_IMMEDIATELY, false, + "finishAllActivitiesLocked"); } } if (noActivitiesInStack) { @@ -4882,7 +4887,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai + r.intent.getComponent().flattenToShortString()); // Force the destroy to skip right to removal. r.app = null; - finishCurrentActivityLocked(r, FINISH_IMMEDIATELY, false); + finishCurrentActivityLocked(r, FINISH_IMMEDIATELY, false, + "handleAppCrashedLocked"); } } } diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index 4928e908ce44..5c30764e1e85 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -1453,7 +1453,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D lifecycleItem = ResumeActivityItem.obtain(mService.isNextTransitionForward()) .setDescription(r.getLifecycleDescription("realStartActivityLocked")); } else { - lifecycleItem = PauseActivityItem.obtain(); + lifecycleItem = PauseActivityItem.obtain() + .setDescription(r.getLifecycleDescription("realStartActivityLocked")); } clientTransaction.setLifecycleStateRequest(lifecycleItem); @@ -1955,7 +1956,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D final ActivityStack stack = r.getStack(); if (stack != null) { if (r.finishing) { - stack.finishCurrentActivityLocked(r, ActivityStack.FINISH_IMMEDIATELY, false); + stack.finishCurrentActivityLocked(r, ActivityStack.FINISH_IMMEDIATELY, false, + "activityIdleInternalLocked"); } else { stack.stopActivityLocked(r); } diff --git a/services/core/java/com/android/server/am/ActivityStartController.java b/services/core/java/com/android/server/am/ActivityStartController.java index da11f6861f54..868f90df5c1b 100644 --- a/services/core/java/com/android/server/am/ActivityStartController.java +++ b/services/core/java/com/android/server/am/ActivityStartController.java @@ -41,6 +41,7 @@ import android.os.Looper; import android.os.Message; import android.provider.Settings; import android.util.Slog; +import android.view.RemoteAnimationAdapter; import com.android.internal.annotations.VisibleForTesting; import com.android.server.am.ActivityStackSupervisor.PendingActivityLaunch; @@ -85,6 +86,8 @@ public class ActivityStartController { private final Handler mHandler; + private final PendingRemoteAnimationRegistry mPendingRemoteAnimationRegistry; + private final class StartHandler extends Handler { public StartHandler(Looper looper) { super(looper, null, true); @@ -123,6 +126,8 @@ public class ActivityStartController { mHandler = new StartHandler(mService.mHandlerThread.getLooper()); mFactory = factory; mFactory.setController(this); + mPendingRemoteAnimationRegistry = new PendingRemoteAnimationRegistry(service, + service.mHandler); } /** @@ -399,6 +404,15 @@ public class ActivityStartController { return mPendingActivityLaunches.size() < pendingLaunches; } + void registerRemoteAnimationForNextActivityStart(String packageName, + RemoteAnimationAdapter adapter) { + mPendingRemoteAnimationRegistry.addPendingAnimation(packageName, adapter); + } + + PendingRemoteAnimationRegistry getPendingRemoteAnimationRegistry() { + return mPendingRemoteAnimationRegistry; + } + void dump(PrintWriter pw, String prefix, String dumpPackage) { pw.print(prefix); pw.print("mLastHomeActivityStartResult="); diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java index 055b89b6a6f0..8205265ba047 100644 --- a/services/core/java/com/android/server/am/ActivityStarter.java +++ b/services/core/java/com/android/server/am/ActivityStarter.java @@ -708,6 +708,8 @@ class ActivityStarter { ActivityOptions checkedOptions = options != null ? options.getOptions(intent, aInfo, callerApp, mSupervisor) : null; + checkedOptions = mService.getActivityStartController().getPendingRemoteAnimationRegistry() + .overrideOptionsIfNeeded(callingPackage, checkedOptions); if (mService.mController != null) { try { // The Intent we give to the watcher has the extra data @@ -930,6 +932,7 @@ class ActivityStarter { // 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/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java index ef82f36fa79f..fba03774e5d9 100644 --- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java +++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java @@ -43,7 +43,10 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ThreadFactory; +import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; /** @@ -65,12 +68,13 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { // There is some accuracy error in wifi reports so allow some slop in the results. private static final long MAX_WIFI_STATS_SAMPLE_ERROR_MILLIS = 750; - private final ExecutorService mExecutorService = Executors.newSingleThreadExecutor( - (ThreadFactory) r -> { - Thread t = new Thread(r, "batterystats-worker"); - t.setPriority(Thread.NORM_PRIORITY); - return t; - }); + private final ScheduledExecutorService mExecutorService = + Executors.newSingleThreadScheduledExecutor( + (ThreadFactory) r -> { + Thread t = new Thread(r, "batterystats-worker"); + t.setPriority(Thread.NORM_PRIORITY); + return t; + }); private final Context mContext; private final BatteryStatsImpl mStats; @@ -85,8 +89,20 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { private String mCurrentReason = null; @GuardedBy("this") + private boolean mOnBattery; + + @GuardedBy("this") + private boolean mOnBatteryScreenOff; + + @GuardedBy("this") + private boolean mUseLatestStates = true; + + @GuardedBy("this") private final IntArray mUidsToRemove = new IntArray(); + @GuardedBy("this") + private Future<?> mWakelockChangesUpdate; + private final Object mWorkerLock = new Object(); @GuardedBy("mWorkerLock") @@ -157,6 +173,50 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { return null; } + @Override + public Future<?> scheduleCpuSyncDueToScreenStateChange( + boolean onBattery, boolean onBatteryScreenOff) { + synchronized (BatteryExternalStatsWorker.this) { + if (mCurrentFuture == null || (mUpdateFlags & UPDATE_CPU) == 0) { + mOnBattery = onBattery; + mOnBatteryScreenOff = onBatteryScreenOff; + mUseLatestStates = false; + } + return scheduleSyncLocked("screen-state", UPDATE_CPU); + } + } + + @Override + public Future<?> scheduleCpuSyncDueToWakelockChange(long delayMillis) { + if (mExecutorService.isShutdown()) { + return CompletableFuture.failedFuture(new IllegalStateException("worker shutdown")); + } + + if (mWakelockChangesUpdate != null) { + // If there's already a scheduled task, leave it as is if we're trying to re-schedule + // it again with a delay, otherwise cancel and re-schedule it. + if (delayMillis == 0) { + mWakelockChangesUpdate.cancel(false); + } else { + return mWakelockChangesUpdate; + } + } + + mWakelockChangesUpdate = mExecutorService.schedule(() -> { + scheduleSync("wakelock-change", UPDATE_CPU); + scheduleRunnable(() -> mStats.postBatteryNeedsCpuUpdateMsg()); + mWakelockChangesUpdate = null; + }, delayMillis, TimeUnit.MILLISECONDS); + return mWakelockChangesUpdate; + } + + @Override + public void cancelCpuSyncDueToWakelockChange() { + if (mWakelockChangesUpdate != null) { + mWakelockChangesUpdate.cancel(false); + } + } + public synchronized Future<?> scheduleWrite() { if (mExecutorService.isShutdown()) { return CompletableFuture.failedFuture(new IllegalStateException("worker shutdown")); @@ -204,14 +264,21 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { final int updateFlags; final String reason; final int[] uidsToRemove; + final boolean onBattery; + final boolean onBatteryScreenOff; + final boolean useLatestStates; synchronized (BatteryExternalStatsWorker.this) { updateFlags = mUpdateFlags; reason = mCurrentReason; uidsToRemove = mUidsToRemove.size() > 0 ? mUidsToRemove.toArray() : EmptyArray.INT; + onBattery = mOnBattery; + onBatteryScreenOff = mOnBatteryScreenOff; + useLatestStates = mUseLatestStates; mUpdateFlags = 0; mCurrentReason = null; mUidsToRemove.clear(); mCurrentFuture = null; + mUseLatestStates = true; } synchronized (mWorkerLock) { @@ -219,7 +286,8 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { Slog.d(TAG, "begin updateExternalStatsSync reason=" + reason); } try { - updateExternalStatsLocked(reason, updateFlags); + updateExternalStatsLocked(reason, updateFlags, onBattery, + onBatteryScreenOff, useLatestStates); } finally { if (DEBUG) { Slog.d(TAG, "end updateExternalStatsSync"); @@ -250,7 +318,8 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { }; @GuardedBy("mWorkerLock") - private void updateExternalStatsLocked(final String reason, int updateFlags) { + private void updateExternalStatsLocked(final String reason, int updateFlags, + boolean onBattery, boolean onBatteryScreenOff, boolean useLatestStates) { // We will request data from external processes asynchronously, and wait on a timeout. SynchronousResultReceiver wifiReceiver = null; SynchronousResultReceiver bluetoothReceiver = null; @@ -306,7 +375,14 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { reason, 0); if ((updateFlags & UPDATE_CPU) != 0) { - mStats.updateCpuTimeLocked(); + if (useLatestStates) { + onBattery = mStats.isOnBatteryLocked(); + onBatteryScreenOff = mStats.isOnBatteryScreenOffLocked(); + } + mStats.updateCpuTimeLocked(onBattery, onBatteryScreenOff); + } + + if ((updateFlags & UPDATE_ALL) != 0) { mStats.updateKernelWakelocksLocked(); mStats.updateKernelMemoryBandwidthLocked(); } diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java index ea52782027ba..9d1adb23957b 100644 --- a/services/core/java/com/android/server/am/BatteryStatsService.java +++ b/services/core/java/com/android/server/am/BatteryStatsService.java @@ -1057,7 +1057,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub // to block such a low level service like BatteryService on external stats like WiFi. mWorker.scheduleRunnable(() -> { synchronized (mStats) { - final boolean onBattery = plugType == BatteryStatsImpl.BATTERY_PLUGGED_NONE; + final boolean onBattery = BatteryStatsImpl.isOnBattery(plugType, status); if (mStats.isOnBattery() == onBattery) { // The battery state has not changed, so we don't need to sync external // stats immediately. diff --git a/services/core/java/com/android/server/am/PendingRemoteAnimationRegistry.java b/services/core/java/com/android/server/am/PendingRemoteAnimationRegistry.java new file mode 100644 index 000000000000..77713f57d017 --- /dev/null +++ b/services/core/java/com/android/server/am/PendingRemoteAnimationRegistry.java @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.server.am; + +import android.annotation.Nullable; +import android.app.ActivityOptions; +import android.os.Handler; +import android.util.ArrayMap; +import android.view.RemoteAnimationAdapter; + +/** + * Registry to keep track of remote animations to be run for activity starts from a certain package. + * + * @see ActivityManagerService#registerRemoteAnimationForNextActivityStart + */ +class PendingRemoteAnimationRegistry { + + private static final long TIMEOUT_MS = 3000; + + private final ArrayMap<String, Entry> mEntries = new ArrayMap<>(); + private final Handler mHandler; + private final ActivityManagerService mService; + + PendingRemoteAnimationRegistry(ActivityManagerService service, Handler handler) { + mService = service; + mHandler = handler; + } + + /** + * Adds a remote animation to be run for all activity starts originating from a certain package. + */ + void addPendingAnimation(String packageName, RemoteAnimationAdapter adapter) { + mEntries.put(packageName, new Entry(packageName, adapter)); + } + + /** + * Overrides the activity options with a registered remote animation for a certain calling + * package if such a remote animation is registered. + */ + ActivityOptions overrideOptionsIfNeeded(String callingPackage, + @Nullable ActivityOptions options) { + final Entry entry = mEntries.get(callingPackage); + if (entry == null) { + return options; + } + if (options == null) { + options = ActivityOptions.makeRemoteAnimation(entry.adapter); + } else { + options.setRemoteAnimationAdapter(entry.adapter); + } + mEntries.remove(callingPackage); + return options; + } + + private class Entry { + final String packageName; + final RemoteAnimationAdapter adapter; + + Entry(String packageName, RemoteAnimationAdapter adapter) { + this.packageName = packageName; + this.adapter = adapter; + mHandler.postDelayed(() -> { + synchronized (mService) { + final Entry entry = mEntries.get(packageName); + if (entry == this) { + mEntries.remove(packageName); + } + } + }, TIMEOUT_MS); + } + } +} diff --git a/services/core/java/com/android/server/am/UriPermission.java b/services/core/java/com/android/server/am/UriPermission.java index 1e071aa3f5dc..3bf1cf487ca0 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 @@ final class UriPermission { 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 fffe7dca7fc4..76e0d8984cb8 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -551,8 +551,6 @@ public class AudioService extends IAudioService.Stub // Used to play ringtones outside system_server private volatile IRingtonePlayer mRingtonePlayer; - private int mDeviceOrientation = Configuration.ORIENTATION_UNDEFINED; - // Request to override default use of A2DP for media. private boolean mBluetoothA2dpEnabled; private final Object mBluetoothA2dpEnabledLock = new Object(); @@ -571,8 +569,6 @@ public class AudioService extends IAudioService.Stub AudioSystem.DEVICE_OUT_AUX_LINE; int mFullVolumeDevices = 0; - // TODO merge orientation and rotation - private final boolean mMonitorOrientation; private final boolean mMonitorRotation; private boolean mDockAudioMediaEnabled = true; @@ -788,13 +784,6 @@ public class AudioService extends IAudioService.Stub intentFilter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED); intentFilter.addAction(Intent.ACTION_CONFIGURATION_CHANGED); - // TODO merge orientation and rotation - mMonitorOrientation = SystemProperties.getBoolean("ro.audio.monitorOrientation", false); - if (mMonitorOrientation) { - Log.v(TAG, "monitoring device orientation"); - // initialize orientation in AudioSystem - setOrientationForAudioSystem(); - } mMonitorRotation = SystemProperties.getBoolean("ro.audio.monitorRotation", false); if (mMonitorRotation) { RotationHelper.init(mContext, mAudioHandler); @@ -963,10 +952,7 @@ public class AudioService extends IAudioService.Stub // Restore ringer mode setRingerModeInt(getRingerModeInternal(), false); - // Reset device orientation (if monitored for this device) - if (mMonitorOrientation) { - setOrientationForAudioSystem(); - } + // Reset device rotation (if monitored for this device) if (mMonitorRotation) { RotationHelper.updateOrientation(); } @@ -1042,14 +1028,16 @@ public class AudioService extends IAudioService.Stub } private void checkAllAliasStreamVolumes() { - synchronized (VolumeStreamState.class) { - int numStreamTypes = AudioSystem.getNumStreamTypes(); - for (int streamType = 0; streamType < numStreamTypes; streamType++) { - mStreamStates[streamType] - .setAllIndexes(mStreamStates[mStreamVolumeAlias[streamType]], TAG); - // apply stream volume - if (!mStreamStates[streamType].mIsMuted) { - mStreamStates[streamType].applyAllVolumes(); + synchronized (mSettingsLock) { + synchronized (VolumeStreamState.class) { + int numStreamTypes = AudioSystem.getNumStreamTypes(); + for (int streamType = 0; streamType < numStreamTypes; streamType++) { + mStreamStates[streamType] + .setAllIndexes(mStreamStates[mStreamVolumeAlias[streamType]], TAG); + // apply stream volume + if (!mStreamStates[streamType].mIsMuted) { + mStreamStates[streamType].applyAllVolumes(); + } } } } @@ -1155,13 +1143,16 @@ public class AudioService extends IAudioService.Stub if (updateVolumes && mStreamStates != null) { updateDefaultVolumes(); - mStreamStates[AudioSystem.STREAM_DTMF].setAllIndexes(mStreamStates[dtmfStreamAlias], - caller); - - mStreamStates[AudioSystem.STREAM_ACCESSIBILITY].mVolumeIndexSettingName = - System.VOLUME_SETTINGS_INT[a11yStreamAlias]; - mStreamStates[AudioSystem.STREAM_ACCESSIBILITY].setAllIndexes( - mStreamStates[a11yStreamAlias], caller); + synchronized (mSettingsLock) { + synchronized (VolumeStreamState.class) { + mStreamStates[AudioSystem.STREAM_DTMF] + .setAllIndexes(mStreamStates[dtmfStreamAlias], caller); + mStreamStates[AudioSystem.STREAM_ACCESSIBILITY].mVolumeIndexSettingName = + System.VOLUME_SETTINGS_INT[a11yStreamAlias]; + mStreamStates[AudioSystem.STREAM_ACCESSIBILITY].setAllIndexes( + mStreamStates[a11yStreamAlias], caller); + } + } if (sIndependentA11yVolume) { // restore the a11y values from the settings mStreamStates[AudioSystem.STREAM_ACCESSIBILITY].readSettings(); @@ -4604,39 +4595,36 @@ public class AudioService extends IAudioService.Stub * @param srcStream * @param caller */ + // must be sync'd on mSettingsLock before VolumeStreamState.class + @GuardedBy("VolumeStreamState.class") public void setAllIndexes(VolumeStreamState srcStream, String caller) { if (mStreamType == srcStream.mStreamType) { return; } - synchronized (mSettingsLock) { - synchronized (VolumeStreamState.class) { - int srcStreamType = srcStream.getStreamType(); - // apply default device volume from source stream to all devices first in case - // some devices are present in this stream state but not in source stream state - int index = srcStream.getIndex(AudioSystem.DEVICE_OUT_DEFAULT); - index = rescaleIndex(index, srcStreamType, mStreamType); - for (int i = 0; i < mIndexMap.size(); i++) { - mIndexMap.put(mIndexMap.keyAt(i), index); - } - // Now apply actual volume for devices in source stream state - SparseIntArray srcMap = srcStream.mIndexMap; - for (int i = 0; i < srcMap.size(); i++) { - int device = srcMap.keyAt(i); - index = srcMap.valueAt(i); - index = rescaleIndex(index, srcStreamType, mStreamType); - - setIndex(index, device, caller); - } - } + int srcStreamType = srcStream.getStreamType(); + // apply default device volume from source stream to all devices first in case + // some devices are present in this stream state but not in source stream state + int index = srcStream.getIndex(AudioSystem.DEVICE_OUT_DEFAULT); + index = rescaleIndex(index, srcStreamType, mStreamType); + for (int i = 0; i < mIndexMap.size(); i++) { + mIndexMap.put(mIndexMap.keyAt(i), index); + } + // Now apply actual volume for devices in source stream state + SparseIntArray srcMap = srcStream.mIndexMap; + for (int i = 0; i < srcMap.size(); i++) { + int device = srcMap.keyAt(i); + index = srcMap.valueAt(i); + index = rescaleIndex(index, srcStreamType, mStreamType); + + setIndex(index, device, caller); } } - @GuardedBy("mSettingsLock") + // must be sync'd on mSettingsLock before VolumeStreamState.class + @GuardedBy("VolumeStreamState.class") public void setAllIndexesToMax() { - synchronized (VolumeStreamState.class) { - for (int i = 0; i < mIndexMap.size(); i++) { - mIndexMap.put(mIndexMap.keyAt(i), mIndexMax); - } + for (int i = 0; i < mIndexMap.size(); i++) { + mIndexMap.put(mIndexMap.keyAt(i), mIndexMax); } } @@ -6192,24 +6180,15 @@ public class AudioService extends IAudioService.Stub // Device orientation //========================================================================================== /** - * Handles device configuration changes that may map to a change in the orientation - * or orientation. - * Monitoring orientation and rotation is optional, and is defined by the definition and value - * of the "ro.audio.monitorOrientation" and "ro.audio.monitorRotation" system properties. + * Handles device configuration changes that may map to a change in rotation. + * Monitoring rotation is optional, and is defined by the definition and value + * of the "ro.audio.monitorRotation" system property. */ private void handleConfigurationChanged(Context context) { try { - // reading new orientation "safely" (i.e. under try catch) in case anything - // goes wrong when obtaining resources and configuration + // reading new configuration "safely" (i.e. under try catch) in case anything + // goes wrong. Configuration config = context.getResources().getConfiguration(); - // TODO merge rotation and orientation - if (mMonitorOrientation) { - int newOrientation = config.orientation; - if (newOrientation != mDeviceOrientation) { - mDeviceOrientation = newOrientation; - setOrientationForAudioSystem(); - } - } sendMsg(mAudioHandler, MSG_CONFIGURE_SAFE_MEDIA_VOLUME, SENDMSG_REPLACE, @@ -6224,15 +6203,17 @@ public class AudioService extends IAudioService.Stub mCameraSoundForced = cameraSoundForced; if (cameraSoundForcedChanged) { if (!mIsSingleVolume) { - VolumeStreamState s = mStreamStates[AudioSystem.STREAM_SYSTEM_ENFORCED]; - if (cameraSoundForced) { - s.setAllIndexesToMax(); - mRingerModeAffectedStreams &= - ~(1 << AudioSystem.STREAM_SYSTEM_ENFORCED); - } else { - s.setAllIndexes(mStreamStates[AudioSystem.STREAM_SYSTEM], TAG); - mRingerModeAffectedStreams |= - (1 << AudioSystem.STREAM_SYSTEM_ENFORCED); + synchronized (VolumeStreamState.class) { + VolumeStreamState s = mStreamStates[AudioSystem.STREAM_SYSTEM_ENFORCED]; + if (cameraSoundForced) { + s.setAllIndexesToMax(); + mRingerModeAffectedStreams &= + ~(1 << AudioSystem.STREAM_SYSTEM_ENFORCED); + } else { + s.setAllIndexes(mStreamStates[AudioSystem.STREAM_SYSTEM], TAG); + mRingerModeAffectedStreams |= + (1 << AudioSystem.STREAM_SYSTEM_ENFORCED); + } } // take new state into account for streams muted by ringer mode setRingerModeInt(getRingerModeInternal(), false); @@ -6261,30 +6242,6 @@ public class AudioService extends IAudioService.Stub } } - //TODO move to an external "orientation helper" class - private void setOrientationForAudioSystem() { - switch (mDeviceOrientation) { - case Configuration.ORIENTATION_LANDSCAPE: - //Log.i(TAG, "orientation is landscape"); - AudioSystem.setParameters("orientation=landscape"); - break; - case Configuration.ORIENTATION_PORTRAIT: - //Log.i(TAG, "orientation is portrait"); - AudioSystem.setParameters("orientation=portrait"); - break; - case Configuration.ORIENTATION_SQUARE: - //Log.i(TAG, "orientation is square"); - AudioSystem.setParameters("orientation=square"); - break; - case Configuration.ORIENTATION_UNDEFINED: - //Log.i(TAG, "orientation is undefined"); - AudioSystem.setParameters("orientation=undefined"); - break; - default: - Log.e(TAG, "Unknown orientation"); - } - } - // Handles request to override default use of A2DP for media. // Must be called synchronized on mConnectedDevices public void setBluetoothA2dpOnInt(boolean on, String eventSource) { diff --git a/services/core/java/com/android/server/audio/OWNERS b/services/core/java/com/android/server/audio/OWNERS new file mode 100644 index 000000000000..b70de299eeea --- /dev/null +++ b/services/core/java/com/android/server/audio/OWNERS @@ -0,0 +1,2 @@ +jmtrivi@google.com +elaurent@google.com diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java index 85b70ca0ffcd..a24f97e53570 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 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { 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 class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { 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/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java index d87a1bb9f96f..79450a0f85b5 100644 --- a/services/core/java/com/android/server/content/SyncManager.java +++ b/services/core/java/com/android/server/content/SyncManager.java @@ -2078,8 +2078,33 @@ public class SyncManager { protected void dumpSyncState(PrintWriter pw) { final StringBuilder sb = new StringBuilder(); - pw.print("data connected: "); pw.println(mDataConnectionIsConnected); - pw.print("auto sync: "); + pw.print("Data connected: "); pw.println(mDataConnectionIsConnected); + pw.print("Battery saver: "); + pw.println((mPowerManager != null) && mPowerManager.isPowerSaveMode()); + + pw.print("Background network restriction: "); + { + final ConnectivityManager cm = getConnectivityManager(); + final int status = (cm == null) ? -1 : cm.getRestrictBackgroundStatus(); + switch (status) { + case ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLED: + pw.println(" disabled"); + break; + case ConnectivityManager.RESTRICT_BACKGROUND_STATUS_WHITELISTED: + pw.println(" whitelisted"); + break; + case ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED: + pw.println(" enabled"); + break; + default: + pw.print("Unknown("); + pw.print(status); + pw.println(")"); + break; + } + } + + pw.print("Auto sync: "); List<UserInfo> users = getAllUsers(); if (users != null) { for (UserInfo user : users) { @@ -2088,26 +2113,26 @@ public class SyncManager { } pw.println(); } - pw.print("memory low: "); pw.println(mStorageIsLow); - pw.print("device idle: "); pw.println(mDeviceIsIdle); - pw.print("reported active: "); pw.println(mReportedSyncActive); + pw.print("Memory low: "); pw.println(mStorageIsLow); + pw.print("Device idle: "); pw.println(mDeviceIsIdle); + pw.print("Reported active: "); pw.println(mReportedSyncActive); final AccountAndUser[] accounts = AccountManagerService.getSingleton().getAllAccounts(); - pw.print("accounts: "); + pw.print("Accounts: "); if (accounts != INITIAL_ACCOUNTS_ARRAY) { pw.println(accounts.length); } else { pw.println("not known yet"); } final long now = SystemClock.elapsedRealtime(); - pw.print("now: "); pw.print(now); + pw.print("Now: "); pw.print(now); pw.println(" (" + formatTime(System.currentTimeMillis()) + ")"); sb.setLength(0); - pw.print("uptime: "); pw.print(formatDurationHMS(sb, now)); + pw.print("Uptime: "); pw.print(formatDurationHMS(sb, now)); pw.println(); - pw.print("time spent syncing: "); + pw.print("Time spent syncing: "); sb.setLength(0); pw.print(formatDurationHMS(sb, diff --git a/services/core/java/com/android/server/content/SyncManagerConstants.java b/services/core/java/com/android/server/content/SyncManagerConstants.java index 2f35687fa44f..3139d5456459 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.provider.Settings.Global; 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 class SyncManagerConstants extends ContentObserver { } 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 b5f94b1ce384..692535c6cebc 100644 --- a/services/core/java/com/android/server/fingerprint/FingerprintService.java +++ b/services/core/java/com/android/server/fingerprint/FingerprintService.java @@ -421,7 +421,7 @@ public class FingerprintService extends SystemService implements IHwBinder.Death byteToken[i] = token.get(i); } // Send to Keystore - KeyStore.getInstance().addAuthToken(byteToken); + KeyStore.getInstance().addAuthToken(byteToken, mCurrentUserId); } if (client != null && client.onAuthenticated(fingerId, groupId)) { removeClient(client); @@ -915,7 +915,7 @@ public class FingerprintService extends SystemService implements IHwBinder.Death 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 @@ public class FingerprintService extends SystemService implements IHwBinder.Death 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 @@ public class FingerprintService extends SystemService implements IHwBinder.Death 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 be48f69e1545..c33d7f4b4451 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.annotation.UserIdInt; 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 @@ public final class JobSchedulerService extends com.android.server.SystemService 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 @@ public final class JobSchedulerService extends com.android.server.SystemService */ 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 @@ public final class JobSchedulerService extends com.android.server.SystemService 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 @@ public final class JobSchedulerService extends com.android.server.SystemService 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 @@ public final class JobSchedulerService extends com.android.server.SystemService 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 @@ public final class JobSchedulerService extends com.android.server.SystemService 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 @@ public final class JobSchedulerService extends com.android.server.SystemService noteJobsNonpending(mPendingJobs); mPendingJobs.clear(); stopNonReadyActiveJobsLocked(); - boolean updated = updateStandbyHeartbeatLocked(); mJobs.forEachJob(mReadyQueueFunctor); - if (updated) updateNextStandbyHeartbeatsLocked(); mReadyQueueFunctor.postProcess(); if (DEBUG) { @@ -1716,38 +1740,80 @@ public final class JobSchedulerService extends com.android.server.SystemService 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); + } + /** * Criteria for moving a job into the pending queue: * - It's ready. @@ -1811,17 +1877,20 @@ public final class JobSchedulerService extends com.android.server.SystemService 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 @@ public final class JobSchedulerService extends com.android.server.SystemService // 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 @@ public final class JobSchedulerService extends com.android.server.SystemService * 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 @@ public final class JobSchedulerService extends com.android.server.SystemService 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 @@ public final class JobSchedulerService extends com.android.server.SystemService } } + // 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 @@ public final class JobSchedulerService extends com.android.server.SystemService 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 d630aab61ce5..63225f34234f 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 @@ public final class JobSchedulerShellCommand extends ShellCommand { return getStorageNotLow(pw); case "get-job-state": return getJobState(pw); + case "heartbeat": + return doHeartbeat(pw); default: return handleDefaultCommands(cmd); } @@ -333,6 +335,20 @@ public final class JobSchedulerShellCommand extends ShellCommand { } } + 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 @@ public final class JobSchedulerShellCommand extends ShellCommand { 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 4988974e95db..1f8cf769ab98 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 @@ public final class JobServiceContext implements ServiceConnection { } } - 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 @@ public final class JobServiceContext implements ServiceConnection { 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 08ff7bdb0eb8..3867306ee521 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 @@ public final class JobStatus { } 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 c9c93293e2ee..c4f1f3d7369d 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsStrongAuth.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsStrongAuth.java @@ -19,9 +19,6 @@ package com.android.server.locksettings; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_TIMEOUT; -import com.android.internal.widget.LockPatternUtils; -import com.android.internal.widget.LockPatternUtils.StrongAuthTracker; - import android.app.AlarmManager; import android.app.AlarmManager.OnAlarmListener; import android.app.admin.DevicePolicyManager; @@ -29,10 +26,9 @@ import android.app.trust.IStrongAuthTracker; import android.content.Context; import android.content.pm.PackageManager; import android.hardware.fingerprint.FingerprintManager; -import android.os.Binder; -import android.os.DeadObjectException; import android.os.Handler; import android.os.Message; +import android.os.RemoteCallbackList; import android.os.RemoteException; import android.os.SystemClock; import android.os.UserHandle; @@ -40,7 +36,7 @@ import android.util.ArrayMap; import android.util.Slog; import android.util.SparseIntArray; -import java.util.ArrayList; +import com.android.internal.widget.LockPatternUtils.StrongAuthTracker; /** * Keeps track of requests for strong authentication. @@ -58,7 +54,7 @@ public class LockSettingsStrongAuth { private static final String STRONG_AUTH_TIMEOUT_ALARM_TAG = "LockSettingsStrongAuth.timeoutForUser"; - private final ArrayList<IStrongAuthTracker> mStrongAuthTrackers = new ArrayList<>(); + private final RemoteCallbackList<IStrongAuthTracker> mTrackers = new RemoteCallbackList<>(); private final SparseIntArray mStrongAuthForUser = new SparseIntArray(); private final ArrayMap<Integer, StrongAuthTimeoutAlarmListener> mStrongAuthTimeoutAlarmListenerForUser = new ArrayMap<>(); @@ -82,12 +78,7 @@ public class LockSettingsStrongAuth { } private void handleAddStrongAuthTracker(IStrongAuthTracker tracker) { - for (int i = 0; i < mStrongAuthTrackers.size(); i++) { - if (mStrongAuthTrackers.get(i).asBinder() == tracker.asBinder()) { - return; - } - } - mStrongAuthTrackers.add(tracker); + mTrackers.register(tracker); for (int i = 0; i < mStrongAuthForUser.size(); i++) { int key = mStrongAuthForUser.keyAt(i); @@ -101,12 +92,7 @@ public class LockSettingsStrongAuth { } private void handleRemoveStrongAuthTracker(IStrongAuthTracker tracker) { - for (int i = 0; i < mStrongAuthTrackers.size(); i++) { - if (mStrongAuthTrackers.get(i).asBinder() == tracker.asBinder()) { - mStrongAuthTrackers.remove(i); - return; - } - } + mTrackers.unregister(tracker); } private void handleRequireStrongAuth(int strongAuthReason, int userId) { @@ -157,16 +143,19 @@ public class LockSettingsStrongAuth { } private void notifyStrongAuthTrackers(int strongAuthReason, int userId) { - for (int i = 0; i < mStrongAuthTrackers.size(); i++) { - try { - mStrongAuthTrackers.get(i).onStrongAuthRequiredChanged(strongAuthReason, userId); - } catch (DeadObjectException e) { - Slog.d(TAG, "Removing dead StrongAuthTracker."); - mStrongAuthTrackers.remove(i); + int i = mTrackers.beginBroadcast(); + try { + while (i > 0) { i--; - } catch (RemoteException e) { - Slog.e(TAG, "Exception while notifying StrongAuthTracker.", e); + try { + mTrackers.getBroadcastItem(i).onStrongAuthRequiredChanged( + strongAuthReason, userId); + } catch (RemoteException e) { + Slog.e(TAG, "Exception while notifying StrongAuthTracker.", e); + } } + } finally { + mTrackers.finishBroadcast(); } } @@ -243,4 +232,5 @@ public class LockSettingsStrongAuth { } } }; + } diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/OWNERS b/services/core/java/com/android/server/locksettings/recoverablekeystore/OWNERS new file mode 100644 index 000000000000..bb487fb52c9f --- /dev/null +++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/OWNERS @@ -0,0 +1,4 @@ +aseemk@google.com +bozhu@google.com +dementyev@google.com +robertberry@google.com diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java index 152c910e65b7..23a66baf4445 100644 --- a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java +++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java @@ -170,11 +170,13 @@ public class RecoverableKeyStoreManager { certXml = CertXml.parse(recoveryServiceCertFile); } catch (CertParsingException e) { // TODO: Do not use raw key bytes anymore once the other components are updated - Log.d(TAG, "Failed to parse the cert file", e); + Log.d(TAG, "Failed to parse the input as a cert file: " + HexDump.toHexString( + recoveryServiceCertFile)); PublicKey publicKey = parseEcPublicKey(recoveryServiceCertFile); if (mDatabase.setRecoveryServicePublicKey(userId, uid, publicKey) > 0) { mDatabase.setShouldCreateSnapshot(userId, uid, true); } + Log.d(TAG, "Successfully set the input as the raw public key"); return; } diff --git a/services/core/java/com/android/server/media/OWNERS b/services/core/java/com/android/server/media/OWNERS new file mode 100644 index 000000000000..6f8d82306e62 --- /dev/null +++ b/services/core/java/com/android/server/media/OWNERS @@ -0,0 +1,2 @@ +lajos@google.com +elaurent@google.com diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index f34662909a85..e2b2d46eb4a6 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java @@ -145,6 +145,7 @@ import android.net.TrafficStats; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiManager; import android.os.Binder; +import android.os.Build; import android.os.Environment; import android.os.Handler; import android.os.HandlerThread; @@ -2759,6 +2760,13 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { return; } + // Fourth check: is caller a testing app on a debug build? + final boolean enableDebug = Build.IS_USERDEBUG || Build.IS_ENG; + if (enableDebug && callingPackage + .equals(SystemProperties.get("fw.sub_plan_owner." + subId, null))) { + return; + } + // Final check: does the caller hold a permission? mContext.enforceCallingOrSelfPermission(MANAGE_SUBSCRIPTION_PLANS, TAG); } diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java index 44b83d8cf759..0a870978d160 100644 --- a/services/core/java/com/android/server/notification/ZenModeHelper.java +++ b/services/core/java/com/android/server/notification/ZenModeHelper.java @@ -1178,7 +1178,7 @@ public class ZenModeHelper { extras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME, mContext.getResources().getString(R.string.global_action_settings)); return new Notification.Builder(mContext, SystemNotificationChannels.SYSTEM_CHANGES) - .setSmallIcon(R.drawable.ic_settings) + .setSmallIcon(R.drawable.ic_settings_24dp) .setContentTitle(mContext.getResources().getString( R.string.zen_upgrade_notification_title)) .setContentText(mContext.getResources().getString( diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java index 1746dd1a2b27..9bba9ed0f612 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 @@ public class Installer extends SystemService { 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 fc73142c4858..9420a6c5c15b 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 @@ public class OtaDexoptService extends IOtaDexopt.Stub { 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 @@ public class OtaDexoptService extends IOtaDexopt.Stub { 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 458d725db030..77bf67daa478 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 android.util.Slog; 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.InstructionSets.getDexCodeInstructionSets; 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 @@ public class PackageDexOptimizer { 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 @@ public class PackageDexOptimizer { 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 @@ public class PackageDexOptimizer { 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 @@ public class PackageDexOptimizer { // 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 @@ public class PackageDexOptimizer { /*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 7ca6bb95c791..884606d23748 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 @@ public class PackageManagerService extends IPackageManager.Stub } // 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 @@ public class PackageManagerService extends IPackageManager.Stub 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 @@ public class PackageManagerService extends IPackageManager.Stub * 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 @@ public class PackageManagerService extends IPackageManager.Stub } } - 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 @@ public class PackageManagerService extends IPackageManager.Stub int dexoptFlags = bootComplete ? DexoptOptions.DEXOPT_BOOT_COMPLETE : 0; int primaryDexOptStaus = performDexOptTraced(new DexoptOptions( pkg.packageName, - pkgCompilerFilter, + pkgCompilationReason, dexoptFlags)); switch (primaryDexOptStaus) { @@ -9081,8 +9080,8 @@ public class PackageManagerService extends IPackageManager.Stub 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 @@ public class PackageManagerService extends IPackageManager.Stub 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 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); @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 19b0d9bc4b90..fce828581c54 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 @@ public class PackageManagerServiceCompilerMapping { 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 a0577b15736b..0eeaf661635d 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -1486,6 +1486,23 @@ public class UserManagerService extends IUserManager.Stub { return restrictions != null && restrictions.getBoolean(restrictionKey); } + /** @return if any user has the given restriction. */ + @Override + public boolean hasUserRestrictionOnAnyUser(String restrictionKey) { + if (!UserRestrictionsUtils.isValidRestriction(restrictionKey)) { + return false; + } + final List<UserInfo> users = getUsers(/* excludeDying= */ true); + for (int i = 0; i < users.size(); i++) { + final int userId = users.get(i).id; + Bundle restrictions = getEffectiveUserRestrictions(userId); + if (restrictions != null && restrictions.getBoolean(restrictionKey)) { + return true; + } + } + return false; + } + /** * @hide * diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java index 23185d7fb5ab..41570c48c474 100644 --- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java +++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java @@ -33,6 +33,7 @@ import android.os.UserHandle; import android.os.UserManager; import android.os.UserManagerInternal; import android.provider.Settings; +import android.provider.Settings.Global; import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; import android.util.Log; @@ -581,6 +582,15 @@ public class UserRestrictionsUtils { Settings.Secure.DOZE_PULSE_ON_DOUBLE_TAP, "0"); } break; + case UserManager.DISALLOW_CONFIG_LOCATION: + // When DISALLOW_CONFIG_LOCATION is set on any user, we undo the global + // kill switch. + if (newValue) { + android.provider.Settings.Global.putString( + context.getContentResolver(), + Global.LOCATION_GLOBAL_KILL_SWITCH, "0"); + } + break; } } finally { Binder.restoreCallingIdentity(id); diff --git a/services/core/java/com/android/server/pm/dex/DexManager.java b/services/core/java/com/android/server/pm/dex/DexManager.java index 0e2730cbd944..3e63fb42f0ef 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 @@ public class DexManager { 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 d4f95cb6b99f..a7a7686b2a6b 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 @@ public final class DexoptOptions { // 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 @@ public final class DexoptOptions { mCompilerFilter = compilerFilter; mFlags = flags; mSplitName = splitName; + mCompilationReason = compilationReason; } public String getPackageName() { @@ -157,4 +164,8 @@ public final class DexoptOptions { public int getFlags() { return mFlags; } + + public int getCompilationReason() { + return mCompilationReason; + } } diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java b/services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java index 941cd4441e23..efcadadce3f9 100644 --- a/services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java +++ b/services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java @@ -19,6 +19,8 @@ package com.android.server.policy.keyguard; import android.app.ActivityManager; import android.content.Context; import android.os.RemoteException; +import android.os.ServiceManager; +import android.security.IKeystoreService; import android.util.Slog; import com.android.internal.policy.IKeyguardService; @@ -51,11 +53,16 @@ public class KeyguardStateMonitor extends IKeyguardStateCallback.Stub { private final LockPatternUtils mLockPatternUtils; private final StateCallback mCallback; + IKeystoreService mKeystoreService; + public KeyguardStateMonitor(Context context, IKeyguardService service, StateCallback callback) { mLockPatternUtils = new LockPatternUtils(context); mCurrentUserId = ActivityManager.getCurrentUser(); mCallback = callback; + mKeystoreService = IKeystoreService.Stub.asInterface(ServiceManager + .getService("android.security.keystore")); + try { service.addStateMonitorCallback(this); } catch (RemoteException e) { @@ -86,6 +93,12 @@ public class KeyguardStateMonitor extends IKeyguardStateCallback.Stub { @Override // Binder interface public void onShowingStateChanged(boolean showing) { mIsShowing = showing; + + if (showing) try { + mKeystoreService.lock(mCurrentUserId); // as long as this doesn't recur... + } catch (RemoteException e) { + Slog.e(TAG, "Error locking keystore", e); + } } @Override // Binder interface 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 37df94fee207..5d76329eb8a1 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 android.util.Slog; 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 @@ public class BatterySavingStats { 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 @@ public class BatterySavingStats { } } - @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 @@ public class BatterySavingStats { 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 @@ public class BatterySavingStats { 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 4bc94047b24e..fa7e53594781 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 class StatsCompanionService extends IStatsCompanionService.Stub { 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 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { .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)); - } } } @@ -361,9 +360,11 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { if (DEBUG) Slog.d(TAG, "Setting anomaly alarm for " + timestampMs); final long callingToken = Binder.clearCallingIdentity(); try { - // using RTC, not RTC_WAKEUP, so if device is asleep, will only fire when it awakens. + // using ELAPSED_REALTIME, not ELAPSED_REALTIME_WAKEUP, so if device is asleep, will + // only fire when it awakens. + // This alarm is inexact, leaving its exactness completely up to the OS optimizations. // AlarmManager will automatically cancel any previous mAnomalyAlarmIntent alarm. - mAlarmManager.setExact(AlarmManager.RTC, timestampMs, mAnomalyAlarmIntent); + mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME, timestampMs, mAnomalyAlarmIntent); } finally { Binder.restoreCallingIdentity(callingToken); } @@ -388,10 +389,12 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { Slog.d(TAG, "Setting pulling alarm for " + timestampMs + " every " + intervalMs + "ms"); final long callingToken = Binder.clearCallingIdentity(); try { - // using RTC, not RTC_WAKEUP, so if device is asleep, will only fire when it awakens. + // using ELAPSED_REALTIME, not ELAPSED_REALTIME_WAKEUP, so if device is asleep, will + // only fire when it awakens. // This alarm is inexact, leaving its exactness completely up to the OS optimizations. // TODO: totally inexact means that stats per bucket could be quite off. Is this okay? - mAlarmManager.setRepeating(AlarmManager.RTC, timestampMs, intervalMs, mPullingAlarmIntent); + mAlarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME, timestampMs, intervalMs, + mPullingAlarmIntent); } finally { Binder.restoreCallingIdentity(callingToken); } diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java index ce3f512deccf..8155656cd5f4 100644 --- a/services/core/java/com/android/server/wm/AppWindowToken.java +++ b/services/core/java/com/android/server/wm/AppWindowToken.java @@ -1766,6 +1766,9 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree layer += Z_BOOST_BASE; } leash.setLayer(layer); + + final DisplayContent dc = getDisplayContent(); + dc.assignStackOrdering(t); } /** diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index f421bf46e84a..2d32c81d13a5 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -3524,39 +3524,47 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo @Override void assignChildLayers(SurfaceControl.Transaction t) { + assignStackOrdering(t); + for (int i = 0; i < mChildren.size(); i++) { + final TaskStack s = mChildren.get(i); + s.assignChildLayers(t); + } + } + + void assignStackOrdering(SurfaceControl.Transaction t) { final int HOME_STACK_STATE = 0; final int NORMAL_STACK_STATE = 1; final int ALWAYS_ON_TOP_STATE = 2; int layer = 0; + int layerForAnimationLayer = 0; + for (int state = 0; state <= ALWAYS_ON_TOP_STATE; state++) { for (int i = 0; i < mChildren.size(); i++) { final TaskStack s = mChildren.get(i); - if (state == HOME_STACK_STATE && s.isActivityTypeHome()) { - s.assignLayer(t, layer++); - } else if (state == NORMAL_STACK_STATE && !s.isActivityTypeHome() - && !s.isAlwaysOnTop()) { - s.assignLayer(t, layer++); - if (s.inSplitScreenWindowingMode() && mSplitScreenDividerAnchor != null) { - t.setLayer(mSplitScreenDividerAnchor, layer++); - } - } else if (state == ALWAYS_ON_TOP_STATE && s.isAlwaysOnTop()) { - s.assignLayer(t, layer++); + if (state == HOME_STACK_STATE && !s.isActivityTypeHome()) { + continue; + } else if (state == NORMAL_STACK_STATE && (s.isActivityTypeHome() + || s.isAlwaysOnTop())) { + continue; + } else if (state == ALWAYS_ON_TOP_STATE && !s.isAlwaysOnTop()) { + continue; + } + s.assignLayer(t, layer++); + if (s.inSplitScreenWindowingMode() && mSplitScreenDividerAnchor != null) { + t.setLayer(mSplitScreenDividerAnchor, layer++); + } + if (s.isSelfOrChildAnimating()) { + // Ensure the animation layer ends up above the + // highest animating stack and no higher. + layerForAnimationLayer = layer++; } - } - // The appropriate place for App-Transitions to occur is right - // above all other animations but still below things in the Picture-and-Picture - // windowing mode. - if (state == NORMAL_STACK_STATE && mAppAnimationLayer != null) { - t.setLayer(mAppAnimationLayer, layer++); } } - for (int i = 0; i < mChildren.size(); i++) { - final TaskStack s = mChildren.get(i); - s.assignChildLayers(t); + if (mAppAnimationLayer != null) { + t.setLayer(mAppAnimationLayer, layerForAnimationLayer); } - } @Override @@ -3854,4 +3862,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo super.prepareSurfaces(); } + + void assignStackOrdering(SurfaceControl.Transaction t) { + mTaskStackContainers.assignStackOrdering(t); + } } diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java index 97a2954b2b16..e4edeb877b70 100644 --- a/services/core/java/com/android/server/wm/RecentsAnimationController.java +++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java @@ -24,15 +24,15 @@ import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_W import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; -import android.app.ActivityManager; import android.app.ActivityManager.TaskSnapshot; import android.app.WindowConfiguration; -import android.graphics.GraphicBuffer; import android.graphics.Point; import android.graphics.Rect; import android.os.Binder; +import android.os.IBinder; import android.os.RemoteException; import android.os.SystemClock; +import android.util.ArraySet; import android.util.Log; import android.util.Slog; import android.view.IRecentsAnimationController; @@ -41,6 +41,7 @@ import android.view.RemoteAnimationTarget; import android.view.SurfaceControl; import android.view.SurfaceControl.Transaction; import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback; +import com.google.android.collect.Sets; import java.io.PrintWriter; import java.util.ArrayList; @@ -99,17 +100,13 @@ public class RecentsAnimationController { final TaskAnimationAdapter adapter = mPendingAnimations.get(i); final Task task = adapter.mTask; if (task.mTaskId == taskId) { - // TODO: Save this screenshot as the task snapshot? - final Rect taskFrame = new Rect(); - task.getBounds(taskFrame); - final GraphicBuffer buffer = SurfaceControl.captureLayers( - task.getSurfaceControl().getHandle(), taskFrame, 1f); - final AppWindowToken topChild = task.getTopChild(); - final WindowState mainWindow = topChild.findMainWindow(); - return new TaskSnapshot(buffer, topChild.getConfiguration().orientation, - mainWindow.mContentInsets, - ActivityManager.isLowRamDeviceStatic() /* reduced */, - 1.0f /* scale */); + final TaskSnapshotController snapshotController = + mService.mTaskSnapshotController; + final ArraySet<Task> tasks = Sets.newArraySet(task); + snapshotController.snapshotTasks(tasks); + snapshotController.addSkipClosingAppSnapshotTasks(tasks); + return snapshotController.getSnapshot(taskId, 0 /* userId */, + false /* restoreFromDisk */, false /* reducedResolution */); } } return null; diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java index a7a2b534131d..3d7b32ca1a9d 100644 --- a/services/core/java/com/android/server/wm/TaskSnapshotController.java +++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java @@ -92,6 +92,7 @@ class TaskSnapshotController { private final TaskSnapshotPersister mPersister = new TaskSnapshotPersister( Environment::getDataSystemCeDirectory); private final TaskSnapshotLoader mLoader = new TaskSnapshotLoader(mPersister); + private final ArraySet<Task> mSkipClosingAppSnapshotTasks = new ArraySet<>(); private final ArraySet<Task> mTmpTasks = new ArraySet<>(); private final Handler mHandler = new Handler(); @@ -149,10 +150,20 @@ class TaskSnapshotController { // either closing or hidden. getClosingTasks(closingApps, mTmpTasks); snapshotTasks(mTmpTasks); + mSkipClosingAppSnapshotTasks.clear(); + } + /** + * Adds the given {@param tasks} to the list of tasks which should not have their snapshots + * taken upon the next processing of the set of closing apps. The caller is responsible for + * calling {@link #snapshotTasks} to ensure that the task has an up-to-date snapshot. + */ + @VisibleForTesting + void addSkipClosingAppSnapshotTasks(ArraySet<Task> tasks) { + mSkipClosingAppSnapshotTasks.addAll(tasks); } - private void snapshotTasks(ArraySet<Task> tasks) { + void snapshotTasks(ArraySet<Task> tasks) { for (int i = tasks.size() - 1; i >= 0; i--) { final Task task = tasks.valueAt(i); final int mode = getSnapshotMode(task); @@ -295,7 +306,7 @@ class TaskSnapshotController { // If the task of the app is not visible anymore, it means no other app in that task // is opening. Thus, the task is closing. - if (task != null && !task.isVisible()) { + if (task != null && !task.isVisible() && !mSkipClosingAppSnapshotTasks.contains(task)) { outClosingTasks.add(task); } } diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index fa4474ba3060..93e9137e322f 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -1129,7 +1129,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< scheduleAnimation(); } - private void reassignLayer(Transaction t) { + void reassignLayer(Transaction t) { final WindowContainer parent = getParent(); if (parent != null) { parent.assignChildLayers(t); diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index e68fbdbac1a9..966f6226caed 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -3005,7 +3005,9 @@ public class WindowManagerService extends IWindowManager.Stub @Override public void dismissKeyguard(IKeyguardDismissCallback callback, CharSequence message) { - checkCallingPermission(permission.CONTROL_KEYGUARD, "dismissKeyguard"); + if (!checkCallingPermission(permission.CONTROL_KEYGUARD, "dismissKeyguard")) { + throw new SecurityException("Requires CONTROL_KEYGUARD permission"); + } synchronized(mWindowMap) { mPolicy.dismissKeyguardLw(callback, message); } diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp index 72f95fb74f4f..0b032815f1fe 100644 --- a/services/core/jni/Android.bp +++ b/services/core/jni/Android.bp @@ -41,6 +41,7 @@ cc_library_static { "com_android_server_tv_TvUinputBridge.cpp", "com_android_server_tv_TvInputHal.cpp", "com_android_server_vr_VrManagerService.cpp", + "com_android_server_UsbAlsaJackDetector.cpp", "com_android_server_UsbDeviceManager.cpp", "com_android_server_UsbDescriptorParser.cpp", "com_android_server_UsbMidiDevice.cpp", @@ -97,6 +98,7 @@ cc_defaults { "libgui", "libusbhost", "libsuspend", + "libtinyalsa", "libEGL", "libGLESv2", "libnetutils", diff --git a/services/core/jni/com_android_server_UsbAlsaJackDetector.cpp b/services/core/jni/com_android_server_UsbAlsaJackDetector.cpp new file mode 100644 index 000000000000..e9d448234e17 --- /dev/null +++ b/services/core/jni/com_android_server_UsbAlsaJackDetector.cpp @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "UsbAlsaJackDetectorJNI" +#include "utils/Log.h" + +#include "jni.h" +#include <nativehelper/JNIHelp.h> +#include "android_runtime/AndroidRuntime.h" +#include "android_runtime/Log.h" + +#include <stdio.h> +#include <string.h> +#include <asm/byteorder.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + +#include <tinyalsa/asoundlib.h> + +#define DRIVER_NAME "/dev/usb_accessory" + +#define USB_IN_JACK_NAME "USB in Jack" +#define USB_OUT_JACK_NAME "USB out Jack" + +namespace android +{ + +static jboolean is_jack_connected(jint card, const char* control) { + struct mixer* card_mixer = mixer_open(card); + if (card_mixer == NULL) { + return true; + } + struct mixer_ctl* ctl = mixer_get_ctl_by_name(card_mixer, control); + if (!ctl) { + return true; + } + mixer_ctl_update(ctl); + int val = mixer_ctl_get_value(ctl, 0); + ALOGI("JACK %s - value %d\n", control, val); + mixer_close(card_mixer); + + return val != 0; +} + +static jboolean android_server_UsbAlsaJackDetector_hasJackDetect(JNIEnv* /* env */, + jobject /* thiz */, + jint card) +{ + struct mixer* card_mixer = mixer_open(card); + if (card_mixer == NULL) { + return false; + } + + jboolean has_jack = false; + if ((mixer_get_ctl_by_name(card_mixer, USB_IN_JACK_NAME) != NULL) || + (mixer_get_ctl_by_name(card_mixer, USB_OUT_JACK_NAME) != NULL)) { + has_jack = true; + } + mixer_close(card_mixer); + return has_jack; +} + + +static jboolean android_server_UsbAlsaJackDetector_inputJackConnected(JNIEnv* /* env */, + jobject /* thiz */, + jint card) +{ + return is_jack_connected(card, USB_IN_JACK_NAME); +} + + +static jboolean android_server_UsbAlsaJackDetector_outputJackConnected(JNIEnv* /* env */, + jobject /* thiz */, + jint card) +{ + return is_jack_connected(card, USB_OUT_JACK_NAME); +} + +static void android_server_UsbAlsaJackDetector_jackDetect(JNIEnv* env, + jobject thiz, + jint card) { + jclass jdclass = env->GetObjectClass(thiz); + jmethodID method_jackDetectCallback = env->GetMethodID(jdclass, "jackDetectCallback", "()Z"); + if (method_jackDetectCallback == NULL) { + ALOGE("Can't find jackDetectCallback"); + return; + } + + struct mixer* m = mixer_open(card); + if (!m) { + ALOGE("Jack detect unable to open mixer\n"); + return; + } + mixer_subscribe_events(m, 1); + do { + + // Wait for a mixer event. Retry if interrupted, exit on error. + int retval; + do { + retval = mixer_wait_event(m, -1); + } while (retval == -EINTR); + if (retval < 0) { + break; + } + mixer_consume_event(m); + } while (env->CallBooleanMethod(thiz, method_jackDetectCallback)); + + mixer_close(m); + return; +} + +static const JNINativeMethod method_table[] = { + { "nativeHasJackDetect", "(I)Z", (void*)android_server_UsbAlsaJackDetector_hasJackDetect }, + { "nativeInputJackConnected", "(I)Z", + (void*)android_server_UsbAlsaJackDetector_inputJackConnected }, + { "nativeOutputJackConnected", "(I)Z", + (void*)android_server_UsbAlsaJackDetector_outputJackConnected }, + { "nativeJackDetect", "(I)Z", (void*)android_server_UsbAlsaJackDetector_jackDetect }, +}; + +int register_android_server_UsbAlsaJackDetector(JNIEnv *env) +{ + jclass clazz = env->FindClass("com/android/server/usb/UsbAlsaJackDetector"); + if (clazz == NULL) { + ALOGE("Can't find com/android/server/usb/UsbAlsaJackDetector"); + return -1; + } + + if (!jniRegisterNativeMethods(env, "com/android/server/usb/UsbAlsaJackDetector", + method_table, NELEM(method_table))) { + ALOGE("Can't register UsbAlsaJackDetector native methods"); + return -1; + } + + return 0; +} + +} diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp index bf2a637cf54e..0ebef37c1f2f 100644 --- a/services/core/jni/onload.cpp +++ b/services/core/jni/onload.cpp @@ -34,6 +34,7 @@ int register_android_server_PowerManagerService(JNIEnv* env); int register_android_server_storage_AppFuse(JNIEnv* env); int register_android_server_SerialService(JNIEnv* env); int register_android_server_SystemServer(JNIEnv* env); +int register_android_server_UsbAlsaJackDetector(JNIEnv* env); int register_android_server_UsbDeviceManager(JNIEnv* env); int register_android_server_UsbMidiDevice(JNIEnv* env); int register_android_server_UsbHostManager(JNIEnv* env); @@ -82,6 +83,7 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) register_android_server_AlarmManagerService(env); register_android_server_UsbDeviceManager(env); register_android_server_UsbMidiDevice(env); + register_android_server_UsbAlsaJackDetector(env); register_android_server_UsbHostManager(env); register_android_server_vr_VrManagerService(env); register_android_server_VibratorService(env); diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 75bb5e4f8a6b..61c8b79ae3ce 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -196,7 +196,7 @@ public final class SystemServer { 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 44ac8039bbed..503adb29471c 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 @@ package com.android.server.backup; import static com.android.server.backup.testing.TransportData.genericTransport; import static com.android.server.backup.testing.TransportTestUtils.mockTransport; import static com.android.server.backup.testing.TransportTestUtils.setUpTransportsForTransportManager; + import static com.google.common.truth.Truth.assertThat; -import static java.util.Arrays.asList; -import static java.util.Collections.emptyList; -import static java.util.Collections.singletonList; -import static java.util.stream.Collectors.toList; -import static java.util.stream.Collectors.toSet; -import static java.util.stream.Stream.concat; + import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.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.mockito.Mockito.when; import static org.robolectric.shadow.api.Shadow.extract; import static org.testng.Assert.expectThrows; +import static java.util.Arrays.asList; +import static java.util.Collections.emptyList; +import static java.util.Collections.singletonList; +import static java.util.stream.Collectors.toList; +import static java.util.stream.Collectors.toSet; +import static java.util.stream.Stream.concat; + import android.annotation.Nullable; import android.app.backup.BackupManager; import android.content.ComponentName; @@ -43,6 +48,7 @@ import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.platform.test.annotations.Presubmit; + import com.android.server.backup.testing.ShadowContextImplForBackup; import com.android.server.backup.testing.TransportData; import com.android.server.backup.testing.TransportTestUtils.TransportMock; @@ -54,11 +60,7 @@ import com.android.server.testing.FrameworkRobolectricTestRunner; 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.RuntimeEnvironment; import org.robolectric.annotation.Config; import org.robolectric.shadows.ShadowPackageManager; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Stream; + @RunWith(FrameworkRobolectricTestRunner.class) @Config( manifest = Config.NONE, @@ -81,6 +89,12 @@ public class TransportManagerTest { 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 @@ public class TransportManagerTest { } @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 @@ public class TransportManagerTest { 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 000000000000..5e3c9741ae70 --- /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 07262e17c752..23fe0ffda60e 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 static org.mockito.Mockito.verify; 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.Before; 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 @@ public class MagnificationGestureHandlerTest { 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 @@ public class MagnificationGestureHandlerTest { 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 @@ public class MagnificationGestureHandlerTest { 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 000000000000..2baf9952cb9e --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/am/PendingRemoteAnimationRegistryTest.java @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.server.am; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + +import android.annotation.Nullable; +import android.app.ActivityOptions; +import android.os.Handler; +import android.platform.test.annotations.Presubmit; +import android.support.test.filters.FlakyTest; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; +import android.util.ArrayMap; +import android.view.RemoteAnimationAdapter; + +import com.android.server.testutils.OffsettableClock; +import com.android.server.testutils.TestHandler; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +/** + * atest PendingRemoteAnimationRegistryTest + */ +@SmallTest +@Presubmit +@FlakyTest +@RunWith(AndroidJUnit4.class) +public class PendingRemoteAnimationRegistryTest extends ActivityTestsBase { + + @Mock RemoteAnimationAdapter mAdapter; + private PendingRemoteAnimationRegistry mRegistry; + private final OffsettableClock mClock = new OffsettableClock.Stopped(); + private TestHandler mHandler; + private ActivityManagerService mService; + + @Before + public void setUp() throws Exception { + super.setUp(); + MockitoAnnotations.initMocks(this); + mService = createActivityManagerService(); + mService.mHandlerThread.getThreadHandler().runWithScissors(() -> { + mHandler = new TestHandler(null, mClock); + }, 0); + mRegistry = new PendingRemoteAnimationRegistry(mService, mHandler); + } + + @Test + public void testOverrideActivityOptions() { + mRegistry.addPendingAnimation("com.android.test", mAdapter); + ActivityOptions opts = ActivityOptions.makeBasic(); + opts = mRegistry.overrideOptionsIfNeeded("com.android.test", opts); + assertEquals(mAdapter, opts.getRemoteAnimationAdapter()); + } + + @Test + public void testOverrideActivityOptions_null() { + mRegistry.addPendingAnimation("com.android.test", mAdapter); + final ActivityOptions opts = mRegistry.overrideOptionsIfNeeded("com.android.test", null); + assertNotNull(opts); + assertEquals(mAdapter, opts.getRemoteAnimationAdapter()); + } + + @Test + public void testTimeout() { + mRegistry.addPendingAnimation("com.android.test", mAdapter); + mClock.fastForward(5000); + mHandler.timeAdvance(); + assertNull(mRegistry.overrideOptionsIfNeeded("com.android.test", null)); + } + + @Test + public void testTimeout_overridenEntry() { + mRegistry.addPendingAnimation("com.android.test", mAdapter); + mClock.fastForward(2500); + mHandler.timeAdvance(); + mRegistry.addPendingAnimation("com.android.test", mAdapter); + mClock.fastForward(1000); + mHandler.timeAdvance(); + final ActivityOptions opts = mRegistry.overrideOptionsIfNeeded("com.android.test", null); + assertEquals(mAdapter, opts.getRemoteAnimationAdapter()); + } +} diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/OWNERS b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/OWNERS new file mode 100644 index 000000000000..bb487fb52c9f --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/OWNERS @@ -0,0 +1,4 @@ +aseemk@google.com +bozhu@google.com +dementyev@google.com +robertberry@google.com diff --git a/services/tests/servicestests/src/com/android/server/net/ConnOnActivityStartTest.java b/services/tests/servicestests/src/com/android/server/net/ConnOnActivityStartTest.java index 6e1808ba1e87..28b54ef84a91 100644 --- a/services/tests/servicestests/src/com/android/server/net/ConnOnActivityStartTest.java +++ b/services/tests/servicestests/src/com/android/server/net/ConnOnActivityStartTest.java @@ -323,6 +323,7 @@ public class ConnOnActivityStartTest { private void turnBatteryOn() throws Exception { executeCommand("cmd battery unplug"); + executeCommand("cmd battery set status " + BatteryManager.BATTERY_STATUS_NOT_CHARGING); assertBatteryOn(); } @@ -336,6 +337,7 @@ public class ConnOnActivityStartTest { private void turnBatteryOff() throws Exception { executeCommand("cmd battery set ac " + BatteryManager.BATTERY_PLUGGED_AC); + executeCommand("cmd battery set status " + BatteryManager.BATTERY_STATUS_CHARGING); } private static void batteryReset() throws Exception { 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 f559986a6f15..93064bc4ab92 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 class DexoptOptionsTests { 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 f7516b2c3694..f7112d43443b 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 @@ public class BatterySavingStatsTest { 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 @@ public class BatterySavingStatsTest { 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 @@ public class BatterySavingStatsTest { 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 @@ public class BatterySavingStatsTest { 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 @@ public class BatterySavingStatsTest { 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 029d9f1a8262..1222b59e92b9 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 class TestHandler extends Handler { } public TestHandler(Callback callback, LongSupplier clock) { - super(callback); + super(Looper.getMainLooper(), callback); mClock = clock; } @@ -132,7 +133,7 @@ public class TestHandler extends Handler { } 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/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java index 78b6077dc1bd..cbbdca6b394f 100644 --- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java +++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java @@ -308,6 +308,7 @@ public class AppStandbyControllerTests { private void reportEvent(AppStandbyController controller, int eventType, long elapsedTime) { // Back to ACTIVE on event + mInjector.mElapsedRealtime = elapsedTime; UsageEvents.Event ev = new UsageEvents.Event(); ev.mPackage = PACKAGE_1; ev.mEventType = eventType; @@ -487,6 +488,89 @@ public class AppStandbyControllerTests { } @Test + public void testCascadingTimeouts() throws Exception { + setChargingState(mController, false); + + reportEvent(mController, USER_INTERACTION, 0); + assertBucket(STANDBY_BUCKET_ACTIVE); + + reportEvent(mController, NOTIFICATION_SEEN, 1000); + assertBucket(STANDBY_BUCKET_ACTIVE); + + mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET, + REASON_PREDICTED, 1000); + assertBucket(STANDBY_BUCKET_ACTIVE); + + mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT, + REASON_PREDICTED, 2000 + mController.mStrongUsageTimeoutMillis); + assertBucket(STANDBY_BUCKET_WORKING_SET); + + mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT, + REASON_PREDICTED, 2000 + mController.mNotificationSeenTimeoutMillis); + assertBucket(STANDBY_BUCKET_FREQUENT); + } + + @Test + public void testOverlappingTimeouts() throws Exception { + setChargingState(mController, false); + + reportEvent(mController, USER_INTERACTION, 0); + assertBucket(STANDBY_BUCKET_ACTIVE); + + reportEvent(mController, NOTIFICATION_SEEN, 1000); + assertBucket(STANDBY_BUCKET_ACTIVE); + + // Overlapping USER_INTERACTION before previous one times out + reportEvent(mController, USER_INTERACTION, mController.mStrongUsageTimeoutMillis - 1000); + assertBucket(STANDBY_BUCKET_ACTIVE); + + // Still in ACTIVE after first USER_INTERACTION times out + mInjector.mElapsedRealtime = mController.mStrongUsageTimeoutMillis + 1000; + mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT, + REASON_PREDICTED, mInjector.mElapsedRealtime); + assertBucket(STANDBY_BUCKET_ACTIVE); + + // Both timed out, so NOTIFICATION_SEEN timeout should be effective + mInjector.mElapsedRealtime = mController.mStrongUsageTimeoutMillis * 2 + 2000; + mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT, + REASON_PREDICTED, mInjector.mElapsedRealtime); + assertBucket(STANDBY_BUCKET_WORKING_SET); + + mInjector.mElapsedRealtime = mController.mNotificationSeenTimeoutMillis + 2000; + mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE, + REASON_PREDICTED, mInjector.mElapsedRealtime); + assertBucket(STANDBY_BUCKET_RARE); + } + + @Test + public void testPredictionNotOverridden() throws Exception { + setChargingState(mController, false); + + reportEvent(mController, USER_INTERACTION, 0); + assertBucket(STANDBY_BUCKET_ACTIVE); + + mInjector.mElapsedRealtime = WORKING_SET_THRESHOLD - 1000; + reportEvent(mController, NOTIFICATION_SEEN, mInjector.mElapsedRealtime); + assertBucket(STANDBY_BUCKET_ACTIVE); + + // Falls back to WORKING_SET + mInjector.mElapsedRealtime += 5000; + mController.checkIdleStates(USER_ID); + assertBucket(STANDBY_BUCKET_WORKING_SET); + + // Predict to ACTIVE + mInjector.mElapsedRealtime += 1000; + mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE, + REASON_PREDICTED, mInjector.mElapsedRealtime); + assertBucket(STANDBY_BUCKET_ACTIVE); + + // CheckIdleStates should not change the prediction + mInjector.mElapsedRealtime += 1000; + mController.checkIdleStates(USER_ID); + assertBucket(STANDBY_BUCKET_ACTIVE); + } + + @Test public void testAddActiveDeviceAdmin() { assertActiveAdmins(USER_ID, (String[]) null); assertActiveAdmins(USER_ID2, (String[]) null); diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotControllerTest.java b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotControllerTest.java index 920796ed6a30..5650050f0420 100644 --- a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotControllerTest.java @@ -29,6 +29,7 @@ import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; import android.util.ArraySet; +import com.google.android.collect.Sets; import org.junit.Test; import org.junit.runner.RunWith; @@ -74,6 +75,21 @@ public class TaskSnapshotControllerTest extends WindowTestsBase { } @Test + public void testGetClosingApps_skipClosingAppsSnapshotTasks() throws Exception { + final WindowState closingWindow = createWindow(null, FIRST_APPLICATION_WINDOW, + "closingWindow"); + closingWindow.mAppToken.setVisibility(null, false /* visible */, TRANSIT_UNSET, + true /* performLayout */, false /* isVoiceInteraction */); + final ArraySet<AppWindowToken> closingApps = new ArraySet<>(); + closingApps.add(closingWindow.mAppToken); + final ArraySet<Task> closingTasks = new ArraySet<>(); + sWm.mTaskSnapshotController.addSkipClosingAppSnapshotTasks( + Sets.newArraySet(closingWindow.mAppToken.getTask())); + sWm.mTaskSnapshotController.getClosingTasks(closingApps, closingTasks); + assertEquals(0, closingTasks.size()); + } + + @Test public void testGetSnapshotMode() throws Exception { final WindowState disabledWindow = createWindow(null, FIRST_APPLICATION_WINDOW, mDisplayContent, "disabledWindow"); diff --git a/services/usage/java/com/android/server/usage/AppIdleHistory.java b/services/usage/java/com/android/server/usage/AppIdleHistory.java index b654a66f7d30..f26c2ae771f0 100644 --- a/services/usage/java/com/android/server/usage/AppIdleHistory.java +++ b/services/usage/java/com/android/server/usage/AppIdleHistory.java @@ -23,6 +23,7 @@ import static android.app.usage.UsageStatsManager.REASON_USAGE; import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_ACTIVE; import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_NEVER; import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE; +import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_WORKING_SET; import android.app.usage.UsageStatsManager; import android.os.SystemClock; @@ -87,7 +88,9 @@ public class AppIdleHistory { // The last time a job was run for this app private static final String ATTR_LAST_RUN_JOB_TIME = "lastJobRunTime"; // The time when the forced active state can be overridden. - private static final String ATTR_BUCKET_TIMEOUT_TIME = "bucketTimeoutTime"; + private static final String ATTR_BUCKET_ACTIVE_TIMEOUT_TIME = "activeTimeoutTime"; + // The time when the forced working_set state can be overridden. + private static final String ATTR_BUCKET_WORKING_SET_TIMEOUT_TIME = "workingSetTimeoutTime"; // device on time = mElapsedDuration + (timeNow - mElapsedSnapshot) private long mElapsedSnapshot; // Elapsed time snapshot when last write of mDeviceOnDuration @@ -117,11 +120,15 @@ public class AppIdleHistory { int lastInformedBucket; // The last time a job was run for this app, using elapsed timebase long lastJobRunTime; - // When should the bucket state timeout, in elapsed timebase, if greater than + // When should the bucket active state timeout, in elapsed timebase, if greater than // lastUsedElapsedTime. // This is used to keep the app in a high bucket regardless of other timeouts and // predictions. - long bucketTimeoutTime; + long bucketActiveTimeoutTime; + // If there's a forced working_set state, this is when it times out. This can be sitting + // under any active state timeout, so that it becomes applicable after the active state + // timeout expires. + long bucketWorkingSetTimeoutTime; } AppIdleHistory(File storageDir, long elapsedRealtime) { @@ -208,11 +215,28 @@ public class AppIdleHistory { * @param packageName name of the app being updated, for logging purposes * @param newBucket the bucket to set the app to * @param elapsedRealtime mark as used time if non-zero - * @param timeout set the timeout of the specified bucket, if non-zero + * @param timeout set the timeout of the specified bucket, if non-zero. Can only be used + * with bucket values of ACTIVE and WORKING_SET. * @return */ public AppUsageHistory reportUsage(AppUsageHistory appUsageHistory, String packageName, int newBucket, long elapsedRealtime, long timeout) { + // Set the timeout if applicable + if (timeout > elapsedRealtime) { + // Convert to elapsed timebase + final long timeoutTime = mElapsedDuration + (timeout - mElapsedSnapshot); + if (newBucket == STANDBY_BUCKET_ACTIVE) { + appUsageHistory.bucketActiveTimeoutTime = Math.max(timeoutTime, + appUsageHistory.bucketActiveTimeoutTime); + } else if (newBucket == STANDBY_BUCKET_WORKING_SET) { + appUsageHistory.bucketWorkingSetTimeoutTime = Math.max(timeoutTime, + appUsageHistory.bucketWorkingSetTimeoutTime); + } else { + throw new IllegalArgumentException("Cannot set a timeout on bucket=" + + newBucket); + } + } + if (elapsedRealtime != 0) { appUsageHistory.lastUsedElapsedTime = mElapsedDuration + (elapsedRealtime - mElapsedSnapshot); @@ -226,12 +250,6 @@ public class AppIdleHistory { .currentBucket + ", reason=" + appUsageHistory.bucketingReason); } - if (timeout > elapsedRealtime) { - // Convert to elapsed timebase - appUsageHistory.bucketTimeoutTime = - Math.max(appUsageHistory.bucketTimeoutTime, - mElapsedDuration + (timeout - mElapsedSnapshot)); - } } appUsageHistory.bucketingReason = REASON_USAGE; @@ -247,7 +265,8 @@ public class AppIdleHistory { * @param userId * @param newBucket the bucket to set the app to * @param elapsedRealtime mark as used time if non-zero - * @param timeout set the timeout of the specified bucket, if non-zero + * @param timeout set the timeout of the specified bucket, if non-zero. Can only be used + * with bucket values of ACTIVE and WORKING_SET. * @return */ public AppUsageHistory reportUsage(String packageName, int userId, int newBucket, @@ -504,8 +523,10 @@ public class AppIdleHistory { parser.getAttributeValue(null, ATTR_BUCKETING_REASON); appUsageHistory.lastJobRunTime = getLongValue(parser, ATTR_LAST_RUN_JOB_TIME, Long.MIN_VALUE); - appUsageHistory.bucketTimeoutTime = getLongValue(parser, - ATTR_BUCKET_TIMEOUT_TIME, 0L); + appUsageHistory.bucketActiveTimeoutTime = getLongValue(parser, + ATTR_BUCKET_ACTIVE_TIMEOUT_TIME, 0L); + appUsageHistory.bucketWorkingSetTimeoutTime = getLongValue(parser, + ATTR_BUCKET_WORKING_SET_TIMEOUT_TIME, 0L); if (appUsageHistory.bucketingReason == null) { appUsageHistory.bucketingReason = REASON_DEFAULT; } @@ -557,9 +578,13 @@ public class AppIdleHistory { xml.attribute(null, ATTR_CURRENT_BUCKET, Integer.toString(history.currentBucket)); xml.attribute(null, ATTR_BUCKETING_REASON, history.bucketingReason); - if (history.bucketTimeoutTime > 0) { - xml.attribute(null, ATTR_BUCKET_TIMEOUT_TIME, Long.toString(history - .bucketTimeoutTime)); + if (history.bucketActiveTimeoutTime > 0) { + xml.attribute(null, ATTR_BUCKET_ACTIVE_TIMEOUT_TIME, Long.toString(history + .bucketActiveTimeoutTime)); + } + if (history.bucketWorkingSetTimeoutTime > 0) { + xml.attribute(null, ATTR_BUCKET_WORKING_SET_TIMEOUT_TIME, Long.toString(history + .bucketWorkingSetTimeoutTime)); } if (history.lastJobRunTime != Long.MIN_VALUE) { xml.attribute(null, ATTR_LAST_RUN_JOB_TIME, Long.toString(history @@ -593,14 +618,19 @@ public class AppIdleHistory { continue; } idpw.print("package=" + packageName); + idpw.print(" userId=" + userId); idpw.print(" lastUsedElapsed="); TimeUtils.formatDuration(totalElapsedTime - appUsageHistory.lastUsedElapsedTime, idpw); idpw.print(" lastUsedScreenOn="); TimeUtils.formatDuration(screenOnTime - appUsageHistory.lastUsedScreenTime, idpw); idpw.print(" lastPredictedTime="); TimeUtils.formatDuration(totalElapsedTime - appUsageHistory.lastPredictedTime, idpw); - idpw.print(" bucketTimeoutTime="); - TimeUtils.formatDuration(totalElapsedTime - appUsageHistory.bucketTimeoutTime, idpw); + idpw.print(" bucketActiveTimeoutTime="); + TimeUtils.formatDuration(totalElapsedTime - appUsageHistory.bucketActiveTimeoutTime, + idpw); + idpw.print(" bucketWorkingSetTimeoutTime="); + TimeUtils.formatDuration(totalElapsedTime - appUsageHistory.bucketWorkingSetTimeoutTime, + idpw); idpw.print(" lastJobRunTime="); TimeUtils.formatDuration(totalElapsedTime - appUsageHistory.lastJobRunTime, idpw); idpw.print(" idle=" + (isIdle(packageName, userId, elapsedRealtime) ? "y" : "n")); diff --git a/services/usage/java/com/android/server/usage/AppStandbyController.java b/services/usage/java/com/android/server/usage/AppStandbyController.java index 32db7526f3c0..c31809e913ad 100644 --- a/services/usage/java/com/android/server/usage/AppStandbyController.java +++ b/services/usage/java/com/android/server/usage/AppStandbyController.java @@ -30,6 +30,7 @@ import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_WORKING_SET; import static com.android.server.SystemService.PHASE_BOOT_COMPLETED; import static com.android.server.SystemService.PHASE_SYSTEM_SERVICES_READY; +import android.annotation.UserIdInt; import android.app.ActivityManager; import android.app.AppGlobals; import android.app.usage.UsageStatsManager.StandbyBuckets; @@ -171,6 +172,8 @@ public class AppStandbyController { static final int MSG_REPORT_CONTENT_PROVIDER_USAGE = 8; static final int MSG_PAROLE_STATE_CHANGED = 9; static final int MSG_ONE_TIME_CHECK_IDLE_STATES = 10; + /** Check the state of one app: arg1 = userId, arg2 = uid, obj = (String) packageName */ + static final int MSG_CHECK_PACKAGE_IDLE_STATE = 11; long mCheckIdleIntervalMillis; long mAppIdleParoleIntervalMillis; @@ -322,7 +325,7 @@ public class AppStandbyController { // Get sync adapters for the authority String[] packages = ContentResolver.getSyncAdapterPackagesForAuthorityAsUser( authority, userId); - final long elapsedRealtime = SystemClock.elapsedRealtime(); + final long elapsedRealtime = mInjector.elapsedRealtime(); for (String packageName: packages) { // Only force the sync adapters to active if the provider is not in the same package and // the sync adapter is a system package. @@ -460,53 +463,8 @@ public class AppStandbyController { for (int p = 0; p < packageCount; p++) { final PackageInfo pi = packages.get(p); final String packageName = pi.packageName; - final boolean isSpecial = isAppSpecial(packageName, - UserHandle.getAppId(pi.applicationInfo.uid), - userId); - if (DEBUG) { - Slog.d(TAG, " Checking idle state for " + packageName + " special=" + - isSpecial); - } - if (isSpecial) { - synchronized (mAppIdleLock) { - mAppIdleHistory.setAppStandbyBucket(packageName, userId, elapsedRealtime, - STANDBY_BUCKET_EXEMPTED, REASON_DEFAULT); - } - maybeInformListeners(packageName, userId, elapsedRealtime, - STANDBY_BUCKET_EXEMPTED, false); - } else { - synchronized (mAppIdleLock) { - AppIdleHistory.AppUsageHistory app = - mAppIdleHistory.getAppUsageHistory(packageName, - userId, elapsedRealtime); - // If the bucket was forced by the developer or the app is within the - // temporary active period, leave it alone. - if (REASON_FORCED.equals(app.bucketingReason) - || !hasBucketTimeoutPassed(app, elapsedRealtime)) { - continue; - } - boolean predictionLate = false; - // If the bucket was moved up due to usage, let the timeouts apply. - if (REASON_DEFAULT.equals(app.bucketingReason) - || REASON_USAGE.equals(app.bucketingReason) - || REASON_TIMEOUT.equals(app.bucketingReason) - || (predictionLate = predictionTimedOut(app, elapsedRealtime))) { - int oldBucket = app.currentBucket; - int newBucket = getBucketForLocked(packageName, userId, - elapsedRealtime); - if (DEBUG) { - Slog.d(TAG, " Old bucket=" + oldBucket - + ", newBucket=" + newBucket); - } - if (oldBucket < newBucket || predictionLate) { - mAppIdleHistory.setAppStandbyBucket(packageName, userId, - elapsedRealtime, newBucket, REASON_TIMEOUT); - maybeInformListeners(packageName, userId, elapsedRealtime, - newBucket, false); - } - } - } - } + checkAndUpdateStandbyState(packageName, userId, pi.applicationInfo.uid, + elapsedRealtime); } } if (DEBUG) { @@ -516,6 +474,90 @@ public class AppStandbyController { return true; } + /** Check if we need to update the standby state of a specific app. */ + private void checkAndUpdateStandbyState(String packageName, @UserIdInt int userId, + int uid, long elapsedRealtime) { + if (uid <= 0) { + try { + uid = mPackageManager.getPackageUidAsUser(packageName, userId); + } catch (PackageManager.NameNotFoundException e) { + // Not a valid package for this user, nothing to do + // TODO: Remove any history of removed packages + return; + } + } + final boolean isSpecial = isAppSpecial(packageName, + UserHandle.getAppId(uid), + userId); + if (DEBUG) { + Slog.d(TAG, " Checking idle state for " + packageName + " special=" + + isSpecial); + } + if (isSpecial) { + synchronized (mAppIdleLock) { + mAppIdleHistory.setAppStandbyBucket(packageName, userId, elapsedRealtime, + STANDBY_BUCKET_EXEMPTED, REASON_DEFAULT); + } + maybeInformListeners(packageName, userId, elapsedRealtime, + STANDBY_BUCKET_EXEMPTED, false); + } else { + synchronized (mAppIdleLock) { + final AppIdleHistory.AppUsageHistory app = + mAppIdleHistory.getAppUsageHistory(packageName, + userId, elapsedRealtime); + String reason = app.bucketingReason; + + // If the bucket was forced by the user/developer, leave it alone. + // A usage event will be the only way to bring it out of this forced state + if (REASON_FORCED.equals(app.bucketingReason)) { + return; + } + final int oldBucket = app.currentBucket; + int newBucket = Math.max(oldBucket, STANDBY_BUCKET_ACTIVE); // Undo EXEMPTED + boolean predictionLate = false; + // Compute age-based bucket + if (REASON_DEFAULT.equals(app.bucketingReason) + || REASON_USAGE.equals(app.bucketingReason) + || REASON_TIMEOUT.equals(app.bucketingReason) + || (predictionLate = predictionTimedOut(app, elapsedRealtime))) { + newBucket = getBucketForLocked(packageName, userId, + elapsedRealtime); + if (DEBUG) { + Slog.d(TAG, "Evaluated AOSP newBucket = " + newBucket); + } + reason = REASON_TIMEOUT; + } + // Check if the app is within one of the timeouts for forced bucket elevation + final long elapsedTimeAdjusted = mAppIdleHistory.getElapsedTime(elapsedRealtime); + if (newBucket >= STANDBY_BUCKET_ACTIVE + && app.bucketActiveTimeoutTime > elapsedTimeAdjusted) { + newBucket = STANDBY_BUCKET_ACTIVE; + reason = REASON_USAGE; + if (DEBUG) { + Slog.d(TAG, " Keeping at ACTIVE due to min timeout"); + } + } else if (newBucket >= STANDBY_BUCKET_WORKING_SET + && app.bucketWorkingSetTimeoutTime > elapsedTimeAdjusted) { + newBucket = STANDBY_BUCKET_WORKING_SET; + reason = REASON_USAGE; + if (DEBUG) { + Slog.d(TAG, " Keeping at WORKING_SET due to min timeout"); + } + } + if (DEBUG) { + Slog.d(TAG, " Old bucket=" + oldBucket + + ", newBucket=" + newBucket); + } + if (oldBucket < newBucket || predictionLate) { + mAppIdleHistory.setAppStandbyBucket(packageName, userId, + elapsedRealtime, newBucket, reason); + maybeInformListeners(packageName, userId, elapsedRealtime, + newBucket, false); + } + } + } + } + private boolean predictionTimedOut(AppIdleHistory.AppUsageHistory app, long elapsedRealtime) { return app.bucketingReason != null && app.bucketingReason.startsWith(REASON_PREDICTED) @@ -526,7 +568,9 @@ public class AppStandbyController { private boolean hasBucketTimeoutPassed(AppIdleHistory.AppUsageHistory app, long elapsedRealtime) { - return app.bucketTimeoutTime < mAppIdleHistory.getElapsedTime(elapsedRealtime); + final long elapsedTimeAdjusted = mAppIdleHistory.getElapsedTime(elapsedRealtime); + return app.bucketActiveTimeoutTime < elapsedTimeAdjusted + && app.bucketWorkingSetTimeoutTime < elapsedTimeAdjusted; } private void maybeInformListeners(String packageName, int userId, @@ -631,16 +675,22 @@ public class AppStandbyController { event.mPackage, userId, elapsedRealtime); final int prevBucket = appHistory.currentBucket; final String prevBucketReason = appHistory.bucketingReason; + final long nextCheckTime; if (event.mEventType == UsageEvents.Event.NOTIFICATION_SEEN) { + // Mild usage elevates to WORKING_SET but doesn't change usage time. mAppIdleHistory.reportUsage(appHistory, event.mPackage, STANDBY_BUCKET_WORKING_SET, - elapsedRealtime, elapsedRealtime + mNotificationSeenTimeoutMillis); + 0, elapsedRealtime + mNotificationSeenTimeoutMillis); + nextCheckTime = mNotificationSeenTimeoutMillis; } else { - mAppIdleHistory.reportUsage(event.mPackage, userId, + mAppIdleHistory.reportUsage(appHistory, event.mPackage, STANDBY_BUCKET_ACTIVE, elapsedRealtime, elapsedRealtime + mStrongUsageTimeoutMillis); + nextCheckTime = mStrongUsageTimeoutMillis; } - + mHandler.sendMessageDelayed(mHandler.obtainMessage + (MSG_CHECK_PACKAGE_IDLE_STATE, userId, -1, event.mPackage), + nextCheckTime); final boolean userStartedInteracting = appHistory.currentBucket == STANDBY_BUCKET_ACTIVE && prevBucket != appHistory.currentBucket && @@ -932,9 +982,24 @@ public class AppStandbyController { // If the bucket is required to stay in a higher state for a specified duration, don't // override unless the duration has passed - if (predicted && app.currentBucket < newBucket - && !hasBucketTimeoutPassed(app, elapsedRealtime)) { - return; + if (predicted) { + // Check if the app is within one of the timeouts for forced bucket elevation + final long elapsedTimeAdjusted = mAppIdleHistory.getElapsedTime(elapsedRealtime); + if (newBucket > STANDBY_BUCKET_ACTIVE + && app.bucketActiveTimeoutTime > elapsedTimeAdjusted) { + newBucket = STANDBY_BUCKET_ACTIVE; + reason = REASON_USAGE; + if (DEBUG) { + Slog.d(TAG, " Keeping at ACTIVE due to min timeout"); + } + } else if (newBucket > STANDBY_BUCKET_WORKING_SET + && app.bucketWorkingSetTimeoutTime > elapsedTimeAdjusted) { + newBucket = STANDBY_BUCKET_WORKING_SET; + reason = REASON_USAGE; + if (DEBUG) { + Slog.d(TAG, " Keeping at WORKING_SET due to min timeout"); + } + } } mAppIdleHistory.setAppStandbyBucket(packageName, userId, elapsedRealtime, newBucket, @@ -1347,6 +1412,10 @@ public class AppStandbyController { + ", Charging state:" + mCharging); informParoleStateChanged(); break; + case MSG_CHECK_PACKAGE_IDLE_STATE: + checkAndUpdateStandbyState((String) msg.obj, msg.arg1, msg.arg2, + mInjector.elapsedRealtime()); + break; default: super.handleMessage(msg); break; diff --git a/services/usb/OWNERS b/services/usb/OWNERS new file mode 100644 index 000000000000..7897a0c8555c --- /dev/null +++ b/services/usb/OWNERS @@ -0,0 +1,4 @@ +badhri@google.com +elaurent@google.com +moltmann@google.com +zhangjerry@google.com diff --git a/services/usb/java/com/android/server/usb/UsbAlsaDevice.java b/services/usb/java/com/android/server/usb/UsbAlsaDevice.java index 7480e5662d7a..9d4db003a297 100644 --- a/services/usb/java/com/android/server/usb/UsbAlsaDevice.java +++ b/services/usb/java/com/android/server/usb/UsbAlsaDevice.java @@ -17,9 +17,14 @@ package com.android.server.usb; import android.annotation.NonNull; +import android.media.AudioSystem; +import android.media.IAudioService; +import android.os.RemoteException; import android.service.usb.UsbAlsaDeviceProto; +import android.util.Slog; import com.android.internal.util.dump.DualDumpOutputStream; +import com.android.server.audio.AudioService; /** * Represents the ALSA specification, and attributes of an ALSA device. @@ -30,25 +35,31 @@ public final class UsbAlsaDevice { private final int mCardNum; private final int mDeviceNum; - private final boolean mHasPlayback; - private final boolean mHasCapture; + private final String mDeviceAddress; + private final boolean mHasOutput; + private final boolean mHasInput; private final boolean mIsInputHeadset; private final boolean mIsOutputHeadset; - private final String mDeviceAddress; + private boolean mSelected = false; + private int mOutputState; + private int mInputState; + private UsbAlsaJackDetector mJackDetector; + private IAudioService mAudioService; private String mDeviceName = ""; private String mDeviceDescription = ""; - public UsbAlsaDevice(int card, int device, String deviceAddress, - boolean hasPlayback, boolean hasCapture, + public UsbAlsaDevice(IAudioService audioService, int card, int device, String deviceAddress, + boolean hasOutput, boolean hasInput, boolean isInputHeadset, boolean isOutputHeadset) { + mAudioService = audioService; mCardNum = card; mDeviceNum = device; mDeviceAddress = deviceAddress; - mHasPlayback = hasPlayback; - mHasCapture = hasCapture; + mHasOutput = hasOutput; + mHasInput = hasInput; mIsInputHeadset = isInputHeadset; mIsOutputHeadset = isOutputHeadset; } @@ -75,71 +86,187 @@ public final class UsbAlsaDevice { } /** - * @returns true if the device supports playback. + * @returns the ALSA card/device address string. */ - public boolean hasPlayback() { - return mHasPlayback; + public String getAlsaCardDeviceString() { + if (mCardNum < 0 || mDeviceNum < 0) { + Slog.e(TAG, "Invalid alsa card or device alsaCard: " + mCardNum + + " alsaDevice: " + mDeviceNum); + return null; + } + return AudioService.makeAlsaAddressString(mCardNum, mDeviceNum); } /** - * @returns true if the device supports capture (recording). + * @returns true if the device supports output. */ - public boolean hasCapture() { - return mHasCapture; + public boolean hasOutput() { + return mHasOutput; } /** - * @returns true if the device is a headset for purposes of capture. + * @returns true if the device supports input (recording). + */ + public boolean hasInput() { + return mHasInput; + } + + /** + * @returns true if the device is a headset for purposes of input. */ public boolean isInputHeadset() { return mIsInputHeadset; } /** - * @returns true if the device is a headset for purposes of playback. + * @returns true if the device is a headset for purposes of output. */ public boolean isOutputHeadset() { return mIsOutputHeadset; } /** + * @returns true if input jack is detected or jack detection is not supported. + */ + private synchronized boolean isInputJackConnected() { + if (mJackDetector == null) { + return true; // If jack detect isn't supported, say it's connected. + } + return mJackDetector.isInputJackConnected(); + } + + /** + * @returns true if input jack is detected or jack detection is not supported. + */ + private synchronized boolean isOutputJackConnected() { + if (mJackDetector == null) { + return true; // if jack detect isn't supported, say it's connected. + } + return mJackDetector.isOutputJackConnected(); + } + + /** Begins a jack-detection thread. */ + private synchronized void startJackDetect() { + // If no jack detect capabilities exist, mJackDetector will be null. + mJackDetector = UsbAlsaJackDetector.startJackDetect(this); + } + + /** Stops a jack-detection thread. */ + private synchronized void stopJackDetect() { + if (mJackDetector != null) { + mJackDetector.pleaseStop(); + } + mJackDetector = null; + } + + /** Start using this device as the selected USB Audio Device. */ + public synchronized void start() { + mSelected = true; + mInputState = 0; + mOutputState = 0; + startJackDetect(); + updateWiredDeviceConnectionState(true); + } + + /** Stop using this device as the selected USB Audio Device. */ + public synchronized void stop() { + stopJackDetect(); + updateWiredDeviceConnectionState(false); + mSelected = false; + } + + /** Updates AudioService with the connection state of the alsaDevice. + * Checks ALSA Jack state for inputs and outputs before reporting. + */ + public synchronized void updateWiredDeviceConnectionState(boolean enable) { + if (!mSelected) { + Slog.e(TAG, "updateWiredDeviceConnectionState on unselected AlsaDevice!"); + return; + } + String alsaCardDeviceString = getAlsaCardDeviceString(); + if (alsaCardDeviceString == null) { + return; + } + try { + // Output Device + if (mHasOutput) { + int device = mIsOutputHeadset + ? AudioSystem.DEVICE_OUT_USB_HEADSET + : AudioSystem.DEVICE_OUT_USB_DEVICE; + if (DEBUG) { + Slog.d(TAG, "pre-call device:0x" + Integer.toHexString(device) + + " addr:" + alsaCardDeviceString + + " name:" + mDeviceName); + } + boolean connected = isOutputJackConnected(); + Slog.i(TAG, "OUTPUT JACK connected: " + connected); + int outputState = (enable && connected) ? 1 : 0; + if (outputState != mOutputState) { + mOutputState = outputState; + mAudioService.setWiredDeviceConnectionState(device, outputState, + alsaCardDeviceString, + mDeviceName, TAG); + } + } + + // Input Device + if (mHasInput) { + int device = mIsInputHeadset ? AudioSystem.DEVICE_IN_USB_HEADSET + : AudioSystem.DEVICE_IN_USB_DEVICE; + boolean connected = isInputJackConnected(); + Slog.i(TAG, "INPUT JACK connected: " + connected); + int inputState = (enable && connected) ? 1 : 0; + if (inputState != mInputState) { + mInputState = inputState; + mAudioService.setWiredDeviceConnectionState( + device, inputState, alsaCardDeviceString, + mDeviceName, TAG); + } + } + } catch (RemoteException e) { + Slog.e(TAG, "RemoteException in setWiredDeviceConnectionState"); + } + } + + + /** * @Override * @returns a string representation of the object. */ - public String toString() { + public synchronized String toString() { return "UsbAlsaDevice: [card: " + mCardNum + ", device: " + mDeviceNum + ", name: " + mDeviceName - + ", hasPlayback: " + mHasPlayback - + ", hasCapture: " + mHasCapture + "]"; + + ", hasOutput: " + mHasOutput + + ", hasInput: " + mHasInput + "]"; } /** * Write a description of the device to a dump stream. */ - public void dump(@NonNull DualDumpOutputStream dump, String idName, long id) { + public synchronized void dump(@NonNull DualDumpOutputStream dump, String idName, long id) { long token = dump.start(idName, id); dump.write("card", UsbAlsaDeviceProto.CARD, mCardNum); dump.write("device", UsbAlsaDeviceProto.DEVICE, mDeviceNum); dump.write("name", UsbAlsaDeviceProto.NAME, mDeviceName); - dump.write("has_playback", UsbAlsaDeviceProto.HAS_PLAYBACK, mHasPlayback); - dump.write("has_capture", UsbAlsaDeviceProto.HAS_CAPTURE, mHasCapture); + dump.write("has_output", UsbAlsaDeviceProto.HAS_PLAYBACK, mHasOutput); + dump.write("has_input", UsbAlsaDeviceProto.HAS_CAPTURE, mHasInput); dump.write("address", UsbAlsaDeviceProto.ADDRESS, mDeviceAddress); dump.end(token); } // called by logDevices - String toShortString() { + synchronized String toShortString() { return "[card:" + mCardNum + " device:" + mDeviceNum + " " + mDeviceName + "]"; } - String getDeviceName() { + synchronized String getDeviceName() { return mDeviceName; } - void setDeviceNameAndDescription(String deviceName, String deviceDescription) { + synchronized void setDeviceNameAndDescription(String deviceName, String deviceDescription) { mDeviceName = deviceName; mDeviceDescription = deviceDescription; } @@ -155,8 +282,8 @@ public final class UsbAlsaDevice { UsbAlsaDevice other = (UsbAlsaDevice) obj; return (mCardNum == other.mCardNum && mDeviceNum == other.mDeviceNum - && mHasPlayback == other.mHasPlayback - && mHasCapture == other.mHasCapture + && mHasOutput == other.mHasOutput + && mHasInput == other.mHasInput && mIsInputHeadset == other.mIsInputHeadset && mIsOutputHeadset == other.mIsOutputHeadset); } @@ -170,8 +297,8 @@ public final class UsbAlsaDevice { int result = 1; result = prime * result + mCardNum; result = prime * result + mDeviceNum; - result = prime * result + (mHasPlayback ? 0 : 1); - result = prime * result + (mHasCapture ? 0 : 1); + result = prime * result + (mHasOutput ? 0 : 1); + result = prime * result + (mHasInput ? 0 : 1); result = prime * result + (mIsInputHeadset ? 0 : 1); result = prime * result + (mIsOutputHeadset ? 0 : 1); diff --git a/services/usb/java/com/android/server/usb/UsbAlsaJackDetector.java b/services/usb/java/com/android/server/usb/UsbAlsaJackDetector.java new file mode 100644 index 000000000000..c4988478df71 --- /dev/null +++ b/services/usb/java/com/android/server/usb/UsbAlsaJackDetector.java @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.usb; + +/** + * Detects and reports ALSA jack state and events. + */ +public final class UsbAlsaJackDetector implements Runnable { + private static final String TAG = "UsbAlsaJackDetector"; + + private static native boolean nativeHasJackDetect(int card); + private native boolean nativeJackDetect(int card); + private native boolean nativeOutputJackConnected(int card); + private native boolean nativeInputJackConnected(int card); + + private boolean mStopJackDetect = false; + private UsbAlsaDevice mAlsaDevice; + + /* use startJackDetect to create a UsbAlsaJackDetector */ + private UsbAlsaJackDetector(UsbAlsaDevice device) { + mAlsaDevice = device; + } + + /** If jack detection is detected on the given Alsa Device, + * create and return a UsbAlsaJackDetector which will update wired device state + * each time a jack detection event is registered. + * + * @returns UsbAlsaJackDetector if jack detect is supported, or null. + */ + public static UsbAlsaJackDetector startJackDetect(UsbAlsaDevice device) { + if (!nativeHasJackDetect(device.getCardNum())) { + return null; + } + UsbAlsaJackDetector jackDetector = new UsbAlsaJackDetector(device); + + // This thread will exit once the USB device disappears. + // It can also be convinced to stop with pleaseStop(). + new Thread(jackDetector, "USB jack detect thread").start(); + return jackDetector; + } + + public boolean isInputJackConnected() { + return nativeInputJackConnected(mAlsaDevice.getCardNum()); + } + + public boolean isOutputJackConnected() { + return nativeOutputJackConnected(mAlsaDevice.getCardNum()); + } + + /** + * Stop the jack detect thread from calling back into UsbAlsaDevice. + * This doesn't force the thread to stop (which is deprecated in java and dangerous due to + * locking issues), but will cause the thread to exit at the next safe opportunity. + */ + public void pleaseStop() { + synchronized (this) { + mStopJackDetect = true; + } + } + + /** + * Called by nativeJackDetect each time a jack detect event is reported. + * @return false when the jackDetect thread should stop. true otherwise. + */ + public boolean jackDetectCallback() { + synchronized (this) { + if (mStopJackDetect) { + return false; + } + mAlsaDevice.updateWiredDeviceConnectionState(true); + } + return true; + } + + /** + * This will call jackDetectCallback each time it detects a jack detect event. + * If jackDetectCallback returns false, this function will return. + */ + public void run() { + nativeJackDetect(mAlsaDevice.getCardNum()); + } +} + diff --git a/services/usb/java/com/android/server/usb/UsbAlsaManager.java b/services/usb/java/com/android/server/usb/UsbAlsaManager.java index 0c5f8f113adc..2f1c5169362a 100644 --- a/services/usb/java/com/android/server/usb/UsbAlsaManager.java +++ b/services/usb/java/com/android/server/usb/UsbAlsaManager.java @@ -20,11 +20,9 @@ import android.content.Context; import android.content.pm.PackageManager; import android.content.res.Resources; import android.hardware.usb.UsbDevice; -import android.media.AudioSystem; import android.media.IAudioService; import android.media.midi.MidiDeviceInfo; import android.os.Bundle; -import android.os.RemoteException; import android.os.ServiceManager; import android.provider.Settings; import android.service.usb.UsbAlsaManagerProto; @@ -32,7 +30,6 @@ import android.util.Slog; import com.android.internal.alsa.AlsaCardsParser; import com.android.internal.util.dump.DualDumpOutputStream; -import com.android.server.audio.AudioService; import com.android.server.usb.descriptors.UsbDescriptorParser; import libcore.io.IoUtils; @@ -58,6 +55,7 @@ public final class UsbAlsaManager { // this is needed to map USB devices to ALSA Audio Devices, especially to remove an // ALSA device when we are notified that its associated USB device has been removed. private final ArrayList<UsbAlsaDevice> mAlsaDevices = new ArrayList<UsbAlsaDevice>(); + private UsbAlsaDevice mSelectedDevice; /** * List of connected MIDI devices @@ -78,17 +76,19 @@ public final class UsbAlsaManager { ServiceManager.getService(Context.AUDIO_SERVICE)); } - // Notifies AudioService when a device is added or removed - // audioDevice - the AudioDevice that was added or removed - // enabled - if true, we're connecting a device (it's arrived), else disconnecting - private void notifyDeviceState(UsbAlsaDevice alsaDevice, boolean enabled) { + /** + * Select the AlsaDevice to be used for AudioService. + * AlsaDevice.start() notifies AudioService of it's connected state. + * + * @param alsaDevice The selected UsbAlsaDevice for system USB audio. + */ + private synchronized void selectAlsaDevice(UsbAlsaDevice alsaDevice) { if (DEBUG) { - Slog.d(TAG, "notifyDeviceState " + enabled + " " + alsaDevice); + Slog.d(TAG, "selectAlsaDevice " + alsaDevice); } - if (mAudioService == null) { - Slog.e(TAG, "no AudioService"); - return; + if (mSelectedDevice != null) { + deselectAlsaDevice(); } // FIXME Does not yet handle the case where the setting is changed @@ -102,40 +102,14 @@ public final class UsbAlsaManager { return; } - int state = (enabled ? 1 : 0); - int cardNum = alsaDevice.getCardNum(); - int deviceNum = alsaDevice.getDeviceNum(); - if (cardNum < 0 || deviceNum < 0) { - Slog.e(TAG, "Invalid alsa card or device alsaCard: " + cardNum - + " alsaDevice: " + deviceNum); - return; - } - - String address = AudioService.makeAlsaAddressString(cardNum, deviceNum); - try { - // Playback Device - if (alsaDevice.hasPlayback()) { - int device = alsaDevice.isOutputHeadset() - ? AudioSystem.DEVICE_OUT_USB_HEADSET - : AudioSystem.DEVICE_OUT_USB_DEVICE; - if (DEBUG) { - Slog.i(TAG, "pre-call device:0x" + Integer.toHexString(device) + - " addr:" + address + " name:" + alsaDevice.getDeviceName()); - } - mAudioService.setWiredDeviceConnectionState( - device, state, address, alsaDevice.getDeviceName(), TAG); - } + mSelectedDevice = alsaDevice; + alsaDevice.start(); + } - // Capture Device - if (alsaDevice.hasCapture()) { - int device = alsaDevice.isInputHeadset() - ? AudioSystem.DEVICE_IN_USB_HEADSET - : AudioSystem.DEVICE_IN_USB_DEVICE; - mAudioService.setWiredDeviceConnectionState( - device, state, address, alsaDevice.getDeviceName(), TAG); - } - } catch (RemoteException e) { - Slog.e(TAG, "RemoteException in setWiredDeviceConnectionState"); + private synchronized void deselectAlsaDevice() { + if (mSelectedDevice != null) { + mSelectedDevice.stop(); + mSelectedDevice = null; } } @@ -168,7 +142,7 @@ public final class UsbAlsaManager { Slog.d(TAG, " alsaDevice:" + alsaDevice); } if (alsaDevice != null) { - notifyDeviceState(alsaDevice, true /*enabled*/); + selectAlsaDevice(alsaDevice); } return alsaDevice; } else { @@ -202,16 +176,21 @@ public final class UsbAlsaManager { if (hasInput || hasOutput) { boolean isInputHeadset = parser.isInputHeadset(); boolean isOutputHeadset = parser.isOutputHeadset(); - UsbAlsaDevice alsaDevice = - new UsbAlsaDevice(cardRec.getCardNum(), 0 /*device*/, deviceAddress, - hasOutput, hasInput, isInputHeadset, isOutputHeadset); - alsaDevice.setDeviceNameAndDescription( - cardRec.getCardName(), cardRec.getCardDescription()); - mAlsaDevices.add(0, alsaDevice); - // Select it + if (mAudioService == null) { + Slog.e(TAG, "no AudioService"); + return; + } + + UsbAlsaDevice alsaDevice = + new UsbAlsaDevice(mAudioService, cardRec.getCardNum(), 0 /*device*/, + deviceAddress, hasOutput, hasInput, + isInputHeadset, isOutputHeadset); if (alsaDevice != null) { - notifyDeviceState(alsaDevice, true /*enabled*/); + alsaDevice.setDeviceNameAndDescription( + cardRec.getCardName(), cardRec.getCardDescription()); + mAlsaDevices.add(0, alsaDevice); + selectAlsaDevice(alsaDevice); } } @@ -256,7 +235,7 @@ public final class UsbAlsaManager { } } - /* package */ void usbDeviceRemoved(String deviceAddress/*UsbDevice usbDevice*/) { + /* package */ synchronized void usbDeviceRemoved(String deviceAddress/*UsbDevice usbDevice*/) { if (DEBUG) { Slog.d(TAG, "deviceRemoved(" + deviceAddress + ")"); } @@ -264,13 +243,9 @@ public final class UsbAlsaManager { // Audio UsbAlsaDevice alsaDevice = removeAlsaDeviceFromList(deviceAddress); Slog.i(TAG, "USB Audio Device Removed: " + alsaDevice); - if (alsaDevice != null) { - if (alsaDevice.hasPlayback() || alsaDevice.hasCapture()) { - notifyDeviceState(alsaDevice, false /*enabled*/); - - // if there any external devices left, select one of them - selectDefaultDevice(); - } + if (alsaDevice != null && alsaDevice == mSelectedDevice) { + deselectAlsaDevice(); + selectDefaultDevice(); // if there any external devices left, select one of them } // MIDI diff --git a/telecomm/OWNERS b/telecomm/OWNERS new file mode 100644 index 000000000000..a3bcfb2cbcfa --- /dev/null +++ b/telecomm/OWNERS @@ -0,0 +1,6 @@ +set noparent + +tgunn@google.com +breadley@google.com +hallliu@google.com +rgreenwalt@google.com diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java index 8c18518a6d67..0c92c2000f92 100644 --- a/telecomm/java/android/telecom/Call.java +++ b/telecomm/java/android/telecom/Call.java @@ -425,8 +425,14 @@ public final class Call { */ public static final int PROPERTY_ASSISTED_DIALING_USED = 0x00000200; + /** + * Indicates that the call is an RTT call. Use {@link #getRttCall()} to get the + * {@link RttCall} object that is used to send and receive text. + */ + public static final int PROPERTY_RTT = 0x00000400; + //****************************************************************************************** - // Next PROPERTY value: 0x00000400 + // Next PROPERTY value: 0x00000800 //****************************************************************************************** private final String mTelecomCallId; @@ -1189,6 +1195,23 @@ public final class Call { return null; } } + + /** + * Closes the underlying file descriptors + * @hide + */ + public void close() { + try { + mReceiveStream.close(); + } catch (IOException e) { + // ignore + } + try { + mTransmitStream.close(); + } catch (IOException e) { + // ignore + } + } } /** @@ -1664,7 +1687,7 @@ public final class Call { * @return true if there is a connection, false otherwise. */ public boolean isRttActive() { - return mRttCall != null; + return mRttCall != null && mDetails.hasProperty(Details.PROPERTY_RTT); } /** @@ -1867,7 +1890,8 @@ public final class Call { boolean isRttChanged = false; boolean rttModeChanged = false; - if (parcelableCall.getParcelableRttCall() != null && parcelableCall.getIsRttCallChanged()) { + if (parcelableCall.getIsRttCallChanged() + && mDetails.hasProperty(Details.PROPERTY_RTT)) { ParcelableRttCall parcelableRttCall = parcelableCall.getParcelableRttCall(); InputStreamReader receiveStream = new InputStreamReader( new ParcelFileDescriptor.AutoCloseInputStream( diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java index 322970544281..26a2f1cb8c4f 100644 --- a/telecomm/java/android/telecom/Connection.java +++ b/telecomm/java/android/telecom/Connection.java @@ -41,6 +41,8 @@ import android.os.SystemClock; import android.util.ArraySet; import android.view.Surface; +import java.io.FileInputStream; +import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; @@ -860,18 +862,19 @@ public abstract class Connection extends Conferenceable { mFdFromInCall = fromInCall; mFdToInCall = toInCall; mPipeFromInCall = new InputStreamReader( - new ParcelFileDescriptor.AutoCloseInputStream(fromInCall)); + new FileInputStream(fromInCall.getFileDescriptor())); mPipeToInCall = new OutputStreamWriter( - new ParcelFileDescriptor.AutoCloseOutputStream(toInCall)); + new FileOutputStream(toInCall.getFileDescriptor())); } /** * Writes the string {@param input} into the text stream to the UI for this RTT call. Since * RTT transmits text in real-time, this method should be called as often as text snippets * are received from the remote user, even if it is only one character. - * + * <p> * This method is not thread-safe -- calling it from multiple threads simultaneously may * lead to interleaved text. + * * @param input The message to send to the in-call app. */ public void write(String input) throws IOException { @@ -884,9 +887,10 @@ public abstract class Connection extends Conferenceable { * Reads a string from the in-call app, blocking if there is no data available. Returns * {@code null} if the RTT conversation has been terminated and there is no further data * to read. - * + * <p> * This method is not thread-safe -- calling it from multiple threads simultaneously may * lead to interleaved text. + * * @return A string containing text entered by the user, or {@code null} if the * conversation has been terminated or if there was an error while reading. */ @@ -901,6 +905,7 @@ public abstract class Connection extends Conferenceable { /** * Non-blocking version of {@link #read()}. Returns {@code null} if there is nothing to * be read. + * * @return A string containing text entered by the user, or {@code null} if the user has * not entered any new text yet. */ @@ -2635,7 +2640,6 @@ public abstract class Connection extends Conferenceable { * {@link #onStartRtt(RttTextStream)} has succeeded. */ public final void sendRttInitiationSuccess() { - setRttProperty(); mListeners.forEach((l) -> l.onRttInitiationSuccess(Connection.this)); } @@ -2647,7 +2651,6 @@ public abstract class Connection extends Conferenceable { * exception of {@link RttModifyStatus#SESSION_MODIFY_REQUEST_SUCCESS}. */ public final void sendRttInitiationFailure(int reason) { - unsetRttProperty(); mListeners.forEach((l) -> l.onRttInitiationFailure(Connection.this, reason)); } @@ -2656,7 +2659,6 @@ public abstract class Connection extends Conferenceable { * side of the coll. */ public final void sendRttSessionRemotelyTerminated() { - unsetRttProperty(); mListeners.forEach((l) -> l.onRttSessionRemotelyTerminated(Connection.this)); } @@ -2956,22 +2958,6 @@ public abstract class Connection extends Conferenceable { */ public void handleRttUpgradeResponse(@Nullable RttTextStream rttTextStream) {} - /** - * Internal method to set {@link #PROPERTY_IS_RTT}. - * @hide - */ - void setRttProperty() { - setConnectionProperties(getConnectionProperties() | PROPERTY_IS_RTT); - } - - /** - * Internal method to un-set {@link #PROPERTY_IS_RTT}. - * @hide - */ - void unsetRttProperty() { - setConnectionProperties(getConnectionProperties() & (~PROPERTY_IS_RTT)); - } - static String toLogSafePhoneNumber(String number) { // For unknown number, log empty string. if (number == null) { diff --git a/telecomm/java/android/telecom/ConnectionRequest.java b/telecomm/java/android/telecom/ConnectionRequest.java index 658b4734b0b5..b6e6b0ed8270 100644 --- a/telecomm/java/android/telecom/ConnectionRequest.java +++ b/telecomm/java/android/telecom/ConnectionRequest.java @@ -143,6 +143,8 @@ public final class ConnectionRequest implements Parcelable { private final boolean mShouldShowIncomingCallUi; private final ParcelFileDescriptor mRttPipeToInCall; private final ParcelFileDescriptor mRttPipeFromInCall; + // Cached return value of getRttTextStream -- we don't want to wrap it more than once. + private Connection.RttTextStream mRttTextStream; /** * @param accountHandle The accountHandle which should be used to place the call. @@ -312,7 +314,10 @@ public final class ConnectionRequest implements Parcelable { */ public Connection.RttTextStream getRttTextStream() { if (isRequestingRtt()) { - return new Connection.RttTextStream(mRttPipeToInCall, mRttPipeFromInCall); + if (mRttTextStream == null) { + mRttTextStream = new Connection.RttTextStream(mRttPipeToInCall, mRttPipeFromInCall); + } + return mRttTextStream; } else { return null; } diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java index 1547857f23e3..ffa0c946d006 100644 --- a/telecomm/java/android/telecom/ConnectionService.java +++ b/telecomm/java/android/telecom/ConnectionService.java @@ -143,6 +143,7 @@ public abstract class ConnectionService extends Service { private static final String SESSION_HANDOVER_COMPLETE = "CS.hC"; private static final String SESSION_EXTRAS_CHANGED = "CS.oEC"; private static final String SESSION_START_RTT = "CS.+RTT"; + private static final String SESSION_UPDATE_RTT_PIPES = "CS.uRTT"; private static final String SESSION_STOP_RTT = "CS.-RTT"; private static final String SESSION_RTT_UPGRADE_RESPONSE = "CS.rTRUR"; private static final String SESSION_CONNECTION_SERVICE_FOCUS_LOST = "CS.cSFL"; @@ -1864,7 +1865,6 @@ public abstract class ConnectionService extends Service { Log.d(this, "stopRtt(%s)", callId); if (mConnectionById.containsKey(callId)) { findConnectionForAction(callId, "stopRtt").onStopRtt(); - findConnectionForAction(callId, "stopRtt").unsetRttProperty(); } else if (mConferenceById.containsKey(callId)) { Log.w(this, "stopRtt called on a conference."); } diff --git a/telephony/java/android/telephony/OWNERS b/telephony/OWNERS index 68dedce89272..6f67bc25f879 100644 --- a/telephony/java/android/telephony/OWNERS +++ b/telephony/OWNERS @@ -1,14 +1,14 @@ set noparent -amitmahajan@google.com +tgunn@google.com breadley@google.com -fionaxu@google.com -jackyu@google.com hallliu@google.com rgreenwalt@google.com -tgunn@google.com -jminjie@google.com mpq@google.com +amitmahajan@google.com +fionaxu@google.com +jackyu@google.com +jminjie@google.com +satk@google.com shuoq@google.com refuhoo@google.com - diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java index cb867abb74d4..ec348dfcc047 100644 --- a/telephony/java/android/telephony/ServiceState.java +++ b/telephony/java/android/telephony/ServiceState.java @@ -1526,7 +1526,9 @@ public class ServiceState implements Parcelable { */ @SystemApi public List<NetworkRegistrationState> getNetworkRegistrationStates() { - return mNetworkRegistrationStates; + synchronized (mNetworkRegistrationStates) { + return new ArrayList<>(mNetworkRegistrationStates); + } } /** @@ -1539,11 +1541,15 @@ public class ServiceState implements Parcelable { @SystemApi public List<NetworkRegistrationState> getNetworkRegistrationStates(int transportType) { List<NetworkRegistrationState> list = new ArrayList<>(); - for (NetworkRegistrationState networkRegistrationState : mNetworkRegistrationStates) { - if (networkRegistrationState.getTransportType() == transportType) { - list.add(networkRegistrationState); + + synchronized (mNetworkRegistrationStates) { + for (NetworkRegistrationState networkRegistrationState : mNetworkRegistrationStates) { + if (networkRegistrationState.getTransportType() == transportType) { + list.add(networkRegistrationState); + } } } + return list; } @@ -1557,12 +1563,36 @@ public class ServiceState implements Parcelable { */ @SystemApi public NetworkRegistrationState getNetworkRegistrationStates(int transportType, int domain) { - for (NetworkRegistrationState networkRegistrationState : mNetworkRegistrationStates) { - if (networkRegistrationState.getTransportType() == transportType - && networkRegistrationState.getDomain() == domain) { - return networkRegistrationState; + synchronized (mNetworkRegistrationStates) { + for (NetworkRegistrationState networkRegistrationState : mNetworkRegistrationStates) { + if (networkRegistrationState.getTransportType() == transportType + && networkRegistrationState.getDomain() == domain) { + return networkRegistrationState; + } } } + return null; } + + /** + * @hide + */ + public void addNetworkRegistrationState(NetworkRegistrationState regState) { + if (regState == null) return; + + synchronized (mNetworkRegistrationStates) { + for (int i = 0; i < mNetworkRegistrationStates.size(); i++) { + NetworkRegistrationState curRegState = mNetworkRegistrationStates.get(i); + if (curRegState.getTransportType() == regState.getTransportType() + && curRegState.getDomain() == regState.getDomain()) { + mNetworkRegistrationStates.remove(i); + break; + } + } + + mNetworkRegistrationStates.add(regState); + } + } + } diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 7afd28ce181f..fefc03d785c4 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -53,6 +53,7 @@ import android.telephony.ims.aidl.IImsMmTelFeature; 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 @@ public class TelephonyManager { 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().isImsRegistered(subId); + } catch (RemoteException | NullPointerException ex) { + return false; + } + } + + /** + * 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().isImsRegisteredForSubscriber(subId); - } catch (RemoteException ex) { - return false; - } catch (NullPointerException ex) { + return getITelephony().isImsRegistered(getSubId()); + } catch (RemoteException | NullPointerException ex) { return false; } } /** - * Returns the Status of Volte + * 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(); - } catch (RemoteException ex) { - return false; - } catch (NullPointerException ex) { - return false; - } - } + try { + return getITelephony().isVolteAvailable(getSubId()); + } catch (RemoteException | NullPointerException ex) { + return false; + } + } /** - * Returns the Status of video telephony (VT) + * 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(); - } catch (RemoteException ex) { - return false; - } catch (NullPointerException ex) { + return getITelephony().isVideoTelephonyAvailable(getSubId()); + } catch (RemoteException | NullPointerException ex) { return false; } } /** - * Returns the Status of Wi-Fi Calling + * 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 bfdd4533275b..1fdbae9186b7 100644 --- a/telephony/java/android/telephony/ims/feature/ImsFeature.java +++ b/telephony/java/android/telephony/ims/feature/ImsFeature.java @@ -80,7 +80,7 @@ public abstract class ImsFeature { 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 2b4c059cf69f..02cc82cf56b0 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -1123,33 +1123,33 @@ interface ITelephony { 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 77e0e90623fa..1040ed169c5b 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 @@ Overview * 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 063060f166dc..228f9bbddcd1 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 @@ public class AppLaunch extends InstrumentationTestCase { 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 @@ public class AppLaunch extends InstrumentationTestCase { 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 @@ public class AppLaunch extends InstrumentationTestCase { 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 @@ public class AppLaunch extends InstrumentationTestCase { 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 @@ public class AppLaunch extends InstrumentationTestCase { 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 @@ public class AppLaunch extends InstrumentationTestCase { 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 @@ public class AppLaunch extends InstrumentationTestCase { 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 @@ public class AppLaunch extends InstrumentationTestCase { 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 @@ public class AppLaunch extends InstrumentationTestCase { } 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 @@ public class AppLaunch extends InstrumentationTestCase { } 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 @@ public class AppLaunch extends InstrumentationTestCase { */ 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)); } } - } - } 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))); + 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++) { - mLaunchOrderList.add(new LaunchOrder(app, - String.format(TRACE_ITERATION, 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 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 @@ public class AppLaunch extends InstrumentationTestCase { } private void dropCache() { - if (true == mDropCache) { + if (mDropCache) { assertNotNull("Issue in dropping the cache", getInstrumentation().getUiAutomation() .executeShellCommand(DROP_CACHE_SCRIPT)); @@ -394,7 +420,7 @@ public class AppLaunch extends InstrumentationTestCase { 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 @@ public class AppLaunch extends InstrumentationTestCase { 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 @@ public class AppLaunch extends InstrumentationTestCase { } } - 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 @@ public class AppLaunch extends InstrumentationTestCase { 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 @@ public class AppLaunch extends InstrumentationTestCase { mApp = app; } + public String getCompilerFilter() { + return mCompilerFilter; + } + public String getLaunchReason() { return mLaunchReason; } @@ -593,9 +656,31 @@ public class AppLaunch extends InstrumentationTestCase { } } + 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 @@ public class AppLaunch extends InstrumentationTestCase { 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 @@ public class AppLaunch extends InstrumentationTestCase { 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 @@ public class AppLaunch extends InstrumentationTestCase { * @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 @@ public class AppLaunch extends InstrumentationTestCase { 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 @@ public class AppLaunch extends InstrumentationTestCase { 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 @@ public class AppLaunch extends InstrumentationTestCase { } 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 cc792cc749d9..03a617c354fa 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.assertNull; 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 @@ public class ConnectivityManagerTest { // 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 @@ public class ConnectivityManagerTest { // 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 @@ public class ConnectivityManagerTest { 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 f6c5532363e8..f186ee55d2c7 100644 --- a/tests/net/java/android/net/IpSecConfigTest.java +++ b/tests/net/java/android/net/IpSecConfigTest.java @@ -17,6 +17,7 @@ package android.net; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; @@ -48,18 +49,12 @@ public class IpSecConfigTest { assertEquals(IpSecManager.INVALID_RESOURCE_ID, c.getSpiResourceId()); } - @Test - public void testParcelUnparcel() throws Exception { - assertParcelingIsLossless(new IpSecConfig()); - + private IpSecConfig getSampleConfig() { IpSecConfig c = new IpSecConfig(); c.setMode(IpSecTransform.MODE_TUNNEL); c.setSourceAddress("0.0.0.0"); c.setDestinationAddress("1.2.3.4"); - c.setEncapType(android.system.OsConstants.UDP_ENCAP_ESPINUDP); - c.setEncapSocketResourceId(7); - c.setEncapRemotePort(22); - c.setNattKeepaliveInterval(42); + c.setSpiResourceId(1984); c.setEncryption( new IpSecAlgorithm( IpSecAlgorithm.CRYPT_AES_CBC, @@ -68,7 +63,37 @@ public class IpSecConfigTest { new IpSecAlgorithm( IpSecAlgorithm.AUTH_HMAC_MD5, new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 0})); - c.setSpiResourceId(1984); + c.setAuthenticatedEncryption( + new IpSecAlgorithm( + IpSecAlgorithm.AUTH_CRYPT_AES_GCM, + new byte[] { + 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 0, 1, 2, 3, 4 + }, + 128)); + c.setEncapType(android.system.OsConstants.UDP_ENCAP_ESPINUDP); + c.setEncapSocketResourceId(7); + c.setEncapRemotePort(22); + c.setNattKeepaliveInterval(42); + c.setMarkValue(12); + c.setMarkMask(23); + + return c; + } + + @Test + public void testCopyConstructor() { + IpSecConfig original = getSampleConfig(); + IpSecConfig copy = new IpSecConfig(original); + + assertTrue(IpSecConfig.equals(original, copy)); + assertFalse(original == copy); + } + + @Test + public void testParcelUnparcel() throws Exception { + assertParcelingIsLossless(new IpSecConfig()); + + IpSecConfig c = getSampleConfig(); assertParcelingIsLossless(c); } diff --git a/tests/net/java/android/net/IpSecTransformTest.java b/tests/net/java/android/net/IpSecTransformTest.java new file mode 100644 index 000000000000..b4342df58549 --- /dev/null +++ b/tests/net/java/android/net/IpSecTransformTest.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net; + +import static org.junit.Assert.assertFalse; + +import android.support.test.filters.SmallTest; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** Unit tests for {@link IpSecTransform}. */ +@SmallTest +@RunWith(JUnit4.class) +public class IpSecTransformTest { + + @Test + public void testCreateTransformCopiesConfig() { + // Create a config with a few parameters to make sure it's not empty + IpSecConfig config = new IpSecConfig(); + config.setSourceAddress("0.0.0.0"); + config.setDestinationAddress("1.2.3.4"); + config.setSpiResourceId(1984); + + IpSecTransform preModification = new IpSecTransform(null, config); + + config.setSpiResourceId(1985); + IpSecTransform postModification = new IpSecTransform(null, config); + + assertFalse(IpSecTransform.equals(preModification, postModification)); + } + + @Test + public void testCreateTransformsWithSameConfigEqual() { + // Create a config with a few parameters to make sure it's not empty + IpSecConfig config = new IpSecConfig(); + config.setSourceAddress("0.0.0.0"); + config.setDestinationAddress("1.2.3.4"); + config.setSpiResourceId(1984); + + IpSecTransform config1 = new IpSecTransform(null, config); + IpSecTransform config2 = new IpSecTransform(null, config); + + assertFalse(IpSecTransform.equals(config1, config2)); + } +} diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java index e7abede4cda4..24639e9e3f76 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_INTERNET; 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 @@ public class ConnectivityServiceTest { 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 @@ public class ConnectivityServiceTest { assertTrue(mNetworkStatusReceived.block(TIMEOUT_MS)); return mRedirectUrl; } + + public NetworkCapabilities getNetworkCapabilities() { + return mNetworkCapabilities; + } } /** @@ -1273,6 +1283,7 @@ public class ConnectivityServiceTest { NETWORK_CAPABILITIES, LINK_PROPERTIES, SUSPENDED, + RESUMED, LOSING, LOST, UNAVAILABLE @@ -1344,6 +1355,11 @@ public class ConnectivityServiceTest { } @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 @@ public class ConnectivityServiceTest { // 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 @@ public class ConnectivityServiceTest { 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 5831875680ac..f064cb14248f 100644 --- a/tools/aapt2/Debug.cpp +++ b/tools/aapt2/Debug.cpp @@ -414,59 +414,78 @@ class XmlPrinter : public xml::ConstVisitor { public: using xml::ConstVisitor::Visit; - void Visit(const xml::Element* el) override { - const size_t previous_size = prefix_.size(); + XmlPrinter(Printer* printer) : printer_(printer) { + } + void Visit(const xml::Element* el) override { for (const xml::NamespaceDecl& decl : el->namespace_decls) { - std::cerr << prefix_ << "N: " << decl.prefix << "=" << decl.uri - << " (line=" << decl.line_number << ")\n"; - prefix_ += " "; + printer_->Println(StringPrintf("N: %s=%s (line=%zu)", decl.prefix.c_str(), decl.uri.c_str(), + decl.line_number)); + printer_->Indent(); } - std::cerr << prefix_ << "E: "; + printer_->Print("E: "); if (!el->namespace_uri.empty()) { - std::cerr << el->namespace_uri << ":"; + printer_->Print(el->namespace_uri); + printer_->Print(":"); } - std::cerr << el->name << " (line=" << el->line_number << ")\n"; + printer_->Println(StringPrintf("%s (line=%zu)", el->name.c_str(), el->line_number)); + printer_->Indent(); for (const xml::Attribute& attr : el->attributes) { - std::cerr << prefix_ << " A: "; + printer_->Print("A: "); if (!attr.namespace_uri.empty()) { - std::cerr << attr.namespace_uri << ":"; + printer_->Print(attr.namespace_uri); + printer_->Print(":"); } - std::cerr << attr.name; + printer_->Print(attr.name); if (attr.compiled_attribute) { - std::cerr << "(" << attr.compiled_attribute.value().id.value_or_default(ResourceId(0x0)) - << ")"; + printer_->Print("("); + printer_->Print( + attr.compiled_attribute.value().id.value_or_default(ResourceId(0)).to_string()); + printer_->Print(")"); } - std::cerr << "="; + printer_->Print("="); if (attr.compiled_value != nullptr) { - std::cerr << *attr.compiled_value; + attr.compiled_value->PrettyPrint(printer_); } else { - std::cerr << attr.value; + printer_->Print("\""); + 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 6209a04c3c2d..382707e1d4cd 100644 --- a/tools/aapt2/Debug.h +++ b/tools/aapt2/Debug.h @@ -37,7 +37,7 @@ struct Debug { text::Printer* printer); static void PrintStyleGraph(ResourceTable* table, const ResourceName& target_style); static void DumpHex(const void* data, size_t len); - static void DumpXml(const xml::XmlResource& doc); + static void DumpXml(const xml::XmlResource& doc, text::Printer* printer); }; } // namespace aapt diff --git a/tools/aapt2/LoadedApk.cpp b/tools/aapt2/LoadedApk.cpp index 20a9f417228c..ac28227b8778 100644 --- a/tools/aapt2/LoadedApk.cpp +++ b/tools/aapt2/LoadedApk.cpp @@ -222,7 +222,9 @@ bool LoadedApk::WriteToArchive(IAaptContext* context, ResourceTable* split_table } else if (manifest != nullptr && path == "AndroidManifest.xml") { BigBuffer buffer(8192); - XmlFlattener xml_flattener(&buffer, {}); + XmlFlattenerOptions xml_flattener_options; + xml_flattener_options.use_utf16 = true; + XmlFlattener xml_flattener(&buffer, xml_flattener_options); if (!xml_flattener.Consume(context, manifest)) { context->GetDiagnostics()->Error(DiagMessage(path) << "flattening failed"); return false; diff --git a/tools/aapt2/ResourceUtils.cpp b/tools/aapt2/ResourceUtils.cpp index 02ac86c94b46..628466d0a281 100644 --- a/tools/aapt2/ResourceUtils.cpp +++ b/tools/aapt2/ResourceUtils.cpp @@ -520,6 +520,10 @@ std::unique_ptr<BinaryPrimitive> TryParseInt(const StringPiece& str) { return util::make_unique<BinaryPrimitive>(value); } +std::unique_ptr<BinaryPrimitive> MakeInt(uint32_t val) { + return util::make_unique<BinaryPrimitive>(android::Res_value::TYPE_INT_DEC, val); +} + std::unique_ptr<BinaryPrimitive> TryParseFloat(const StringPiece& str) { std::u16string str16 = util::Utf8ToUtf16(util::TrimWhitespace(str)); android::Res_value value; diff --git a/tools/aapt2/ResourceUtils.h b/tools/aapt2/ResourceUtils.h index 36f6c2bda8f8..f83d49ee5591 100644 --- a/tools/aapt2/ResourceUtils.h +++ b/tools/aapt2/ResourceUtils.h @@ -165,6 +165,9 @@ std::unique_ptr<BinaryPrimitive> MakeBool(bool val); */ std::unique_ptr<BinaryPrimitive> TryParseInt(const android::StringPiece& str); +// Returns an integer BinaryPrimitive. +std::unique_ptr<BinaryPrimitive> MakeInt(uint32_t value); + /* * Returns a BinaryPrimitve object representing a floating point number * (float, dimension, etc) if the string was parsed as one. diff --git a/tools/aapt2/cmd/Convert.cpp b/tools/aapt2/cmd/Convert.cpp index d80307cd154a..7f956c525bed 100644 --- a/tools/aapt2/cmd/Convert.cpp +++ b/tools/aapt2/cmd/Convert.cpp @@ -139,6 +139,7 @@ class BinaryApkSerializer : public IApkSerializer { 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 3d2fb556cf59..8e7e5e59bc31 100644 --- a/tools/aapt2/cmd/Dump.cpp +++ b/tools/aapt2/cmd/Dump.cpp @@ -38,6 +38,13 @@ using ::android::base::StringPrintf; namespace aapt { +struct DumpOptions { + DebugPrintTableOptions print_options; + + // The path to a file within an APK to dump. + Maybe<std::string> file_to_dump_path; +}; + static const char* ResourceFileTypeToString(const ResourceFile::Type& type) { switch (type) { case ResourceFile::Type::kPng: @@ -69,8 +76,52 @@ static void DumpCompiledFile(const ResourceFile& file, const Source& source, off printer->Println(StringPrintf("Data: offset=%" PRIi64 " length=%zd", offset, len)); } +static bool DumpXmlFile(IAaptContext* context, io::IFile* file, bool proto, + text::Printer* printer) { + std::unique_ptr<xml::XmlResource> doc; + if (proto) { + std::unique_ptr<io::InputStream> in = file->OpenInputStream(); + if (in == nullptr) { + context->GetDiagnostics()->Error(DiagMessage() << "failed to open file"); + return false; + } + + io::ZeroCopyInputAdaptor adaptor(in.get()); + pb::XmlNode pb_node; + if (!pb_node.ParseFromZeroCopyStream(&adaptor)) { + context->GetDiagnostics()->Error(DiagMessage() << "failed to parse file as proto XML"); + return false; + } + + std::string err; + doc = DeserializeXmlResourceFromPb(pb_node, &err); + if (doc == nullptr) { + context->GetDiagnostics()->Error(DiagMessage() << "failed to deserialize proto XML"); + return false; + } + printer->Println("Proto XML"); + } else { + std::unique_ptr<io::IData> data = file->OpenAsData(); + if (data == nullptr) { + context->GetDiagnostics()->Error(DiagMessage() << "failed to open file"); + return false; + } + + std::string err; + doc = xml::Inflate(data->data(), data->size(), &err); + if (doc == nullptr) { + context->GetDiagnostics()->Error(DiagMessage() << "failed to parse file as binary XML"); + return false; + } + printer->Println("Binary XML"); + } + + Debug::DumpXml(*doc, printer); + return true; +} + static bool TryDumpFile(IAaptContext* context, const std::string& file_path, - const DebugPrintTableOptions& print_options) { + const DumpOptions& options) { // Use a smaller buffer so that there is less latency for dumping to stdout. constexpr size_t kStdOutBufferSize = 1024u; io::FileOutputStream fout(STDOUT_FILENO, kStdOutBufferSize); @@ -80,7 +131,10 @@ static bool TryDumpFile(IAaptContext* context, const std::string& file_path, std::unique_ptr<io::ZipFileCollection> zip = io::ZipFileCollection::Create(file_path, &err); if (zip) { ResourceTable table; + bool proto = false; if (io::IFile* file = zip->FindFile("resources.pb")) { + proto = true; + std::unique_ptr<io::IData> data = file->OpenAsData(); if (data == nullptr) { context->GetDiagnostics()->Error(DiagMessage(file_path) << "failed to open resources.pb"); @@ -98,8 +152,6 @@ static bool TryDumpFile(IAaptContext* context, const std::string& file_path, << "failed to parse table: " << err); return false; } - - printer.Println("Proto APK"); } else if (io::IFile* file = zip->FindFile("resources.arsc")) { std::unique_ptr<io::IData> data = file->OpenAsData(); if (!data) { @@ -112,12 +164,26 @@ static bool TryDumpFile(IAaptContext* context, const std::string& file_path, if (!parser.Parse()) { return false; } + } - printer.Println("Binary APK"); + if (!options.file_to_dump_path) { + if (proto) { + printer.Println("Proto APK"); + } else { + printer.Println("Binary APK"); + } + Debug::PrintTable(table, options.print_options, &printer); + return true; } - Debug::PrintTable(table, print_options, &printer); - return true; + io::IFile* file = zip->FindFile(options.file_to_dump_path.value()); + if (file == nullptr) { + context->GetDiagnostics()->Error(DiagMessage(file_path) + << "file '" << options.file_to_dump_path.value() + << "' not found in APK"); + return false; + } + return DumpXmlFile(context, file, proto, &printer); } err.clear(); @@ -159,7 +225,7 @@ static bool TryDumpFile(IAaptContext* context, const std::string& file_path, } printer.Indent(); - Debug::PrintTable(table, print_options, &printer); + Debug::PrintTable(table, options.print_options, &printer); printer.Undent(); } else if (entry->Type() == ContainerEntryType::kResFile) { printer.Println("kResFile"); @@ -243,10 +309,13 @@ class DumpContext : public IAaptContext { int Dump(const std::vector<StringPiece>& args) { bool verbose = false; bool no_values = false; + DumpOptions options; Flags flags = Flags() .OptionalSwitch("--no-values", "Suppresses output of values when displaying resource tables.", &no_values) + .OptionalFlag("--file", "Dumps the specified file from the APK passed as arg.", + &options.file_to_dump_path) .OptionalSwitch("-v", "increase verbosity of output", &verbose); if (!flags.Parse("aapt2 dump", args, &std::cerr)) { return 1; @@ -255,11 +324,10 @@ int Dump(const std::vector<StringPiece>& args) { DumpContext context; context.SetVerbose(verbose); - DebugPrintTableOptions dump_table_options; - dump_table_options.show_sources = true; - dump_table_options.show_values = !no_values; + options.print_options.show_sources = true; + options.print_options.show_values = !no_values; for (const std::string& arg : flags.GetArgs()) { - if (!TryDumpFile(&context, arg, dump_table_options)) { + if (!TryDumpFile(&context, arg, options)) { return 1; } } diff --git a/tools/aapt2/configuration/ConfigurationParser.cpp b/tools/aapt2/configuration/ConfigurationParser.cpp index eabeb47fccec..902334b98d00 100644 --- a/tools/aapt2/configuration/ConfigurationParser.cpp +++ b/tools/aapt2/configuration/ConfigurationParser.cpp @@ -20,6 +20,7 @@ #include <functional> #include <map> #include <memory> +#include <string> #include <utility> #include "android-base/file.h" @@ -93,6 +94,7 @@ class NoopDiagnostics : public IDiagnostics { }; NoopDiagnostics noop_; +/** Returns the value of the label attribute for a given element. */ std::string GetLabel(const Element* element, IDiagnostics* diag) { std::string label; for (const auto& attr : element->attributes) { @@ -108,6 +110,18 @@ std::string GetLabel(const Element* element, IDiagnostics* diag) { return label; } +/** Returns the value of the version-code-order attribute for a given element. */ +Maybe<int32_t> GetVersionCodeOrder(const Element* element, IDiagnostics* diag) { + const xml::Attribute* version = element->FindAttribute("", "version-code-order"); + if (version == nullptr) { + std::string label = GetLabel(element, diag); + diag->Error(DiagMessage() << "No version-code-order found for element '" << element->name + << "' with label '" << label << "'"); + return {}; + } + return std::stoi(version->value); +} + /** XML node visitor that removes all of the namespace URIs from the node and all children. */ class NamespaceVisitor : public xml::Visitor { public: @@ -437,26 +451,37 @@ Maybe<std::vector<OutputArtifact>> ConfigurationParser::Parse( // Convert from a parsed configuration to a list of artifacts for processing. const std::string& apk_name = file::GetFilename(apk_path).to_string(); std::vector<OutputArtifact> output_artifacts; - bool has_errors = false; PostProcessingConfiguration& config = maybe_config.value(); - config.SortArtifacts(); + bool valid = true; int version = 1; + for (const ConfiguredArtifact& artifact : config.artifacts) { Maybe<OutputArtifact> output_artifact = ToOutputArtifact(artifact, apk_name, config, diag_); if (!output_artifact) { // Defer return an error condition so that all errors are reported. - has_errors = true; + valid = false; } else { output_artifact.value().version = version++; output_artifacts.push_back(std::move(output_artifact.value())); } } - if (has_errors) { + if (!config.ValidateVersionCodeOrdering(diag_)) { + diag_->Error(DiagMessage() << "could not validate post processing configuration"); + valid = false; + } + + if (valid) { + // Sorting artifacts requires that all references are valid as it uses them to determine order. + config.SortArtifacts(); + } + + if (!valid) { return {}; } + return {output_artifacts}; } @@ -509,8 +534,15 @@ bool AbiGroupTagHandler(PostProcessingConfiguration* config, Element* root_eleme return false; } - auto& group = GetOrCreateGroup(label, &config->abi_groups); bool valid = true; + OrderedEntry<Abi>& entry = config->abi_groups[label]; + Maybe<int32_t> order = GetVersionCodeOrder(root_element, diag); + if (!order) { + valid = false; + } else { + entry.order = order.value(); + } + auto& group = entry.entry; // Special case for empty abi-group tag. Label will be used as the ABI. if (root_element->GetChildElements().empty()) { @@ -519,7 +551,7 @@ bool AbiGroupTagHandler(PostProcessingConfiguration* config, Element* root_eleme return false; } group.push_back(abi->second); - return true; + return valid; } for (auto* child : root_element->GetChildElements()) { @@ -553,8 +585,15 @@ bool ScreenDensityGroupTagHandler(PostProcessingConfiguration* config, Element* return false; } - auto& group = GetOrCreateGroup(label, &config->screen_density_groups); bool valid = true; + OrderedEntry<ConfigDescription>& entry = config->screen_density_groups[label]; + Maybe<int32_t> order = GetVersionCodeOrder(root_element, diag); + if (!order) { + valid = false; + } else { + entry.order = order.value(); + } + auto& group = entry.entry; // Special case for empty screen-density-group tag. Label will be used as the screen density. if (root_element->GetChildElements().empty()) { @@ -613,8 +652,15 @@ bool LocaleGroupTagHandler(PostProcessingConfiguration* config, Element* root_el return false; } - auto& group = GetOrCreateGroup(label, &config->locale_groups); bool valid = true; + OrderedEntry<ConfigDescription>& entry = config->locale_groups[label]; + Maybe<int32_t> order = GetVersionCodeOrder(root_element, diag); + if (!order) { + valid = false; + } else { + entry.order = order.value(); + } + auto& group = entry.entry; // Special case to auto insert a locale for an empty group. Label will be used for locale. if (root_element->GetChildElements().empty()) { @@ -728,8 +774,15 @@ bool GlTextureGroupTagHandler(PostProcessingConfiguration* config, Element* root return false; } - auto& group = GetOrCreateGroup(label, &config->gl_texture_groups); bool valid = true; + OrderedEntry<GlTexture>& entry = config->gl_texture_groups[label]; + Maybe<int32_t> order = GetVersionCodeOrder(root_element, diag); + if (!order) { + valid = false; + } else { + entry.order = order.value(); + } + auto& group = entry.entry; GlTexture result; for (auto* child : root_element->GetChildElements()) { @@ -771,8 +824,15 @@ bool DeviceFeatureGroupTagHandler(PostProcessingConfiguration* config, Element* return false; } - auto& group = GetOrCreateGroup(label, &config->device_feature_groups); bool valid = true; + OrderedEntry<DeviceFeature>& entry = config->device_feature_groups[label]; + Maybe<int32_t> order = GetVersionCodeOrder(root_element, diag); + if (!order) { + valid = false; + } else { + entry.order = order.value(); + } + auto& group = entry.entry; for (auto* child : root_element->GetChildElements()) { if (child->name != "supports-feature") { diff --git a/tools/aapt2/configuration/ConfigurationParser.internal.h b/tools/aapt2/configuration/ConfigurationParser.internal.h index a583057427e6..f071a69fc9e3 100644 --- a/tools/aapt2/configuration/ConfigurationParser.internal.h +++ b/tools/aapt2/configuration/ConfigurationParser.internal.h @@ -33,18 +33,31 @@ namespace configuration { template <typename T> struct OrderedEntry { - size_t order; + int32_t order; std::vector<T> entry; }; -/** A mapping of group labels to group of configuration items. */ -template <class T> -using Group = std::unordered_map<std::string, OrderedEntry<T>>; - /** A mapping of group label to a single configuration item. */ template <class T> using Entry = std::unordered_map<std::string, T>; +/** A mapping of group labels to group of configuration items. */ +template <class T> +using Group = Entry<OrderedEntry<T>>; + +template<typename T> +bool IsGroupValid(const Group<T>& group, const std::string& name, IDiagnostics* diag) { + std::set<int32_t> orders; + for (const auto& p : group) { + orders.insert(p.second.order); + } + bool valid = orders.size() == group.size(); + if (!valid) { + diag->Error(DiagMessage() << name << " have overlapping version-code-order attributes"); + } + return valid; +} + /** Retrieves an entry from the provided Group, creating a new instance if one does not exist. */ template <typename T> std::vector<T>& GetOrCreateGroup(std::string label, Group<T>* group) { @@ -93,7 +106,7 @@ class ComparisonChain { private: template <typename T> - inline size_t GetGroupOrder(const Group<T>& groups, const Maybe<std::string>& label) { + inline size_t GetGroupOrder(const Entry<T>& groups, const Maybe<std::string>& label) { if (!label) { return std::numeric_limits<size_t>::max(); } @@ -141,6 +154,15 @@ struct PostProcessingConfiguration { Group<GlTexture> gl_texture_groups; Entry<AndroidSdk> android_sdks; + bool ValidateVersionCodeOrdering(IDiagnostics* diag) { + bool valid = IsGroupValid(abi_groups, "abi-groups", diag); + valid &= IsGroupValid(screen_density_groups, "screen-density-groups", diag); + valid &= IsGroupValid(locale_groups, "locale-groups", diag); + valid &= IsGroupValid(device_feature_groups, "device-feature-groups", diag); + valid &= IsGroupValid(gl_texture_groups, "gl-texture-groups", diag); + return valid; + } + /** * Sorts the configured artifacts based on the ordering of the groups in the configuration file. * The only exception to this rule is Android SDK versions. Larger SDK versions will have a larger diff --git a/tools/aapt2/configuration/ConfigurationParser_test.cpp b/tools/aapt2/configuration/ConfigurationParser_test.cpp index 0329846a5bb5..febbb2ed11a0 100644 --- a/tools/aapt2/configuration/ConfigurationParser_test.cpp +++ b/tools/aapt2/configuration/ConfigurationParser_test.cpp @@ -82,22 +82,22 @@ using ::testing::StrEq; constexpr const char* kValidConfig = R"(<?xml version="1.0" encoding="utf-8" ?> <post-process xmlns="http://schemas.android.com/tools/aapt"> <abi-groups> - <abi-group label="arm"> - <abi>armeabi-v7a</abi> - <abi>arm64-v8a</abi> - </abi-group> - <abi-group label="other"> + <abi-group label="other" version-code-order="2"> <abi>x86</abi> <abi>mips</abi> </abi-group> + <abi-group label="arm" version-code-order="1"> + <abi>armeabi-v7a</abi> + <abi>arm64-v8a</abi> + </abi-group> </abi-groups> <screen-density-groups> - <screen-density-group label="large"> + <screen-density-group label="large" version-code-order="2"> <screen-density>xhdpi</screen-density> <screen-density>xxhdpi</screen-density> <screen-density>xxxhdpi</screen-density> </screen-density-group> - <screen-density-group label="alldpi"> + <screen-density-group label="alldpi" version-code-order="1"> <screen-density>ldpi</screen-density> <screen-density>mdpi</screen-density> <screen-density>hdpi</screen-density> @@ -107,17 +107,20 @@ constexpr const char* kValidConfig = R"(<?xml version="1.0" encoding="utf-8" ?> </screen-density-group> </screen-density-groups> <locale-groups> - <locale-group label="europe"> + <locale-group label="europe" version-code-order="1"> <locale>en</locale> <locale>es</locale> <locale>fr</locale> <locale>de</locale> </locale-group> - <locale-group label="north-america"> + <locale-group label="north-america" version-code-order="2"> <locale>en</locale> <locale>es-rMX</locale> <locale>fr-rCA</locale> </locale-group> + <locale-group label="all" version-code-order="-1"> + <locale /> + </locale-group> </locale-groups> <android-sdks> <android-sdk @@ -131,14 +134,14 @@ constexpr const char* kValidConfig = R"(<?xml version="1.0" encoding="utf-8" ?> </android-sdk> </android-sdks> <gl-texture-groups> - <gl-texture-group label="dxt1"> + <gl-texture-group label="dxt1" version-code-order="2"> <gl-texture name="GL_EXT_texture_compression_dxt1"> <texture-path>assets/dxt1/*</texture-path> </gl-texture> </gl-texture-group> </gl-texture-groups> <device-feature-groups> - <device-feature-group label="low-latency"> + <device-feature-group label="low-latency" version-code-order="2"> <supports-feature>android.hardware.audio.low_latency</supports-feature> </device-feature-group> </device-feature-groups> @@ -188,19 +191,22 @@ TEST_F(ConfigurationParserTest, ExtractConfiguration) { auto& arm = config.abi_groups["arm"]; auto& other = config.abi_groups["other"]; - EXPECT_EQ(arm.order, 1ul); - EXPECT_EQ(other.order, 2ul); + EXPECT_EQ(arm.order, 1); + EXPECT_EQ(other.order, 2); auto& large = config.screen_density_groups["large"]; auto& alldpi = config.screen_density_groups["alldpi"]; - EXPECT_EQ(large.order, 1ul); - EXPECT_EQ(alldpi.order, 2ul); + EXPECT_EQ(large.order, 2); + EXPECT_EQ(alldpi.order, 1); auto& north_america = config.locale_groups["north-america"]; auto& europe = config.locale_groups["europe"]; + auto& all = config.locale_groups["all"]; // Checked in reverse to make sure access order does not matter. - EXPECT_EQ(north_america.order, 2ul); - EXPECT_EQ(europe.order, 1ul); + EXPECT_EQ(north_america.order, 2); + EXPECT_EQ(europe.order, 1); + EXPECT_EQ(all.order, -1); + EXPECT_EQ(3ul, config.locale_groups.size()); } TEST_F(ConfigurationParserTest, ValidateFile) { @@ -392,7 +398,7 @@ TEST_F(ConfigurationParserTest, ArtifactFormatAction) { TEST_F(ConfigurationParserTest, AbiGroupAction) { static constexpr const char* xml = R"xml( - <abi-group label="arm"> + <abi-group label="arm" version-code-order="2"> <!-- First comment. --> <abi> armeabi-v7a @@ -415,7 +421,8 @@ TEST_F(ConfigurationParserTest, AbiGroupAction) { } TEST_F(ConfigurationParserTest, AbiGroupAction_EmptyGroup) { - static constexpr const char* xml = R"xml(<abi-group label="arm64-v8a"/>)xml"; + static constexpr const char* xml = + R"xml(<abi-group label="arm64-v8a" version-code-order="3"/>)xml"; auto doc = test::BuildXmlDom(xml); @@ -426,12 +433,23 @@ TEST_F(ConfigurationParserTest, AbiGroupAction_EmptyGroup) { EXPECT_THAT(config.abi_groups, SizeIs(1ul)); ASSERT_EQ(1u, config.abi_groups.count("arm64-v8a")); - auto& out = config.abi_groups["arm64-v8a"].entry; - ASSERT_THAT(out, ElementsAre(Abi::kArm64V8a)); + auto& out = config.abi_groups["arm64-v8a"]; + ASSERT_THAT(out.entry, ElementsAre(Abi::kArm64V8a)); + EXPECT_EQ(3, out.order); +} + +TEST_F(ConfigurationParserTest, AbiGroupAction_EmptyGroup_NoOrder) { + static constexpr const char* xml = R"xml(<abi-group label="arm64-v8a"/>)xml"; + + auto doc = test::BuildXmlDom(xml); + + PostProcessingConfiguration config; + bool ok = AbiGroupTagHandler(&config, NodeCast<Element>(doc.get()->root.get()), &diag_); + ASSERT_FALSE(ok); } TEST_F(ConfigurationParserTest, AbiGroupAction_InvalidEmptyGroup) { - static constexpr const char* xml = R"xml(<abi-group label="arm"/>)xml"; + static constexpr const char* xml = R"xml(<abi-group label="arm" order="2"/>)xml"; auto doc = test::BuildXmlDom(xml); @@ -442,7 +460,7 @@ TEST_F(ConfigurationParserTest, AbiGroupAction_InvalidEmptyGroup) { TEST_F(ConfigurationParserTest, ScreenDensityGroupAction) { static constexpr const char* xml = R"xml( - <screen-density-group label="large"> + <screen-density-group label="large" version-code-order="2"> <screen-density>xhdpi</screen-density> <screen-density> xxhdpi @@ -471,7 +489,8 @@ TEST_F(ConfigurationParserTest, ScreenDensityGroupAction) { } TEST_F(ConfigurationParserTest, ScreenDensityGroupAction_EmtpyGroup) { - static constexpr const char* xml = R"xml(<screen-density-group label="xhdpi"/>)xml"; + static constexpr const char* xml = + R"xml(<screen-density-group label="xhdpi" version-code-order="4"/>)xml"; auto doc = test::BuildXmlDom(xml); @@ -485,8 +504,19 @@ TEST_F(ConfigurationParserTest, ScreenDensityGroupAction_EmtpyGroup) { ConfigDescription xhdpi; xhdpi.density = ResTable_config::DENSITY_XHIGH; - auto& out = config.screen_density_groups["xhdpi"].entry; - ASSERT_THAT(out, ElementsAre(xhdpi)); + auto& out = config.screen_density_groups["xhdpi"]; + EXPECT_THAT(out.entry, ElementsAre(xhdpi)); + EXPECT_EQ(4, out.order); +} + +TEST_F(ConfigurationParserTest, ScreenDensityGroupAction_EmtpyGroup_NoVersion) { + static constexpr const char* xml = R"xml(<screen-density-group label="xhdpi"/>)xml"; + + auto doc = test::BuildXmlDom(xml); + + PostProcessingConfiguration config; + bool ok = ScreenDensityGroupTagHandler(&config, NodeCast<Element>(doc.get()->root.get()), &diag_); + ASSERT_FALSE(ok); } TEST_F(ConfigurationParserTest, ScreenDensityGroupAction_InvalidEmtpyGroup) { @@ -501,7 +531,7 @@ TEST_F(ConfigurationParserTest, ScreenDensityGroupAction_InvalidEmtpyGroup) { TEST_F(ConfigurationParserTest, LocaleGroupAction) { static constexpr const char* xml = R"xml( - <locale-group label="europe"> + <locale-group label="europe" version-code-order="2"> <locale>en</locale> <locale>es</locale> <locale>fr</locale> @@ -528,7 +558,7 @@ TEST_F(ConfigurationParserTest, LocaleGroupAction) { } TEST_F(ConfigurationParserTest, LocaleGroupAction_EmtpyGroup) { - static constexpr const char* xml = R"xml(<locale-group label="en"/>)xml"; + static constexpr const char* xml = R"xml(<locale-group label="en" version-code-order="6"/>)xml"; auto doc = test::BuildXmlDom(xml); @@ -539,11 +569,22 @@ TEST_F(ConfigurationParserTest, LocaleGroupAction_EmtpyGroup) { ASSERT_EQ(1ul, config.locale_groups.size()); ASSERT_EQ(1u, config.locale_groups.count("en")); - const auto& out = config.locale_groups["en"].entry; + const auto& out = config.locale_groups["en"]; ConfigDescription en = test::ParseConfigOrDie("en"); - ASSERT_THAT(out, ElementsAre(en)); + EXPECT_THAT(out.entry, ElementsAre(en)); + EXPECT_EQ(6, out.order); +} + +TEST_F(ConfigurationParserTest, LocaleGroupAction_EmtpyGroup_NoOrder) { + static constexpr const char* xml = R"xml(<locale-group label="en"/>)xml"; + + auto doc = test::BuildXmlDom(xml); + + PostProcessingConfiguration config; + bool ok = LocaleGroupTagHandler(&config, NodeCast<Element>(doc.get()->root.get()), &diag_); + ASSERT_FALSE(ok); } TEST_F(ConfigurationParserTest, LocaleGroupAction_InvalidEmtpyGroup) { @@ -695,7 +736,7 @@ TEST_F(ConfigurationParserTest, AndroidSdkGroupAction_NonNumeric) { TEST_F(ConfigurationParserTest, GlTextureGroupAction) { static constexpr const char* xml = R"xml( - <gl-texture-group label="dxt1"> + <gl-texture-group label="dxt1" version-code-order="2"> <gl-texture name="GL_EXT_texture_compression_dxt1"> <texture-path>assets/dxt1/main/*</texture-path> <texture-path> @@ -726,7 +767,7 @@ TEST_F(ConfigurationParserTest, GlTextureGroupAction) { TEST_F(ConfigurationParserTest, DeviceFeatureGroupAction) { static constexpr const char* xml = R"xml( - <device-feature-group label="low-latency"> + <device-feature-group label="low-latency" version-code-order="2"> <supports-feature>android.hardware.audio.low_latency</supports-feature> <supports-feature> android.hardware.audio.pro @@ -749,6 +790,30 @@ TEST_F(ConfigurationParserTest, DeviceFeatureGroupAction) { ASSERT_THAT(out, ElementsAre(low_latency, pro)); } +TEST_F(ConfigurationParserTest, Group_Valid) { + Group<int32_t> group; + group["item1"].order = 1; + group["item2"].order = 2; + group["item3"].order = 3; + group["item4"].order = 4; + group["item5"].order = 5; + group["item6"].order = 6; + + EXPECT_TRUE(IsGroupValid(group, "test", &diag_)); +} + +TEST_F(ConfigurationParserTest, Group_OverlappingOrder) { + Group<int32_t> group; + group["item1"].order = 1; + group["item2"].order = 2; + group["item3"].order = 3; + group["item4"].order = 2; + group["item5"].order = 5; + group["item6"].order = 1; + + EXPECT_FALSE(IsGroupValid(group, "test", &diag_)); +} + // Artifact name parser test cases. TEST(ArtifactTest, Simple) { diff --git a/tools/aapt2/configuration/aapt2.xsd b/tools/aapt2/configuration/aapt2.xsd index fb2f49bae7b7..a28e28be48f4 100644 --- a/tools/aapt2/configuration/aapt2.xsd +++ b/tools/aapt2/configuration/aapt2.xsd @@ -81,6 +81,7 @@ <xsd:element name="gl-texture" type="gl-texture" maxOccurs="unbounded"/> </xsd:sequence> <xsd:attribute name="label" type="xsd:string"/> + <xsd:attribute name="version-code-order" type="xsd:unsignedInt" use="required"/> </xsd:complexType> <xsd:complexType name="gl-texture"> @@ -95,6 +96,7 @@ <xsd:element name="supports-feature" type="xsd:string" maxOccurs="unbounded"/> </xsd:sequence> <xsd:attribute name="label" type="xsd:string"/> + <xsd:attribute name="version-code-order" type="xsd:unsignedInt" use="required"/> </xsd:complexType> <xsd:complexType name="abi-group"> @@ -102,6 +104,7 @@ <xsd:element name="abi" type="abi-name" maxOccurs="unbounded"/> </xsd:sequence> <xsd:attribute name="label" type="xsd:string"/> + <xsd:attribute name="version-code-order" type="xsd:unsignedInt" use="required"/> </xsd:complexType> <xsd:simpleType name="abi-name"> @@ -122,6 +125,7 @@ <xsd:element name="screen-density" type="screen-density" maxOccurs="unbounded"/> </xsd:sequence> <xsd:attribute name="label" type="xsd:string"/> + <xsd:attribute name="version-code-order" type="xsd:unsignedInt" use="required"/> </xsd:complexType> <xsd:simpleType name="screen-density"> @@ -158,6 +162,7 @@ <xsd:element name="locale" type="locale" maxOccurs="unbounded"/> </xsd:sequence> <xsd:attribute name="label" type="xsd:string"/> + <xsd:attribute name="version-code-order" type="xsd:unsignedInt" use="required"/> </xsd:complexType> <xsd:complexType name="locale"> diff --git a/tools/aapt2/configuration/example/config.xml b/tools/aapt2/configuration/example/config.xml index d8aba09e836d..e6db2a0f461b 100644 --- a/tools/aapt2/configuration/example/config.xml +++ b/tools/aapt2/configuration/example/config.xml @@ -36,28 +36,28 @@ </android-sdks> <abi-groups> - <abi-group label="arm"> + <abi-group label="arm" version-code-order="1"> <abi>armeabi-v7a</abi> <abi>arm64-v8a</abi> </abi-group> - <abi-group label="other"> + <abi-group label="other" version-code-order="2"> <abi>x86</abi> <abi>mips</abi> </abi-group> </abi-groups> <screen-density-groups> - <screen-density-group label="large"> + <screen-density-group label="alldpi" version-code-order="1"> + <screen-density>ldpi</screen-density> + <screen-density>mdpi</screen-density> + <screen-density>hdpi</screen-density> <screen-density>xhdpi</screen-density> <screen-density>xxhdpi</screen-density> <screen-density>xxxhdpi</screen-density> </screen-density-group> - <screen-density-group label="alldpi"> - <screen-density>ldpi</screen-density> - <screen-density>mdpi</screen-density> - <screen-density>hdpi</screen-density> + <screen-density-group label="large" version-code-order="2"> <screen-density>xhdpi</screen-density> <screen-density>xxhdpi</screen-density> <screen-density>xxxhdpi</screen-density> @@ -65,26 +65,26 @@ </screen-density-groups> <locale-groups> - <locale-group label="europe"> + <locale-group label="europe" version-code-order="1"> <locale lang="en"/> <locale lang="es"/> <locale lang="fr"/> <locale lang="de" compressed="true"/> </locale-group> - <locale-group label="north-america"> + <locale-group label="north-america" version-code-order="2"> <locale lang="en"/> <locale lang="es" region="MX"/> <locale lang="fr" region="CA" compressed="true"/> </locale-group> - <locale-group label="all"> + <locale-group label="all" version-code-order="0"> <locale compressed="true"/> </locale-group> </locale-groups> <gl-texture-groups> - <gl-texture-group label="dxt1"> + <gl-texture-group label="dxt1" version-code-order="1"> <gl-texture name="GL_EXT_texture_compression_dxt1"> <texture-path>assets/dxt1/*</texture-path> </gl-texture> @@ -92,7 +92,7 @@ </gl-texture-groups> <device-feature-groups> - <device-feature-group label="low-latency"> + <device-feature-group label="low-latency" version-code-order="1"> <supports-feature>android.hardware.audio.low_latency</supports-feature> </device-feature-group> </device-feature-groups> diff --git a/tools/aapt2/link/XmlCompatVersioner_test.cpp b/tools/aapt2/link/XmlCompatVersioner_test.cpp index 1ed4536c4566..a98ab0f76de4 100644 --- a/tools/aapt2/link/XmlCompatVersioner_test.cpp +++ b/tools/aapt2/link/XmlCompatVersioner_test.cpp @@ -23,6 +23,7 @@ using ::aapt::test::ValueEq; using ::testing::Eq; using ::testing::IsNull; using ::testing::NotNull; +using ::testing::Pointee; using ::testing::SizeIs; namespace aapt { @@ -287,13 +288,13 @@ TEST_F(XmlCompatVersionerTest, DegradeRuleOverridesExistingAttribute) { 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 @@ TEST_F(XmlCompatVersionerTest, DegradeRuleOverridesExistingAttribute) { 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 991faadcafc4..588b3316e6fa 100644 --- a/tools/aapt2/optimize/MultiApkGenerator.cpp +++ b/tools/aapt2/optimize/MultiApkGenerator.cpp @@ -322,22 +322,56 @@ bool MultiApkGenerator::UpdateManifest(const OutputArtifact& artifact, std::unique_ptr<xml::Element> new_screens_el = util::make_unique<xml::Element>(); new_screens_el->name = "compatible-screens"; screens_el = new_screens_el.get(); - manifest_el->InsertChild(0, std::move(new_screens_el)); + manifest_el->AppendChild(std::move(new_screens_el)); } else { // clear out the old element. screens_el->GetChildElements().clear(); } for (const auto& density : artifact.screen_densities) { - std::unique_ptr<xml::Element> screen_el = util::make_unique<xml::Element>(); - screen_el->name = "screen"; - const char* density_str = density.toString().string(); - screen_el->attributes.push_back(xml::Attribute{kSchemaAndroid, "screenDensity", density_str}); - screens_el->AppendChild(std::move(screen_el)); + AddScreens(density, screens_el); } } return true; } +/** + * Adds a screen element with both screenSize and screenDensity set. Since we only know the density + * we add it for all screen sizes. + * + * This requires the resource IDs for the attributes from the framework library. Since these IDs are + * a part of the public API (and in public.xml) we hard code the values. + * + * The excert from the framework is as follows: + * <public type="attr" name="screenSize" id="0x010102ca" /> + * <public type="attr" name="screenDensity" id="0x010102cb" /> + */ +void MultiApkGenerator::AddScreens(const ConfigDescription& config, xml::Element* parent) { + // Hard coded integer representation of the supported screen sizes: + // small = 200 + // normal = 300 + // large = 400 + // xlarge = 500 + constexpr const uint32_t kScreenSizes[4] = {200, 300, 400, 500,}; + constexpr const uint32_t kScreenSizeResourceId = 0x010102ca; + constexpr const uint32_t kScreenDensityResourceId = 0x010102cb; + + for (uint32_t screen_size : kScreenSizes) { + std::unique_ptr<xml::Element> screen = util::make_unique<xml::Element>(); + screen->name = "screen"; + + xml::Attribute* size = screen->FindOrCreateAttribute(kSchemaAndroid, "screenSize"); + size->compiled_attribute = xml::AaptAttribute(Attribute(), {kScreenSizeResourceId}); + size->compiled_value = ResourceUtils::MakeInt(screen_size); + + xml::Attribute* density = screen->FindOrCreateAttribute(kSchemaAndroid, "screenDensity"); + density->compiled_attribute = xml::AaptAttribute(Attribute(), {kScreenDensityResourceId}); + density->compiled_value = ResourceUtils::MakeInt(config.density); + + + parent->AppendChild(std::move(screen)); + } +} + } // namespace aapt diff --git a/tools/aapt2/optimize/MultiApkGenerator.h b/tools/aapt2/optimize/MultiApkGenerator.h index 19f64cc0e588..c8588791662a 100644 --- a/tools/aapt2/optimize/MultiApkGenerator.h +++ b/tools/aapt2/optimize/MultiApkGenerator.h @@ -63,6 +63,11 @@ class MultiApkGenerator { bool UpdateManifest(const configuration::OutputArtifact& artifact, std::unique_ptr<xml::XmlResource>* updated_manifest, IDiagnostics* diag); + /** + * Adds the <screen> elements to the parent node for the provided density configuration. + */ + void AddScreens(const ConfigDescription& config, xml::Element* parent); + LoadedApk* apk_; IAaptContext* context_; }; diff --git a/tools/aapt2/test/Common.h b/tools/aapt2/test/Common.h index 4e318a92f3fa..aca161a5189d 100644 --- a/tools/aapt2/test/Common.h +++ b/tools/aapt2/test/Common.h @@ -146,97 +146,70 @@ MATCHER_P(StrEq, a, 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 {}; + explicit ValueEqImpl(const Value* expected) : expected_(expected) { + } - 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); + } - 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 DescribeTo(::std::ostream* os) const override { - *os << "is equal to " << *expected_; - } + void DescribeNegationTo(::std::ostream* os) const override { + *os << "is not equal to " << *expected_; + } - void DescribeNegationTo(::std::ostream* os) const override { - *os << "is not equal to " << *expected_; - } + private: + DISALLOW_COPY_AND_ASSIGN(ValueEqImpl); - private: - DISALLOW_COPY_AND_ASSIGN(Impl); + const Value* expected_; +}; - const Value* expected_; - }; +template <typename TValue> +class ValueEqMatcher { + public: + ValueEqMatcher(TValue expected) : expected_(std::move(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); + operator ::testing::Matcher<T>() const { + return ::testing::Matcher<T>(new ValueEqImpl<T>(&expected_)); + } - const Value* expected_; - }; + private: + TValue expected_; +}; - ValueEq(const Value& expected) : expected_(&expected) { - } - ValueEq(const Value* expected) : expected_(expected) { +template <typename TValue> +class ValueEqPointerMatcher { + public: + ValueEqPointerMatcher(const TValue* 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_)); + return ::testing::Matcher<T>(new ValueEqImpl<T>(expected_)); } private: - const Value* expected_; + const TValue* expected_; }; -// MATCHER_P(ValueEq, a, -// std::string(negation ? "isn't" : "is") + " equal to " + ::testing::PrintToString(a)) { -// return arg.Equals(&a); -//} +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 fddb6b8c5d87..7b748ce78cbc 100644 --- a/tools/aapt2/xml/XmlDom.cpp +++ b/tools/aapt2/xml/XmlDom.cpp @@ -244,14 +244,13 @@ static void CopyAttributes(Element* el, android::ResXMLParser* parser, StringPoo 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 e5012d67163d..486b53ada6bb 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 @@ TEST(XmlDomTest, BinaryInflate) { 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 @@ TEST(XmlDomTest, BinaryInflate) { 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 @@ TEST(XmlDomTest, BinaryInflate) { 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/Android.bp b/tools/stats_log_api_gen/Android.bp index e301eb140fc9..dc2ed433d345 100644 --- a/tools/stats_log_api_gen/Android.bp +++ b/tools/stats_log_api_gen/Android.bp @@ -102,6 +102,7 @@ cc_library_shared { export_generated_headers: ["statslog.h"], shared_libs: [ "liblog", + "libutils", ], } diff --git a/tools/stats_log_api_gen/main.cpp b/tools/stats_log_api_gen/main.cpp index 3dbb50306cc6..f0628c099e2e 100644 --- a/tools/stats_log_api_gen/main.cpp +++ b/tools/stats_log_api_gen/main.cpp @@ -105,6 +105,7 @@ static int write_stats_log_cpp(FILE *out, const Atoms &atoms, fprintf(out, "#include <log/log_event_list.h>\n"); fprintf(out, "#include <log/log.h>\n"); fprintf(out, "#include <statslog.h>\n"); + fprintf(out, "#include <utils/SystemClock.h>\n"); fprintf(out, "\n"); fprintf(out, "namespace android {\n"); @@ -145,6 +146,7 @@ static int write_stats_log_cpp(FILE *out, const Atoms &atoms, 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++) { @@ -212,6 +214,7 @@ static int write_stats_log_cpp(FILE *out, const Atoms &atoms, 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 000000000000..0efa4646a80a --- /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/RttManager.java b/wifi/java/android/net/wifi/RttManager.java index bdbc149a0a42..a61ac54e0ff1 100644 --- a/wifi/java/android/net/wifi/RttManager.java +++ b/wifi/java/android/net/wifi/RttManager.java @@ -15,6 +15,7 @@ import android.net.wifi.rtt.WifiRttManager; import android.os.Handler; import android.os.Parcel; import android.os.Parcelable; +import android.os.SystemClock; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; @@ -986,11 +987,16 @@ public class RttManager { legacyResults[i] = new RttResult(); legacyResults[i].status = result.getStatus(); legacyResults[i].bssid = result.getMacAddress().toString(); - legacyResults[i].distance = result.getDistanceMm() / 10; - legacyResults[i].distanceStandardDeviation = - result.getDistanceStdDevMm() / 10; - legacyResults[i].rssi = result.getRssi(); - legacyResults[i].ts = result.getRangingTimestampUs(); + if (result.getStatus() == RangingResult.STATUS_SUCCESS) { + legacyResults[i].distance = result.getDistanceMm() / 10; + legacyResults[i].distanceStandardDeviation = + result.getDistanceStdDevMm() / 10; + legacyResults[i].rssi = result.getRssi(); + legacyResults[i].ts = result.getRangingTimestampUs(); + } else { + // just in case legacy API needed some relatively real timestamp + legacyResults[i].ts = SystemClock.elapsedRealtime() * 1000; + } } listener.onSuccess(legacyResults); } diff --git a/wifi/java/android/net/wifi/rtt/RangingRequest.java b/wifi/java/android/net/wifi/rtt/RangingRequest.java index 52b3d8691c79..4348ed4f73e1 100644 --- a/wifi/java/android/net/wifi/rtt/RangingRequest.java +++ b/wifi/java/android/net/wifi/rtt/RangingRequest.java @@ -122,6 +122,11 @@ public final class RangingRequest implements Parcelable { * 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 @@ public final class RangingRequest implements Parcelable { * 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 f7c85671c220..5c9bd198e05b 100644 --- a/wifi/java/android/net/wifi/rtt/RangingResult.java +++ b/wifi/java/android/net/wifi/rtt/RangingResult.java @@ -42,7 +42,7 @@ public final class RangingResult implements Parcelable { 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 @@ public final class RangingResult implements Parcelable { * <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; |