diff options
108 files changed, 2775 insertions, 1266 deletions
diff --git a/apct-tests/perftests/core/src/android/text/MeasuredTextMemoryUsageTest.java b/apct-tests/perftests/core/src/android/text/MeasuredTextMemoryUsageTest.java new file mode 100644 index 000000000000..fc6302ea9394 --- /dev/null +++ b/apct-tests/perftests/core/src/android/text/MeasuredTextMemoryUsageTest.java @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES 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 static android.text.TextDirectionHeuristics.LTR; + +import android.perftests.utils.BenchmarkState; +import android.perftests.utils.PerfStatusReporter; + +import android.support.test.filters.LargeTest; +import android.support.test.runner.AndroidJUnit4; + +import android.app.Activity; +import android.os.Bundle; +import android.support.test.InstrumentationRegistry; +import android.content.res.ColorStateList; +import android.graphics.Canvas; +import android.graphics.Typeface; +import android.text.Layout; +import android.text.style.TextAppearanceSpan; +import android.view.DisplayListCanvas; +import android.view.RenderNode; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.nio.CharBuffer; +import java.util.Random; + +@LargeTest +@RunWith(AndroidJUnit4.class) +public class MeasuredTextMemoryUsageTest { + private static final int WORD_LENGTH = 9; // Random word has 9 characters. + private static final boolean NO_STYLE_TEXT = false; + + private static TextPaint PAINT = new TextPaint(); + + private static int TRIAL_COUNT = 100; + + public MeasuredTextMemoryUsageTest() {} + + private TextPerfUtils mTextUtil = new TextPerfUtils(); + + @Before + public void setUp() { + mTextUtil.resetRandom(0 /* seed */); + } + + private void reportMemoryUsage(int memoryUsage, String key) { + Bundle status = new Bundle(); + status.putInt(key + "_median", memoryUsage); + InstrumentationRegistry.getInstrumentation().sendStatus(Activity.RESULT_OK, status); + } + + private int median(int[] values) { + return values.length % 2 == 0 ? + (values[values.length / 2] + values[values.length / 2 - 1]) / 2: + values[values.length / 2]; + } + + @Test + public void testMemoryUsage_NoHyphenation() { + int[] memories = new int[TRIAL_COUNT]; + // 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().getMemoryUsage(); + } + reportMemoryUsage(median(memories), "MemoryUsage_NoHyphenation"); + } + + @Test + public void testMemoryUsage_Hyphenation() { + int[] memories = new int[TRIAL_COUNT]; + // 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().getMemoryUsage(); + } + reportMemoryUsage(median(memories), "MemoryUsage_Hyphenation"); + } + + @Test + public void testMemoryUsage_NoHyphenation_WidthOnly() { + int[] memories = new int[TRIAL_COUNT]; + // 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(false /* width only */).getMemoryUsage(); + } + reportMemoryUsage(median(memories), "MemoryUsage_NoHyphenation_WidthOnly"); + } + + @Test + public void testMemoryUsage_Hyphenatation_WidthOnly() { + int[] memories = new int[TRIAL_COUNT]; + // 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(false /* width only */).getMemoryUsage(); + } + reportMemoryUsage(median(memories), "MemoryUsage_Hyphenation_WidthOnly"); + } +} diff --git a/apct-tests/perftests/core/src/android/text/MeasuredTextPerfTest.java b/apct-tests/perftests/core/src/android/text/MeasuredTextPerfTest.java new file mode 100644 index 000000000000..98f2bd5e5736 --- /dev/null +++ b/apct-tests/perftests/core/src/android/text/MeasuredTextPerfTest.java @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package android.text; + +import static android.text.TextDirectionHeuristics.LTR; + +import android.perftests.utils.BenchmarkState; +import android.perftests.utils.PerfStatusReporter; + +import android.support.test.filters.LargeTest; +import android.support.test.runner.AndroidJUnit4; + +import android.content.res.ColorStateList; +import android.graphics.Canvas; +import android.graphics.Typeface; +import android.text.Layout; +import android.text.style.TextAppearanceSpan; +import android.view.DisplayListCanvas; +import android.view.RenderNode; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.nio.CharBuffer; +import java.util.Random; + +@LargeTest +@RunWith(AndroidJUnit4.class) +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; + private static final boolean STYLE_TEXT = true; + + private static TextPaint PAINT = new TextPaint(); + private static final int TEXT_WIDTH = WORDS_IN_LINE * WORD_LENGTH * (int) PAINT.getTextSize(); + + public MeasuredTextPerfTest() {} + + @Rule + public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter(); + + private TextPerfUtils mTextUtil = new TextPerfUtils(); + + @Before + public void setUp() { + mTextUtil.resetRandom(0 /* seed */); + } + + @Test + public void testCreate_NoStyled_Hyphenation() { + final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + state.pauseTiming(); + final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT); + state.resumeTiming(); + + 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(); + while (state.keepRunning()) { + state.pauseTiming(); + final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT); + state.resumeTiming(); + + 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(); + while (state.keepRunning()) { + state.pauseTiming(); + final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT); + state.resumeTiming(); + + 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(); + while (state.keepRunning()) { + state.pauseTiming(); + final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT); + state.resumeTiming(); + + 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(); + while (state.keepRunning()) { + state.pauseTiming(); + final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT); + state.resumeTiming(); + + 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(); + while (state.keepRunning()) { + state.pauseTiming(); + final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT); + state.resumeTiming(); + + 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(); + while (state.keepRunning()) { + state.pauseTiming(); + final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT); + state.resumeTiming(); + + 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(); + while (state.keepRunning()) { + state.pauseTiming(); + final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT); + state.resumeTiming(); + + 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 682885b3120d..bab2a859698c 100644 --- a/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java +++ b/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java @@ -43,74 +43,30 @@ import java.util.Random; @LargeTest @RunWith(AndroidJUnit4.class) public class StaticLayoutPerfTest { - - public StaticLayoutPerfTest() {} - - @Rule - public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter(); - 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 int PARA_LENGTH = 500; // Number of characters in a paragraph. - private static final boolean NO_STYLE_TEXT = false; private static final boolean STYLE_TEXT = true; - private Random mRandom; - - private static final String ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; - private static final int ALPHABET_LENGTH = ALPHABET.length(); - - private static final ColorStateList TEXT_COLOR = ColorStateList.valueOf(0x00000000); - private static final String[] FAMILIES = { "sans-serif", "serif", "monospace" }; - private static final int[] STYLES = { - Typeface.NORMAL, Typeface.BOLD, Typeface.ITALIC, Typeface.BOLD_ITALIC - }; - - private final char[] mBuffer = new char[PARA_LENGTH]; - private static TextPaint PAINT = new TextPaint(); private static final int TEXT_WIDTH = WORDS_IN_LINE * WORD_LENGTH * (int) PAINT.getTextSize(); - private CharSequence generateRandomParagraph(int wordLen, boolean applyRandomStyle) { - for (int i = 0; i < PARA_LENGTH; i++) { - if (i % (wordLen + 1) == wordLen) { - mBuffer[i] = ' '; - } else { - mBuffer[i] = ALPHABET.charAt(mRandom.nextInt(ALPHABET_LENGTH)); - } - } - - CharSequence cs = CharBuffer.wrap(mBuffer); - if (!applyRandomStyle) { - return cs; - } - - SpannableStringBuilder ssb = new SpannableStringBuilder(cs); - for (int i = 0; i < ssb.length(); i += WORD_LENGTH) { - final int spanStart = i; - final int spanEnd = (i + WORD_LENGTH) > ssb.length() ? ssb.length() : i + WORD_LENGTH; + public StaticLayoutPerfTest() {} - final TextAppearanceSpan span = new TextAppearanceSpan( - FAMILIES[mRandom.nextInt(FAMILIES.length)], - STYLES[mRandom.nextInt(STYLES.length)], - 24 + mRandom.nextInt(32), // text size. min 24 max 56 - TEXT_COLOR, TEXT_COLOR); + @Rule + public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter(); - ssb.setSpan(span, spanStart, spanEnd, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); - } - return ssb; - } + private TextPerfUtils mTextUtil = new TextPerfUtils(); @Before public void setUp() { - mRandom = new Random(0); + mTextUtil.resetRandom(0 /* seed */); } @Test public void testCreate_FixedText_NoStyle_Greedy_NoHyphenation() { final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); - final CharSequence text = generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT); + final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT); while (state.keepRunning()) { StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH) .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE) @@ -124,7 +80,7 @@ public class StaticLayoutPerfTest { final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); while (state.keepRunning()) { state.pauseTiming(); - final CharSequence text = generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT); + final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT); state.resumeTiming(); StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH) @@ -139,7 +95,7 @@ public class StaticLayoutPerfTest { final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); while (state.keepRunning()) { state.pauseTiming(); - final CharSequence text = generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT); + final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT); state.resumeTiming(); StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH) @@ -154,7 +110,7 @@ public class StaticLayoutPerfTest { final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); while (state.keepRunning()) { state.pauseTiming(); - final CharSequence text = generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT); + final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT); state.resumeTiming(); StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH) @@ -169,7 +125,7 @@ public class StaticLayoutPerfTest { final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); while (state.keepRunning()) { state.pauseTiming(); - final CharSequence text = generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT); + final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT); state.resumeTiming(); StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH) @@ -184,7 +140,7 @@ public class StaticLayoutPerfTest { final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); while (state.keepRunning()) { state.pauseTiming(); - final CharSequence text = generateRandomParagraph(WORD_LENGTH, STYLE_TEXT); + final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT); state.resumeTiming(); StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH) @@ -200,7 +156,7 @@ public class StaticLayoutPerfTest { while (state.keepRunning()) { state.pauseTiming(); final MeasuredText text = new MeasuredText.Builder( - generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT) + mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT) .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE) .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE) .build(); @@ -219,7 +175,7 @@ public class StaticLayoutPerfTest { while (state.keepRunning()) { state.pauseTiming(); final MeasuredText text = new MeasuredText.Builder( - generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT) + mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT) .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE) .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL) .build(); @@ -238,7 +194,7 @@ public class StaticLayoutPerfTest { while (state.keepRunning()) { state.pauseTiming(); final MeasuredText text = new MeasuredText.Builder( - generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT) + mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT) .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED) .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE) .build(); @@ -257,7 +213,7 @@ public class StaticLayoutPerfTest { while (state.keepRunning()) { state.pauseTiming(); final MeasuredText text = new MeasuredText.Builder( - generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT) + mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT) .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED) .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL) .build(); @@ -276,7 +232,7 @@ public class StaticLayoutPerfTest { while (state.keepRunning()) { state.pauseTiming(); final MeasuredText text = new MeasuredText.Builder( - generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT) + mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT) .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE) .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE) .build(); @@ -292,7 +248,7 @@ public class StaticLayoutPerfTest { @Test public void testDraw_FixedText_NoStyled() { final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); - final CharSequence text = generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT); + final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT); final RenderNode node = RenderNode.create("benchmark", null); while (state.keepRunning()) { state.pauseTiming(); @@ -311,7 +267,7 @@ public class StaticLayoutPerfTest { final RenderNode node = RenderNode.create("benchmark", null); while (state.keepRunning()) { state.pauseTiming(); - final CharSequence text = generateRandomParagraph(WORD_LENGTH, STYLE_TEXT); + final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT); final StaticLayout layout = StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build(); final DisplayListCanvas c = node.start(1200, 200); @@ -327,7 +283,7 @@ public class StaticLayoutPerfTest { final RenderNode node = RenderNode.create("benchmark", null); while (state.keepRunning()) { state.pauseTiming(); - final CharSequence text = generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT); + final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT); final StaticLayout layout = StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build(); final DisplayListCanvas c = node.start(1200, 200); @@ -343,7 +299,7 @@ public class StaticLayoutPerfTest { final RenderNode node = RenderNode.create("benchmark", null); while (state.keepRunning()) { state.pauseTiming(); - final CharSequence text = generateRandomParagraph(WORD_LENGTH, STYLE_TEXT); + final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT); final StaticLayout layout = StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build(); final DisplayListCanvas c = node.start(1200, 200); @@ -360,7 +316,7 @@ public class StaticLayoutPerfTest { final RenderNode node = RenderNode.create("benchmark", null); while (state.keepRunning()) { state.pauseTiming(); - final CharSequence text = generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT); + final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT); final StaticLayout layout = StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build(); final DisplayListCanvas c = node.start(1200, 200); @@ -378,7 +334,7 @@ public class StaticLayoutPerfTest { while (state.keepRunning()) { state.pauseTiming(); final MeasuredText text = new MeasuredText.Builder( - generateRandomParagraph(WORD_LENGTH, STYLE_TEXT), PAINT).build(); + mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT), PAINT).build(); final StaticLayout layout = StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build(); final DisplayListCanvas c = node.start(1200, 200); @@ -395,7 +351,7 @@ public class StaticLayoutPerfTest { while (state.keepRunning()) { state.pauseTiming(); final MeasuredText text = new MeasuredText.Builder( - generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT).build(); + mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT).build(); final StaticLayout layout = StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build(); final DisplayListCanvas c = node.start(1200, 200); @@ -412,7 +368,7 @@ public class StaticLayoutPerfTest { while (state.keepRunning()) { state.pauseTiming(); final MeasuredText text = new MeasuredText.Builder( - generateRandomParagraph(WORD_LENGTH, STYLE_TEXT), PAINT).build(); + mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT), PAINT).build(); final StaticLayout layout = StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build(); final DisplayListCanvas c = node.start(1200, 200); @@ -430,7 +386,7 @@ public class StaticLayoutPerfTest { while (state.keepRunning()) { state.pauseTiming(); final MeasuredText text = new MeasuredText.Builder( - generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT).build(); + mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT).build(); final StaticLayout layout = StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build(); final DisplayListCanvas c = node.start(1200, 200); diff --git a/apct-tests/perftests/core/src/android/text/TextPerfUtils.java b/apct-tests/perftests/core/src/android/text/TextPerfUtils.java new file mode 100644 index 000000000000..dccb34be9d07 --- /dev/null +++ b/apct-tests/perftests/core/src/android/text/TextPerfUtils.java @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package android.text; + +import static android.text.TextDirectionHeuristics.LTR; + +import android.perftests.utils.BenchmarkState; +import android.perftests.utils.PerfStatusReporter; + +import android.support.test.filters.LargeTest; +import android.support.test.runner.AndroidJUnit4; + +import android.content.res.ColorStateList; +import android.graphics.Canvas; +import android.graphics.Typeface; +import android.text.Layout; +import android.text.style.TextAppearanceSpan; +import android.view.DisplayListCanvas; +import android.view.RenderNode; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.nio.CharBuffer; +import java.util.Random; + +public class TextPerfUtils { + + private static final int PARA_LENGTH = 500; // Number of characters in a paragraph. + + private Random mRandom = new Random(0); + + private static final String ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + private static final int ALPHABET_LENGTH = ALPHABET.length(); + + private static final ColorStateList TEXT_COLOR = ColorStateList.valueOf(0x00000000); + private static final String[] FAMILIES = { "sans-serif", "serif", "monospace" }; + private static final int[] STYLES = { + Typeface.NORMAL, Typeface.BOLD, Typeface.ITALIC, Typeface.BOLD_ITALIC + }; + + private final char[] mBuffer = new char[PARA_LENGTH]; + + public void resetRandom(long seed) { + mRandom = new Random(seed); + } + + public CharSequence nextRandomParagraph(int wordLen, boolean applyRandomStyle) { + for (int i = 0; i < PARA_LENGTH; i++) { + if (i % (wordLen + 1) == wordLen) { + mBuffer[i] = ' '; + } else { + mBuffer[i] = ALPHABET.charAt(mRandom.nextInt(ALPHABET_LENGTH)); + } + } + + CharSequence cs = CharBuffer.wrap(mBuffer); + if (!applyRandomStyle) { + return cs; + } + + SpannableStringBuilder ssb = new SpannableStringBuilder(cs); + for (int i = 0; i < ssb.length(); i += wordLen) { + final int spanStart = i; + final int spanEnd = (i + wordLen) > ssb.length() ? ssb.length() : i + wordLen; + + final TextAppearanceSpan span = new TextAppearanceSpan( + FAMILIES[mRandom.nextInt(FAMILIES.length)], + STYLES[mRandom.nextInt(STYLES.length)], + 24 + mRandom.nextInt(32), // text size. min 24 max 56 + TEXT_COLOR, TEXT_COLOR); + + ssb.setSpan(span, spanStart, spanEnd, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); + } + return ssb; + } +} diff --git a/api/current.txt b/api/current.txt index 7185c99a143a..e774f07090b5 100644 --- a/api/current.txt +++ b/api/current.txt @@ -38296,6 +38296,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(); } @@ -38322,6 +38323,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); @@ -38411,6 +38413,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(); } @@ -38428,6 +38432,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); diff --git a/api/system-current.txt b/api/system-current.txt index ed3897c7a8bb..396ff1a9aa33 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -2603,12 +2603,17 @@ package android.media.audiopolicy { method public void onStatusChange(); } + public static abstract class AudioPolicy.AudioPolicyVolumeCallback { + method public void onVolumeAdjustment(int); + } + public static class AudioPolicy.Builder { ctor public AudioPolicy.Builder(android.content.Context); method public android.media.audiopolicy.AudioPolicy.Builder addMix(android.media.audiopolicy.AudioMix) throws java.lang.IllegalArgumentException; method public android.media.audiopolicy.AudioPolicy build(); method public void setAudioPolicyFocusListener(android.media.audiopolicy.AudioPolicy.AudioPolicyFocusListener); method public void setAudioPolicyStatusListener(android.media.audiopolicy.AudioPolicy.AudioPolicyStatusListener); + method public android.media.audiopolicy.AudioPolicy.Builder setAudioPolicyVolumeCallback(android.media.audiopolicy.AudioPolicy.AudioPolicyVolumeCallback); method public android.media.audiopolicy.AudioPolicy.Builder setIsAudioFocusPolicy(boolean); method public android.media.audiopolicy.AudioPolicy.Builder setLooper(android.os.Looper) throws java.lang.IllegalArgumentException; } diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index 27fa67212af8..b32af027f2fd 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -1214,8 +1214,14 @@ message AppStartMemoryStateCaptured { // # of major page-faults optional int64 pgmajfault = 5; - // RSS+CACHE(+SWAP) - optional int64 usage_in_bytes = 6; + // RSS + optional int64 rss_in_bytes = 6; + + // CACHE + optional int64 cache_in_bytes = 7; + + // SWAP + optional int64 swap_in_bytes = 8; } /* @@ -1237,8 +1243,14 @@ message ProcessMemoryState { // # of major page-faults optional int64 pgmajfault = 5; - // RSS+CACHE(+SWAP) - optional int64 usage_in_bytes = 6; + // RSS + optional int64 rss_in_bytes = 6; + + // CACHE + optional int64 cache_in_bytes = 7; + + // SWAP + optional int64 swap_in_bytes = 8; } /* @@ -1277,8 +1289,14 @@ message LmkKillOccurred { // # of major page-faults optional int64 pgmajfault = 5; - // RSS+CACHE(+SWAP) - optional int64 usage_in_bytes = 6; + // RSS + optional int64 rss_in_bytes = 6; + + // CACHE + optional int64 cache_in_bytes = 7; + + // SWAP + optional int64 swap_in_bytes = 8; } /* diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index fd7ad88b4a21..e7d0c8be4761 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -17,7 +17,6 @@ package android.app; import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS; - import static java.lang.Character.MIN_VALUE; import android.annotation.CallSuper; @@ -4713,7 +4712,6 @@ public class Activity extends ContextThemeWrapper * their launch had come from the original activity. * @param intent The Intent to start. * @param options ActivityOptions or null. - * @param permissionToken Token received from the system that permits this call to be made. * @param ignoreTargetSecurity If true, the activity manager will not check whether the * caller it is doing the start is, is actually allowed to start the target activity. * If you set this to true, you must set an explicit component in the Intent and do any @@ -4722,7 +4720,7 @@ public class Activity extends ContextThemeWrapper * @hide */ public void startActivityAsCaller(Intent intent, @Nullable Bundle options, - IBinder permissionToken, boolean ignoreTargetSecurity, int userId) { + boolean ignoreTargetSecurity, int userId) { if (mParent != null) { throw new RuntimeException("Can't be called from a child"); } @@ -4730,7 +4728,7 @@ public class Activity extends ContextThemeWrapper Instrumentation.ActivityResult ar = mInstrumentation.execStartActivityAsCaller( this, mMainThread.getApplicationThread(), mToken, this, - intent, -1, options, permissionToken, ignoreTargetSecurity, userId); + intent, -1, options, ignoreTargetSecurity, userId); if (ar != null) { mMainThread.sendActivityResult( mToken, mEmbeddedID, -1, ar.getResultCode(), diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 4d5ac6f45703..e2ce8b177d2e 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -449,31 +449,6 @@ public class ActivityManager { */ public static final int INTENT_SENDER_FOREGROUND_SERVICE = 5; - /** - * Extra included on intents that are delegating the call to - * ActivityManager#startActivityAsCaller to another app. This token is necessary for that call - * to succeed. Type is IBinder. - * @hide - */ - public static final String EXTRA_PERMISSION_TOKEN = "android.app.extra.PERMISSION_TOKEN"; - - /** - * Extra included on intents that contain an EXTRA_INTENT, with options that the contained - * intent may want to be started with. Type is Bundle. - * TODO: remove once the ChooserActivity moves to systemui - * @hide - */ - public static final String EXTRA_OPTIONS = "android.app.extra.OPTIONS"; - - /** - * Extra included on intents that contain an EXTRA_INTENT, use this boolean value for the - * parameter of the same name when starting the contained intent. - * TODO: remove once the ChooserActivity moves to systemui - * @hide - */ - public static final String EXTRA_IGNORE_TARGET_SECURITY = - "android.app.extra.EXTRA_IGNORE_TARGET_SECURITY"; - /** @hide User operation call: success! */ public static final int USER_OP_SUCCESS = 0; diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index 6dcecf197ed2..02be00268a45 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -456,11 +456,10 @@ interface IActivityManager { boolean isTopOfTask(in IBinder token); void notifyLaunchTaskBehindComplete(in IBinder token); void notifyEnterAnimationComplete(in IBinder token); - IBinder requestStartActivityPermissionToken(in IBinder delegatorToken); int startActivityAsCaller(in IApplicationThread caller, in String callingPackage, in Intent intent, in String resolvedType, in IBinder resultTo, in String resultWho, int requestCode, int flags, in ProfilerInfo profilerInfo, in Bundle options, - in IBinder permissionToken, boolean ignoreTargetSecurity, int userId); + boolean ignoreTargetSecurity, int userId); int addAppTask(in IBinder activityToken, in Intent intent, in ActivityManager.TaskDescription description, in Bitmap thumbnail); Point getAppTaskThumbnailSize(); diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java index f90b276ac17f..198bce684c1d 100644 --- a/core/java/android/app/Instrumentation.java +++ b/core/java/android/app/Instrumentation.java @@ -1879,8 +1879,8 @@ public class Instrumentation { */ public ActivityResult execStartActivityAsCaller( Context who, IBinder contextThread, IBinder token, Activity target, - Intent intent, int requestCode, Bundle options, IBinder permissionToken, - boolean ignoreTargetSecurity, int userId) { + Intent intent, int requestCode, Bundle options, boolean ignoreTargetSecurity, + int userId) { IApplicationThread whoThread = (IApplicationThread) contextThread; if (mActivityMonitors != null) { synchronized (mSync) { @@ -1911,8 +1911,7 @@ public class Instrumentation { .startActivityAsCaller(whoThread, who.getBasePackageName(), intent, intent.resolveTypeIfNeeded(who.getContentResolver()), token, target != null ? target.mEmbeddedID : null, - requestCode, 0, null, options, permissionToken, - ignoreTargetSecurity, userId); + requestCode, 0, null, options, ignoreTargetSecurity, userId); checkStartActivityResult(result, intent); } catch (RemoteException e) { throw new RuntimeException("Failure from system", e); diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 3b4511e4e0e3..131abb5e4664 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -8261,7 +8261,7 @@ public class DevicePolicyManager { } /** - * Called by a device or profile owner to restrict packages from accessing metered data. + * Called by a device or profile owner to restrict packages from using metered data. * * @param admin which {@link DeviceAdminReceiver} this request is associated with. * @param packageNames the list of package names to be restricted. @@ -8283,7 +8283,7 @@ public class DevicePolicyManager { /** * Called by a device or profile owner to retrieve the list of packages which are restricted - * by the admin from accessing metered data. + * by the admin from using metered data. * * @param admin which {@link DeviceAdminReceiver} this request is associated with. * @return the list of restricted package names. @@ -8302,6 +8302,30 @@ public class DevicePolicyManager { } /** + * Called by the system to check if a package is restricted from using metered data + * by {@param admin}. + * + * @param admin which {@link DeviceAdminReceiver} this request is associated with. + * @param packageName the package whose restricted status is needed. + * @param userId the user to which {@param packageName} belongs. + * @return {@code true} if the package is restricted by admin, otherwise {@code false} + * @throws SecurityException if the caller doesn't run with {@link Process#SYSTEM_UID} + * @hide + */ + public boolean isMeteredDataDisabledForUser(@NonNull ComponentName admin, String packageName, + @UserIdInt int userId) { + throwIfParentInstance("getMeteredDataDisabledForUser"); + if (mService != null) { + try { + return mService.isMeteredDataDisabledForUser(admin, packageName, userId); + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } + } + return false; + } + + /** * Called by device owners to retrieve device logs from before the device's last reboot. * <p> * <strong> This API is not supported on all devices. Calling this API on unsupported devices diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index 7fc31b1a639d..cba9311ff68b 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -413,4 +413,6 @@ interface IDevicePolicyManager { List<ApnSetting> getOverrideApns(in ComponentName admin); void setOverrideApnsEnabled(in ComponentName admin, boolean enabled); boolean isOverrideApnEnabled(in ComponentName admin); + + boolean isMeteredDataDisabledForUser(in ComponentName admin, String packageName, int userId); } diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 7b27fc023e9d..b3c8737a2831 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -7057,7 +7057,7 @@ public class Intent implements Parcelable, Cloneable { * * @param name The name of the desired item. * - * @return the value of an item that previously added with putExtra() + * @return the value of an item previously added with putExtra(), * or null if none was found. * * @deprecated @@ -7075,7 +7075,7 @@ public class Intent implements Parcelable, Cloneable { * @param defaultValue the value to be returned if no value of the desired * type is stored with the given name. * - * @return the value of an item that previously added with putExtra() + * @return the value of an item previously added with putExtra(), * or the default value if none was found. * * @see #putExtra(String, boolean) @@ -7092,7 +7092,7 @@ public class Intent implements Parcelable, Cloneable { * @param defaultValue the value to be returned if no value of the desired * type is stored with the given name. * - * @return the value of an item that previously added with putExtra() + * @return the value of an item previously added with putExtra(), * or the default value if none was found. * * @see #putExtra(String, byte) @@ -7109,7 +7109,7 @@ public class Intent implements Parcelable, Cloneable { * @param defaultValue the value to be returned if no value of the desired * type is stored with the given name. * - * @return the value of an item that previously added with putExtra() + * @return the value of an item previously added with putExtra(), * or the default value if none was found. * * @see #putExtra(String, short) @@ -7126,7 +7126,7 @@ public class Intent implements Parcelable, Cloneable { * @param defaultValue the value to be returned if no value of the desired * type is stored with the given name. * - * @return the value of an item that previously added with putExtra() + * @return the value of an item previously added with putExtra(), * or the default value if none was found. * * @see #putExtra(String, char) @@ -7143,7 +7143,7 @@ public class Intent implements Parcelable, Cloneable { * @param defaultValue the value to be returned if no value of the desired * type is stored with the given name. * - * @return the value of an item that previously added with putExtra() + * @return the value of an item previously added with putExtra(), * or the default value if none was found. * * @see #putExtra(String, int) @@ -7160,7 +7160,7 @@ public class Intent implements Parcelable, Cloneable { * @param defaultValue the value to be returned if no value of the desired * type is stored with the given name. * - * @return the value of an item that previously added with putExtra() + * @return the value of an item previously added with putExtra(), * or the default value if none was found. * * @see #putExtra(String, long) @@ -7177,7 +7177,7 @@ public class Intent implements Parcelable, Cloneable { * @param defaultValue the value to be returned if no value of the desired * type is stored with the given name. * - * @return the value of an item that previously added with putExtra(), + * @return the value of an item previously added with putExtra(), * or the default value if no such item is present * * @see #putExtra(String, float) @@ -7194,7 +7194,7 @@ public class Intent implements Parcelable, Cloneable { * @param defaultValue the value to be returned if no value of the desired * type is stored with the given name. * - * @return the value of an item that previously added with putExtra() + * @return the value of an item previously added with putExtra(), * or the default value if none was found. * * @see #putExtra(String, double) @@ -7209,7 +7209,7 @@ public class Intent implements Parcelable, Cloneable { * * @param name The name of the desired item. * - * @return the value of an item that previously added with putExtra() + * @return the value of an item previously added with putExtra(), * or null if no String value was found. * * @see #putExtra(String, String) @@ -7223,7 +7223,7 @@ public class Intent implements Parcelable, Cloneable { * * @param name The name of the desired item. * - * @return the value of an item that previously added with putExtra() + * @return the value of an item previously added with putExtra(), * or null if no CharSequence value was found. * * @see #putExtra(String, CharSequence) @@ -7237,7 +7237,7 @@ public class Intent implements Parcelable, Cloneable { * * @param name The name of the desired item. * - * @return the value of an item that previously added with putExtra() + * @return the value of an item previously added with putExtra(), * or null if no Parcelable value was found. * * @see #putExtra(String, Parcelable) @@ -7251,7 +7251,7 @@ public class Intent implements Parcelable, Cloneable { * * @param name The name of the desired item. * - * @return the value of an item that previously added with putExtra() + * @return the value of an item previously added with putExtra(), * or null if no Parcelable[] value was found. * * @see #putExtra(String, Parcelable[]) @@ -7265,8 +7265,9 @@ public class Intent implements Parcelable, Cloneable { * * @param name The name of the desired item. * - * @return the value of an item that previously added with putExtra() - * or null if no ArrayList<Parcelable> value was found. + * @return the value of an item previously added with + * putParcelableArrayListExtra(), or null if no + * ArrayList<Parcelable> value was found. * * @see #putParcelableArrayListExtra(String, ArrayList) */ @@ -7279,7 +7280,7 @@ public class Intent implements Parcelable, Cloneable { * * @param name The name of the desired item. * - * @return the value of an item that previously added with putExtra() + * @return the value of an item previously added with putExtra(), * or null if no Serializable value was found. * * @see #putExtra(String, Serializable) @@ -7293,8 +7294,9 @@ public class Intent implements Parcelable, Cloneable { * * @param name The name of the desired item. * - * @return the value of an item that previously added with putExtra() - * or null if no ArrayList<Integer> value was found. + * @return the value of an item previously added with + * putIntegerArrayListExtra(), or null if no + * ArrayList<Integer> value was found. * * @see #putIntegerArrayListExtra(String, ArrayList) */ @@ -7307,8 +7309,9 @@ public class Intent implements Parcelable, Cloneable { * * @param name The name of the desired item. * - * @return the value of an item that previously added with putExtra() - * or null if no ArrayList<String> value was found. + * @return the value of an item previously added with + * putStringArrayListExtra(), or null if no + * ArrayList<String> value was found. * * @see #putStringArrayListExtra(String, ArrayList) */ @@ -7321,8 +7324,9 @@ public class Intent implements Parcelable, Cloneable { * * @param name The name of the desired item. * - * @return the value of an item that previously added with putExtra() - * or null if no ArrayList<CharSequence> value was found. + * @return the value of an item previously added with + * putCharSequenceArrayListExtra, or null if no + * ArrayList<CharSequence> value was found. * * @see #putCharSequenceArrayListExtra(String, ArrayList) */ @@ -7335,7 +7339,7 @@ public class Intent implements Parcelable, Cloneable { * * @param name The name of the desired item. * - * @return the value of an item that previously added with putExtra() + * @return the value of an item previously added with putExtra(), * or null if no boolean array value was found. * * @see #putExtra(String, boolean[]) @@ -7349,7 +7353,7 @@ public class Intent implements Parcelable, Cloneable { * * @param name The name of the desired item. * - * @return the value of an item that previously added with putExtra() + * @return the value of an item previously added with putExtra(), * or null if no byte array value was found. * * @see #putExtra(String, byte[]) @@ -7363,7 +7367,7 @@ public class Intent implements Parcelable, Cloneable { * * @param name The name of the desired item. * - * @return the value of an item that previously added with putExtra() + * @return the value of an item previously added with putExtra(), * or null if no short array value was found. * * @see #putExtra(String, short[]) @@ -7377,7 +7381,7 @@ public class Intent implements Parcelable, Cloneable { * * @param name The name of the desired item. * - * @return the value of an item that previously added with putExtra() + * @return the value of an item previously added with putExtra(), * or null if no char array value was found. * * @see #putExtra(String, char[]) @@ -7391,7 +7395,7 @@ public class Intent implements Parcelable, Cloneable { * * @param name The name of the desired item. * - * @return the value of an item that previously added with putExtra() + * @return the value of an item previously added with putExtra(), * or null if no int array value was found. * * @see #putExtra(String, int[]) @@ -7405,7 +7409,7 @@ public class Intent implements Parcelable, Cloneable { * * @param name The name of the desired item. * - * @return the value of an item that previously added with putExtra() + * @return the value of an item previously added with putExtra(), * or null if no long array value was found. * * @see #putExtra(String, long[]) @@ -7419,7 +7423,7 @@ public class Intent implements Parcelable, Cloneable { * * @param name The name of the desired item. * - * @return the value of an item that previously added with putExtra() + * @return the value of an item previously added with putExtra(), * or null if no float array value was found. * * @see #putExtra(String, float[]) @@ -7433,7 +7437,7 @@ public class Intent implements Parcelable, Cloneable { * * @param name The name of the desired item. * - * @return the value of an item that previously added with putExtra() + * @return the value of an item previously added with putExtra(), * or null if no double array value was found. * * @see #putExtra(String, double[]) @@ -7447,7 +7451,7 @@ public class Intent implements Parcelable, Cloneable { * * @param name The name of the desired item. * - * @return the value of an item that previously added with putExtra() + * @return the value of an item previously added with putExtra(), * or null if no String array value was found. * * @see #putExtra(String, String[]) @@ -7461,7 +7465,7 @@ public class Intent implements Parcelable, Cloneable { * * @param name The name of the desired item. * - * @return the value of an item that previously added with putExtra() + * @return the value of an item previously added with putExtra(), * or null if no CharSequence array value was found. * * @see #putExtra(String, CharSequence[]) @@ -7475,7 +7479,7 @@ public class Intent implements Parcelable, Cloneable { * * @param name The name of the desired item. * - * @return the value of an item that previously added with putExtra() + * @return the value of an item previously added with putExtra(), * or null if no Bundle value was found. * * @see #putExtra(String, Bundle) @@ -7489,7 +7493,7 @@ public class Intent implements Parcelable, Cloneable { * * @param name The name of the desired item. * - * @return the value of an item that previously added with putExtra() + * @return the value of an item previously added with putExtra(), * or null if no IBinder value was found. * * @see #putExtra(String, IBinder) @@ -7509,7 +7513,7 @@ public class Intent implements Parcelable, Cloneable { * @param defaultValue The default value to return in case no item is * associated with the key 'name' * - * @return the value of an item that previously added with putExtra() + * @return the value of an item previously added with putExtra(), * or defaultValue if none was found. * * @see #putExtra diff --git a/core/java/android/database/CursorWindow.java b/core/java/android/database/CursorWindow.java index f84ec65fe0ae..a748f4d2cbce 100644 --- a/core/java/android/database/CursorWindow.java +++ b/core/java/android/database/CursorWindow.java @@ -28,6 +28,7 @@ import android.util.Log; import android.util.LongSparseArray; import android.util.SparseIntArray; +import dalvik.annotation.optimization.FastNative; import dalvik.system.CloseGuard; /** @@ -62,28 +63,43 @@ public class CursorWindow extends SQLiteClosable implements Parcelable { private static native void nativeDispose(long windowPtr); private static native void nativeWriteToParcel(long windowPtr, Parcel parcel); + private static native String nativeGetName(long windowPtr); + private static native byte[] nativeGetBlob(long windowPtr, int row, int column); + private static native String nativeGetString(long windowPtr, int row, int column); + private static native void nativeCopyStringToBuffer(long windowPtr, int row, int column, + CharArrayBuffer buffer); + private static native boolean nativePutBlob(long windowPtr, byte[] value, int row, int column); + private static native boolean nativePutString(long windowPtr, String value, + int row, int column); + + // Below native methods don't do unconstrained work, so are FastNative for performance + + @FastNative private static native void nativeClear(long windowPtr); + @FastNative private static native int nativeGetNumRows(long windowPtr); + @FastNative private static native boolean nativeSetNumColumns(long windowPtr, int columnNum); + @FastNative private static native boolean nativeAllocRow(long windowPtr); + @FastNative private static native void nativeFreeLastRow(long windowPtr); + @FastNative private static native int nativeGetType(long windowPtr, int row, int column); - private static native byte[] nativeGetBlob(long windowPtr, int row, int column); - private static native String nativeGetString(long windowPtr, int row, int column); + @FastNative private static native long nativeGetLong(long windowPtr, int row, int column); + @FastNative private static native double nativeGetDouble(long windowPtr, int row, int column); - private static native void nativeCopyStringToBuffer(long windowPtr, int row, int column, - CharArrayBuffer buffer); - private static native boolean nativePutBlob(long windowPtr, byte[] value, int row, int column); - private static native boolean nativePutString(long windowPtr, String value, int row, int column); + @FastNative private static native boolean nativePutLong(long windowPtr, long value, int row, int column); + @FastNative private static native boolean nativePutDouble(long windowPtr, double value, int row, int column); + @FastNative private static native boolean nativePutNull(long windowPtr, int row, int column); - private static native String nativeGetName(long windowPtr); /** * Creates a new empty cursor window and gives it a name. diff --git a/core/java/android/hardware/Sensor.java b/core/java/android/hardware/Sensor.java index 7fb0c89e7f92..7297426130ac 100644 --- a/core/java/android/hardware/Sensor.java +++ b/core/java/android/hardware/Sensor.java @@ -22,7 +22,9 @@ import android.os.Build; /** * Class representing a sensor. Use {@link SensorManager#getSensorList} to get - * the list of available Sensors. + * the list of available sensors. For more information about Android sensors, + * read the + * <a href="/guide/topics/sensors/sensors_motion.html">Motion Sensors guide</a>.</p> * * @see SensorManager * @see SensorEventListener diff --git a/core/java/android/inputmethodservice/KeyboardView.java b/core/java/android/inputmethodservice/KeyboardView.java index 13b9206b12ee..2306e5fa3dc1 100644 --- a/core/java/android/inputmethodservice/KeyboardView.java +++ b/core/java/android/inputmethodservice/KeyboardView.java @@ -61,6 +61,7 @@ import java.util.Map; * @attr ref android.R.styleable#KeyboardView_keyBackground * @attr ref android.R.styleable#KeyboardView_keyPreviewLayout * @attr ref android.R.styleable#KeyboardView_keyPreviewOffset + * @attr ref android.R.styleable#KeyboardView_keyPreviewHeight * @attr ref android.R.styleable#KeyboardView_labelTextSize * @attr ref android.R.styleable#KeyboardView_keyTextSize * @attr ref android.R.styleable#KeyboardView_keyTextColor diff --git a/core/java/android/os/BatteryManagerInternal.java b/core/java/android/os/BatteryManagerInternal.java index f3a95b90d5c1..a86237dd271f 100644 --- a/core/java/android/os/BatteryManagerInternal.java +++ b/core/java/android/os/BatteryManagerInternal.java @@ -24,26 +24,63 @@ package android.os; public abstract class BatteryManagerInternal { /** * Returns true if the device is plugged into any of the specified plug types. + * + * This is a simple accessor that's safe to be called from any locks, but internally it may + * wait on the battery service lock. */ public abstract boolean isPowered(int plugTypeSet); /** * Returns the current plug type. + * + * This is a simple accessor that's safe to be called from any locks, but internally it may + * wait on the battery service lock. */ public abstract int getPlugType(); /** * Returns battery level as a percentage. + * + * This is a simple accessor that's safe to be called from any locks, but internally it may + * wait on the battery service lock. */ public abstract int getBatteryLevel(); /** + * Instantaneous battery capacity in uA-h, as defined in the HealthInfo HAL struct. + * Please note apparently it could be bigger than {@link #getBatteryFullCharge}. + * + * This is a simple accessor that's safe to be called from any locks, but internally it may + * wait on the battery service lock. + * + * @see android.hardware.health.V1_0.HealthInfo#batteryChargeCounter + */ + public abstract int getBatteryChargeCounter(); + + /** + * Battery charge value when it is considered to be "full" in uA-h , as defined in the + * HealthInfo HAL struct. + * + * This is a simple accessor that's safe to be called from any locks, but internally it may + * wait on the battery service lock. + * + * @see android.hardware.health.V1_0.HealthInfo#batteryFullCharge + */ + public abstract int getBatteryFullCharge(); + + /** * Returns whether we currently consider the battery level to be low. + * + * This is a simple accessor that's safe to be called from any locks, but internally it may + * wait on the battery service lock. */ public abstract boolean getBatteryLevelLow(); /** * Returns a non-zero value if an unsupported charger is attached. + * + * This is a simple accessor that's safe to be called from any locks, but internally it may + * wait on the battery service lock. */ public abstract int getInvalidCharger(); } diff --git a/core/java/android/security/IKeystoreService.aidl b/core/java/android/security/IKeystoreService.aidl index 738eb6865230..c4b7715b458a 100644 --- a/core/java/android/security/IKeystoreService.aidl +++ b/core/java/android/security/IKeystoreService.aidl @@ -71,7 +71,7 @@ interface IKeystoreService { in byte[] entropy); int abort(IBinder handle); boolean isOperationAuthorized(IBinder token); - int addAuthToken(in byte[] authToken); + int addAuthToken(in byte[] authToken, in int androidId); int onUserAdded(int userId, int parentId); int onUserRemoved(int userId); int attestKey(String alias, in KeymasterArguments params, out KeymasterCertificateChain chain); diff --git a/core/java/android/security/keymaster/KeymasterDefs.java b/core/java/android/security/keymaster/KeymasterDefs.java index 346437032845..479231db70b9 100644 --- a/core/java/android/security/keymaster/KeymasterDefs.java +++ b/core/java/android/security/keymaster/KeymasterDefs.java @@ -74,6 +74,7 @@ public final class KeymasterDefs { public static final int KM_TAG_AUTH_TIMEOUT = KM_UINT | 505; public static final int KM_TAG_ALLOW_WHILE_ON_BODY = KM_BOOL | 506; public static final int KM_TAG_TRUSTED_USER_PRESENCE_REQUIRED = KM_BOOL | 507; + public static final int KM_TAG_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; @@ -215,6 +216,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; @@ -261,6 +263,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/text/MeasuredParagraph.java b/core/java/android/text/MeasuredParagraph.java index 45fbf6f58209..aafcf44a73fc 100644 --- a/core/java/android/text/MeasuredParagraph.java +++ b/core/java/android/text/MeasuredParagraph.java @@ -672,6 +672,13 @@ public class MeasuredParagraph { return width; } + /** + * This only works if the MeasuredParagraph is computed with buildForStaticLayout. + */ + @IntRange(from = 0) int getMemoryUsage() { + return nGetMemoryUsage(mNativePtr); + } + private static native /* Non Zero */ long nInitBuilder(); /** @@ -718,4 +725,7 @@ public class MeasuredParagraph { @CriticalNative private static native /* Non Zero */ long nGetReleaseFunc(); + + @CriticalNative + private static native int nGetMemoryUsage(/* Non Zero */ long nativePtr); } diff --git a/core/java/android/text/MeasuredText.java b/core/java/android/text/MeasuredText.java index ff23395d92a5..bb7a9e0b7906 100644 --- a/core/java/android/text/MeasuredText.java +++ b/core/java/android/text/MeasuredText.java @@ -333,6 +333,20 @@ public class MeasuredText implements Spanned { 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 // diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java index 25a177edd27c..8af4b7150a31 100644 --- a/core/java/android/util/FeatureFlagUtils.java +++ b/core/java/android/util/FeatureFlagUtils.java @@ -42,7 +42,6 @@ public class FeatureFlagUtils { DEFAULT_FLAGS.put("settings_connected_device_v2", "true"); DEFAULT_FLAGS.put("settings_battery_v2", "true"); DEFAULT_FLAGS.put("settings_battery_display_app_list", "false"); - DEFAULT_FLAGS.put("settings_security_settings_v2", "true"); DEFAULT_FLAGS.put("settings_zone_picker_v2", "true"); DEFAULT_FLAGS.put("settings_suggestion_ui_v2", "false"); DEFAULT_FLAGS.put("settings_about_phone_v2", "false"); diff --git a/core/java/android/widget/MediaControlView2.java b/core/java/android/widget/MediaControlView2.java index 84d18509f14a..a4da05f8b982 100644 --- a/core/java/android/widget/MediaControlView2.java +++ b/core/java/android/widget/MediaControlView2.java @@ -26,7 +26,6 @@ import android.media.update.MediaControlView2Provider; import android.media.update.ViewProvider; import android.util.AttributeSet; import android.view.MotionEvent; -import android.view.View; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -41,10 +40,10 @@ import java.lang.annotation.RetentionPolicy; * adds it to the view. * 2) Initialize MediaControlView2 programmatically and add it to a ViewGroup instance. * - * In the first option, VideoView2 automatically connects MediaControlView2 to MediaController2, + * In the first option, VideoView2 automatically connects MediaControlView2 to MediaController, * which is necessary to communicate with MediaSession2. In the second option, however, the - * developer needs to manually retrieve a MediaController2 instance and set it to MediaControlView2 - * by calling setController(MediaController2 controller). + * developer needs to manually retrieve a MediaController instance and set it to MediaControlView2 + * by calling setController(MediaController controller). * * TODO PUBLIC API * @hide @@ -67,18 +66,67 @@ public class MediaControlView2 extends FrameLayout { @Retention(RetentionPolicy.SOURCE) public @interface Button {} + /** + * MediaControlView2 button value for playing and pausing media. + */ public static final int BUTTON_PLAY_PAUSE = 1; + /** + * MediaControlView2 button value for jumping 30 seconds forward. + */ public static final int BUTTON_FFWD = 2; + /** + * MediaControlView2 button value for jumping 10 seconds backward. + */ public static final int BUTTON_REW = 3; + /** + * MediaControlView2 button value for jumping to next media. + */ public static final int BUTTON_NEXT = 4; + /** + * MediaControlView2 button value for jumping to previous media. + */ public static final int BUTTON_PREV = 5; + /** + * MediaControlView2 button value for showing/hiding subtitle track. + */ public static final int BUTTON_SUBTITLE = 6; + /** + * MediaControlView2 button value for toggling full screen. + */ public static final int BUTTON_FULL_SCREEN = 7; + /** + * MediaControlView2 button value for showing/hiding overflow buttons. + */ public static final int BUTTON_OVERFLOW = 8; + /** + * MediaControlView2 button value for muting audio. + */ public static final int BUTTON_MUTE = 9; + /** + * MediaControlView2 button value for adjusting aspect ratio of view. + */ public static final int BUTTON_ASPECT_RATIO = 10; + /** + * MediaControlView2 button value for showing/hiding settings page. + */ public static final int BUTTON_SETTINGS = 11; + /** + * String for receiving command to show subtitle from MediaSession. Can be checked by + * implementing {@link android.media.session.MediaSession.Callback#onCommand} + */ + public static final String COMMAND_SHOW_SUBTITLE = "showSubtitle"; + /** + * String for receiving command to hide subtitle from MediaSession. Can be checked by + * implementing {@link android.media.session.MediaSession.Callback#onCommand} + */ + public static final String COMMAND_HIDE_SUBTITLE = "hideSubtitle"; + /** + * String for receiving command to set fullscreen from MediaSession. Can be checked by + * implementing {@link android.media.session.MediaSession.Callback#onCommand} + */ + public static final String COMMAND_SET_FULLSCREEN = "setFullscreen"; + private final MediaControlView2Provider mProvider; public MediaControlView2(@NonNull Context context) { @@ -90,12 +138,12 @@ public class MediaControlView2 extends FrameLayout { } public MediaControlView2(@NonNull Context context, @Nullable AttributeSet attrs, - int defStyleAttr) { + int defStyleAttr) { this(context, attrs, defStyleAttr, 0); } public MediaControlView2(@NonNull Context context, @Nullable AttributeSet attrs, - int defStyleAttr, int defStyleRes) { + int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); mProvider = ApiLoader.getProvider(context) @@ -110,7 +158,7 @@ public class MediaControlView2 extends FrameLayout { } /** - * Sets MediaController2 instance to control corresponding MediaSession2. + * Sets MediaController instance to control corresponding MediaSession. */ public void setController(MediaController controller) { mProvider.setController_impl(controller); @@ -128,7 +176,7 @@ public class MediaControlView2 extends FrameLayout { * Shows the control view on screen. It will disappear automatically after {@code timeout} * milliseconds of inactivity. */ - public void show(int timeout) { + public void show(long timeout) { mProvider.show_impl(timeout); } @@ -147,41 +195,26 @@ public class MediaControlView2 extends FrameLayout { } /** - * If the media selected has a subtitle track, calling this method will display the subtitle at - * the bottom of the view. If a media has multiple subtitle tracks, this method will select the - * first one of them. - */ - public void showSubtitle() { - mProvider.showSubtitle_impl(); - } - - /** - * Hides the currently displayed subtitle. - */ - public void hideSubtitle() { - mProvider.hideSubtitle_impl(); - } - - /** - * Set listeners for previous and next buttons to customize the behavior of clicking them. - * The UI for these buttons are provided as default and will be automatically displayed when - * this method is called. - * - * @param next Listener for clicking next button - * @param prev Listener for clicking previous button - */ - public void setPrevNextListeners(View.OnClickListener next, View.OnClickListener prev) { - mProvider.setPrevNextListeners_impl(next, prev); - } - - /** - * Hides the specified button from view. + * Changes the visibility state of an individual button. Default value is View.Visible. * - * @param button the constant integer assigned to individual buttons - * @param visible whether the button should be visible or not + * @param button the {@code Button} assigned to individual buttons + * <ul> + * <li>{@link #BUTTON_PLAY_PAUSE} + * <li>{@link #BUTTON_FFWD} + * <li>{@link #BUTTON_REW} + * <li>{@link #BUTTON_NEXT} + * <li>{@link #BUTTON_PREV} + * <li>{@link #BUTTON_SUBTITLE} + * <li>{@link #BUTTON_FULL_SCREEN} + * <li>{@link #BUTTON_MUTE} + * <li>{@link #BUTTON_OVERFLOW} + * <li>{@link #BUTTON_ASPECT_RATIO} + * <li>{@link #BUTTON_SETTINGS} + * </ul> + * @param visibility One of {@link #VISIBLE}, {@link #INVISIBLE}, or {@link #GONE}. */ - public void setButtonVisibility(int button, boolean visible) { - mProvider.setButtonVisibility_impl(button, visible); + public void setButtonVisibility(@Button int button, @Visibility int visibility) { + mProvider.setButtonVisibility_impl(button, visibility); } @Override diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index 997d47fe8cf0..6e0ba3413e8c 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -841,7 +841,7 @@ public class ChooserActivity extends ResolverActivity { } @Override - public boolean startAsCaller(ResolverActivity activity, Bundle options, int userId) { + public boolean startAsCaller(Activity activity, Bundle options, int userId) { final Intent intent = getBaseIntentToSend(); if (intent == null) { return false; @@ -860,7 +860,8 @@ public class ChooserActivity extends ResolverActivity { final boolean ignoreTargetSecurity = mSourceInfo != null && mSourceInfo.getResolvedComponentName().getPackageName() .equals(mChooserTarget.getComponentName().getPackageName()); - return activity.startAsCallerImpl(intent, options, ignoreTargetSecurity, userId); + activity.startActivityAsCaller(intent, options, ignoreTargetSecurity, userId); + return true; } @Override diff --git a/core/java/com/android/internal/app/IntentForwarderActivity.java b/core/java/com/android/internal/app/IntentForwarderActivity.java index 86731bcb4bf6..398d08791b5c 100644 --- a/core/java/com/android/internal/app/IntentForwarderActivity.java +++ b/core/java/com/android/internal/app/IntentForwarderActivity.java @@ -107,7 +107,7 @@ public class IntentForwarderActivity extends Activity { || ChooserActivity.class.getName().equals(ri.activityInfo.name)); try { - startActivityAsCaller(newIntent, null, null, false, targetUserId); + startActivityAsCaller(newIntent, null, false, targetUserId); } catch (RuntimeException e) { int launchedFromUid = -1; String launchedFromPackage = "?"; diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java index d6d44908a15b..ceb06f511108 100644 --- a/core/java/com/android/internal/app/ResolverActivity.java +++ b/core/java/com/android/internal/app/ResolverActivity.java @@ -43,7 +43,6 @@ import android.net.Uri; import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; -import android.os.IBinder; import android.os.PatternMatcher; import android.os.RemoteException; import android.os.StrictMode; @@ -858,36 +857,6 @@ public class ResolverActivity extends Activity { } } - public boolean startAsCallerImpl(Intent intent, Bundle options, boolean ignoreTargetSecurity, - int userId) { - // Pass intent to delegate chooser activity with permission token. - // TODO: This should move to a trampoline Activity in the system when the ChooserActivity - // moves into systemui - try { - // TODO: Once this is a small springboard activity, it can move off the UI process - // and we can move the request method to ActivityManagerInternal. - IBinder permissionToken = ActivityManager.getService() - .requestStartActivityPermissionToken(getActivityToken()); - final Intent chooserIntent = new Intent(); - final ComponentName delegateActivity = ComponentName.unflattenFromString( - Resources.getSystem().getString(R.string.config_chooserActivity)); - chooserIntent.setClassName(delegateActivity.getPackageName(), - delegateActivity.getClassName()); - chooserIntent.putExtra(ActivityManager.EXTRA_PERMISSION_TOKEN, permissionToken); - - // TODO: These extras will change as chooser activity moves into systemui - chooserIntent.putExtra(Intent.EXTRA_INTENT, intent); - chooserIntent.putExtra(ActivityManager.EXTRA_OPTIONS, options); - chooserIntent.putExtra(ActivityManager.EXTRA_IGNORE_TARGET_SECURITY, - ignoreTargetSecurity); - chooserIntent.putExtra(Intent.EXTRA_USER_ID, userId); - startActivity(chooserIntent); - } catch (RemoteException e) { - Log.e(TAG, e.toString()); - } - return true; - } - public void onActivityStarted(TargetInfo cti) { // Do nothing } @@ -1212,8 +1181,9 @@ public class ResolverActivity extends Activity { } @Override - public boolean startAsCaller(ResolverActivity activity, Bundle options, int userId) { - return activity.startAsCallerImpl(mResolvedIntent, options, false, userId); + public boolean startAsCaller(Activity activity, Bundle options, int userId) { + activity.startActivityAsCaller(mResolvedIntent, options, false, userId); + return true; } @Override @@ -1272,7 +1242,7 @@ public class ResolverActivity extends Activity { * @param userId userId to start as or {@link UserHandle#USER_NULL} for activity's caller * @return true if the start completed successfully */ - boolean startAsCaller(ResolverActivity activity, Bundle options, int userId); + boolean startAsCaller(Activity activity, Bundle options, int userId); /** * Start the activity referenced by this target as a given user. diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index e91b67a4b178..7def87655ae3 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -10896,7 +10896,7 @@ public class BatteryStatsImpl extends BatteryStats { return null; } - /** + /** * Distribute WiFi energy info and network traffic to apps. * @param info The energy information from the WiFi controller. */ @@ -11160,6 +11160,9 @@ public class BatteryStatsImpl extends BatteryStats { } } + private ModemActivityInfo mLastModemActivityInfo = + new ModemActivityInfo(0, 0, 0, new int[0], 0, 0); + /** * Distribute Cell radio energy info and network traffic to apps. */ @@ -11180,6 +11183,22 @@ public class BatteryStatsImpl extends BatteryStats { } } + int rxTimeMs = 0; + int[] txTimeMs = new int[ModemActivityInfo.TX_POWER_LEVELS]; + int idleTimeMs = 0; + int sleepTimeMs = 0; + if (activityInfo != null) { + rxTimeMs = activityInfo.getRxTimeMillis() - mLastModemActivityInfo.getRxTimeMillis(); + for (int i = 0; i < ModemActivityInfo.TX_POWER_LEVELS; i++) { + txTimeMs[i] = activityInfo.getTxTimeMillis()[i] + - mLastModemActivityInfo.getTxTimeMillis()[i]; + } + idleTimeMs = + activityInfo.getIdleTimeMillis() - mLastModemActivityInfo.getIdleTimeMillis(); + sleepTimeMs = + activityInfo.getSleepTimeMillis() - mLastModemActivityInfo.getSleepTimeMillis(); + } + synchronized (this) { if (!mOnBatteryInternal) { if (delta != null) { @@ -11191,11 +11210,11 @@ public class BatteryStatsImpl extends BatteryStats { if (activityInfo != null) { mHasModemReporting = true; mModemActivity.getIdleTimeCounter().addCountLocked( - activityInfo.getIdleTimeMillis()); - mModemActivity.getRxTimeCounter().addCountLocked(activityInfo.getRxTimeMillis()); + idleTimeMs); + mModemActivity.getRxTimeCounter().addCountLocked(rxTimeMs); for (int lvl = 0; lvl < ModemActivityInfo.TX_POWER_LEVELS; lvl++) { mModemActivity.getTxTimeCounters()[lvl] - .addCountLocked(activityInfo.getTxTimeMillis()[lvl]); + .addCountLocked(txTimeMs[lvl]); } // POWER_MODEM_CONTROLLER_OPERATING_VOLTAGE is measured in mV, so convert to V. @@ -11203,16 +11222,15 @@ public class BatteryStatsImpl extends BatteryStats { PowerProfile.POWER_MODEM_CONTROLLER_OPERATING_VOLTAGE) / 1000.0; if (opVolt != 0) { double energyUsed = - activityInfo.getSleepTimeMillis() * + sleepTimeMs * mPowerProfile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_SLEEP) - + activityInfo.getIdleTimeMillis() * + + idleTimeMs * mPowerProfile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_IDLE) - + activityInfo.getRxTimeMillis() * + + rxTimeMs * mPowerProfile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_RX); - int[] txCurrentMa = activityInfo.getTxTimeMillis(); - for (int i = 0; i < Math.min(txCurrentMa.length, + for (int i = 0; i < Math.min(txTimeMs.length, SignalStrength.NUM_SIGNAL_STRENGTH_BINS); i++) { - energyUsed += txCurrentMa[i] * mPowerProfile.getAveragePower( + energyUsed += txTimeMs[i] * mPowerProfile.getAveragePower( PowerProfile.POWER_MODEM_CONTROLLER_TX, i); } @@ -11293,7 +11311,7 @@ public class BatteryStatsImpl extends BatteryStats { ControllerActivityCounterImpl activityCounter = u.getOrCreateModemControllerActivityLocked(); if (totalRxPackets > 0 && entry.rxPackets > 0) { - final long rxMs = (entry.rxPackets * activityInfo.getRxTimeMillis()) + final long rxMs = (entry.rxPackets * rxTimeMs) / totalRxPackets; activityCounter.getRxTimeCounter().addCountLocked(rxMs); } @@ -11301,7 +11319,7 @@ public class BatteryStatsImpl extends BatteryStats { if (totalTxPackets > 0 && entry.txPackets > 0) { for (int lvl = 0; lvl < ModemActivityInfo.TX_POWER_LEVELS; lvl++) { long txMs = - entry.txPackets * activityInfo.getTxTimeMillis()[lvl]; + entry.txPackets * txTimeMs[lvl]; txMs /= totalTxPackets; activityCounter.getTxTimeCounters()[lvl].addCountLocked(txMs); } @@ -11322,6 +11340,10 @@ public class BatteryStatsImpl extends BatteryStats { } } + // Cache last value for comparison. + private BluetoothActivityEnergyInfo mLastBluetoothActivityEnergyInfo = + new BluetoothActivityEnergyInfo(0, 0, 0, 0, 0, 0); + /** * Distribute Bluetooth energy info and network traffic to apps. * @param info The energy information from the bluetooth controller. @@ -11338,14 +11360,17 @@ public class BatteryStatsImpl extends BatteryStats { mHasBluetoothReporting = true; final long elapsedRealtimeMs = mClocks.elapsedRealtime(); - final long rxTimeMs = info.getControllerRxTimeMillis(); - final long txTimeMs = info.getControllerTxTimeMillis(); - + final long rxTimeMs = info.getControllerRxTimeMillis() - + mLastBluetoothActivityEnergyInfo.getControllerRxTimeMillis(); + final long txTimeMs = info.getControllerTxTimeMillis() - + mLastBluetoothActivityEnergyInfo.getControllerTxTimeMillis(); + final long idleTimeMs = info.getControllerIdleTimeMillis() - + mLastBluetoothActivityEnergyInfo.getControllerIdleTimeMillis(); if (DEBUG_ENERGY) { Slog.d(TAG, "------ BEGIN BLE power blaming ------"); Slog.d(TAG, " Tx Time: " + txTimeMs + " ms"); Slog.d(TAG, " Rx Time: " + rxTimeMs + " ms"); - Slog.d(TAG, " Idle Time: " + info.getControllerIdleTimeMillis() + " ms"); + Slog.d(TAG, " Idle Time: " + idleTimeMs + " ms"); } long totalScanTimeMs = 0; @@ -11424,9 +11449,25 @@ public class BatteryStatsImpl extends BatteryStats { long totalRxBytes = 0; final UidTraffic[] uidTraffic = info.getUidTraffic(); - final int numUids = uidTraffic != null ? uidTraffic.length : 0; - for (int i = 0; i < numUids; i++) { - final UidTraffic traffic = uidTraffic[i]; + final UidTraffic[] lastUidTraffic = mLastBluetoothActivityEnergyInfo.getUidTraffic(); + final ArrayList<UidTraffic> deltaTraffic = new ArrayList<>(); + int m = 0, n = 0; + for (; m < uidTraffic.length && n < lastUidTraffic.length; m++) { + final UidTraffic traffic = uidTraffic[m]; + final UidTraffic lastTraffic = lastUidTraffic[n]; + if (traffic.getUid() == lastTraffic.getUid()) { + deltaTraffic.add(new UidTraffic(traffic.getUid(), + traffic.getRxBytes() - lastTraffic.getRxBytes(), + traffic.getTxBytes() - lastTraffic.getTxBytes())); + n++; + } + } + for (; m < uidTraffic.length; m ++) { + deltaTraffic.add(uidTraffic[m]); + } + + for (int i = 0, j = 0; i < deltaTraffic.size(); i++) { + final UidTraffic traffic = deltaTraffic.get(i); // Add to the global counters. mNetworkByteActivityCounters[NETWORK_BT_RX_DATA].addCountLocked( @@ -11446,8 +11487,8 @@ public class BatteryStatsImpl extends BatteryStats { if ((totalTxBytes != 0 || totalRxBytes != 0) && (leftOverRxTimeMs != 0 || leftOverTxTimeMs != 0)) { - for (int i = 0; i < numUids; i++) { - final UidTraffic traffic = uidTraffic[i]; + for (int i = 0; i < deltaTraffic.size(); i++) { + final UidTraffic traffic = deltaTraffic.get(i); final Uid u = getUidStatsLocked(mapUid(traffic.getUid())); final ControllerActivityCounterImpl counter = @@ -11478,12 +11519,9 @@ public class BatteryStatsImpl extends BatteryStats { } } - mBluetoothActivity.getRxTimeCounter().addCountLocked( - info.getControllerRxTimeMillis()); - mBluetoothActivity.getTxTimeCounters()[0].addCountLocked( - info.getControllerTxTimeMillis()); - mBluetoothActivity.getIdleTimeCounter().addCountLocked( - info.getControllerIdleTimeMillis()); + mBluetoothActivity.getRxTimeCounter().addCountLocked(rxTimeMs); + mBluetoothActivity.getTxTimeCounters()[0].addCountLocked(txTimeMs); + mBluetoothActivity.getIdleTimeCounter().addCountLocked(idleTimeMs); // POWER_BLUETOOTH_CONTROLLER_OPERATING_VOLTAGE is measured in mV, so convert to V. final double opVolt = mPowerProfile.getAveragePower( @@ -11491,8 +11529,10 @@ public class BatteryStatsImpl extends BatteryStats { if (opVolt != 0) { // We store the power drain as mAms. mBluetoothActivity.getPowerCounter().addCountLocked( - (long) (info.getControllerEnergyUsed() / opVolt)); + (long) ((info.getControllerEnergyUsed() - + mLastBluetoothActivityEnergyInfo.getControllerEnergyUsed() )/ opVolt)); } + mLastBluetoothActivityEnergyInfo = info; } /** diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp index 49cbb545b019..115d0d5a608b 100644 --- a/core/jni/android/graphics/Paint.cpp +++ b/core/jni/android/graphics/Paint.cpp @@ -24,6 +24,7 @@ #include "core_jni_helpers.h" #include <nativehelper/ScopedStringChars.h> #include <nativehelper/ScopedUtfChars.h> +#include <nativehelper/ScopedPrimitiveArray.h> #include "SkBlurDrawLooper.h" #include "SkColorFilter.h" @@ -515,11 +516,10 @@ namespace PaintGlue { jint start, jint end, jint contextStart, jint contextEnd, jboolean isRtl, jint offset) { const Paint* paint = reinterpret_cast<Paint*>(paintHandle); const Typeface* typeface = paint->getAndroidTypeface(); - jchar* textArray = (jchar*) env->GetPrimitiveArrayCritical(text, nullptr); - jfloat result = doRunAdvance(paint, typeface, textArray + contextStart, + ScopedCharArrayRO textArray(env, text); + jfloat result = doRunAdvance(paint, typeface, textArray.get() + contextStart, start - contextStart, end - start, contextEnd - contextStart, isRtl, offset - contextStart); - env->ReleasePrimitiveArrayCritical(text, textArray, JNI_ABORT); return result; } @@ -537,11 +537,10 @@ namespace PaintGlue { jboolean isRtl, jfloat advance) { const Paint* paint = reinterpret_cast<Paint*>(paintHandle); const Typeface* typeface = paint->getAndroidTypeface(); - jchar* textArray = (jchar*) env->GetPrimitiveArrayCritical(text, nullptr); - jint result = doOffsetForAdvance(paint, typeface, textArray + contextStart, + ScopedCharArrayRO textArray(env, text); + jint result = doOffsetForAdvance(paint, typeface, textArray.get() + contextStart, start - contextStart, end - start, contextEnd - contextStart, isRtl, advance); result += contextStart; - env->ReleasePrimitiveArrayCritical(text, textArray, JNI_ABORT); return result; } diff --git a/core/jni/android_database_CursorWindow.cpp b/core/jni/android_database_CursorWindow.cpp index 0d09b56a2935..86cda4455734 100644 --- a/core/jni/android_database_CursorWindow.cpp +++ b/core/jni/android_database_CursorWindow.cpp @@ -519,8 +519,21 @@ static const JNINativeMethod sMethods[] = (void*)nativeDispose }, { "nativeWriteToParcel", "(JLandroid/os/Parcel;)V", (void*)nativeWriteToParcel }, + { "nativeGetName", "(J)Ljava/lang/String;", (void*)nativeGetName }, + { "nativeGetBlob", "(JII)[B", + (void*)nativeGetBlob }, + { "nativeGetString", "(JII)Ljava/lang/String;", + (void*)nativeGetString }, + { "nativeCopyStringToBuffer", "(JIILandroid/database/CharArrayBuffer;)V", + (void*)nativeCopyStringToBuffer }, + { "nativePutBlob", "(J[BII)Z", + (void*)nativePutBlob }, + { "nativePutString", "(JLjava/lang/String;II)Z", + (void*)nativePutString }, + + // ------- @FastNative below here ---------------------- { "nativeClear", "(J)V", (void*)nativeClear }, { "nativeGetNumRows", "(J)I", @@ -533,20 +546,11 @@ static const JNINativeMethod sMethods[] = (void*)nativeFreeLastRow }, { "nativeGetType", "(JII)I", (void*)nativeGetType }, - { "nativeGetBlob", "(JII)[B", - (void*)nativeGetBlob }, - { "nativeGetString", "(JII)Ljava/lang/String;", - (void*)nativeGetString }, { "nativeGetLong", "(JII)J", (void*)nativeGetLong }, { "nativeGetDouble", "(JII)D", (void*)nativeGetDouble }, - { "nativeCopyStringToBuffer", "(JIILandroid/database/CharArrayBuffer;)V", - (void*)nativeCopyStringToBuffer }, - { "nativePutBlob", "(J[BII)Z", - (void*)nativePutBlob }, - { "nativePutString", "(JLjava/lang/String;II)Z", - (void*)nativePutString }, + { "nativePutLong", "(JJII)Z", (void*)nativePutLong }, { "nativePutDouble", "(JDII)Z", diff --git a/core/jni/android_text_MeasuredParagraph.cpp b/core/jni/android_text_MeasuredParagraph.cpp index f0e449d52998..dddddbbb3796 100644 --- a/core/jni/android_text_MeasuredParagraph.cpp +++ b/core/jni/android_text_MeasuredParagraph.cpp @@ -115,6 +115,10 @@ static jlong nGetReleaseFunc() { return toJLong(&releaseMeasuredParagraph); } +static jint nGetMemoryUsage(jlong ptr) { + return static_cast<jint>(toMeasuredParagraph(ptr)->getMemoryUsage()); +} + static const JNINativeMethod gMethods[] = { // MeasuredParagraphBuilder native functions. {"nInitBuilder", "()J", (void*) nInitBuilder}, @@ -126,6 +130,7 @@ static const JNINativeMethod gMethods[] = { // MeasuredParagraph native functions. {"nGetWidth", "(JII)F", (void*) nGetWidth}, // Critical Natives {"nGetReleaseFunc", "()J", (void*) nGetReleaseFunc}, // Critical Natives + {"nGetMemoryUsage", "(J)I", (void*) nGetMemoryUsage}, // Critical Native }; int register_android_text_MeasuredParagraph(JNIEnv* env) { diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 36082ca1b3b8..a0ba3ade4b34 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -1959,12 +1959,6 @@ <permission android:name="android.permission.START_ANY_ACTIVITY" android:protectionLevel="signature" /> - <!-- Allows an application to start an activity as another app, provided that app has been - granted a permissionToken from the ActivityManagerService. - @hide --> - <permission android:name="android.permission.START_ACTIVITY_AS_CALLER" - android:protectionLevel="signature" /> - <!-- @deprecated The {@link android.app.ActivityManager#restartPackage} API is no longer supported. --> <permission android:name="android.permission.RESTART_PACKAGES" diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 38f890a1e95e..a22ca8726dca 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -2283,10 +2283,7 @@ Can be customized for other product types --> <string name="config_chooseTypeAndAccountActivity" translatable="false" >android/android.accounts.ChooseTypeAndAccountActivity</string> - <!-- Name of the activity that will handle requests to the system to choose an activity for - the purposes of resolving an intent. --> - <string name="config_chooserActivity" translatable="false" - >com.android.systemui/com.android.systemui.chooser.ChooserActivity</string> + <!-- Component name of a custom ResolverActivity (Intent resolver) to be used instead of the default framework version. If left empty, then the framework version will be used. Example: com.google.android.myapp/.resolver.MyResolverActivity --> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index e8ab0be78b36..09d3121dfa72 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -1071,7 +1071,6 @@ <java-symbol type="string" name="owner_name" /> <java-symbol type="string" name="config_chooseAccountActivity" /> <java-symbol type="string" name="config_chooseTypeAndAccountActivity" /> - <java-symbol type="string" name="config_chooserActivity" /> <java-symbol type="string" name="config_customResolverActivity" /> <java-symbol type="string" name="config_appsAuthorizedForSharedAccounts" /> <java-symbol type="string" name="error_message_title" /> diff --git a/core/tests/coretests/src/com/android/internal/app/IntentForwarderActivityTest.java b/core/tests/coretests/src/com/android/internal/app/IntentForwarderActivityTest.java index c0bc3a8eeb9e..b18fa747557d 100644 --- a/core/tests/coretests/src/com/android/internal/app/IntentForwarderActivityTest.java +++ b/core/tests/coretests/src/com/android/internal/app/IntentForwarderActivityTest.java @@ -24,7 +24,6 @@ import android.content.pm.IPackageManager; import android.content.pm.PackageManager; import android.content.pm.UserInfo; import android.os.Bundle; -import android.os.IBinder; import android.os.UserHandle; import android.os.UserManager; import android.support.test.InstrumentationRegistry; @@ -270,8 +269,8 @@ public class IntentForwarderActivityTest { } @Override - public void startActivityAsCaller(Intent intent, @Nullable Bundle options, - IBinder permissionToken, boolean ignoreTargetSecurity, int userId) { + public void startActivityAsCaller(Intent intent, @Nullable Bundle options, boolean + ignoreTargetSecurity, int userId) { mStartActivityIntent = intent; mUserIdActivityLaunchedIn = userId; } @@ -294,4 +293,4 @@ public class IntentForwarderActivityTest { return mPm; } } -} +}
\ No newline at end of file diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index 6c8aaf0b1e8c..8addffbb02db 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -369,7 +369,6 @@ applications that come with the platform <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/> <permission name="android.permission.REAL_GET_TASKS"/> <permission name="android.permission.RECEIVE_MEDIA_RESOURCE_USAGE"/> - <permission name="android.permission.START_ACTIVITY_AS_CALLER"/> <permission name="android.permission.START_TASKS_FROM_RECENTS"/> <permission name="android.permission.STATUS_BAR"/> <permission name="android.permission.STOP_APP_SWITCHES"/> diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java index ded427eb244a..c429fd382d67 100644 --- a/keystore/java/android/security/KeyStore.java +++ b/keystore/java/android/security/KeyStore.java @@ -618,9 +618,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; diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java index f721ed3af7ba..419eb24e1cc1 100644 --- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java +++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java @@ -243,12 +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 */); + KeymasterUtils.addUserAuthArgs(new KeymasterArguments(), spec); } catch (IllegalStateException | IllegalArgumentException e) { throw new InvalidAlgorithmParameterException(e); } @@ -284,15 +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 */); - 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 d1eb6888bbfd..d68a33de2c61 100644 --- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java +++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java @@ -344,12 +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 */); + KeymasterUtils.addUserAuthArgs(new KeymasterArguments(), mSpec); } catch (IllegalArgumentException | IllegalStateException e) { throw new InvalidAlgorithmParameterException(e); } @@ -540,12 +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 */); + 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 440e0863fbb1..fc86ca0443b0 100644 --- a/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java +++ b/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java @@ -497,12 +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()); + KeymasterUtils.addUserAuthArgs(importArgs, spec); importArgs.addDateIfNotNull(KeymasterDefs.KM_TAG_ACTIVE_DATETIME, spec.getKeyValidityStart()); importArgs.addDateIfNotNull(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME, @@ -699,12 +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()); + 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 a896c72463fb..0291b8ac6989 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"); @@ -264,6 +265,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec { private final boolean mUserAuthenticationValidWhileOnBody; private final boolean mInvalidatedByBiometricEnrollment; private final boolean mIsStrongBoxBacked; + private final boolean mUnlockedDeviceRequired; /** * @hide should be built with Builder @@ -293,7 +295,8 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec { boolean uniqueIdIncluded, boolean userAuthenticationValidWhileOnBody, boolean invalidatedByBiometricEnrollment, - boolean isStrongBoxBacked) { + boolean isStrongBoxBacked, + boolean unlockedDeviceRequired) { if (TextUtils.isEmpty(keyStoreAlias)) { throw new IllegalArgumentException("keyStoreAlias must not be empty"); } @@ -341,6 +344,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec { mUserAuthenticationValidWhileOnBody = userAuthenticationValidWhileOnBody; mInvalidatedByBiometricEnrollment = invalidatedByBiometricEnrollment; mIsStrongBoxBacked = isStrongBoxBacked; + mUnlockedDeviceRequired = unlockedDeviceRequired; } /** @@ -646,6 +650,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 { @@ -675,6 +695,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec { private boolean mUserAuthenticationValidWhileOnBody; private boolean mInvalidatedByBiometricEnrollment = true; private boolean mIsStrongBoxBacked = false; + private boolean mUnlockedDeviceRequired = false; /** * Creates a new instance of the {@code Builder}. @@ -1220,6 +1241,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 @@ -1249,7 +1282,8 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec { mUniqueIdIncluded, mUserAuthenticationValidWhileOnBody, mInvalidatedByBiometricEnrollment, - mIsStrongBoxBacked); + mIsStrongBoxBacked, + mUnlockedDeviceRequired); } } } diff --git a/keystore/java/android/security/keystore/KeyProtection.java b/keystore/java/android/security/keystore/KeyProtection.java index dbacb9c53dd6..a5b85ceec1ad 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; @@ -228,6 +228,8 @@ public final class KeyProtection implements ProtectionParameter { private final boolean mInvalidatedByBiometricEnrollment; private final long mBoundToSecureUserId; private final boolean mCriticalToDeviceEncryption; + private final boolean mTrustedUserPresenceRequired; + private final boolean mUnlockedDeviceRequired; private KeyProtection( Date keyValidityStart, @@ -241,10 +243,12 @@ public final class KeyProtection implements ProtectionParameter { boolean randomizedEncryptionRequired, boolean userAuthenticationRequired, int userAuthenticationValidityDurationSeconds, + boolean trustedUserPresenceRequired, boolean userAuthenticationValidWhileOnBody, boolean invalidatedByBiometricEnrollment, long boundToSecureUserId, - boolean criticalToDeviceEncryption) { + boolean criticalToDeviceEncryption, + boolean unlockedDeviceRequired) { mKeyValidityStart = Utils.cloneIfNotNull(keyValidityStart); mKeyValidityForOriginationEnd = Utils.cloneIfNotNull(keyValidityForOriginationEnd); mKeyValidityForConsumptionEnd = Utils.cloneIfNotNull(keyValidityForConsumptionEnd); @@ -262,6 +266,8 @@ public final class KeyProtection implements ProtectionParameter { mInvalidatedByBiometricEnrollment = invalidatedByBiometricEnrollment; mBoundToSecureUserId = boundToSecureUserId; mCriticalToDeviceEncryption = criticalToDeviceEncryption; + mTrustedUserPresenceRequired = trustedUserPresenceRequired; + mUnlockedDeviceRequired = unlockedDeviceRequired; } /** @@ -414,6 +420,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. @@ -471,6 +485,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 { @@ -488,6 +511,9 @@ public final class KeyProtection implements ProtectionParameter { private int mUserAuthenticationValidityDurationSeconds = -1; private boolean mUserAuthenticationValidWhileOnBody; private boolean mInvalidatedByBiometricEnrollment = true; + private boolean mTrustedUserPresenceRequired = false; + private boolean mUnlockedDeviceRequired = false; + private long mBoundToSecureUserId = GateKeeper.INVALID_SECURE_USER_ID; private boolean mCriticalToDeviceEncryption = false; @@ -764,6 +790,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 @@ -845,6 +881,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 @@ -863,10 +911,12 @@ public final class KeyProtection implements ProtectionParameter { mRandomizedEncryptionRequired, mUserAuthenticationRequired, mUserAuthenticationValidityDurationSeconds, + mTrustedUserPresenceRequired, mUserAuthenticationValidWhileOnBody, mInvalidatedByBiometricEnrollment, mBoundToSecureUserId, - mCriticalToDeviceEncryption); + mCriticalToDeviceEncryption, + mUnlockedDeviceRequired); } } } diff --git a/keystore/java/android/security/keystore/KeymasterUtils.java b/keystore/java/android/security/keystore/KeymasterUtils.java index 34c8d1f75f60..eb6a2a2c9e67 100644 --- a/keystore/java/android/security/keystore/KeymasterUtils.java +++ b/keystore/java/android/security/keystore/KeymasterUtils.java @@ -98,17 +98,23 @@ public abstract class KeymasterUtils { * require user authentication. */ public static void addUserAuthArgs(KeymasterArguments args, - boolean userAuthenticationRequired, - int userAuthenticationValidityDurationSeconds, - boolean userAuthenticationValidWhileOnBody, - boolean invalidatedByBiometricEnrollment, - long boundToSpecificSecureUserId) { - if (!userAuthenticationRequired) { + UserAuthArgs spec) { + args.addUnsignedInt(KeymasterDefs.KM_TAG_USER_ID, 0); + + if (spec.isTrustedUserPresenceRequired()) { + args.addBoolean(KeymasterDefs.KM_TAG_TRUSTED_USER_PRESENCE_REQUIRED); + } + + if (spec.isUnlockedDeviceRequired()) { + args.addBoolean(KeymasterDefs.KM_TAG_UNLOCKED_DEVICE_REQUIRED); + } + + 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 = @@ -124,9 +130,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; @@ -139,14 +145,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. @@ -157,8 +163,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..6fb348639594 --- /dev/null +++ b/keystore/java/android/security/keystore/UserAuthArgs.java @@ -0,0 +1,37 @@ +/* + * 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(); + long getBoundToSpecificSecureUserId(); + +} diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index 2ac4063d1b08..7533701138d0 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -400,6 +400,18 @@ public class AudioManager { public static final int ADJUST_TOGGLE_MUTE = 101; /** @hide */ + @IntDef(flag = false, prefix = "ADJUST", value = { + ADJUST_RAISE, + ADJUST_LOWER, + ADJUST_SAME, + ADJUST_MUTE, + ADJUST_UNMUTE, + ADJUST_TOGGLE_MUTE } + ) + @Retention(RetentionPolicy.SOURCE) + public @interface VolumeAdjustement {} + + /** @hide */ public static final String adjustToString(int adj) { switch (adj) { case ADJUST_RAISE: return "ADJUST_RAISE"; @@ -2989,7 +3001,7 @@ public class AudioManager { final IAudioService service = getService(); try { String regId = service.registerAudioPolicy(policy.getConfig(), policy.cb(), - policy.hasFocusListener(), policy.isFocusPolicy()); + policy.hasFocusListener(), policy.isFocusPolicy(), policy.isVolumeController()); if (regId == null) { return ERROR; } else { diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl index 6c6522328e8d..88d0a6088026 100644 --- a/media/java/android/media/IAudioService.aidl +++ b/media/java/android/media/IAudioService.aidl @@ -166,7 +166,8 @@ interface IAudioService { boolean isHdmiSystemAudioSupported(); String registerAudioPolicy(in AudioPolicyConfig policyConfig, - in IAudioPolicyCallback pcb, boolean hasFocusListener, boolean isFocusPolicy); + in IAudioPolicyCallback pcb, boolean hasFocusListener, boolean isFocusPolicy, + boolean isVolumeController); oneway void unregisterAudioPolicyAsync(in IAudioPolicyCallback pcb); diff --git a/media/java/android/media/MediaPlayer2.java b/media/java/android/media/MediaPlayer2.java index d36df845cc2e..d84eedf94820 100644 --- a/media/java/android/media/MediaPlayer2.java +++ b/media/java/android/media/MediaPlayer2.java @@ -96,22 +96,13 @@ import java.util.UUID; * {@link #close()} is called, it is in the <em>End</em> state. Between these * two states is the life cycle of the MediaPlayer2 object. * <ul> - * <li>There is a subtle but important difference between a newly constructed - * MediaPlayer2 object and the MediaPlayer2 object after {@link #reset()} - * is called. It is a programming error to invoke methods such + * <li> It is a programming error to invoke methods such * as {@link #getCurrentPosition()}, * {@link #getDuration()}, {@link #getVideoHeight()}, * {@link #getVideoWidth()}, {@link #setAudioAttributes(AudioAttributes)}, * {@link #setVolume(float, float)}, {@link #pause()}, {@link #play()}, * {@link #seekTo(long, int)} or - * {@link #prepareAsync()} in the <em>Idle</em> state for both cases. If any of these - * methods is called right after a MediaPlayer2 object is constructed, - * the user supplied callback method OnErrorListener.onError() won't be - * called by the internal player engine and the object state remains - * unchanged; but if these methods are called right after {@link #reset()}, - * the user supplied callback method OnErrorListener.onError() will be - * invoked by the internal player engine and the object will be - * transfered to the <em>Error</em> state. </li> + * {@link #prepareAsync()} in the <em>Idle</em> state. * <li>It is also recommended that once * a MediaPlayer2 object is no longer being used, call {@link #close()} immediately * so that resources used by the internal player engine associated with the diff --git a/media/java/android/media/MediaPlayer2Impl.java b/media/java/android/media/MediaPlayer2Impl.java index 86a285cccaf9..222c66ea4551 100644 --- a/media/java/android/media/MediaPlayer2Impl.java +++ b/media/java/android/media/MediaPlayer2Impl.java @@ -1960,6 +1960,13 @@ public final class MediaPlayer2Impl extends MediaPlayer2 { mTimeProvider = null; } + synchronized (mEventCbLock) { + mEventCallbackRecords.clear(); + } + synchronized (mDrmEventCbLock) { + mDrmEventCallbackRecords.clear(); + } + stayAwake(false); _reset(); // make sure none of the listeners get called anymore @@ -3049,8 +3056,7 @@ public final class MediaPlayer2Impl extends MediaPlayer2 { stayAwake(false); updateSurfaceScreenOn(); synchronized (mEventCbLock) { - mEventCb = null; - mEventExec = null; + mEventCallbackRecords.clear(); } if (mTimeProvider != null) { mTimeProvider.close(); @@ -3061,8 +3067,7 @@ public final class MediaPlayer2Impl extends MediaPlayer2 { // Modular DRM clean up mOnDrmConfigHelper = null; synchronized (mDrmEventCbLock) { - mDrmEventCb = null; - mDrmEventExec = null; + mDrmEventCallbackRecords.clear(); } resetDrmState(); @@ -3118,18 +3123,8 @@ public final class MediaPlayer2Impl extends MediaPlayer2 { Log.w(TAG, "mediaplayer2 went away with unhandled events"); return; } - final Executor eventExec; - final EventCallback eventCb; - synchronized (mEventCbLock) { - eventExec = mEventExec; - eventCb = mEventCb; - } - final Executor drmEventExec; - final DrmEventCallback drmEventCb; - synchronized (mDrmEventCbLock) { - drmEventExec = mDrmEventExec; - drmEventCb = mDrmEventCb; - } + final int what = msg.arg1; + final int extra = msg.arg2; switch(msg.what) { case MEDIA_PREPARED: try { @@ -3143,33 +3138,36 @@ public final class MediaPlayer2Impl extends MediaPlayer2 { sendMessage(msg2); } - if (eventCb != null && eventExec != null) { - eventExec.execute(() -> eventCb.onInfo( - mMediaPlayer, 0, MEDIA_INFO_PREPARED, 0)); + synchronized (mEventCbLock) { + for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) { + cb.first.execute(() -> cb.second.onInfo( + mMediaPlayer, 0, MEDIA_INFO_PREPARED, 0)); + } } return; case MEDIA_DRM_INFO: - Log.v(TAG, "MEDIA_DRM_INFO " + mDrmEventCb); - if (msg.obj == null) { Log.w(TAG, "MEDIA_DRM_INFO msg.obj=NULL"); } else if (msg.obj instanceof Parcel) { - if (drmEventExec != null && drmEventCb != null) { - // The parcel was parsed already in postEventFromNative - final DrmInfoImpl drmInfo; - - synchronized (mDrmLock) { - if (mDrmInfoImpl != null) { - drmInfo = mDrmInfoImpl.makeCopy(); - } else { - drmInfo = null; - } + // The parcel was parsed already in postEventFromNative + final DrmInfoImpl drmInfo; + + synchronized (mDrmLock) { + if (mDrmInfoImpl != null) { + drmInfo = mDrmInfoImpl.makeCopy(); + } else { + drmInfo = null; } + } - // notifying the client outside the lock - if (drmInfo != null) { - drmEventExec.execute(() -> drmEventCb.onDrmInfo(mMediaPlayer, drmInfo)); + // notifying the client outside the lock + if (drmInfo != null) { + synchronized (mEventCbLock) { + for (Pair<Executor, DrmEventCallback> cb : mDrmEventCallbackRecords) { + cb.first.execute(() -> cb.second.onDrmInfo( + mMediaPlayer, drmInfo)); + } } } } else { @@ -3178,9 +3176,11 @@ public final class MediaPlayer2Impl extends MediaPlayer2 { return; case MEDIA_PLAYBACK_COMPLETE: - if (eventCb != null && eventExec != null) { - eventExec.execute(() -> eventCb.onInfo( - mMediaPlayer, 0, MEDIA_INFO_PLAYBACK_COMPLETE, 0)); + synchronized (mEventCbLock) { + for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) { + cb.first.execute(() -> cb.second.onInfo( + mMediaPlayer, 0, MEDIA_INFO_PLAYBACK_COMPLETE, 0)); + } } stayAwake(false); return; @@ -3205,16 +3205,21 @@ public final class MediaPlayer2Impl extends MediaPlayer2 { break; case MEDIA_BUFFERING_UPDATE: - if (eventCb != null && eventExec != null) { - final int percent = msg.arg1; - eventExec.execute(() -> eventCb.onBufferingUpdate(mMediaPlayer, 0, percent)); + final int percent = msg.arg1; + synchronized (mEventCbLock) { + for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) { + cb.first.execute(() -> cb.second.onBufferingUpdate( + mMediaPlayer, 0, percent)); + } } return; case MEDIA_SEEK_COMPLETE: - if (eventCb != null && eventExec != null) { - eventExec.execute(() -> eventCb.onInfo( - mMediaPlayer, 0, MEDIA_INFO_COMPLETE_CALL_SEEK, 0)); + synchronized (mEventCbLock) { + for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) { + cb.first.execute(() -> cb.second.onInfo( + mMediaPlayer, 0, MEDIA_INFO_COMPLETE_CALL_SEEK, 0)); + } } // fall through @@ -3228,61 +3233,68 @@ public final class MediaPlayer2Impl extends MediaPlayer2 { return; case MEDIA_SET_VIDEO_SIZE: - if (eventCb != null && eventExec != null) { - final int width = msg.arg1; - final int height = msg.arg2; - eventExec.execute(() -> eventCb.onVideoSizeChanged( - mMediaPlayer, 0, width, height)); + final int width = msg.arg1; + final int height = msg.arg2; + synchronized (mEventCbLock) { + for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) { + cb.first.execute(() -> cb.second.onVideoSizeChanged( + mMediaPlayer, 0, width, height)); + } } return; case MEDIA_ERROR: Log.e(TAG, "Error (" + msg.arg1 + "," + msg.arg2 + ")"); - if (eventCb != null && eventExec != null) { - final int what = msg.arg1; - final int extra = msg.arg2; - eventExec.execute(() -> eventCb.onError(mMediaPlayer, 0, what, extra)); - eventExec.execute(() -> eventCb.onInfo( - mMediaPlayer, 0, MEDIA_INFO_PLAYBACK_COMPLETE, 0)); + synchronized (mEventCbLock) { + for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) { + cb.first.execute(() -> cb.second.onError( + mMediaPlayer, 0, what, extra)); + cb.first.execute(() -> cb.second.onInfo( + mMediaPlayer, 0, MEDIA_INFO_PLAYBACK_COMPLETE, 0)); + } } stayAwake(false); return; case MEDIA_INFO: switch (msg.arg1) { - case MEDIA_INFO_VIDEO_TRACK_LAGGING: - Log.i(TAG, "Info (" + msg.arg1 + "," + msg.arg2 + ")"); - break; - case MEDIA_INFO_METADATA_UPDATE: - try { - scanInternalSubtitleTracks(); - } catch (RuntimeException e) { - Message msg2 = obtainMessage( - MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, MEDIA_ERROR_UNSUPPORTED, null); - sendMessage(msg2); - } - // fall through + case MEDIA_INFO_VIDEO_TRACK_LAGGING: + Log.i(TAG, "Info (" + msg.arg1 + "," + msg.arg2 + ")"); + break; - case MEDIA_INFO_EXTERNAL_METADATA_UPDATE: - msg.arg1 = MEDIA_INFO_METADATA_UPDATE; - // update default track selection - if (mSubtitleController != null) { - mSubtitleController.selectDefaultTrack(); - } - break; - case MEDIA_INFO_BUFFERING_START: - case MEDIA_INFO_BUFFERING_END: - TimeProvider timeProvider = mTimeProvider; - if (timeProvider != null) { - timeProvider.onBuffering(msg.arg1 == MEDIA_INFO_BUFFERING_START); - } - break; + case MEDIA_INFO_METADATA_UPDATE: + try { + scanInternalSubtitleTracks(); + } catch (RuntimeException e) { + Message msg2 = obtainMessage( + MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, MEDIA_ERROR_UNSUPPORTED, + null); + sendMessage(msg2); + } + // fall through + + case MEDIA_INFO_EXTERNAL_METADATA_UPDATE: + msg.arg1 = MEDIA_INFO_METADATA_UPDATE; + // update default track selection + if (mSubtitleController != null) { + mSubtitleController.selectDefaultTrack(); + } + break; + + case MEDIA_INFO_BUFFERING_START: + case MEDIA_INFO_BUFFERING_END: + TimeProvider timeProvider = mTimeProvider; + if (timeProvider != null) { + timeProvider.onBuffering(msg.arg1 == MEDIA_INFO_BUFFERING_START); + } + break; } - if (eventCb != null && eventExec != null) { - final int what = msg.arg1; - final int extra = msg.arg2; - eventExec.execute(() -> eventCb.onInfo(mMediaPlayer, 0, what, extra)); + synchronized (mEventCbLock) { + for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) { + cb.first.execute(() -> cb.second.onInfo( + mMediaPlayer, 0, what, extra)); + } } // No real default action so far. return; @@ -3295,17 +3307,18 @@ public final class MediaPlayer2Impl extends MediaPlayer2 { return; case MEDIA_TIMED_TEXT: - if (eventCb == null || eventExec == null) { - return; - } - if (msg.obj == null) { - eventExec.execute(() -> eventCb.onTimedText(mMediaPlayer, 0, null)); + final TimedText text; + if (msg.obj instanceof Parcel) { + Parcel parcel = (Parcel)msg.obj; + text = new TimedText(parcel); + parcel.recycle(); } else { - if (msg.obj instanceof Parcel) { - Parcel parcel = (Parcel)msg.obj; - TimedText text = new TimedText(parcel); - parcel.recycle(); - eventExec.execute(() -> eventCb.onTimedText(mMediaPlayer, 0, text)); + text = null; + } + + synchronized (mEventCbLock) { + for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) { + cb.first.execute(() -> cb.second.onTimedText(mMediaPlayer, 0, text)); } } return; @@ -3324,15 +3337,20 @@ public final class MediaPlayer2Impl extends MediaPlayer2 { return; case MEDIA_META_DATA: - if (eventCb == null || eventExec == null) { - return; - } + final TimedMetaData data; if (msg.obj instanceof Parcel) { Parcel parcel = (Parcel) msg.obj; - TimedMetaData data = TimedMetaData.createTimedMetaDataFromParcel(parcel); + data = TimedMetaData.createTimedMetaDataFromParcel(parcel); parcel.recycle(); - eventExec.execute(() -> eventCb.onTimedMetaDataAvailable( - mMediaPlayer, 0, data)); + } else { + data = null; + } + + synchronized (mEventCbLock) { + for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) { + cb.first.execute(() -> cb.second.onTimedMetaDataAvailable( + mMediaPlayer, 0, data)); + } } return; @@ -3420,9 +3438,9 @@ public final class MediaPlayer2Impl extends MediaPlayer2 { } } - private Executor mEventExec; - private EventCallback mEventCb; private final Object mEventCbLock = new Object(); + private ArrayList<Pair<Executor, EventCallback> > mEventCallbackRecords + = new ArrayList<Pair<Executor, EventCallback> >(); /** * Register a callback to be invoked when the media source is ready @@ -3441,9 +3459,7 @@ public final class MediaPlayer2Impl extends MediaPlayer2 { throw new IllegalArgumentException("Illegal null Executor for the EventCallback"); } synchronized (mEventCbLock) { - // TODO: support multiple callbacks. - mEventExec = executor; - mEventCb = eventCallback; + mEventCallbackRecords.add(new Pair(executor, eventCallback)); } } @@ -3455,9 +3471,10 @@ public final class MediaPlayer2Impl extends MediaPlayer2 { @Override public void unregisterEventCallback(EventCallback callback) { synchronized (mEventCbLock) { - if (callback == mEventCb) { - mEventExec = null; - mEventCb = null; + for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) { + if (cb.second == callback) { + mEventCallbackRecords.remove(cb); + } } } } @@ -3497,9 +3514,9 @@ public final class MediaPlayer2Impl extends MediaPlayer2 { private OnDrmConfigHelper mOnDrmConfigHelper; - private Executor mDrmEventExec; - private DrmEventCallback mDrmEventCb; private final Object mDrmEventCbLock = new Object(); + private ArrayList<Pair<Executor, DrmEventCallback> > mDrmEventCallbackRecords + = new ArrayList<Pair<Executor, DrmEventCallback> >(); /** * Register a callback to be invoked when the media source is ready @@ -3518,9 +3535,7 @@ public final class MediaPlayer2Impl extends MediaPlayer2 { throw new IllegalArgumentException("Illegal null Executor for the EventCallback"); } synchronized (mDrmEventCbLock) { - // TODO: support multiple callbacks. - mDrmEventExec = executor; - mDrmEventCb = eventCallback; + mDrmEventCallbackRecords.add(new Pair(executor, eventCallback)); } } @@ -3532,9 +3547,11 @@ public final class MediaPlayer2Impl extends MediaPlayer2 { @Override public void unregisterDrmEventCallback(DrmEventCallback callback) { synchronized (mDrmEventCbLock) { - if (callback == mDrmEventCb) { - mDrmEventExec = null; - mDrmEventCb = null; + for (Pair<Executor, DrmEventCallback> cb : mDrmEventCallbackRecords) { + if (cb.second == callback) { + mDrmEventCallbackRecords.remove(cb); + break; + } } } } @@ -3733,15 +3750,11 @@ public final class MediaPlayer2Impl extends MediaPlayer2 { // if finished successfully without provisioning, call the callback outside the lock if (allDoneWithoutProvisioning) { - final Executor drmEventExec; - final DrmEventCallback drmEventCb; synchronized (mDrmEventCbLock) { - drmEventExec = mDrmEventExec; - drmEventCb = mDrmEventCb; - } - if (drmEventExec != null && drmEventCb != null) { - drmEventExec.execute(() -> drmEventCb.onDrmPrepared( - this, PREPARE_DRM_STATUS_SUCCESS)); + for (Pair<Executor, DrmEventCallback> cb : mDrmEventCallbackRecords) { + cb.first.execute(() -> cb.second.onDrmPrepared( + this, PREPARE_DRM_STATUS_SUCCESS)); + } } } @@ -4324,14 +4337,12 @@ public final class MediaPlayer2Impl extends MediaPlayer2 { boolean succeeded = false; - final Executor drmEventExec; - final DrmEventCallback drmEventCb; + boolean hasCallback = false; synchronized (mDrmEventCbLock) { - drmEventExec = mDrmEventExec; - drmEventCb = mDrmEventCb; + hasCallback = !mDrmEventCallbackRecords.isEmpty(); } // non-blocking mode needs the lock - if (drmEventExec != null && drmEventCb != null) { + if (hasCallback) { synchronized (drmLock) { // continuing with prepareDrm @@ -4349,7 +4360,11 @@ public final class MediaPlayer2Impl extends MediaPlayer2 { } // synchronized // calling the callback outside the lock - drmEventExec.execute(() -> drmEventCb.onDrmPrepared(mediaPlayer, status)); + synchronized (mDrmEventCbLock) { + for (Pair<Executor, DrmEventCallback> cb : mDrmEventCallbackRecords) { + cb.first.execute(() -> cb.second.onDrmPrepared(mediaPlayer, status)); + } + } } else { // blocking mode already has the lock // continuing with prepareDrm @@ -4397,13 +4412,11 @@ public final class MediaPlayer2Impl extends MediaPlayer2 { int result; // non-blocking: this is not the final result - final Executor drmEventExec; - final DrmEventCallback drmEventCb; + boolean hasCallback = false; synchronized (mDrmEventCbLock) { - drmEventExec = mDrmEventExec; - drmEventCb = mDrmEventCb; + hasCallback = !mDrmEventCallbackRecords.isEmpty(); } - if (drmEventCb != null && drmEventExec != null) { + if (hasCallback) { result = PREPARE_DRM_STATUS_SUCCESS; } else { // if blocking mode, wait till provisioning is done diff --git a/media/java/android/media/audiopolicy/AudioPolicy.java b/media/java/android/media/audiopolicy/AudioPolicy.java index 7e88c277cc23..d0bba6db9db5 100644 --- a/media/java/android/media/audiopolicy/AudioPolicy.java +++ b/media/java/android/media/audiopolicy/AudioPolicy.java @@ -89,6 +89,8 @@ public class AudioPolicy { private AudioPolicyFocusListener mFocusListener; + private final AudioPolicyVolumeCallback mVolCb; + private Context mContext; private AudioPolicyConfig mConfig; @@ -99,12 +101,15 @@ public class AudioPolicy { public boolean hasFocusListener() { return mFocusListener != null; } /** @hide */ public boolean isFocusPolicy() { return mIsFocusPolicy; } + /** @hide */ + public boolean isVolumeController() { return mVolCb != null; } /** * The parameter is guaranteed non-null through the Builder */ private AudioPolicy(AudioPolicyConfig config, Context context, Looper looper, - AudioPolicyFocusListener fl, AudioPolicyStatusListener sl, boolean isFocusPolicy) { + AudioPolicyFocusListener fl, AudioPolicyStatusListener sl, boolean isFocusPolicy, + AudioPolicyVolumeCallback vc) { mConfig = config; mStatus = POLICY_STATUS_UNREGISTERED; mContext = context; @@ -120,6 +125,7 @@ public class AudioPolicy { mFocusListener = fl; mStatusListener = sl; mIsFocusPolicy = isFocusPolicy; + mVolCb = vc; } /** @@ -134,6 +140,7 @@ public class AudioPolicy { private AudioPolicyFocusListener mFocusListener; private AudioPolicyStatusListener mStatusListener; private boolean mIsFocusPolicy = false; + private AudioPolicyVolumeCallback mVolCb; /** * Constructs a new Builder with no audio mixes. @@ -208,6 +215,22 @@ public class AudioPolicy { mStatusListener = l; } + @SystemApi + /** + * Sets the callback to receive all volume key-related events. + * The callback will only be called if the device is configured to handle volume events + * in the PhoneWindowManager (see config_handleVolumeKeysInWindowManager) + * @param vc + * @return the same Builder instance. + */ + public Builder setAudioPolicyVolumeCallback(@NonNull AudioPolicyVolumeCallback vc) { + if (vc == null) { + throw new IllegalArgumentException("Invalid null volume callback"); + } + mVolCb = vc; + return this; + } + /** * Combines all of the attributes that have been set on this {@code Builder} and returns a * new {@link AudioPolicy} object. @@ -229,7 +252,7 @@ public class AudioPolicy { + "an AudioPolicyFocusListener"); } return new AudioPolicy(new AudioPolicyConfig(mMixes), mContext, mLooper, - mFocusListener, mStatusListener, mIsFocusPolicy); + mFocusListener, mStatusListener, mIsFocusPolicy, mVolCb); } } @@ -455,6 +478,23 @@ public class AudioPolicy { public void onAudioFocusAbandon(AudioFocusInfo afi) {} } + @SystemApi + /** + * Callback class to receive volume change-related events. + * See {@link #Builder.setAudioPolicyVolumeCallback(AudioPolicyCallback)} to configure the + * {@link AudioPolicy} to receive those events. + * + */ + public static abstract class AudioPolicyVolumeCallback { + /** @hide */ + public AudioPolicyVolumeCallback() {} + /** + * Called when volume key-related changes are triggered, on the key down event. + * @param adjustement the type of volume adjustment for the key. + */ + public void onVolumeAdjustment(@AudioManager.VolumeAdjustement int adjustement) {} + } + private void onPolicyStatusChange() { AudioPolicyStatusListener l; synchronized (mLock) { @@ -517,6 +557,13 @@ public class AudioPolicy { } } } + + public void notifyVolumeAdjust(int adjustment) { + sendMsg(MSG_VOL_ADJUST, null /* ignored */, adjustment); + if (DEBUG) { + Log.v(TAG, "notifyVolumeAdjust: " + adjustment); + } + } }; //================================================== @@ -528,6 +575,7 @@ public class AudioPolicy { private final static int MSG_MIX_STATE_UPDATE = 3; private final static int MSG_FOCUS_REQUEST = 4; private final static int MSG_FOCUS_ABANDON = 5; + private final static int MSG_VOL_ADJUST = 6; private class EventHandler extends Handler { public EventHandler(AudioPolicy ap, Looper looper) { @@ -571,6 +619,12 @@ public class AudioPolicy { Log.e(TAG, "Invalid null focus listener for focus abandon event"); } break; + case MSG_VOL_ADJUST: + if (mVolCb != null) { + mVolCb.onVolumeAdjustment(msg.arg1); + } else { // should never be null, but don't crash + Log.e(TAG, "Invalid null volume event"); + } default: Log.e(TAG, "Unknown event " + msg.what); } diff --git a/media/java/android/media/audiopolicy/IAudioPolicyCallback.aidl b/media/java/android/media/audiopolicy/IAudioPolicyCallback.aidl index 86abbb4dc8d9..107e7cd59ca2 100644 --- a/media/java/android/media/audiopolicy/IAudioPolicyCallback.aidl +++ b/media/java/android/media/audiopolicy/IAudioPolicyCallback.aidl @@ -31,4 +31,7 @@ oneway interface IAudioPolicyCallback { // callback for mix activity status update void notifyMixStateUpdate(in String regId, int state); + + // callback for volume events + void notifyVolumeAdjust(int adjustment); } diff --git a/media/java/android/media/update/MediaControlView2Provider.java b/media/java/android/media/update/MediaControlView2Provider.java index 6b38c9265cfc..4464f8f2ac6e 100644 --- a/media/java/android/media/update/MediaControlView2Provider.java +++ b/media/java/android/media/update/MediaControlView2Provider.java @@ -37,11 +37,8 @@ import android.view.View; public interface MediaControlView2Provider extends ViewProvider { void setController_impl(MediaController controller); void show_impl(); - void show_impl(int timeout); + void show_impl(long timeout); boolean isShowing_impl(); void hide_impl(); - void showSubtitle_impl(); - void hideSubtitle_impl(); - void setPrevNextListeners_impl(View.OnClickListener next, View.OnClickListener prev); - void setButtonVisibility_impl(int button, boolean visible); + void setButtonVisibility_impl(int button, int visibility); } diff --git a/media/jni/android_media_MediaMetadataRetriever.cpp b/media/jni/android_media_MediaMetadataRetriever.cpp index 42ebf6ab8cfc..ff854c51c437 100644 --- a/media/jni/android_media_MediaMetadataRetriever.cpp +++ b/media/jni/android_media_MediaMetadataRetriever.cpp @@ -68,17 +68,26 @@ static void process_media_retriever_call(JNIEnv *env, status_t opStatus, const c } } -static MediaMetadataRetriever* getRetriever(JNIEnv* env, jobject thiz) +static sp<MediaMetadataRetriever> getRetriever(JNIEnv* env, jobject thiz) { // No lock is needed, since it is called internally by other methods that are protected MediaMetadataRetriever* retriever = (MediaMetadataRetriever*) env->GetLongField(thiz, fields.context); return retriever; } -static void setRetriever(JNIEnv* env, jobject thiz, MediaMetadataRetriever* retriever) +static void setRetriever(JNIEnv* env, jobject thiz, const sp<MediaMetadataRetriever> &retriever) { // No lock is needed, since it is called internally by other methods that are protected - env->SetLongField(thiz, fields.context, (jlong) retriever); + + if (retriever != NULL) { + retriever->incStrong(thiz); + } + sp<MediaMetadataRetriever> old = getRetriever(env, thiz); + if (old != NULL) { + old->decStrong(thiz); + } + + env->SetLongField(thiz, fields.context, (jlong) retriever.get()); } static void @@ -87,7 +96,7 @@ android_media_MediaMetadataRetriever_setDataSourceAndHeaders( jobjectArray keys, jobjectArray values) { ALOGV("setDataSource"); - MediaMetadataRetriever* retriever = getRetriever(env, thiz); + sp<MediaMetadataRetriever> retriever = getRetriever(env, thiz); if (retriever == 0) { jniThrowException( env, @@ -146,7 +155,7 @@ android_media_MediaMetadataRetriever_setDataSourceAndHeaders( static void android_media_MediaMetadataRetriever_setDataSourceFD(JNIEnv *env, jobject thiz, jobject fileDescriptor, jlong offset, jlong length) { ALOGV("setDataSource"); - MediaMetadataRetriever* retriever = getRetriever(env, thiz); + sp<MediaMetadataRetriever> retriever = getRetriever(env, thiz); if (retriever == 0) { jniThrowException(env, "java/lang/IllegalStateException", "No retriever available"); return; @@ -175,7 +184,7 @@ static void android_media_MediaMetadataRetriever_setDataSourceFD(JNIEnv *env, jo static void android_media_MediaMetadataRetriever_setDataSourceCallback(JNIEnv *env, jobject thiz, jobject dataSource) { ALOGV("setDataSourceCallback"); - MediaMetadataRetriever* retriever = getRetriever(env, thiz); + sp<MediaMetadataRetriever> retriever = getRetriever(env, thiz); if (retriever == 0) { jniThrowException(env, "java/lang/IllegalStateException", "No retriever available"); return; @@ -325,7 +334,7 @@ static jobject android_media_MediaMetadataRetriever_getFrameAtTime( { ALOGV("getFrameAtTime: %lld us option: %d dst width: %d heigh: %d", (long long)timeUs, option, dst_width, dst_height); - MediaMetadataRetriever* retriever = getRetriever(env, thiz); + sp<MediaMetadataRetriever> retriever = getRetriever(env, thiz); if (retriever == 0) { jniThrowException(env, "java/lang/IllegalStateException", "No retriever available"); return NULL; @@ -349,7 +358,7 @@ static jobject android_media_MediaMetadataRetriever_getImageAtIndex( JNIEnv *env, jobject thiz, jint index) { ALOGV("getImageAtIndex: index %d", index); - MediaMetadataRetriever* retriever = getRetriever(env, thiz); + sp<MediaMetadataRetriever> retriever = getRetriever(env, thiz); if (retriever == 0) { jniThrowException(env, "java/lang/IllegalStateException", "No retriever available"); return NULL; @@ -373,7 +382,7 @@ static jobjectArray android_media_MediaMetadataRetriever_getFrameAtIndex( JNIEnv *env, jobject thiz, jint frameIndex, jint numFrames) { ALOGV("getFrameAtIndex: frameIndex %d, numFrames %d", frameIndex, numFrames); - MediaMetadataRetriever* retriever = getRetriever(env, thiz); + sp<MediaMetadataRetriever> retriever = getRetriever(env, thiz); if (retriever == 0) { jniThrowException(env, "java/lang/IllegalStateException", "No retriever available"); @@ -411,7 +420,7 @@ static jbyteArray android_media_MediaMetadataRetriever_getEmbeddedPicture( JNIEnv *env, jobject thiz, jint pictureType) { ALOGV("getEmbeddedPicture: %d", pictureType); - MediaMetadataRetriever* retriever = getRetriever(env, thiz); + sp<MediaMetadataRetriever> retriever = getRetriever(env, thiz); if (retriever == 0) { jniThrowException(env, "java/lang/IllegalStateException", "No retriever available"); return NULL; @@ -446,7 +455,7 @@ static jbyteArray android_media_MediaMetadataRetriever_getEmbeddedPicture( static jobject android_media_MediaMetadataRetriever_extractMetadata(JNIEnv *env, jobject thiz, jint keyCode) { ALOGV("extractMetadata"); - MediaMetadataRetriever* retriever = getRetriever(env, thiz); + sp<MediaMetadataRetriever> retriever = getRetriever(env, thiz); if (retriever == 0) { jniThrowException(env, "java/lang/IllegalStateException", "No retriever available"); return NULL; @@ -464,9 +473,7 @@ static void android_media_MediaMetadataRetriever_release(JNIEnv *env, jobject th { ALOGV("release"); Mutex::Autolock lock(sLock); - MediaMetadataRetriever* retriever = getRetriever(env, thiz); - delete retriever; - setRetriever(env, thiz, (MediaMetadataRetriever*) 0); + setRetriever(env, thiz, NULL); } static void android_media_MediaMetadataRetriever_native_finalize(JNIEnv *env, jobject thiz) @@ -533,7 +540,7 @@ static void android_media_MediaMetadataRetriever_native_init(JNIEnv *env) static void android_media_MediaMetadataRetriever_native_setup(JNIEnv *env, jobject thiz) { ALOGV("native_setup"); - MediaMetadataRetriever* retriever = new MediaMetadataRetriever(); + sp<MediaMetadataRetriever> retriever = new MediaMetadataRetriever(); if (retriever == 0) { jniThrowException(env, "java/lang/RuntimeException", "Out of memory"); return; diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java index fce5dd9cb828..7728f667ea51 100644 --- a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java +++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java @@ -363,6 +363,26 @@ public class RestrictedLockUtils { } /** + * Check if {@param packageName} is restricted by the profile or device owner from using + * metered data. + * + * @return EnforcedAdmin object containing the enforced admin component and admin user details, + * or {@code null} if the {@param packageName} is not restricted. + */ + public static EnforcedAdmin checkIfMeteredDataRestricted(Context context, + String packageName, int userId) { + final EnforcedAdmin enforcedAdmin = getProfileOrDeviceOwner(context, userId); + if (enforcedAdmin == null) { + return null; + } + + final DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService( + Context.DEVICE_POLICY_SERVICE); + return dpm.isMeteredDataDisabledForUser(enforcedAdmin.component, packageName, userId) + ? enforcedAdmin : null; + } + + /** * Checks if {@link android.app.admin.DevicePolicyManager#setAutoTimeRequired} is enforced * on the device. * diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java index 5c73d5485e2a..3a0ae9f532bb 100644 --- a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java +++ b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java @@ -48,6 +48,7 @@ import android.os.RemoteException; import android.os.SystemClock; import android.os.UserHandle; import android.os.UserManager; +import android.support.annotation.VisibleForTesting; import android.text.format.Formatter; import android.util.IconDrawableFactory; import android.util.Log; @@ -1282,7 +1283,8 @@ public class ApplicationsState { // A location where extra info can be placed to be used by custom filters. public Object extraInfo; - AppEntry(Context context, ApplicationInfo info, long id) { + @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE) + public AppEntry(Context context, ApplicationInfo info, long id) { apkFile = new File(info.sourceDir); this.id = id; this.info = info; diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java index 754b88117613..e11017ce3449 100644 --- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java @@ -32,7 +32,6 @@ import android.net.NetworkKey; import android.net.NetworkScoreManager; import android.net.NetworkScorerAppData; import android.net.ScoredNetwork; -import android.net.WifiKey; import android.net.wifi.IWifiManager; import android.net.wifi.ScanResult; import android.net.wifi.WifiConfiguration; @@ -43,6 +42,7 @@ import android.net.wifi.WifiManager; import android.net.wifi.WifiNetworkScoreCache; import android.net.wifi.hotspot2.PasspointConfiguration; import android.os.Bundle; +import android.os.Parcelable; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemClock; @@ -52,6 +52,7 @@ import android.text.Spannable; import android.text.SpannableString; import android.text.TextUtils; import android.text.style.TtsSpan; +import android.util.ArraySet; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; @@ -60,11 +61,11 @@ import com.android.settingslib.R; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; +import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.Map; -import java.util.Objects; -import java.util.concurrent.ConcurrentHashMap; +import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; @@ -91,6 +92,9 @@ public class AccessPoint implements Comparable<AccessPoint> { */ public static final int HIGHER_FREQ_5GHZ = 5900; + /** The key which identifies this AccessPoint grouping. */ + private String mKey; + @IntDef({Speed.NONE, Speed.SLOW, Speed.MODERATE, Speed.FAST, Speed.VERY_FAST}) @Retention(RetentionPolicy.SOURCE) public @interface Speed { @@ -116,14 +120,8 @@ public class AccessPoint implements Comparable<AccessPoint> { int VERY_FAST = 30; } - /** - * Experimental: we should be able to show the user the list of BSSIDs and bands - * for that SSID. - * For now this data is used only with Verbose Logging so as to show the band and number - * of BSSIDs on which that network is seen. - */ - private final ConcurrentHashMap<String, ScanResult> mScanResultCache = - new ConcurrentHashMap<String, ScanResult>(32); + /** The underlying set of scan results comprising this AccessPoint. */ + private final ArraySet<ScanResult> mScanResults = new ArraySet<>(); /** * Map of BSSIDs to scored networks for individual bssids. @@ -133,17 +131,13 @@ public class AccessPoint implements Comparable<AccessPoint> { */ private final Map<String, TimestampedScoredNetwork> mScoredNetworkCache = new HashMap<>(); - /** Maximum age of scan results to hold onto while actively scanning. **/ - private static final long MAX_SCAN_RESULT_AGE_MILLIS = 25000; - static final String KEY_NETWORKINFO = "key_networkinfo"; static final String KEY_WIFIINFO = "key_wifiinfo"; - static final String KEY_SCANRESULT = "key_scanresult"; static final String KEY_SSID = "key_ssid"; static final String KEY_SECURITY = "key_security"; static final String KEY_SPEED = "key_speed"; static final String KEY_PSKTYPE = "key_psktype"; - static final String KEY_SCANRESULTCACHE = "key_scanresultcache"; + static final String KEY_SCANRESULTS = "key_scanresults"; static final String KEY_SCOREDNETWORKCACHE = "key_scorednetworkcache"; static final String KEY_CONFIG = "key_config"; static final String KEY_FQDN = "key_fqdn"; @@ -216,7 +210,10 @@ public class AccessPoint implements Comparable<AccessPoint> { public AccessPoint(Context context, Bundle savedState) { mContext = context; - mConfig = savedState.getParcelable(KEY_CONFIG); + + if (savedState.containsKey(KEY_CONFIG)) { + mConfig = savedState.getParcelable(KEY_CONFIG); + } if (mConfig != null) { loadConfig(mConfig); } @@ -236,12 +233,11 @@ public class AccessPoint implements Comparable<AccessPoint> { if (savedState.containsKey(KEY_NETWORKINFO)) { mNetworkInfo = savedState.getParcelable(KEY_NETWORKINFO); } - if (savedState.containsKey(KEY_SCANRESULTCACHE)) { - ArrayList<ScanResult> scanResultArrayList = - savedState.getParcelableArrayList(KEY_SCANRESULTCACHE); - mScanResultCache.clear(); - for (ScanResult result : scanResultArrayList) { - mScanResultCache.put(result.BSSID, result); + if (savedState.containsKey(KEY_SCANRESULTS)) { + Parcelable[] scanResults = savedState.getParcelableArray(KEY_SCANRESULTS); + mScanResults.clear(); + for (Parcelable result : scanResults) { + mScanResults.add((ScanResult) result); } } if (savedState.containsKey(KEY_SCOREDNETWORKCACHE)) { @@ -268,8 +264,10 @@ public class AccessPoint implements Comparable<AccessPoint> { } update(mConfig, mInfo, mNetworkInfo); - // Do not evict old scan results on initial creation + // Calculate required fields + updateKey(); updateRssi(); + mId = sLastId.incrementAndGet(); } @@ -295,30 +293,75 @@ public class AccessPoint implements Comparable<AccessPoint> { copyFrom(other); } - AccessPoint(Context context, ScanResult result) { + AccessPoint(Context context, Collection<ScanResult> results) { mContext = context; - initWithScanResult(result); + + if (results.isEmpty()) { + throw new IllegalArgumentException("Cannot construct with an empty ScanResult list"); + } + mScanResults.addAll(results); + + // Information derived from scan results + ScanResult firstResult = results.iterator().next(); + ssid = firstResult.SSID; + bssid = firstResult.BSSID; + security = getSecurity(firstResult); + if (security == SECURITY_PSK) { + pskType = getPskType(firstResult); + } + updateKey(); + updateRssi(); + + // Passpoint Info + mIsCarrierAp = firstResult.isCarrierAp; + mCarrierApEapType = firstResult.carrierApEapType; + mCarrierName = firstResult.carrierName; + mId = sLastId.incrementAndGet(); } + @VisibleForTesting void loadConfig(WifiConfiguration config) { + ssid = (config.SSID == null ? "" : removeDoubleQuotes(config.SSID)); + bssid = config.BSSID; + security = getSecurity(config); + updateKey(); + networkId = config.networkId; + mConfig = config; + } + + /** Updates {@link #mKey} and should only called upon object creation/initialization. */ + private void updateKey() { + // TODO(sghuman): Consolidate Key logic on ScanResultMatchInfo + + StringBuilder builder = new StringBuilder(); + + if (TextUtils.isEmpty(getSsidStr())) { + builder.append(getBssid()); + } else { + builder.append(getSsidStr()); + } + + builder.append(',').append(getSecurity()); + mKey = builder.toString(); + } + /** * Copy accesspoint information. NOTE: We do not copy tag information because that is never * set on the internal copy. - * @param that */ void copyFrom(AccessPoint that) { - that.evictOldScanResults(); 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.mScanResultCache.clear(); - this.mScanResultCache.putAll(that.mScanResultCache); + this.mScanResults.clear(); + this.mScanResults.addAll(that.mScanResults); this.mScoredNetworkCache.clear(); this.mScoredNetworkCache.putAll(that.mScoredNetworkCache); this.mId = that.mId; @@ -426,7 +469,7 @@ public class AccessPoint implements Comparable<AccessPoint> { if (WifiTracker.sVerboseLogging) { builder.append(",rssi=").append(mRssi); - builder.append(",scan cache size=").append(mScanResultCache.size()); + builder.append(",scan cache size=").append(mScanResults.size()); } return builder.append(')').toString(); @@ -468,7 +511,7 @@ public class AccessPoint implements Comparable<AccessPoint> { */ private boolean updateScores(WifiNetworkScoreCache scoreCache, long maxScoreCacheAgeMillis) { long nowMillis = SystemClock.elapsedRealtime(); - for (ScanResult result : mScanResultCache.values()) { + for (ScanResult result : mScanResults) { ScoredNetwork score = scoreCache.getScoredNetwork(result); if (score == null) { continue; @@ -555,7 +598,7 @@ public class AccessPoint implements Comparable<AccessPoint> { mIsScoredNetworkMetered |= score.meteredHint; } } else { - for (ScanResult result : mScanResultCache.values()) { + for (ScanResult result : mScanResults) { ScoredNetwork score = scoreCache.getScoredNetwork(result); if (score == null) { continue; @@ -566,19 +609,21 @@ public class AccessPoint implements Comparable<AccessPoint> { return oldMetering == mIsScoredNetworkMetered; } - private void evictOldScanResults() { - long nowMs = SystemClock.elapsedRealtime(); - for (Iterator<ScanResult> iter = mScanResultCache.values().iterator(); iter.hasNext(); ) { - ScanResult result = iter.next(); - // result timestamp is in microseconds - if (nowMs - result.timestamp / 1000 > MAX_SCAN_RESULT_AGE_MILLIS) { - iter.remove(); - } + public static String getKey(ScanResult result) { + StringBuilder builder = new StringBuilder(); + + if (TextUtils.isEmpty(result.SSID)) { + builder.append(result.BSSID); + } else { + builder.append(result.SSID); } + + builder.append(',').append(getSecurity(result)); + return builder.toString(); } - public boolean matches(ScanResult result) { - return ssid.equals(result.SSID) && security == getSecurity(result); + public String getKey() { + return mKey; } public boolean matches(WifiConfiguration config) { @@ -622,9 +667,12 @@ public class AccessPoint implements Comparable<AccessPoint> { return mRssi; } - public ConcurrentHashMap<String, ScanResult> getScanResults() { - return mScanResultCache; - } + /** + * Returns the underlying scan result set. + * + * <p>Callers should not modify this set. + */ + public Set<ScanResult> getScanResults() { return mScanResults; } public Map<String, TimestampedScoredNetwork> getScoredNetworkCache() { return mScoredNetworkCache; @@ -645,7 +693,7 @@ public class AccessPoint implements Comparable<AccessPoint> { } int rssi = UNREACHABLE_RSSI; - for (ScanResult result : mScanResultCache.values()) { + for (ScanResult result : mScanResults) { if (result.level > rssi) { rssi = result.level; } @@ -853,7 +901,6 @@ public class AccessPoint implements Comparable<AccessPoint> { } if (WifiTracker.sVerboseLogging) { - evictOldScanResults(); summary.append(WifiUtils.buildLoggingSummary(this, config)); } @@ -950,28 +997,6 @@ public class AccessPoint implements Comparable<AccessPoint> { mConfig.allowedKeyManagement.set(KeyMgmt.NONE); } - void loadConfig(WifiConfiguration config) { - ssid = (config.SSID == null ? "" : removeDoubleQuotes(config.SSID)); - bssid = config.BSSID; - security = getSecurity(config); - networkId = config.networkId; - mConfig = config; - } - - private void initWithScanResult(ScanResult result) { - ssid = result.SSID; - bssid = result.BSSID; - security = getSecurity(result); - if (security == SECURITY_PSK) - pskType = getPskType(result); - - mScanResultCache.put(result.BSSID, result); - updateRssi(); - mIsCarrierAp = result.isCarrierAp; - mCarrierApEapType = result.carrierApEapType; - mCarrierName = result.carrierName; - } - public void saveWifiState(Bundle savedState) { if (ssid != null) savedState.putString(KEY_SSID, getSsidStr()); savedState.putInt(KEY_SECURITY, security); @@ -979,9 +1004,8 @@ public class AccessPoint implements Comparable<AccessPoint> { savedState.putInt(KEY_PSKTYPE, pskType); if (mConfig != null) savedState.putParcelable(KEY_CONFIG, mConfig); savedState.putParcelable(KEY_WIFIINFO, mInfo); - evictOldScanResults(); - savedState.putParcelableArrayList(KEY_SCANRESULTCACHE, - new ArrayList<ScanResult>(mScanResultCache.values())); + savedState.putParcelableArray(KEY_SCANRESULTS, + mScanResults.toArray(new Parcelable[mScanResults.size()])); savedState.putParcelableArrayList(KEY_SCOREDNETWORKCACHE, new ArrayList<>(mScoredNetworkCache.values())); if (mNetworkInfo != null) { @@ -1003,49 +1027,58 @@ public class AccessPoint implements Comparable<AccessPoint> { } /** - * Update the AP with the given scan result. + * Sets {@link #mScanResults} to the given collection. * - * @param result the ScanResult to add to the AccessPoint scan cache - * @param evictOldScanResults whether stale scan results should be removed - * from the cache during this update process - * @return true if the scan result update caused a change in state which would impact ranking - * or AccessPoint rendering (e.g. wifi level, security) + * @param scanResults a collection of scan results to add to the internal set + * @throws IllegalArgumentException if any of the given ScanResults did not belong to this AP */ - boolean update(ScanResult result, boolean evictOldScanResults) { - if (matches(result)) { - int oldLevel = getLevel(); - - /* Add or update the scan result for the BSSID */ - mScanResultCache.put(result.BSSID, result); - if (evictOldScanResults) evictOldScanResults(); - updateRssi(); - int newLevel = getLevel(); - - if (newLevel > 0 && newLevel != oldLevel) { - // Only update labels on visible rssi changes - updateSpeed(); - if (mAccessPointListener != null) { - mAccessPointListener.onLevelChanged(this); - } + void setScanResults(Collection<ScanResult> scanResults) { + + // Validate scan results are for current AP only + String key = getKey(); + for (ScanResult result : scanResults) { + String scanResultKey = AccessPoint.getKey(result); + if (!mKey.equals(scanResultKey)) { + throw new IllegalArgumentException( + String.format("ScanResult %s\nkey of %s did not match current AP key %s", + result, scanResultKey, key)); } + } + + + int oldLevel = getLevel(); + mScanResults.clear(); + mScanResults.addAll(scanResults); + updateRssi(); + int newLevel = getLevel(); + + // If newLevel is 0, there will be no displayed Preference since the AP is unreachable + if (newLevel > 0 && newLevel != oldLevel) { + // Only update labels on visible rssi changes + updateSpeed(); + if (mAccessPointListener != null) { + mAccessPointListener.onLevelChanged(this); + } + } + + if (mAccessPointListener != null) { + mAccessPointListener.onAccessPointChanged(this); + } + + if (!scanResults.isEmpty()) { + ScanResult result = scanResults.iterator().next(); + // This flag only comes from scans, is not easily saved in config if (security == SECURITY_PSK) { pskType = getPskType(result); } - if (mAccessPointListener != null) { - mAccessPointListener.onAccessPointChanged(this); - } - // The carrier info in the ScanResult is set by the platform based on the SSID and will // always be the same for all matching scan results. mIsCarrierAp = result.isCarrierAp; mCarrierApEapType = result.carrierApEapType; mCarrierName = result.carrierName; - - return true; } - return false; } /** Attempt to update the AccessPoint and return true if an update occurred. */ diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java index dd55188e390f..109eb97ed2d8 100644 --- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java @@ -77,19 +77,6 @@ public class AccessPointPreference extends TwoTargetPreference { private int mDefaultIconResId; private int mWifiSpeed = Speed.NONE; - public static String generatePreferenceKey(AccessPoint accessPoint) { - StringBuilder builder = new StringBuilder(); - - if (TextUtils.isEmpty(accessPoint.getSsidStr())) { - builder.append(accessPoint.getBssid()); - } else { - builder.append(accessPoint.getSsidStr()); - } - - builder.append(',').append(accessPoint.getSecurity()); - return builder.toString(); - } - @Nullable private static StateListDrawable getFrictionStateListDrawable(Context context) { TypedArray frictionSld; diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/TestAccessPointBuilder.java b/packages/SettingsLib/src/com/android/settingslib/wifi/TestAccessPointBuilder.java index 3dec1d382026..2993a0de0658 100644 --- a/packages/SettingsLib/src/com/android/settingslib/wifi/TestAccessPointBuilder.java +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/TestAccessPointBuilder.java @@ -23,6 +23,7 @@ import android.net.wifi.ScanResult; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiInfo; import android.os.Bundle; +import android.os.Parcelable; import android.support.annotation.Keep; import com.android.settingslib.wifi.AccessPoint.Speed; @@ -58,7 +59,7 @@ public class TestAccessPointBuilder { private String mCarrierName = null; Context mContext; - private ArrayList<ScanResult> mScanResultCache; + private ArrayList<ScanResult> mScanResults; private ArrayList<TimestampedScoredNetwork> mScoredNetworkCache; @Keep @@ -84,8 +85,9 @@ public class TestAccessPointBuilder { if (mProviderFriendlyName != null) { bundle.putString(AccessPoint.KEY_PROVIDER_FRIENDLY_NAME, mProviderFriendlyName); } - if (mScanResultCache != null) { - bundle.putParcelableArrayList(AccessPoint.KEY_SCANRESULTCACHE, mScanResultCache); + if (mScanResults != null) { + bundle.putParcelableArray(AccessPoint.KEY_SCANRESULTS, + mScanResults.toArray(new Parcelable[mScanResults.size()])); } if (mScoredNetworkCache != null) { bundle.putParcelableArrayList(AccessPoint.KEY_SCOREDNETWORKCACHE, mScoredNetworkCache); @@ -229,8 +231,8 @@ public class TestAccessPointBuilder { return this; } - public TestAccessPointBuilder setScanResultCache(ArrayList<ScanResult> scanResultCache) { - mScanResultCache = scanResultCache; + public TestAccessPointBuilder setScanResults(ArrayList<ScanResult> scanResults) { + mScanResults = scanResults; return this; } diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java index c56e1da14921..1ac56a9de98f 100644 --- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java @@ -39,11 +39,13 @@ import android.os.HandlerThread; import android.os.Looper; import android.os.Message; import android.os.Process; +import android.os.SystemClock; import android.provider.Settings; import android.support.annotation.GuardedBy; import android.support.annotation.NonNull; import android.support.annotation.VisibleForTesting; import android.text.format.DateUtils; +import android.util.ArrayMap; import android.util.ArraySet; import android.util.Log; import android.util.SparseArray; @@ -78,6 +80,9 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro */ private static final long DEFAULT_MAX_CACHED_SCORE_AGE_MILLIS = 20 * DateUtils.MINUTE_IN_MILLIS; + /** Maximum age of scan results to hold onto while actively scanning. **/ + private static final long MAX_SCAN_RESULT_AGE_MILLIS = 25000; + private static final String TAG = "WifiTracker"; private static final boolean DBG() { return Log.isLoggable(TAG, Log.DEBUG); @@ -142,6 +147,8 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro = new AccessPointListenerAdapter(); private final HashMap<String, Integer> mSeenBssids = new HashMap<>(); + + // TODO(sghuman): Change this to be keyed on AccessPoint.getKey private final HashMap<String, ScanResult> mScanResultCache = new HashMap<>(); private Integer mScanId = 0; @@ -455,34 +462,42 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro } private Collection<ScanResult> updateScanResultCache(final List<ScanResult> newResults) { - mScanId++; + // TODO(sghuman): Delete this and replace it with the Map of Ap Keys to ScanResults for (ScanResult newResult : newResults) { if (newResult.SSID == null || newResult.SSID.isEmpty()) { continue; } mScanResultCache.put(newResult.BSSID, newResult); - mSeenBssids.put(newResult.BSSID, mScanId); } - if (mScanId > NUM_SCANS_TO_CONFIRM_AP_LOSS) { - if (DBG()) Log.d(TAG, "------ Dumping SSIDs that were expired on this scan ------"); - Integer threshold = mScanId - NUM_SCANS_TO_CONFIRM_AP_LOSS; - for (Iterator<Map.Entry<String, Integer>> it = mSeenBssids.entrySet().iterator(); - it.hasNext(); /* nothing */) { - Map.Entry<String, Integer> e = it.next(); - if (e.getValue() < threshold) { - ScanResult result = mScanResultCache.get(e.getKey()); - if (DBG()) Log.d(TAG, "Removing " + e.getKey() + ":(" + result.SSID + ")"); - mScanResultCache.remove(e.getKey()); - it.remove(); - } - } - if (DBG()) Log.d(TAG, "---- Done Dumping SSIDs that were expired on this scan ----"); + // Don't evict old results if no new scan results + if (!mStaleScanResults) { + evictOldScans(); } + // TODO(sghuman): Update a Map<ApKey, List<ScanResults>> variable to be reused later after + // double threads have been removed. + return mScanResultCache.values(); } + /** + * Remove old scan results from the cache. + * + * <p>Should only ever be invoked from {@link #updateScanResultCache(List)} when + * {@link #mStaleScanResults} is false. + */ + private void evictOldScans() { + long nowMs = SystemClock.elapsedRealtime(); + for (Iterator<ScanResult> iter = mScanResultCache.values().iterator(); iter.hasNext(); ) { + ScanResult result = iter.next(); + // result timestamp is in microseconds + if (nowMs - result.timestamp / 1000 > MAX_SCAN_RESULT_AGE_MILLIS) { + iter.remove(); + } + } + } + private WifiConfiguration getWifiConfigurationForNetworkId( int networkId, final List<WifiConfiguration> configs) { if (configs != null) { @@ -541,10 +556,12 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro /* Lookup table to more quickly update AccessPoints by only considering objects with the * correct SSID. Maps SSID -> List of AccessPoints with the given SSID. */ - Multimap<String, AccessPoint> apMap = new Multimap<String, AccessPoint>(); + Multimap<String, AccessPoint> existingApMap = new Multimap<String, AccessPoint>(); final Collection<ScanResult> results = updateScanResultCache(newScanResults); + // TODO(sghuman): This entire block only exists to populate the WifiConfiguration for + // APs, remove and refactor if (configs != null) { for (WifiConfiguration config : configs) { if (config.selfAdded && config.numAssociation == 0) { @@ -568,7 +585,7 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro accessPoint.setUnreachable(); } accessPoints.add(accessPoint); - apMap.put(accessPoint.getSsidStr(), accessPoint); + existingApMap.put(accessPoint.getSsidStr(), accessPoint); } else { // If we aren't using saved networks, drop them into the cache so that // we have access to their saved info. @@ -579,6 +596,9 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro final List<NetworkKey> scoresToRequest = new ArrayList<>(); if (results != null) { + // TODO(sghuman): Move this loop to updateScanResultCache and make instance variable + // after double handlers are removed. + ArrayMap<String, List<ScanResult>> scanResultsByApKey = new ArrayMap<>(); for (ScanResult result : results) { // Ignore hidden and ad-hoc networks. if (result.SSID == null || result.SSID.length() == 0 || @@ -591,27 +611,45 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro scoresToRequest.add(key); } + String apKey = AccessPoint.getKey(result); + List<ScanResult> resultList; + if (scanResultsByApKey.containsKey(apKey)) { + resultList = scanResultsByApKey.get(apKey); + } else { + resultList = new ArrayList<>(); + scanResultsByApKey.put(apKey, resultList); + } + + resultList.add(result); + } + + for (Map.Entry<String, List<ScanResult>> entry : scanResultsByApKey.entrySet()) { + // List can not be empty as it is dynamically constructed on each iteration + ScanResult firstResult = entry.getValue().get(0); boolean found = false; - for (AccessPoint accessPoint : apMap.getAll(result.SSID)) { - // We want to evict old scan results if are current results are not stale - if (accessPoint.update(result, !mStaleScanResults)) { - found = true; - break; - } + for (AccessPoint accessPoint : existingApMap.getAll(firstResult.SSID)) { + accessPoint.setScanResults(entry.getValue()); + found = true; + break; } - if (!found && mIncludeScans) { - AccessPoint accessPoint = getCachedOrCreate(result, cachedAccessPoints); + + // Only create a new AP / add to the list if it wasn't already in the saved configs + if (!found) { + AccessPoint accessPoint = + getCachedOrCreate(entry.getValue(), cachedAccessPoints); if (mLastInfo != null && mLastNetworkInfo != null) { accessPoint.update(connectionConfig, mLastInfo, mLastNetworkInfo); } - if (result.isPasspointNetwork()) { + // TODO(sghuman): Move isPasspointNetwork logic into AccessPoint.java + if (firstResult.isPasspointNetwork()) { // Retrieve a WifiConfiguration for a Passpoint provider that matches // the given ScanResult. This is used for showing that a given AP // (ScanResult) is available via a Passpoint provider (provider friendly // name). try { - WifiConfiguration config = mWifiManager.getMatchingWifiConfig(result); + WifiConfiguration config = + mWifiManager.getMatchingWifiConfig(firstResult); if (config != null) { accessPoint.update(config); } @@ -621,7 +659,6 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro } accessPoints.add(accessPoint); - apMap.put(accessPoint.getSsidStr(), accessPoint); } } } @@ -662,17 +699,18 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro } @VisibleForTesting - AccessPoint getCachedOrCreate(ScanResult result, List<AccessPoint> cache) { + AccessPoint getCachedOrCreate( + List<ScanResult> scanResults, + List<AccessPoint> cache) { final int N = cache.size(); for (int i = 0; i < N; i++) { - if (cache.get(i).matches(result)) { + if (cache.get(i).getKey().equals(AccessPoint.getKey(scanResults.get(0)))) { AccessPoint ret = cache.remove(i); - // evict old scan results only if we have fresh results - ret.update(result, !mStaleScanResults); + ret.setScanResults(scanResults); return ret; } } - final AccessPoint accessPoint = new AccessPoint(mContext, result); + final AccessPoint accessPoint = new AccessPoint(mContext, scanResults); accessPoint.setListener(mAccessPointListenerAdapter); return accessPoint; } diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java index 932c6fd82c50..fd48eea25e00 100644 --- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java @@ -109,7 +109,7 @@ public class WifiUtils { // TODO: sort list by RSSI or age long nowMs = SystemClock.elapsedRealtime(); - for (ScanResult result : accessPoint.getScanResults().values()) { + for (ScanResult result : accessPoint.getScanResults()) { if (result.frequency >= AccessPoint.LOWER_FREQ_5GHZ && result.frequency <= AccessPoint.HIGHER_FREQ_5GHZ) { // Strictly speaking: [4915, 5825] 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 ec594a69ef03..144031108662 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 @@ -40,6 +40,7 @@ import android.net.wifi.WifiSsid; import android.net.wifi.hotspot2.PasspointConfiguration; import android.net.wifi.hotspot2.pps.HomeSp; import android.os.Bundle; +import android.os.Parcelable; import android.os.SystemClock; import android.support.test.InstrumentationRegistry; import android.support.test.filters.SmallTest; @@ -277,7 +278,8 @@ public class AccessPointTest { scanResult.BSSID = "bssid"; scanResult.timestamp = SystemClock.elapsedRealtime() * 1000; scanResult.capabilities = ""; - assertThat(ap.update(scanResult, true /* evict old scan results */)).isTrue(); + + ap.setScanResults(Collections.singletonList(scanResult)); assertThat(ap.getRssi()).isEqualTo(expectedRssi); } @@ -477,7 +479,7 @@ public class AccessPointTest { result.carrierApEapType = WifiEnterpriseConfig.Eap.SIM; result.carrierName = carrierName; - AccessPoint ap = new AccessPoint(mContext, result); + AccessPoint ap = new AccessPoint(mContext, Collections.singletonList(result)); assertThat(ap.getSummary()).isEqualTo(String.format(mContext.getString( R.string.available_via_carrier), carrierName)); assertThat(ap.isCarrierAp()).isEqualTo(true); @@ -513,7 +515,7 @@ public class AccessPointTest { } @Test - public void testUpdateScanResultWithCarrierInfo() { + public void testSetScanResultWithCarrierInfo() { String ssid = "ssid"; AccessPoint ap = new TestAccessPointBuilder(mContext).setSsid(ssid).build(); assertThat(ap.isCarrierAp()).isEqualTo(false); @@ -529,8 +531,9 @@ public class AccessPointTest { scanResult.isCarrierAp = true; scanResult.carrierApEapType = carrierApEapType; scanResult.carrierName = carrierName; - assertThat(ap.update(scanResult, true /* evictOldScanresults */)).isTrue(); + + ap.setScanResults(Collections.singletonList(scanResult)); assertThat(ap.isCarrierAp()).isEqualTo(true); assertThat(ap.getCarrierApEapType()).isEqualTo(carrierApEapType); assertThat(ap.getCarrierName()).isEqualTo(carrierName); @@ -552,7 +555,9 @@ public class AccessPointTest { private AccessPoint createAccessPointWithScanResultCache() { Bundle bundle = new Bundle(); - bundle.putParcelableArrayList(AccessPoint.KEY_SCANRESULTCACHE, SCAN_RESULTS); + bundle.putParcelableArray( + AccessPoint.KEY_SCANRESULTS, + SCAN_RESULTS.toArray(new Parcelable[SCAN_RESULTS.size()])); return new AccessPoint(mContext, bundle); } @@ -903,7 +908,7 @@ public class AccessPointTest { .setActive(true) .setNetworkId(networkId) .setSsid(TEST_SSID) - .setScanResultCache(scanResults) + .setScanResults(scanResults) .setWifiInfo(info) .build(); @@ -990,7 +995,7 @@ public class AccessPointTest { .setActive(true) .setScoredNetworkCache( new ArrayList(Arrays.asList(recentScore))) - .setScanResultCache(SCAN_RESULTS) + .setScanResults(SCAN_RESULTS) .build(); when(mockWifiNetworkScoreCache.getScoredNetwork(any(ScanResult.class))) @@ -1018,7 +1023,7 @@ public class AccessPointTest { .setActive(true) .setScoredNetworkCache( new ArrayList(Arrays.asList(recentScore))) - .setScanResultCache(SCAN_RESULTS) + .setScanResults(SCAN_RESULTS) .build(); int newSpeed = Speed.MODERATE; 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 6615b8c02297..b36dda9deecf 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 @@ -29,6 +29,7 @@ import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; @@ -57,9 +58,9 @@ import android.os.HandlerThread; import android.os.SystemClock; import android.provider.Settings; import android.support.test.InstrumentationRegistry; +import android.support.test.filters.FlakyTest; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; -import android.support.test.filters.FlakyTest; import org.junit.After; import org.junit.Before; @@ -76,7 +77,9 @@ import org.mockito.stubbing.Answer; import java.util.ArrayList; import java.util.Arrays; import java.util.BitSet; +import java.util.Collections; import java.util.List; +import java.util.Map; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -399,7 +402,8 @@ public class WifiTrackerTest { WifiTracker tracker = new WifiTracker( InstrumentationRegistry.getTargetContext(), null, true, true); - AccessPoint result = tracker.getCachedOrCreate(scanResult, new ArrayList<AccessPoint>()); + AccessPoint result = tracker.getCachedOrCreate( + Collections.singletonList(scanResult), new ArrayList<AccessPoint>()); assertTrue(result.mAccessPointListener != null); } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/AccessPointPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/AccessPointPreferenceTest.java index a4c821f235b2..3fee16bd12ac 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/AccessPointPreferenceTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/AccessPointPreferenceTest.java @@ -61,34 +61,6 @@ public class AccessPointPreferenceTest { } @Test - public void generatePreferenceKey_returnsSsidPlusSecurity() { - String ssid = "ssid"; - String bssid = "00:00:00:00:00:00"; - int security = AccessPoint.SECURITY_WEP; - String expectedKey = ssid + ',' + security; - - TestAccessPointBuilder builder = new TestAccessPointBuilder(mContext); - builder.setBssid(bssid).setSsid(ssid).setSecurity(security); - - assertThat(AccessPointPreference.generatePreferenceKey(builder.build())) - .isEqualTo(expectedKey); - } - - @Test - public void generatePreferenceKey_emptySsidReturnsBssidPlusSecurity() { - String ssid = ""; - String bssid = "00:00:00:00:00:00"; - int security = AccessPoint.SECURITY_WEP; - String expectedKey = bssid + ',' + security; - - TestAccessPointBuilder builder = new TestAccessPointBuilder(mContext); - builder.setBssid(bssid).setSsid(ssid).setSecurity(security); - - assertThat(AccessPointPreference.generatePreferenceKey(builder.build())) - .isEqualTo(expectedKey); - } - - @Test public void refresh_openNetwork_updateContentDescription() { final String ssid = "ssid"; final String summary = "connected"; diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiUtilsTest.java index c5795d34eae8..9310b73afdcc 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiUtilsTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiUtilsTest.java @@ -29,6 +29,7 @@ import android.net.WifiKey; import android.net.wifi.ScanResult; import android.net.wifi.WifiNetworkScoreCache; import android.os.Bundle; +import android.os.Parcelable; import android.os.SystemClock; import android.text.format.DateUtils; @@ -72,7 +73,8 @@ public class WifiUtilsTest { Bundle bundle = new Bundle(); ArrayList<ScanResult> scanResults = buildScanResultCache(); - bundle.putParcelableArrayList(AccessPoint.KEY_SCANRESULTCACHE, scanResults); + bundle.putParcelableArray(AccessPoint.KEY_SCANRESULTS, + scanResults.toArray(new Parcelable[scanResults.size()])); AccessPoint ap = new AccessPoint(mContext, bundle); when(mockWifiNetworkScoreCache.getScoredNetwork(any(ScanResult.class))) diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index 1fc36bef215d..9613a6a8c059 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -90,7 +90,6 @@ <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" /> <uses-permission android:name="android.permission.GET_TOP_ACTIVITY_INFO" /> <uses-permission android:name="android.permission.MANAGE_ACTIVITY_STACKS" /> - <uses-permission android:name="android.permission.START_ACTIVITY_AS_CALLER" /> <uses-permission android:name="android.permission.START_TASKS_FROM_RECENTS" /> <uses-permission android:name="android.permission.GET_INTENT_SENDER_INTENT" /> @@ -560,22 +559,6 @@ </intent-filter> </activity> - <activity android:name=".chooser.ChooserActivity" - android:theme="@*android:style/Theme.NoDisplay" - android:finishOnCloseSystemDialogs="true" - android:excludeFromRecents="true" - android:documentLaunchMode="never" - android:relinquishTaskIdentity="true" - android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation|keyboard|keyboardHidden" - android:process=":ui" - android:visibleToInstantApps="true"> - <intent-filter> - <action android:name="android.intent.action.CHOOSER_UI" /> - <category android:name="android.intent.category.DEFAULT" /> - <category android:name="android.intent.category.VOICE" /> - </intent-filter> - </activity> - <!-- Doze with notifications, run in main sysui process for every user --> <service android:name=".doze.DozeService" diff --git a/packages/SystemUI/shared/Android.mk b/packages/SystemUI/shared/Android.mk index 5f75f7d71584..21b0ed85a420 100644 --- a/packages/SystemUI/shared/Android.mk +++ b/packages/SystemUI/shared/Android.mk @@ -30,7 +30,7 @@ include $(BUILD_STATIC_JAVA_LIBRARY) include $(CLEAR_VARS) -LOCAL_PACKAGE_NAME := SystemUISharedLib +LOCAL_PACKAGE_NAME := SysUISharedLib LOCAL_SRC_FILES := $(call all-java-files-under, src) LOCAL_JAVA_LIBRARIES := SystemUISharedLib @@ -39,4 +39,4 @@ LOCAL_PROGUARD_ENABLED := disabled include $(BUILD_PACKAGE) -include $(call all-makefiles-under,$(LOCAL_PATH))
\ No newline at end of file +include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java index 8666b0c873e7..1ae06d751255 100644 --- a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java +++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java @@ -119,18 +119,10 @@ public class BatteryMeterView extends LinearLayout implements addView(mBatteryIconView, mlp); updateShowPercent(); - - Context dualToneDarkTheme = new ContextThemeWrapper(context, - Utils.getThemeAttr(context, R.attr.darkIconTheme)); - Context dualToneLightTheme = new ContextThemeWrapper(context, - Utils.getThemeAttr(context, R.attr.lightIconTheme)); - mDarkModeBackgroundColor = Utils.getColorAttr(dualToneDarkTheme, R.attr.backgroundColor); - mDarkModeFillColor = Utils.getColorAttr(dualToneDarkTheme, R.attr.fillColor); - mLightModeBackgroundColor = Utils.getColorAttr(dualToneLightTheme, R.attr.backgroundColor); - mLightModeFillColor = Utils.getColorAttr(dualToneLightTheme, R.attr.fillColor); - + setColorsFromContext(context); // Init to not dark at all. onDarkChanged(new Rect(), 0, DarkIconDispatcher.DEFAULT_ICON_TINT); + mUserTracker = new CurrentUserTracker(mContext) { @Override public void onUserSwitched(int newUserId) { @@ -148,6 +140,21 @@ public class BatteryMeterView extends LinearLayout implements updateShowPercent(); } + public void setColorsFromContext(Context context) { + if (context == null) { + return; + } + + Context dualToneDarkTheme = new ContextThemeWrapper(context, + Utils.getThemeAttr(context, R.attr.darkIconTheme)); + Context dualToneLightTheme = new ContextThemeWrapper(context, + Utils.getThemeAttr(context, R.attr.lightIconTheme)); + mDarkModeBackgroundColor = Utils.getColorAttr(dualToneDarkTheme, R.attr.backgroundColor); + mDarkModeFillColor = Utils.getColorAttr(dualToneDarkTheme, R.attr.fillColor); + mLightModeBackgroundColor = Utils.getColorAttr(dualToneLightTheme, R.attr.backgroundColor); + mLightModeFillColor = Utils.getColorAttr(dualToneLightTheme, R.attr.fillColor); + } + @Override public boolean hasOverlappingRendering() { return false; diff --git a/packages/SystemUI/src/com/android/systemui/chooser/ChooserActivity.java b/packages/SystemUI/src/com/android/systemui/chooser/ChooserActivity.java deleted file mode 100644 index 085ece75362d..000000000000 --- a/packages/SystemUI/src/com/android/systemui/chooser/ChooserActivity.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.chooser; - -import android.app.Activity; -import android.app.ActivityManager; -import android.content.Intent; -import android.os.Bundle; -import android.os.IBinder; -import android.util.Log; - -import com.android.systemui.R; - -import java.lang.Thread; -import java.util.ArrayList; - -public final class ChooserActivity extends Activity { - - private static final String TAG = "ChooserActivity"; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - ChooserHelper.onChoose(this); - finish(); - } -} diff --git a/packages/SystemUI/src/com/android/systemui/chooser/ChooserHelper.java b/packages/SystemUI/src/com/android/systemui/chooser/ChooserHelper.java deleted file mode 100644 index ac22568f7368..000000000000 --- a/packages/SystemUI/src/com/android/systemui/chooser/ChooserHelper.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.chooser; - -import android.app.Activity; -import android.app.ActivityManager; -import android.content.Intent; -import android.os.Bundle; -import android.os.IBinder; -import android.util.Log; - -import com.android.systemui.R; - -public class ChooserHelper { - - private static final String TAG = "ChooserHelper"; - - static void onChoose(Activity activity) { - final Intent thisIntent = activity.getIntent(); - final Bundle thisExtras = thisIntent.getExtras(); - final Intent chosenIntent = thisIntent.getParcelableExtra(Intent.EXTRA_INTENT); - final Bundle options = thisIntent.getParcelableExtra(ActivityManager.EXTRA_OPTIONS); - final IBinder permissionToken = - thisExtras.getBinder(ActivityManager.EXTRA_PERMISSION_TOKEN); - final boolean ignoreTargetSecurity = - thisIntent.getBooleanExtra(ActivityManager.EXTRA_IGNORE_TARGET_SECURITY, false); - final int userId = thisIntent.getIntExtra(Intent.EXTRA_USER_ID, -1); - activity.startActivityAsCaller( - chosenIntent, options, permissionToken, ignoreTargetSecurity, userId); - } -} diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java index 77768b1a4bd1..4d7333b99eee 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java @@ -97,7 +97,6 @@ public class QuickStatusBarHeader extends RelativeLayout mIconManager.setTint(fillColor); BatteryMeterView battery = findViewById(R.id.battery); - battery.setFillColor(Color.WHITE); battery.setForceShowPercent(true); mActivityStarter = Dependency.get(ActivityStarter.class); @@ -216,6 +215,11 @@ public class QuickStatusBarHeader extends RelativeLayout //host.setHeaderView(mExpandIndicator); mHeaderQsPanel.setQSPanelAndHeader(mQsPanel, this); mHeaderQsPanel.setHost(host, null /* No customization in header */); + + // Use SystemUI context to get battery meter colors, and let it use the default tint (white) + BatteryMeterView battery = findViewById(R.id.battery); + battery.setColorsFromContext(mHost.getContext()); + battery.onDarkChanged(new Rect(), 0, DarkIconDispatcher.DEFAULT_ICON_TINT); } public void setCallback(Callback qsPanelCallback) { 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 907af690a615..11d20b221051 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java @@ -56,10 +56,15 @@ public class ActivityLaunchAnimator { public static final long ANIMATION_DELAY_ICON_FADE_IN = ANIMATION_DURATION - CollapsedStatusBarFragment.FADE_IN_DURATION - CollapsedStatusBarFragment.FADE_IN_DELAY - 16; + private static final long LAUNCH_TIMEOUT = 500; private final NotificationPanelView mNotificationPanel; private final NotificationListContainer mNotificationContainer; private final StatusBarWindowView mStatusBarWindow; - private final StatusBar mStatusBar; + private StatusBar mStatusBar; + private final Runnable mTimeoutRunnable = () -> { + setAnimationPending(false); + mStatusBar.collapsePanel(true /* animate */); + }; private boolean mAnimationPending; public ActivityLaunchAnimator(StatusBarWindowView statusBarWindow, @@ -92,6 +97,11 @@ public class ActivityLaunchAnimator { private void setAnimationPending(boolean pending) { mAnimationPending = pending; mStatusBarWindow.setExpandAnimationPending(pending); + if (pending) { + mStatusBarWindow.postDelayed(mTimeoutRunnable, LAUNCH_TIMEOUT); + } else { + mStatusBarWindow.removeCallbacks(mTimeoutRunnable); + } } class AnimationRunner extends IRemoteAnimationRunner.Stub { 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 b51982415bbd..426268ba490c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -2656,6 +2656,10 @@ public class StatusBar extends SystemUI implements DemoMode, if (mStatusBarView != null) { dumpBarTransitions(pw, "mStatusBarView", mStatusBarView.getBarTransitions()); } + pw.println(" StatusBarWindowView: "); + if (mStatusBarWindow != null) { + mStatusBarWindow.dump(fd, pw, args); + } pw.println(" mMediaManager: "); if (mMediaManager != null) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java index e32914fa368b..a79a41b07797 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java @@ -62,6 +62,9 @@ import com.android.systemui.statusbar.DragDownHelper; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.stack.NotificationStackScrollLayout; +import java.io.FileDescriptor; +import java.io.PrintWriter; + public class StatusBarWindowView extends FrameLayout { public static final String TAG = "StatusBarWindowView"; @@ -398,6 +401,13 @@ public class StatusBarWindowView extends FrameLayout { mExpandAnimationPending = pending; } + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + pw.print(" mExpandAnimationPending="); pw.println(mExpandAnimationPending); + pw.print(" mExpandAnimationRunning="); pw.println(mExpandAnimationRunning); + pw.print(" mTouchCancelled="); pw.println(mTouchCancelled); + pw.print(" mTouchActive="); pw.println(mTouchActive); + } + public class LayoutParams extends FrameLayout.LayoutParams { public boolean ignoreRightInset; diff --git a/packages/SystemUI/tests/src/com/android/systemui/chooser/ChooserHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/chooser/ChooserHelperTest.java deleted file mode 100644 index 8e0426a15eee..000000000000 --- a/packages/SystemUI/tests/src/com/android/systemui/chooser/ChooserHelperTest.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.chooser; - -import android.app.Activity; -import android.app.ActivityManager; -import android.content.Intent; -import android.os.Binder; -import android.support.test.runner.AndroidJUnit4; -import android.test.suitebuilder.annotation.SmallTest; -import android.util.Log; - -import com.android.systemui.chooser.ChooserHelper; -import com.android.systemui.SysuiTestCase; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.anyBoolean; -import static org.mockito.Mockito.anyFloat; -import static org.mockito.Mockito.anyInt; -import static org.mockito.Mockito.anyString; -import static org.mockito.Mockito.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -@SmallTest -@RunWith(AndroidJUnit4.class) -public class ChooserHelperTest extends SysuiTestCase { - - @Test - public void testOnChoose_CallsStartActivityAsCallerWithToken() { - final Intent intent = new Intent(); - final Binder token = new Binder(); - intent.putExtra(ActivityManager.EXTRA_PERMISSION_TOKEN, token); - - final Activity mockActivity = mock(Activity.class); - when(mockActivity.getIntent()).thenReturn(intent); - - ChooserHelper.onChoose(mockActivity); - verify(mockActivity, times(1)).startActivityAsCaller( - any(), any(), eq(token), anyBoolean(), anyInt()); - } -} diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java index ba9883b4de59..8265262d3824 100644 --- a/services/core/java/com/android/server/BatteryService.java +++ b/services/core/java/com/android/server/BatteryService.java @@ -835,6 +835,9 @@ public final class BatteryService extends SystemService { case "level": mHealthInfo.batteryLevel = Integer.parseInt(value); break; + case "counter": + mHealthInfo.batteryChargeCounter = Integer.parseInt(value); + break; case "temp": mHealthInfo.batteryTemperature = Integer.parseInt(value); break; @@ -1164,6 +1167,20 @@ public final class BatteryService extends SystemService { } @Override + public int getBatteryChargeCounter() { + synchronized (mLock) { + return mHealthInfo.batteryChargeCounter; + } + } + + @Override + public int getBatteryFullCharge() { + synchronized (mLock) { + return mHealthInfo.batteryFullCharge; + } + } + + @Override public boolean getBatteryLevelLow() { synchronized (mLock) { return mBatteryLevelLow; diff --git a/services/core/java/com/android/server/EventLogTags.logtags b/services/core/java/com/android/server/EventLogTags.logtags index 732ac66b41de..219facd0b002 100644 --- a/services/core/java/com/android/server/EventLogTags.logtags +++ b/services/core/java/com/android/server/EventLogTags.logtags @@ -34,6 +34,7 @@ option java_package com.android.server 2731 power_soft_sleep_requested (savedwaketimems|2) # Power save state has changed. See BatterySaverController.java for the details. 2739 battery_saver_mode (prevOffOrOn|1|5),(nowOffOrOn|1|5),(interactive|1|5),(features|3|5) +27390 battery_saving_stats (batterySaver|1|5),(interactive|1|5),(doze|1|5),(delta_duration|2|3),(delta_battery_drain|1|6),(total_duration|2|3),(total_battery_drain|1|6) # # Leave IDs through 2740 for more power logs (2730 used by battery_discharge above) diff --git a/services/core/java/com/android/server/PinnerService.java b/services/core/java/com/android/server/PinnerService.java index 3d7408ee6a54..2869114601b7 100644 --- a/services/core/java/com/android/server/PinnerService.java +++ b/services/core/java/com/android/server/PinnerService.java @@ -372,15 +372,19 @@ public final class PinnerService extends SystemService { @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return; + long totalSize = 0; pw.println("Pinned Files:"); synchronized(this) { for (int i = 0; i < mPinnedFiles.size(); i++) { pw.println(mPinnedFiles.get(i).mFilename); + totalSize += mPinnedFiles.get(i).mLength; } for (int i = 0; i < mPinnedCameraFiles.size(); i++) { pw.println(mPinnedCameraFiles.get(i).mFilename); + totalSize += mPinnedCameraFiles.get(i).mLength; } } + pw.println("Total size: " + totalSize); } } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index fffb4b578eea..f6f7d4bf4fd5 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -26,7 +26,6 @@ import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW; import static android.Manifest.permission.MANAGE_ACTIVITY_STACKS; import static android.Manifest.permission.READ_FRAME_BUFFER; import static android.Manifest.permission.REMOVE_TASKS; -import static android.Manifest.permission.START_ACTIVITY_AS_CALLER; import static android.Manifest.permission.START_TASKS_FROM_RECENTS; import static android.app.ActivityManager.LOCK_TASK_MODE_NONE; import static android.app.ActivityManager.RESIZE_MODE_PRESERVE_WINDOW; @@ -571,23 +570,6 @@ public class ActivityManagerService extends IActivityManager.Stub // could take much longer than usual. static final int PROC_START_TIMEOUT_WITH_WRAPPER = 1200*1000; - // Permission tokens are used to temporarily granted a trusted app the ability to call - // #startActivityAsCaller. A client is expected to dump its token after this time has elapsed, - // showing any appropriate error messages to the user. - private static final long START_AS_CALLER_TOKEN_TIMEOUT = - 10 * DateUtils.MINUTE_IN_MILLIS; - - // How long before the service actually expires a token. This is slightly longer than - // START_AS_CALLER_TOKEN_TIMEOUT, to provide a buffer so clients will rarely encounter the - // expiration exception. - private static final long START_AS_CALLER_TOKEN_TIMEOUT_IMPL = - START_AS_CALLER_TOKEN_TIMEOUT + 2*1000; - - // How long the service will remember expired tokens, for the purpose of providing error - // messaging when a client uses an expired token. - private static final long START_AS_CALLER_TOKEN_EXPIRED_TIMEOUT = - START_AS_CALLER_TOKEN_TIMEOUT_IMPL + 20 * DateUtils.MINUTE_IN_MILLIS; - // How long we allow a receiver to run before giving up on it. static final int BROADCAST_FG_TIMEOUT = 10*1000; static final int BROADCAST_BG_TIMEOUT = 60*1000; @@ -696,13 +678,6 @@ public class ActivityManagerService extends IActivityManager.Stub final ArrayList<ActiveInstrumentation> mActiveInstrumentation = new ArrayList<>(); - // Activity tokens of system activities that are delegating their call to - // #startActivityByCaller, keyed by the permissionToken granted to the delegate. - final HashMap<IBinder, IBinder> mStartActivitySources = new HashMap<>(); - - // Permission tokens that have expired, but we remember for error reporting. - final ArrayList<IBinder> mExpiredStartAsCallerTokens = new ArrayList<>(); - public final IntentFirewall mIntentFirewall; // Whether we should show our dialogs (ANR, crash, etc) or just perform their @@ -1881,8 +1856,6 @@ public class ActivityManagerService extends IActivityManager.Stub static final int PUSH_TEMP_WHITELIST_UI_MSG = 68; static final int SERVICE_FOREGROUND_CRASH_MSG = 69; static final int DISPATCH_OOM_ADJ_OBSERVER_MSG = 70; - static final int EXPIRE_START_AS_CALLER_TOKEN_MSG = 75; - static final int FORGET_START_AS_CALLER_TOKEN_MSG = 76; static final int FIRST_ACTIVITY_STACK_MSG = 100; static final int FIRST_BROADCAST_QUEUE_MSG = 200; @@ -2547,19 +2520,6 @@ public class ActivityManagerService extends IActivityManager.Stub } } } break; - case EXPIRE_START_AS_CALLER_TOKEN_MSG: { - synchronized (ActivityManagerService.this) { - final IBinder permissionToken = (IBinder)msg.obj; - mStartActivitySources.remove(permissionToken); - mExpiredStartAsCallerTokens.add(permissionToken); - } - } break; - case FORGET_START_AS_CALLER_TOKEN_MSG: { - synchronized (ActivityManagerService.this) { - final IBinder permissionToken = (IBinder)msg.obj; - mExpiredStartAsCallerTokens.remove(permissionToken); - } - } break; } } }; @@ -4845,54 +4805,16 @@ public class ActivityManagerService extends IActivityManager.Stub } - /** - * Only callable from the system. This token grants a temporary permission to call - * #startActivityAsCallerWithToken. The token will time out after - * START_AS_CALLER_TOKEN_TIMEOUT if it is not used. - * - * @param delegatorToken The Binder token referencing the system Activity that wants to delegate - * the #startActivityAsCaller to another app. The "caller" will be the caller of this - * activity's token, not the delegate's caller (which is probably the delegator itself). - * - * @return Returns a token that can be given to a "delegate" app that may call - * #startActivityAsCaller - */ @Override - public IBinder requestStartActivityPermissionToken(IBinder delegatorToken) { - int callingUid = Binder.getCallingUid(); - if (UserHandle.getAppId(callingUid) != SYSTEM_UID) { - throw new SecurityException("Only the system process can request a permission token, " + - "received request from uid: " + callingUid); - } - IBinder permissionToken = new Binder(); - synchronized (this) { - mStartActivitySources.put(permissionToken, delegatorToken); - } - - Message expireMsg = mHandler.obtainMessage(EXPIRE_START_AS_CALLER_TOKEN_MSG, - permissionToken); - mHandler.sendMessageDelayed(expireMsg, START_AS_CALLER_TOKEN_TIMEOUT_IMPL); - - Message forgetMsg = mHandler.obtainMessage(FORGET_START_AS_CALLER_TOKEN_MSG, - permissionToken); - mHandler.sendMessageDelayed(forgetMsg, START_AS_CALLER_TOKEN_EXPIRED_TIMEOUT); - - return permissionToken; - } + public final int startActivityAsCaller(IApplicationThread caller, String callingPackage, + Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode, + int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, boolean ignoreTargetSecurity, + int userId) { - @Override - public final int startActivityAsCaller(IApplicationThread caller, - String callingPackage, Intent intent, String resolvedType, IBinder resultTo, - String resultWho, int requestCode, int startFlags, ProfilerInfo profilerInfo, - Bundle bOptions, IBinder permissionToken, boolean ignoreTargetSecurity, int userId) { // This is very dangerous -- it allows you to perform a start activity (including - // permission grants) as any app that may launch one of your own activities. So we only - // allow this in two cases: - // 1) The caller is an activity that is part of the core framework, and then only when it - // is running as the system. - // 2) The caller provides a valid permissionToken. Permission tokens are one-time use and - // can only be requested by a system activity, which may then delegate this call to - // another app. + // permission grants) as any app that may launch one of your own activities. So + // we will only allow this to be done from activities that are part of the core framework, + // and then only when they are running as the system. final ActivityRecord sourceRecord; final int targetUid; final String targetPackage; @@ -4900,47 +4822,17 @@ public class ActivityManagerService extends IActivityManager.Stub if (resultTo == null) { throw new SecurityException("Must be called from an activity"); } - - final IBinder sourceToken; - if (permissionToken != null) { - // To even attempt to use a permissionToken, an app must also have this signature - // permission. - enforceCallingPermission(android.Manifest.permission.START_ACTIVITY_AS_CALLER, - "startActivityAsCaller"); - // If called with a permissionToken, we want the sourceRecord from the delegator - // activity that requested this token. - sourceToken = - mStartActivitySources.remove(permissionToken); - if (sourceToken == null) { - // Invalid permissionToken, check if it recently expired. - if (mExpiredStartAsCallerTokens.contains(permissionToken)) { - throw new SecurityException("Called with expired permission token: " - + permissionToken); - } else { - throw new SecurityException("Called with invalid permission token: " - + permissionToken); - } - } - } else { - // This method was called directly by the source. - sourceToken = resultTo; - } - - sourceRecord = mStackSupervisor.isInAnyStackLocked(sourceToken); + sourceRecord = mStackSupervisor.isInAnyStackLocked(resultTo); if (sourceRecord == null) { - throw new SecurityException("Called with bad activity token: " + sourceToken); + throw new SecurityException("Called with bad activity token: " + resultTo); + } + if (!sourceRecord.info.packageName.equals("android")) { + throw new SecurityException( + "Must be called from an activity that is declared in the android package"); } if (sourceRecord.app == null) { throw new SecurityException("Called without a process attached to activity"); } - - // Whether called directly or from a delegate, the source activity must be from the - // android package. - if (!sourceRecord.info.packageName.equals("android")) { - throw new SecurityException("Must be called from an activity that is " + - "declared in the android package"); - } - if (UserHandle.getAppId(sourceRecord.app.uid) != SYSTEM_UID) { // This is still okay, as long as this activity is running under the // uid of the original calling activity. @@ -4951,7 +4843,6 @@ public class ActivityManagerService extends IActivityManager.Stub + sourceRecord.launchedFromUid); } } - if (ignoreTargetSecurity) { if (intent.getComponent() == null) { throw new SecurityException( @@ -14853,6 +14744,8 @@ public class ActivityManagerService extends IActivityManager.Stub mUserController.sendUserSwitchBroadcasts(-1, currentUserId); BinderInternal.nSetBinderProxyCountEnabled(true); + //STOPSHIP: Temporary BinderProxy Threshold for b/71353150 + BinderInternal.nSetBinderProxyCountWatermarks(1500, 1200); BinderInternal.setBinderProxyCountCallback( new BinderInternal.BinderProxyLimitListener() { @Override diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java index ec8cf91a44cf..fd3f8ec92cf3 100644 --- a/services/core/java/com/android/server/am/ActivityStack.java +++ b/services/core/java/com/android/server/am/ActivityStack.java @@ -453,6 +453,9 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai mStackId = stackId; mCurrentUser = mService.mUserController.getCurrentUserId(); mTmpRect2.setEmpty(); + // Set display id before setting activity and window type to make sure it won't affect + // stacks on a wrong display. + mDisplayId = display.mDisplayId; setActivityType(activityType); setWindowingMode(windowingMode); mWindowContainerController = createStackWindowController(display.mDisplayId, onTop, diff --git a/services/core/java/com/android/server/am/RecentsAnimation.java b/services/core/java/com/android/server/am/RecentsAnimation.java index f0b27a74a66b..c188ccb4d273 100644 --- a/services/core/java/com/android/server/am/RecentsAnimation.java +++ b/services/core/java/com/android/server/am/RecentsAnimation.java @@ -70,55 +70,61 @@ class RecentsAnimation implements RecentsAnimationCallbacks { void startRecentsActivity(Intent intent, IRecentsAnimationRunner recentsAnimationRunner, ComponentName recentsComponent, int recentsUid) { + mWindowManager.deferSurfaceLayout(); + try { + // Cancel the previous recents animation if necessary + mWindowManager.cancelRecentsAnimation(); - // Cancel the previous recents animation if necessary - mWindowManager.cancelRecentsAnimation(); - - final boolean hasExistingHomeActivity = mStackSupervisor.getHomeActivity() != null; - if (!hasExistingHomeActivity) { - // No home activity - final ActivityOptions opts = ActivityOptions.makeBasic(); - opts.setLaunchActivityType(ACTIVITY_TYPE_HOME); - opts.setAvoidMoveToFront(); - intent.addFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_NO_ANIMATION); - - mActivityStartController.obtainStarter(intent, "startRecentsActivity_noHomeActivity") - .setCallingUid(recentsUid) - .setCallingPackage(recentsComponent.getPackageName()) - .setActivityOptions(SafeActivityOptions.fromBundle(opts.toBundle())) - .setMayWait(mUserController.getCurrentUserId()) - .execute(); - mWindowManager.prepareAppTransition(TRANSIT_NONE, false); - - // TODO: Maybe wait for app to draw in this particular case? - } + final boolean hasExistingHomeActivity = mStackSupervisor.getHomeActivity() != null; + if (!hasExistingHomeActivity) { + // No home activity + final ActivityOptions opts = ActivityOptions.makeBasic(); + opts.setLaunchActivityType(ACTIVITY_TYPE_HOME); + opts.setAvoidMoveToFront(); + intent.addFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_NO_ANIMATION); + + mActivityStartController + .obtainStarter(intent, "startRecentsActivity_noHomeActivity") + .setCallingUid(recentsUid) + .setCallingPackage(recentsComponent.getPackageName()) + .setActivityOptions(SafeActivityOptions.fromBundle(opts.toBundle())) + .setMayWait(mUserController.getCurrentUserId()) + .execute(); + mWindowManager.prepareAppTransition(TRANSIT_NONE, false); - final ActivityRecord homeActivity = mStackSupervisor.getHomeActivity(); - final ActivityDisplay display = homeActivity.getDisplay(); + // TODO: Maybe wait for app to draw in this particular case? + } - // Save the initial position of the home activity stack to be restored to after the - // animation completes - mRestoreHomeBehindStack = hasExistingHomeActivity - ? display.getStackAboveHome() - : null; + final ActivityRecord homeActivity = mStackSupervisor.getHomeActivity(); + final ActivityDisplay display = homeActivity.getDisplay(); - // Move the home activity into place for the animation - display.moveHomeStackBehindBottomMostVisibleStack(); + // Save the initial position of the home activity stack to be restored to after the + // animation completes + mRestoreHomeBehindStack = hasExistingHomeActivity + ? display.getStackAboveHome() + : null; - // Mark the home activity as launch-behind to bump its visibility for the - // duration of the gesture that is driven by the recents component - homeActivity.mLaunchTaskBehind = true; + // Move the home activity into place for the animation + display.moveHomeStackBehindBottomMostVisibleStack(); - // Fetch all the surface controls and pass them to the client to get the animation - // started - mWindowManager.initializeRecentsAnimation(recentsAnimationRunner, this, display.mDisplayId); + // Mark the home activity as launch-behind to bump its visibility for the + // duration of the gesture that is driven by the recents component + homeActivity.mLaunchTaskBehind = true; - // If we updated the launch-behind state, update the visibility of the activities after we - // fetch the visible tasks to be controlled by the animation - mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, PRESERVE_WINDOWS); + // Fetch all the surface controls and pass them to the client to get the animation + // started + mWindowManager.initializeRecentsAnimation(recentsAnimationRunner, this, + display.mDisplayId); - // Post a timeout for the animation - mHandler.postDelayed(mCancelAnimationRunnable, RECENTS_ANIMATION_TIMEOUT); + // If we updated the launch-behind state, update the visibility of the activities after + // we fetch the visible tasks to be controlled by the animation + mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, PRESERVE_WINDOWS); + + // Post a timeout for the animation + mHandler.postDelayed(mCancelAnimationRunnable, RECENTS_ANIMATION_TIMEOUT); + } finally { + mWindowManager.continueSurfaceLayout(); + } } @Override @@ -128,35 +134,40 @@ class RecentsAnimation implements RecentsAnimationCallbacks { if (mWindowManager.getRecentsAnimationController() == null) return; mWindowManager.inSurfaceTransaction(() -> { - mWindowManager.cleanupRecentsAnimation(); - - // Move the home stack to the front - final ActivityRecord homeActivity = mStackSupervisor.getHomeActivity(); - if (homeActivity == null) { - return; + mWindowManager.deferSurfaceLayout(); + try { + mWindowManager.cleanupRecentsAnimation(); + + // Move the home stack to the front + final ActivityRecord homeActivity = mStackSupervisor.getHomeActivity(); + if (homeActivity == null) { + return; + } + + // Restore the launched-behind state + homeActivity.mLaunchTaskBehind = false; + + if (moveHomeToTop) { + // Bring the home stack to the front + final ActivityStack homeStack = homeActivity.getStack(); + homeStack.mNoAnimActivities.add(homeActivity); + homeStack.moveToFront("RecentsAnimation.onAnimationFinished()"); + } else { + // Restore the home stack to its previous position + final ActivityDisplay display = homeActivity.getDisplay(); + display.moveHomeStackBehindStack(mRestoreHomeBehindStack); + } + + mWindowManager.prepareAppTransition(TRANSIT_NONE, false); + mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, false); + mStackSupervisor.resumeFocusedStackTopActivityLocked(); + + // No reason to wait for the pausing activity in this case, as the hiding of + // surfaces needs to be done immediately. + mWindowManager.executeAppTransition(); + } finally { + mWindowManager.continueSurfaceLayout(); } - - // Restore the launched-behind state - homeActivity.mLaunchTaskBehind = false; - - if (moveHomeToTop) { - // Bring the home stack to the front - final ActivityStack homeStack = homeActivity.getStack(); - homeStack.mNoAnimActivities.add(homeActivity); - homeStack.moveToFront("RecentsAnimation.onAnimationFinished()"); - } else { - // Restore the home stack to its previous position - final ActivityDisplay display = homeActivity.getDisplay(); - display.moveHomeStackBehindStack(mRestoreHomeBehindStack); - } - - mWindowManager.prepareAppTransition(TRANSIT_NONE, false); - mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, false); - mStackSupervisor.resumeFocusedStackTopActivityLocked(); - - // No reason to wait for the pausing activity in this case, as the hiding of - // surfaces needs to be done immediately. - mWindowManager.executeAppTransition(); }); } } diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index f6bcb2524534..f247de7adadc 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -1329,8 +1329,20 @@ public class AudioService extends IAudioService.Stub /** @see AudioManager#adjustVolume(int, int) */ public void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags, String callingPackage, String caller) { - adjustSuggestedStreamVolume(direction, suggestedStreamType, flags, callingPackage, - caller, Binder.getCallingUid()); + final IAudioPolicyCallback extVolCtlr; + synchronized (mExtVolumeControllerLock) { + extVolCtlr = mExtVolumeController; + } + if (extVolCtlr != null) { + try { + mExtVolumeController.notifyVolumeAdjust(direction); + } catch(RemoteException e) { + // nothing we can do about this. Do not log error, too much potential for spam + } + } else { + adjustSuggestedStreamVolume(direction, suggestedStreamType, flags, callingPackage, + caller, Binder.getCallingUid()); + } } private void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags, @@ -6964,7 +6976,7 @@ public class AudioService extends IAudioService.Stub // Audio policy management //========================================================================================== public String registerAudioPolicy(AudioPolicyConfig policyConfig, IAudioPolicyCallback pcb, - boolean hasFocusListener, boolean isFocusPolicy) { + boolean hasFocusListener, boolean isFocusPolicy, boolean isVolumeController) { AudioSystem.setDynamicPolicyCallback(mDynPolicyCallback); if (DEBUG_AP) Log.d(TAG, "registerAudioPolicy for " + pcb.asBinder() @@ -6987,7 +6999,7 @@ public class AudioService extends IAudioService.Stub return null; } AudioPolicyProxy app = new AudioPolicyProxy(policyConfig, pcb, hasFocusListener, - isFocusPolicy); + isFocusPolicy, isVolumeController); pcb.asBinder().linkToDeath(app, 0/*flags*/); regId = app.getRegistrationId(); mAudioPolicies.put(pcb.asBinder(), app); @@ -7053,6 +7065,23 @@ public class AudioService extends IAudioService.Stub return AudioManager.SUCCESS; } + private final Object mExtVolumeControllerLock = new Object(); + private IAudioPolicyCallback mExtVolumeController; + private void setExtVolumeController(IAudioPolicyCallback apc) { + if (!mContext.getResources().getBoolean( + com.android.internal.R.bool.config_handleVolumeKeysInWindowManager)) { + Log.e(TAG, "Cannot set external volume controller: device not set for volume keys" + + " handled in PhoneWindowManager"); + return; + } + synchronized (mExtVolumeControllerLock) { + if (mExtVolumeController != null && !mExtVolumeController.asBinder().pingBinder()) { + Log.e(TAG, "Cannot set external volume controller: existing controller"); + } + mExtVolumeController = apc; + } + } + private void dumpAudioPolicies(PrintWriter pw) { pw.println("\nAudio policies:"); synchronized (mAudioPolicies) { @@ -7185,8 +7214,9 @@ public class AudioService extends IAudioService.Stub */ public class AudioPolicyProxy extends AudioPolicyConfig implements IBinder.DeathRecipient { private static final String TAG = "AudioPolicyProxy"; - IAudioPolicyCallback mPolicyCallback; - boolean mHasFocusListener; + final IAudioPolicyCallback mPolicyCallback; + final boolean mHasFocusListener; + final boolean mIsVolumeController; /** * Audio focus ducking behavior for an audio policy. * This variable reflects the value that was successfully set in @@ -7198,11 +7228,12 @@ public class AudioService extends IAudioService.Stub boolean mIsFocusPolicy = false; AudioPolicyProxy(AudioPolicyConfig config, IAudioPolicyCallback token, - boolean hasFocusListener, boolean isFocusPolicy) { + boolean hasFocusListener, boolean isFocusPolicy, boolean isVolumeController) { super(config); setRegistration(new String(config.hashCode() + ":ap:" + mAudioPolicyCounter++)); mPolicyCallback = token; mHasFocusListener = hasFocusListener; + mIsVolumeController = isVolumeController; if (mHasFocusListener) { mMediaFocusControl.addFocusFollower(mPolicyCallback); // can only ever be true if there is a focus listener @@ -7211,6 +7242,9 @@ public class AudioService extends IAudioService.Stub mMediaFocusControl.setFocusPolicy(mPolicyCallback); } } + if (mIsVolumeController) { + setExtVolumeController(mPolicyCallback); + } connectMixes(); } @@ -7220,6 +7254,11 @@ public class AudioService extends IAudioService.Stub release(); mAudioPolicies.remove(mPolicyCallback.asBinder()); } + if (mIsVolumeController) { + synchronized (mExtVolumeControllerLock) { + mExtVolumeController = null; + } + } } String getRegistrationId() { diff --git a/services/core/java/com/android/server/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java index b5f94b1ce384..25a2100ff885 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); diff --git a/services/core/java/com/android/server/pm/PackageSignatures.java b/services/core/java/com/android/server/pm/PackageSignatures.java index bfc858f4a7d0..0229a371cf26 100644 --- a/services/core/java/com/android/server/pm/PackageSignatures.java +++ b/services/core/java/com/android/server/pm/PackageSignatures.java @@ -296,6 +296,7 @@ class PackageSignatures { PackageManagerService.reportSettingsProblem(Log.WARN, "Unknown element under <sigs>: " + parser.getName()); + XmlUtils.skipCurrentTag(parser); } } return pos; diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 0f394a4eed65..0502848d698e 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -1025,7 +1025,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { public void run() { // send interaction hint to improve redraw performance mPowerManagerInternal.powerHint(PowerHint.INTERACTION, 0); - if (isRotationChoiceEnabled()) { + if (isRotationChoicePossible(mCurrentAppOrientation)) { final boolean isValid = isValidRotationChoice(mCurrentAppOrientation, mRotation); sendProposedRotationChangeToStatusBarInternal(mRotation, isValid); @@ -7144,7 +7144,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { mOrientationListener.setCurrentRotation(rotation); } - public boolean isRotationChoiceEnabled() { + public boolean isRotationChoicePossible(int orientation) { // Rotation choice is only shown when the user is in locked mode. if (mUserRotationMode != WindowManagerPolicy.USER_ROTATION_LOCKED) return false; @@ -7184,50 +7184,45 @@ public class PhoneWindowManager implements WindowManagerPolicy { return false; } - // Rotation isn't forced, enable choice - return true; + // Ensure that some rotation choice is possible for the given orientation + switch (orientation) { + case ActivityInfo.SCREEN_ORIENTATION_FULL_USER: + case ActivityInfo.SCREEN_ORIENTATION_USER: + case ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED: + case ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE: + case ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT: + // NOSENSOR description is ambiguous, in reality WM ignores user choice + return true; + } + + // Rotation is forced, should be controlled by system + return false; } public boolean isValidRotationChoice(int orientation, final int preferredRotation) { - // Determine if the given app orientation can be chosen and, if so, if it is compatible - // with the provided rotation choice - + // Determine if the given app orientation is compatible with the provided rotation choice switch (orientation) { - case ActivityInfo.SCREEN_ORIENTATION_PORTRAIT: - case ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE: - case ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT: - case ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE: - case ActivityInfo.SCREEN_ORIENTATION_LOCKED: - return false; // Forced into a particular rotation, no user choice - - case ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE: - case ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT: - case ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR: - case ActivityInfo.SCREEN_ORIENTATION_SENSOR: - return false; // Sensor overrides user choice + case ActivityInfo.SCREEN_ORIENTATION_FULL_USER: + // Works with any of the 4 rotations + return preferredRotation >= 0; - case ActivityInfo.SCREEN_ORIENTATION_NOSENSOR: - // TODO Can sensor be used to indirectly determine the orientation? - return false; + case ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT: + // It's possible for the user pref to be set at 180 because of FULL_USER. This would + // make switching to USER_PORTRAIT appear at 180. Provide choice to back to portrait + // but never to go to 180. + return preferredRotation == mPortraitRotation; case ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE: - // If the user has locked sensor-based rotation, this behaves the same as landscape - return false; // User has locked the rotation, will behave as LANDSCAPE - case ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT: - // If the user has locked sensor-based rotation, this behaves the same as portrait - return false; // User has locked the rotation, will behave as PORTRAIT + // Works landscape or seascape + return isLandscapeOrSeascape(preferredRotation); + case ActivityInfo.SCREEN_ORIENTATION_USER: + case ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED: // Works with any rotation except upside down return (preferredRotation >= 0) && (preferredRotation != mUpsideDownRotation); - case ActivityInfo.SCREEN_ORIENTATION_FULL_USER: - // Works with any of the 4 rotations - return preferredRotation >= 0; - - default: - // TODO: how to handle SCREEN_ORIENTATION_BEHIND, UNSET? - // For UNSPECIFIED use preferred orientation matching SCREEN_ORIENTATION_USER - return (preferredRotation >= 0) && (preferredRotation != mUpsideDownRotation); } + + return false; } private boolean isLandscapeOrSeascape(int rotation) { 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/BatterySaverPolicy.java b/services/core/java/com/android/server/power/BatterySaverPolicy.java index a538967e501d..847c90a08c5c 100644 --- a/services/core/java/com/android/server/power/BatterySaverPolicy.java +++ b/services/core/java/com/android/server/power/BatterySaverPolicy.java @@ -33,6 +33,7 @@ import android.util.Slog; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; +import com.android.server.power.batterysaver.BatterySavingStats; import com.android.server.power.batterysaver.CpuFrequencies; import java.io.PrintWriter; @@ -498,6 +499,8 @@ public class BatterySaverPolicy extends ContentObserver { pw.print(" Noninteractive File values:\n"); dumpMap(pw, " ", mFilesForNoninteractive); pw.println(); + pw.println(); + BatterySavingStats.getInstance().dump(pw, " "); } } diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java index d4627c2da07e..32f38b7cf772 100644 --- a/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java +++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java @@ -28,6 +28,7 @@ import android.content.Intent; import android.content.IntentFilter; import android.hardware.power.V1_0.PowerHint; import android.net.Uri; +import android.os.BatteryManager; import android.os.Handler; import android.os.Looper; import android.os.Message; @@ -50,6 +51,9 @@ import com.android.server.LocalServices; import com.android.server.power.BatterySaverPolicy; import com.android.server.power.BatterySaverPolicy.BatterySaverPolicyListener; import com.android.server.power.PowerManagerService; +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 java.util.ArrayList; @@ -70,6 +74,8 @@ public class BatterySaverController implements BatterySaverPolicyListener { private final BatterySaverPolicy mBatterySaverPolicy; + private final BatterySavingStats mBatterySavingStats; + private static final String WARNING_LINK_URL = "http://goto.google.com/extreme-battery-saver"; @GuardedBy("mLock") @@ -78,6 +84,9 @@ public class BatterySaverController implements BatterySaverPolicyListener { @GuardedBy("mLock") private boolean mEnabled; + @GuardedBy("mLock") + private boolean mIsPluggedIn; + /** * Previously enabled or not; only for the event logging. Only use it from * {@link #handleBatterySaverStateChanged}. @@ -104,15 +113,28 @@ public class BatterySaverController implements BatterySaverPolicyListener { private final BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { + if (DEBUG) { + Slog.d(TAG, "onReceive: " + intent); + } switch (intent.getAction()) { case Intent.ACTION_SCREEN_ON: case Intent.ACTION_SCREEN_OFF: if (!isEnabled()) { + updateBatterySavingStats(); return; // No need to send it if not enabled. } // Don't send the broadcast, because we never did so in this case. mHandler.postStateChanged(/*sendBroadcast=*/ false); break; + case Intent.ACTION_BATTERY_CHANGED: + synchronized (mLock) { + mIsPluggedIn = (intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0) != 0); + } + // Fall-through. + case PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED: + case PowerManager.ACTION_LIGHT_DEVICE_IDLE_MODE_CHANGED: + updateBatterySavingStats(); + break; } } }; @@ -126,6 +148,7 @@ public class BatterySaverController implements BatterySaverPolicyListener { mBatterySaverPolicy = policy; mBatterySaverPolicy.addListener(this); mFileUpdater = new FileUpdater(context); + mBatterySavingStats = BatterySavingStats.getInstance(); // Initialize plugins. final ArrayList<Plugin> plugins = new ArrayList<>(); @@ -149,6 +172,9 @@ public class BatterySaverController implements BatterySaverPolicyListener { public void systemReady() { final IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON); filter.addAction(Intent.ACTION_SCREEN_OFF); + filter.addAction(Intent.ACTION_BATTERY_CHANGED); + filter.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED); + filter.addAction(PowerManager.ACTION_LIGHT_DEVICE_IDLE_MODE_CHANGED); mContext.registerReceiver(mReceiver, filter); mFileUpdater.systemReady(LocalServices.getService(ActivityManagerInternal.class) @@ -280,7 +306,6 @@ public class BatterySaverController implements BatterySaverPolicyListener { enabled = mEnabled; mIsInteractive = isInteractive; - if (enabled) { fileValues = mBatterySaverPolicy.getFileValues(isInteractive); } else { @@ -293,6 +318,8 @@ public class BatterySaverController implements BatterySaverPolicyListener { pmi.powerHint(PowerHint.LOW_POWER, enabled ? 1 : 0); } + updateBatterySavingStats(); + if (ArrayUtils.isEmpty(fileValues)) { mFileUpdater.restoreDefault(); } else { @@ -332,7 +359,6 @@ public class BatterySaverController implements BatterySaverPolicyListener { mContext.sendBroadcastAsUser(intent, UserHandle.ALL, Manifest.permission.DEVICE_POWER); - for (LowPowerModeListener listener : listeners) { final PowerSaveState result = mBatterySaverPolicy.getBatterySaverPolicy( @@ -388,4 +414,28 @@ public class BatterySaverController implements BatterySaverPolicyListener { foregroundUser); } } + + private void updateBatterySavingStats() { + final PowerManager pm = getPowerManager(); + if (pm == null) { + Slog.wtf(TAG, "PowerManager not initialized"); + return; + } + final boolean isInteractive = pm.isInteractive(); + final int dozeMode = + pm.isDeviceIdleMode() ? DozeState.DEEP + : pm.isLightDeviceIdleMode() ? DozeState.LIGHT + : DozeState.NOT_DOZING; + + synchronized (mLock) { + if (mIsPluggedIn) { + mBatterySavingStats.startCharging(); + return; + } + mBatterySavingStats.transitionState( + mEnabled ? BatterySaverState.ON : BatterySaverState.OFF, + isInteractive ? InteractiveState.INTERACTIVE : InteractiveState.NON_INTERACTIVE, + dozeMode); + } + } } diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySavingStats.java b/services/core/java/com/android/server/power/batterysaver/BatterySavingStats.java new file mode 100644 index 000000000000..df4f8eca5595 --- /dev/null +++ b/services/core/java/com/android/server/power/batterysaver/BatterySavingStats.java @@ -0,0 +1,353 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.power.batterysaver; + +import android.os.BatteryManagerInternal; +import android.os.SystemClock; +import android.util.ArrayMap; +import android.util.Slog; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; +import com.android.server.EventLogTags; +import com.android.server.LocalServices; +import com.android.server.power.BatterySaverPolicy; + +import java.io.PrintWriter; + +/** + * This class keeps track of battery drain rate. + * + * Test: + atest $ANDROID_BUILD_TOP/frameworks/base/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySavingStatsTest.java + */ +public class BatterySavingStats { + + private static final String TAG = "BatterySavingStats"; + + private static final boolean DEBUG = BatterySaverPolicy.DEBUG; + + private final Object mLock = new Object(); + + /** Whether battery saver is on or off. */ + interface BatterySaverState { + int OFF = 0; + int ON = 1; + + int SHIFT = 0; + int BITS = 1; + int MASK = (1 << BITS) - 1; + + static int fromIndex(int index) { + return (index >> SHIFT) & MASK; + } + } + + /** Whether the device is interactive (i.e. screen on) or not. */ + interface InteractiveState { + int NON_INTERACTIVE = 0; + int INTERACTIVE = 1; + + int SHIFT = BatterySaverState.SHIFT + BatterySaverState.BITS; + int BITS = 1; + int MASK = (1 << BITS) - 1; + + static int fromIndex(int index) { + return (index >> SHIFT) & MASK; + } + } + + /** Doze mode. */ + interface DozeState { + int NOT_DOZING = 0; + int LIGHT = 1; + int DEEP = 2; + + int SHIFT = InteractiveState.SHIFT + InteractiveState.BITS; + int BITS = 2; + int MASK = (1 << BITS) - 1; + + static int fromIndex(int index) { + return (index >> SHIFT) & MASK; + } + } + + /** + * Various stats in each state. + */ + static class Stat { + public long startTime; + public long endTime; + + public int startBatteryLevel; + public int endBatteryLevel; + + public long totalTimeMillis; + public int totalBatteryDrain; + + public long totalMinutes() { + return totalTimeMillis / 60_000; + } + + public double drainPerHour() { + if (totalTimeMillis == 0) { + return 0; + } + return (double) totalBatteryDrain / (totalTimeMillis / (60.0 * 60 * 1000)); + } + + @VisibleForTesting + String toStringForTest() { + return "{" + totalMinutes() + "m," + totalBatteryDrain + "," + + String.format("%.2f", drainPerHour()) + "}"; + } + } + + private static BatterySavingStats sInstance; + + private BatteryManagerInternal mBatteryManagerInternal; + + private static final int STATE_NOT_INITIALIZED = -1; + private static final int STATE_CHARGING = -2; + + /** + * Current state, one of STATE_* or values returned by {@link #statesToIndex}. + */ + @GuardedBy("mLock") + private int mCurrentState = STATE_NOT_INITIALIZED; + + /** + * Stats in each state. + */ + @VisibleForTesting + @GuardedBy("mLock") + final ArrayMap<Integer, Stat> mStats = new ArrayMap<>(); + + /** + * Don't call it directly -- use {@link #getInstance()}. Not private for testing. + */ + @VisibleForTesting + BatterySavingStats() { + mBatteryManagerInternal = LocalServices.getService(BatteryManagerInternal.class); + } + + public static synchronized BatterySavingStats getInstance() { + if (sInstance == null) { + sInstance = new BatterySavingStats(); + } + return sInstance; + } + + private BatteryManagerInternal getBatteryManagerInternal() { + if (mBatteryManagerInternal == null) { + mBatteryManagerInternal = LocalServices.getService(BatteryManagerInternal.class); + } + return mBatteryManagerInternal; + } + + /** + * Takes a state triplet and generates a state index. + */ + @VisibleForTesting + static int statesToIndex( + int batterySaverState, int interactiveState, int dozeState) { + int ret = batterySaverState & BatterySaverState.MASK; + ret |= (interactiveState & InteractiveState.MASK) << InteractiveState.SHIFT; + ret |= (dozeState & DozeState.MASK) << DozeState.SHIFT; + return ret; + } + + /** + * Takes a state index and returns a string for logging. + */ + @VisibleForTesting + static String stateToString(int state) { + switch (state) { + case STATE_NOT_INITIALIZED: + return "NotInitialized"; + case STATE_CHARGING: + return "Charging"; + } + return "BS=" + BatterySaverState.fromIndex(state) + + ",I=" + InteractiveState.fromIndex(state) + + ",D=" + DozeState.fromIndex(state); + } + + /** + * @return {@link Stat} fo a given state. + */ + @VisibleForTesting + Stat getStat(int stateIndex) { + synchronized (mLock) { + Stat stat = mStats.get(stateIndex); + if (stat == null) { + stat = new Stat(); + mStats.put(stateIndex, stat); + } + return stat; + } + } + + /** + * @return {@link Stat} fo a given state triplet. + */ + private Stat getStat(int batterySaverState, int interactiveState, int dozeState) { + return getStat(statesToIndex(batterySaverState, interactiveState, dozeState)); + } + + long injectCurrentTime() { + return SystemClock.elapsedRealtime(); + } + + int injectBatteryLevel() { + final BatteryManagerInternal bmi = getBatteryManagerInternal(); + if (bmi == null) { + Slog.wtf(TAG, "BatteryManagerInternal not initialized"); + return 0; + } + return bmi.getBatteryChargeCounter(); + } + + /** + * Called from the outside whenever any of the states changes, when the device is not plugged + * in. + */ + public void transitionState(int batterySaverState, int interactiveState, int dozeState) { + synchronized (mLock) { + + final int newState = statesToIndex( + batterySaverState, interactiveState, dozeState); + if (mCurrentState == newState) { + return; + } + + endLastStateLocked(); + startNewStateLocked(newState); + } + } + + /** + * Called from the outside when the device is plugged in. + */ + public void startCharging() { + synchronized (mLock) { + if (mCurrentState < 0) { + return; + } + + endLastStateLocked(); + startNewStateLocked(STATE_CHARGING); + } + } + + private void endLastStateLocked() { + if (mCurrentState < 0) { + return; + } + final Stat stat = getStat(mCurrentState); + + stat.endBatteryLevel = injectBatteryLevel(); + stat.endTime = injectCurrentTime(); + + final long deltaTime = stat.endTime - stat.startTime; + final int deltaDrain = stat.startBatteryLevel - stat.endBatteryLevel; + + stat.totalTimeMillis += deltaTime; + stat.totalBatteryDrain += deltaDrain; + + if (DEBUG) { + Slog.d(TAG, "State summary: " + stateToString(mCurrentState) + + ": " + (deltaTime / 1_000) + "s " + + "Start level: " + stat.startBatteryLevel + "uA " + + "End level: " + stat.endBatteryLevel + "uA " + + deltaDrain + "uA"); + } + EventLogTags.writeBatterySavingStats( + BatterySaverState.fromIndex(mCurrentState), + InteractiveState.fromIndex(mCurrentState), + DozeState.fromIndex(mCurrentState), + deltaTime, + deltaDrain, + stat.totalTimeMillis, + stat.totalBatteryDrain); + } + + private void startNewStateLocked(int newState) { + if (DEBUG) { + Slog.d(TAG, "New state: " + stateToString(newState)); + } + mCurrentState = newState; + + if (mCurrentState < 0) { + return; + } + + final Stat stat = getStat(mCurrentState); + stat.startBatteryLevel = injectBatteryLevel(); + stat.startTime = injectCurrentTime(); + stat.endTime = 0; + } + + public void dump(PrintWriter pw, String indent) { + synchronized (mLock) { + pw.print(indent); + pw.println("Battery Saving Stats:"); + + indent = indent + " "; + + pw.print(indent); + pw.println("Battery Saver: Off On"); + dumpLineLocked(pw, indent, InteractiveState.NON_INTERACTIVE, "NonIntr", + DozeState.NOT_DOZING, "NonDoze"); + dumpLineLocked(pw, indent, InteractiveState.INTERACTIVE, " Intr", + DozeState.NOT_DOZING, " "); + + dumpLineLocked(pw, indent, InteractiveState.NON_INTERACTIVE, "NonIntr", + DozeState.DEEP, "Deep "); + dumpLineLocked(pw, indent, InteractiveState.INTERACTIVE, " Intr", + DozeState.DEEP, " "); + + dumpLineLocked(pw, indent, InteractiveState.NON_INTERACTIVE, "NonIntr", + DozeState.LIGHT, "Light "); + dumpLineLocked(pw, indent, InteractiveState.INTERACTIVE, " Intr", + DozeState.LIGHT, " "); + + pw.println(); + } + } + + private void dumpLineLocked(PrintWriter pw, String indent, + int interactiveState, String interactiveLabel, + int dozeState, String dozeLabel) { + pw.print(indent); + pw.print(dozeLabel); + pw.print(" "); + pw.print(interactiveLabel); + pw.print(": "); + + final Stat offStat = getStat(BatterySaverState.OFF, interactiveState, dozeState); + final Stat onStat = getStat(BatterySaverState.ON, interactiveState, dozeState); + + pw.println(String.format("%6dm %6dmA %8.1fmA/h %6dm %6dmA %8.1fmA/h", + offStat.totalMinutes(), + offStat.totalBatteryDrain / 1000, + offStat.drainPerHour() / 1000.0, + onStat.totalMinutes(), + onStat.totalBatteryDrain / 1000, + onStat.drainPerHour() / 1000.0)); + } +} + diff --git a/services/core/java/com/android/server/slice/PinnedSliceState.java b/services/core/java/com/android/server/slice/PinnedSliceState.java index 5811714c73c4..192fd63a9af7 100644 --- a/services/core/java/com/android/server/slice/PinnedSliceState.java +++ b/services/core/java/com/android/server/slice/PinnedSliceState.java @@ -166,6 +166,7 @@ public class PinnedSliceState { ContentProviderClient getClient() { ContentProviderClient client = mService.getContext().getContentResolver().acquireContentProviderClient(mUri); + if (client == null) return null; client.setDetectNotResponding(SLICE_TIMEOUT); return client; } @@ -181,6 +182,7 @@ public class PinnedSliceState { } if (!isPinned()) { // All the listeners died, remove from pinned state. + mService.unlisten(mUri); mService.removePinnedSlice(mUri); } } @@ -210,6 +212,7 @@ public class PinnedSliceState { } if (!isPinned()) { // All the listeners died, remove from pinned state. + mService.unlisten(mUri); mService.removePinnedSlice(mUri); } } @@ -217,6 +220,7 @@ public class PinnedSliceState { private Slice doBind(String overridePkg) { try (ContentProviderClient client = getClient()) { + if (client == null) return null; Bundle extras = new Bundle(); extras.putParcelable(SliceProvider.EXTRA_BIND_URI, mUri); extras.putParcelableArrayList(SliceProvider.EXTRA_SUPPORTED_SPECS, @@ -237,6 +241,7 @@ public class PinnedSliceState { private void handleSendPinned() { try (ContentProviderClient client = getClient()) { + if (client == null) return; Bundle b = new Bundle(); b.putParcelable(SliceProvider.EXTRA_BIND_URI, mUri); try { @@ -249,6 +254,7 @@ public class PinnedSliceState { private void handleSendUnpinned() { try (ContentProviderClient client = getClient()) { + if (client == null) return; Bundle b = new Bundle(); b.putParcelable(SliceProvider.EXTRA_BIND_URI, mUri); try { diff --git a/services/core/java/com/android/server/wm/Dimmer.java b/services/core/java/com/android/server/wm/Dimmer.java index 9fe16ae85cfb..b435605b654f 100644 --- a/services/core/java/com/android/server/wm/Dimmer.java +++ b/services/core/java/com/android/server/wm/Dimmer.java @@ -17,34 +17,115 @@ package com.android.server.wm; import android.util.ArrayMap; -import android.util.Slog; import android.view.SurfaceControl; import android.graphics.Rect; +import com.android.internal.annotations.VisibleForTesting; + /** * Utility class for use by a WindowContainer implementation to add "DimLayer" support, that is * black layers of varying opacity at various Z-levels which create the effect of a Dim. */ class Dimmer { private static final String TAG = "WindowManager"; + private static final int DEFAULT_DIM_ANIM_DURATION = 200; + + private class DimAnimatable implements SurfaceAnimator.Animatable { + private final SurfaceControl mDimLayer; + + private DimAnimatable(SurfaceControl dimLayer) { + mDimLayer = dimLayer; + } + + @Override + public SurfaceControl.Transaction getPendingTransaction() { + return mHost.getPendingTransaction(); + } + + @Override + public void commitPendingTransaction() { + mHost.commitPendingTransaction(); + } + + @Override + public void onAnimationLeashCreated(SurfaceControl.Transaction t, SurfaceControl leash) { + } + + @Override + public void onAnimationLeashDestroyed(SurfaceControl.Transaction t) { + } + + @Override + public void destroyAfterPendingTransaction(SurfaceControl surface) { + mHost.destroyAfterPendingTransaction(surface); + } + + @Override + public SurfaceControl.Builder makeAnimationLeash() { + return mHost.makeAnimationLeash(); + } + + @Override + public SurfaceControl getAnimationLeashParent() { + return mHost.getSurfaceControl(); + } + + @Override + public SurfaceControl getSurfaceControl() { + return mDimLayer; + } + + @Override + public SurfaceControl getParentSurfaceControl() { + return mHost.getSurfaceControl(); + } + + @Override + public int getSurfaceWidth() { + // This will determine the size of the leash created. This should be the size of the + // host and not the dim layer since the dim layer may get bigger during animation. If + // that occurs, the leash size cannot change so we need to ensure the leash is big + // enough that the dim layer can grow. + // This works because the mHost will be a Task which has the display bounds. + return mHost.getSurfaceWidth(); + } + + @Override + public int getSurfaceHeight() { + // See getSurfaceWidth() above for explanation. + return mHost.getSurfaceHeight(); + } + } - private class DimState { - SurfaceControl mSurfaceControl; + @VisibleForTesting + class DimState { + /** + * The layer where property changes should be invoked on. + */ + SurfaceControl mDimLayer; boolean mDimming; + boolean isVisible; + SurfaceAnimator mSurfaceAnimator; /** - * Used for Dims not assosciated with a WindowContainer. See {@link Dimmer#dimAbove} for + * Used for Dims not associated with a WindowContainer. See {@link Dimmer#dimAbove} for * details on Dim lifecycle. */ boolean mDontReset; - DimState(SurfaceControl ctl) { - mSurfaceControl = ctl; + DimState(SurfaceControl dimLayer) { + mDimLayer = dimLayer; mDimming = true; + mSurfaceAnimator = new SurfaceAnimator(new DimAnimatable(dimLayer), () -> { + if (!mDimming) { + mDimLayer.destroy(); + } + }, mHost.mService.mAnimator::addAfterPrepareSurfacesRunnable, mHost.mService); } - }; + } - private ArrayMap<WindowContainer, DimState> mDimLayerUsers = new ArrayMap<>(); + @VisibleForTesting + ArrayMap<WindowContainer, DimState> mDimLayerUsers = new ArrayMap<>(); /** * The {@link WindowContainer} that our Dim's are bounded to. We may be dimming on behalf of the @@ -56,19 +137,18 @@ class Dimmer { mHost = host; } - SurfaceControl makeDimLayer() { - final SurfaceControl control = mHost.makeChildSurface(null) + private SurfaceControl makeDimLayer() { + return mHost.makeChildSurface(null) .setParent(mHost.getSurfaceControl()) .setColorLayer(true) .setName("Dim Layer for - " + mHost.getName()) .build(); - return control; } /** * Retreive the DimState for a given child of the host. */ - DimState getDimState(WindowContainer container) { + private DimState getDimState(WindowContainer container) { DimState state = mDimLayerUsers.get(container); if (state == null) { final SurfaceControl ctl = makeDimLayer(); @@ -88,14 +168,12 @@ class Dimmer { private void dim(SurfaceControl.Transaction t, WindowContainer container, int relativeLayer, float alpha) { final DimState d = getDimState(container); - t.show(d.mSurfaceControl); if (container != null) { - t.setRelativeLayer(d.mSurfaceControl, - container.getSurfaceControl(), relativeLayer); + t.setRelativeLayer(d.mDimLayer, container.getSurfaceControl(), relativeLayer); } else { - t.setLayer(d.mSurfaceControl, Integer.MAX_VALUE); + t.setLayer(d.mDimLayer, Integer.MAX_VALUE); } - t.setAlpha(d.mSurfaceControl, alpha); + t.setAlpha(d.mDimLayer, alpha); d.mDimming = true; } @@ -107,16 +185,18 @@ class Dimmer { */ void stopDim(SurfaceControl.Transaction t) { DimState d = getDimState(null); - t.hide(d.mSurfaceControl); + t.hide(d.mDimLayer); + d.isVisible = false; d.mDontReset = false; } + /** * Place a Dim above the entire host container. The caller is responsible for calling stopDim to * remove this effect. If the Dim can be assosciated with a particular child of the host * consider using the other variant of dimAbove which ties the Dim lifetime to the child * lifetime more explicitly. * - * @param t A transaction in which to apply the Dim. + * @param t A transaction in which to apply the Dim. * @param alpha The alpha at which to Dim. */ void dimAbove(SurfaceControl.Transaction t, float alpha) { @@ -128,9 +208,9 @@ class Dimmer { * for each call to {@link WindowContainer#prepareSurfaces} the Dim state will be reset * and the child should call dimAbove again to request the Dim to continue. * - * @param t A transaction in which to apply the Dim. + * @param t A transaction in which to apply the Dim. * @param container The container which to dim above. Should be a child of our host. - * @param alpha The alpha at which to Dim. + * @param alpha The alpha at which to Dim. */ void dimAbove(SurfaceControl.Transaction t, WindowContainer container, float alpha) { dim(t, container, 1, alpha); @@ -139,9 +219,9 @@ class Dimmer { /** * Like {@link #dimAbove} but places the dim below the given container. * - * @param t A transaction in which to apply the Dim. + * @param t A transaction in which to apply the Dim. * @param container The container which to dim below. Should be a child of our host. - * @param alpha The alpha at which to Dim. + * @param alpha The alpha at which to Dim. */ void dimBelow(SurfaceControl.Transaction t, WindowContainer container, float alpha) { @@ -159,7 +239,7 @@ class Dimmer { void resetDimStates() { for (int i = mDimLayerUsers.size() - 1; i >= 0; i--) { final DimState state = mDimLayerUsers.valueAt(i); - if (state.mDontReset == false) { + if (!state.mDontReset) { state.mDimming = false; } } @@ -169,7 +249,7 @@ class Dimmer { * Call after invoking {@link WindowContainer#prepareSurfaces} on children as * described in {@link #resetDimStates}. * - * @param t A transaction in which to update the dims. + * @param t A transaction in which to update the dims. * @param bounds The bounds at which to dim. * @return true if any Dims were updated. */ @@ -177,19 +257,80 @@ class Dimmer { boolean didSomething = false; for (int i = mDimLayerUsers.size() - 1; i >= 0; i--) { DimState state = mDimLayerUsers.valueAt(i); + WindowContainer container = mDimLayerUsers.keyAt(i); + // TODO: We want to animate the addition and removal of Dim's instead of immediately // acting. When we do this we need to take care to account for the "Replacing Windows" // case (and seamless dim transfer). - if (state.mDimming == false) { + if (!state.mDimming) { mDimLayerUsers.removeAt(i); - state.mSurfaceControl.destroy(); + startDimExit(container, state.mSurfaceAnimator, t); } else { didSomething = true; // TODO: Once we use geometry from hierarchy this falls away. - t.setSize(state.mSurfaceControl, bounds.width(), bounds.height()); - t.setPosition(state.mSurfaceControl, bounds.left, bounds.top); + t.setSize(state.mDimLayer, bounds.width(), bounds.height()); + t.setPosition(state.mDimLayer, bounds.left, bounds.top); + if (!state.isVisible) { + state.isVisible = true; + t.show(state.mDimLayer); + startDimEnter(container, state.mSurfaceAnimator, t); + } } } return didSomething; } + + private void startDimEnter(WindowContainer container, SurfaceAnimator animator, + SurfaceControl.Transaction t) { + startAnim(container, animator, t, 0 /* startAlpha */, 1 /* endAlpha */); + } + + private void startDimExit(WindowContainer container, SurfaceAnimator animator, + SurfaceControl.Transaction t) { + startAnim(container, animator, t, 1 /* startAlpha */, 0 /* endAlpha */); + } + + private void startAnim(WindowContainer container, SurfaceAnimator animator, + SurfaceControl.Transaction t, float startAlpha, float endAlpha) { + animator.startAnimation(t, new LocalAnimationAdapter( + new AlphaAnimationSpec(startAlpha, endAlpha, getDimDuration(container)), + mHost.mService.mSurfaceAnimationRunner), false /* hidden */); + } + + private long getDimDuration(WindowContainer container) { + // If there's no container, then there isn't an animation occurring while dimming. Set the + // duration to 0 so it immediately dims to the set alpha. + if (container == null) { + return 0; + } + + // Otherwise use the same duration as the animation on the WindowContainer + AnimationAdapter animationAdapter = container.mSurfaceAnimator.getAnimation(); + return animationAdapter == null ? DEFAULT_DIM_ANIM_DURATION + : animationAdapter.getDurationHint(); + } + + private static class AlphaAnimationSpec implements LocalAnimationAdapter.AnimationSpec { + private final long mDuration; + private final float mFromAlpha; + private final float mToAlpha; + + AlphaAnimationSpec(float fromAlpha, float toAlpha, long duration) { + mFromAlpha = fromAlpha; + mToAlpha = toAlpha; + mDuration = duration; + } + + @Override + public long getDuration() { + return mDuration; + } + + @Override + public void apply(SurfaceControl.Transaction t, SurfaceControl sc, long currentPlayTime) { + float alpha = ((float) currentPlayTime / getDuration()) * (mToAlpha - mFromAlpha) + + mFromAlpha; + t.setAlpha(sc, alpha); + } + } } diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 3f49f0cd5c15..7674b5e9ed2c 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -3599,6 +3599,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } private final class AboveAppWindowContainers extends NonAppWindowContainers { + private final Dimmer mDimmer = new Dimmer(this); + private final Rect mTmpDimBoundsRect = new Rect(); AboveAppWindowContainers(String name, WindowManagerService service) { super(name, service); } @@ -3630,6 +3632,22 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo imeContainer.assignRelativeLayer(t, getSurfaceControl(), Integer.MAX_VALUE); } } + + @Override + Dimmer getDimmer() { + return mDimmer; + } + + @Override + void prepareSurfaces() { + mDimmer.resetDimStates(); + super.prepareSurfaces(); + getBounds(mTmpDimBoundsRect); + + if (mDimmer.updateDims(getPendingTransaction(), mTmpDimBoundsRect)) { + scheduleAnimation(); + } + } } /** diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java index 7d4eafb07fe9..8269a3b783c4 100644 --- a/services/core/java/com/android/server/wm/RemoteAnimationController.java +++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java @@ -88,6 +88,10 @@ class RemoteAnimationController { * Called when the transition is ready to be started, and all leashes have been set up. */ void goodToGo() { + if (mPendingAnimations.isEmpty()) { + onAnimationFinished(); + return; + } mHandler.postDelayed(mTimeoutRunnable, TIMEOUT_MS); try { mRemoteAnimationAdapter.getRunner().onAnimationStart(createAnimations(), diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 0628436a4204..7d970d9a06b5 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -462,8 +462,8 @@ class Task extends WindowContainer<AppWindowToken> { } else { mStack.getBounds(mTmpRect); mTmpRect.intersect(getBounds()); + out.set(mTmpRect); } - out.set(mTmpRect); } else { out.set(getBounds()); } @@ -640,6 +640,7 @@ class Task extends WindowContainer<AppWindowToken> { mPreserveNonFloatingState = false; } + @Override Dimmer getDimmer() { return mDimmer; } diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java index bc0f9ad71a61..cf54b67e1229 100644 --- a/services/core/java/com/android/server/wm/TaskStack.java +++ b/services/core/java/com/android/server/wm/TaskStack.java @@ -1722,6 +1722,7 @@ public class TaskStack extends WindowContainer<Task> implements || activityType == ACTIVITY_TYPE_ASSISTANT; } + @Override Dimmer getDimmer() { return mDimmer; } diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index a1026bd420cb..0c0ce0e17dd8 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -1231,4 +1231,11 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< outPos.offset(-parentBounds.left, -parentBounds.top); } } + + Dimmer getDimmer() { + if (mParent == null) { + return null; + } + return mParent.getDimmer(); + } } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 4fb239085e5c..066e4e6a8c67 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -907,9 +907,16 @@ public class WindowManagerService extends IWindowManager.Stub public static WindowManagerService main(final Context context, final InputManagerService im, final boolean haveInputMethods, final boolean showBootMsgs, final boolean onlyCore, WindowManagerPolicy policy) { + return main(context, im, haveInputMethods, showBootMsgs, onlyCore, policy, + new SurfaceAnimationRunner()); + } + + public static WindowManagerService main(final Context context, final InputManagerService im, + final boolean haveInputMethods, final boolean showBootMsgs, final boolean onlyCore, + WindowManagerPolicy policy, SurfaceAnimationRunner surfaceAnimationRunner) { DisplayThread.getHandler().runWithScissors(() -> sInstance = new WindowManagerService(context, im, haveInputMethods, showBootMsgs, - onlyCore, policy), 0); + onlyCore, policy, surfaceAnimationRunner), 0); return sInstance; } @@ -932,7 +939,7 @@ public class WindowManagerService extends IWindowManager.Stub private WindowManagerService(Context context, InputManagerService inputManager, boolean haveInputMethods, boolean showBootMsgs, boolean onlyCore, - WindowManagerPolicy policy) { + WindowManagerPolicy policy, SurfaceAnimationRunner surfaceAnimationRunner) { installLock(this, INDEX_WINDOW); mContext = context; mHaveInputMethods = haveInputMethods; @@ -1059,7 +1066,7 @@ public class WindowManagerService extends IWindowManager.Stub PowerManager.SCREEN_BRIGHT_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE, TAG_WM); mHoldingScreenWakeLock.setReferenceCounted(false); - mSurfaceAnimationRunner = new SurfaceAnimationRunner(); + mSurfaceAnimationRunner = surfaceAnimationRunner; mAllowTheaterModeWakeFromLayout = context.getResources().getBoolean( com.android.internal.R.bool.config_allowTheaterModeWakeFromWindowLayout); diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 477dd2bb9633..55c982c174a6 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -2138,18 +2138,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP mInputWindowHandle.inputChannel = null; } - private Dimmer getDimmer() { - Task task = getTask(); - if (task != null) { - return task.getDimmer(); - } - TaskStack taskStack = getStack(); - if (taskStack != null) { - return taskStack.getDimmer(); - } - return null; - } - /** Returns true if the replacement window was removed. */ boolean removeReplacedWindowIfNeeded(WindowState replacement) { if (mWillReplaceWindow && mReplacementWindow == replacement && replacement.hasDrawnLw()) { @@ -4516,11 +4504,11 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP private void applyDims(Dimmer dimmer) { if (!mAnimatingExit && mAppDied) { mIsDimming = true; - getDimmer().dimAbove(getPendingTransaction(), this, DEFAULT_DIM_AMOUNT_DEAD_WINDOW); + dimmer.dimAbove(getPendingTransaction(), this, DEFAULT_DIM_AMOUNT_DEAD_WINDOW); } else if ((mAttrs.flags & FLAG_DIM_BEHIND) != 0 - && !mAnimatingExit && isVisible()) { + && !mAnimatingExit && isVisible() && !mWinAnimator.mLastHidden) { mIsDimming = true; - getDimmer().dimBelow(getPendingTransaction(), this, mAttrs.dimAmount); + dimmer.dimBelow(getPendingTransaction(), this, mAttrs.dimAmount); } } @@ -4531,7 +4519,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP if (dimmer != null) { applyDims(dimmer); } - updateSurfacePosition(mPendingTransaction); mWinAnimator.prepareSurfaceLocked(true); diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java index 552942614db7..d1cc5de821c7 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java @@ -178,4 +178,10 @@ abstract class BaseIDevicePolicyManager extends IDevicePolicyManager.Stub { public void clearSystemUpdatePolicyFreezePeriodRecord() { } + + @Override + public boolean isMeteredDataDisabledForUser(ComponentName admin, + String packageName, int userId) { + return false; + } } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 05897908df36..dae760576b4f 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -744,7 +744,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (deviceOwner != null) { Bundle extras = new Bundle(); extras.putParcelable(Intent.EXTRA_USER, UserHandle.of(userHandle)); - sendAdminCommandLocked(deviceOwner, action, extras, null); + sendAdminCommandLocked(deviceOwner, action, extras, /* result */ null, + /* inForeground */ true); } } } @@ -11409,6 +11410,27 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } } + @Override + public boolean isMeteredDataDisabledForUser(ComponentName who, + String packageName, int userId) { + Preconditions.checkNotNull(who); + + if (!mHasFeature) { + return false; + } + if (!isCallerWithSystemUid()) { + throw new SecurityException( + "Only the system can query restricted pkgs for a specific user"); + } + synchronized (this) { + final ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userId); + if (admin != null && admin.meteredDisabledPackages != null) { + return admin.meteredDisabledPackages.contains(packageName); + } + } + return false; + } + private void pushMeteredDisabledPackagesLocked(int userId) { mInjector.getNetworkPolicyManagerInternal().setMeteredRestrictedPackages( getMeteredDisabledPackagesLocked(userId), userId); diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java index 6b87ea965686..00a85a539538 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java @@ -2160,6 +2160,51 @@ public class DevicePolicyManagerTest extends DpmTestBase { () -> dpm.getMeteredDataDisabled(admin1)); } + public void testGetMeteredDataDisabledForUser() throws Exception { + setAsProfileOwner(admin1); + + // Setup + final ArrayList<String> emptyList = new ArrayList<>(); + final ArrayList<String> pkgsToRestrict = new ArrayList<>(); + final String package1 = "com.example.one"; + final String package2 = "com.example.two"; + final String package3 = "com.example.three"; + pkgsToRestrict.add(package1); + pkgsToRestrict.add(package2); + setupPackageInPackageManager(package1, DpmMockContext.CALLER_USER_HANDLE, 123, 0); + setupPackageInPackageManager(package2, DpmMockContext.CALLER_USER_HANDLE, 456, 0); + List<String> excludedPkgs = dpm.setMeteredDataDisabled(admin1, pkgsToRestrict); + + // Verify + assertEquals(emptyList, excludedPkgs); + mContext.binder.callingUid = DpmMockContext.SYSTEM_UID; + assertTrue(package1 + "should be restricted", + dpm.isMeteredDataDisabledForUser(admin1, package1, + DpmMockContext.CALLER_USER_HANDLE)); + assertTrue(package2 + "should be restricted", + dpm.isMeteredDataDisabledForUser(admin1, package2, + DpmMockContext.CALLER_USER_HANDLE)); + assertFalse(package3 + "should not be restricted", + dpm.isMeteredDataDisabledForUser(admin1, package3, + DpmMockContext.CALLER_USER_HANDLE)); + } + + public void testGetMeteredDataDisabledForUser_nonSystemUidCaller() throws Exception { + setAsProfileOwner(admin1); + assertExpectException(SecurityException.class, + /* messageRegex= */ "Only the system can query restricted pkgs", + () -> dpm.isMeteredDataDisabledForUser( + admin1, "com.example.one", DpmMockContext.CALLER_USER_HANDLE)); + dpm.clearProfileOwner(admin1); + + setDeviceOwner(); + assertExpectException(SecurityException.class, + /* messageRegex= */ "Only the system can query restricted pkgs", + () -> dpm.isMeteredDataDisabledForUser( + admin1, "com.example.one", DpmMockContext.CALLER_USER_HANDLE)); + clearDeviceOwner(); + } + public void testCreateAdminSupportIntent() throws Exception { // Setup device owner. mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; 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 new file mode 100644 index 000000000000..f788caccbdce --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySavingStatsTest.java @@ -0,0 +1,205 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.power.batterysaver; + +import static org.junit.Assert.assertEquals; + +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; + +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 java.io.ByteArrayOutputStream; +import java.io.PrintWriter; + +/** + atest $ANDROID_BUILD_TOP/frameworks/base/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySavingStatsTest.java + */ +@SmallTest +@RunWith(AndroidJUnit4.class) +public class BatterySavingStatsTest { + private class BatterySavingStatsTestable extends BatterySavingStats { + private long mTime = 1_000_000; // Some random starting time. + + private int mBatteryLevel = 100; + + @Override + long injectCurrentTime() { + return mTime; + } + + @Override + int injectBatteryLevel() { + return mBatteryLevel; + } + + void assertDumpable() { + final ByteArrayOutputStream out = new ByteArrayOutputStream(); + dump(new PrintWriter(out), ""); // Just make sure it won't crash. + } + + void advanceClock(int minutes) { + mTime += 60_000 * minutes; + } + + void drainBattery(int percent) { + mBatteryLevel -= percent; + if (mBatteryLevel < 0) { + mBatteryLevel = 0; + } + } + + String toDebugString() { + final StringBuilder sb = new StringBuilder(); + String sep = ""; + for (int i = 0; i < mStats.size(); i++) { + sb.append(sep); + sb.append(stateToString(mStats.keyAt(i))); + sb.append(":"); + sb.append(mStats.valueAt(i).toStringForTest()); + sep = "\n"; + } + return sb.toString(); + } + } + + @Test + public void testAll() { + final BatterySavingStatsTestable target = new BatterySavingStatsTestable(); + + target.assertDumpable(); + + target.advanceClock(1); + target.drainBattery(2); + + target.transitionState( + BatterySaverState.OFF, + InteractiveState.INTERACTIVE, + DozeState.NOT_DOZING); + + target.advanceClock(4); + target.drainBattery(1); + + target.transitionState( + BatterySaverState.OFF, + InteractiveState.NON_INTERACTIVE, + DozeState.NOT_DOZING); + + target.advanceClock(2); + target.drainBattery(5); + + target.transitionState( + BatterySaverState.OFF, + InteractiveState.INTERACTIVE, + DozeState.NOT_DOZING); + + target.advanceClock(4); + target.drainBattery(1); + + target.transitionState( + BatterySaverState.OFF, + InteractiveState.NON_INTERACTIVE, + DozeState.NOT_DOZING); + + target.advanceClock(2); + target.drainBattery(5); + + target.transitionState( + BatterySaverState.OFF, + InteractiveState.INTERACTIVE, + DozeState.NOT_DOZING); + + target.advanceClock(3); + target.drainBattery(1); + + target.transitionState( + BatterySaverState.OFF, + InteractiveState.NON_INTERACTIVE, + DozeState.LIGHT); + + target.advanceClock(5); + target.drainBattery(1); + + target.transitionState( + BatterySaverState.OFF, + InteractiveState.NON_INTERACTIVE, + DozeState.DEEP); + + target.advanceClock(1); + target.drainBattery(2); + + target.transitionState( + BatterySaverState.ON, + InteractiveState.INTERACTIVE, + DozeState.NOT_DOZING); + + target.advanceClock(1); + target.drainBattery(3); + + target.transitionState( + BatterySaverState.OFF, + InteractiveState.INTERACTIVE, + DozeState.NOT_DOZING); + + target.advanceClock(3); + target.drainBattery(5); + + target.transitionState( + BatterySaverState.ON, + InteractiveState.INTERACTIVE, + DozeState.NOT_DOZING); + + target.advanceClock(3); + target.drainBattery(5); + + target.startCharging(); + + target.advanceClock(5); + target.drainBattery(10); + + target.transitionState( + BatterySaverState.ON, + InteractiveState.INTERACTIVE, + DozeState.NOT_DOZING); + + target.advanceClock(5); + target.drainBattery(1); + + target.startCharging(); + + target.assertDumpable(); + + assertEquals( + "BS=0,I=0,D=0:{4m,10,150.00}\n" + + "BS=1,I=0,D=0:{0m,0,0.00}\n" + + "BS=0,I=1,D=0:{14m,8,34.29}\n" + + "BS=1,I=1,D=0:{9m,9,60.00}\n" + + "BS=0,I=0,D=1:{5m,1,12.00}\n" + + "BS=1,I=0,D=1:{0m,0,0.00}\n" + + "BS=0,I=1,D=1:{0m,0,0.00}\n" + + "BS=1,I=1,D=1:{0m,0,0.00}\n" + + "BS=0,I=0,D=2:{1m,2,120.00}\n" + + "BS=1,I=0,D=2:{0m,0,0.00}\n" + + "BS=0,I=1,D=2:{0m,0,0.00}\n" + + "BS=1,I=1,D=2:{0m,0,0.00}", + target.toDebugString()); + } +} diff --git a/services/tests/servicestests/src/com/android/server/wm/DimmerTests.java b/services/tests/servicestests/src/com/android/server/wm/DimmerTests.java index 70906dfbdb14..396fef40466f 100644 --- a/services/tests/servicestests/src/com/android/server/wm/DimmerTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/DimmerTests.java @@ -16,11 +16,14 @@ package com.android.server.wm; -import java.util.HashMap; - -import org.junit.Test; -import org.junit.Before; -import org.junit.runner.RunWith; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; import android.graphics.Rect; import android.platform.test.annotations.Presubmit; @@ -28,22 +31,25 @@ import android.support.test.runner.AndroidJUnit4; import android.view.SurfaceControl; import android.view.SurfaceSession; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertEquals; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.eq; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.stubbing.Answer; /** * Build/Install/Run: - * bit FrameworksServicesTests:com.android.server.wm.DimmerTests; + * atest FrameworksServicesTests:com.android.server.wm.DimmerTests; */ @Presubmit +@Ignore("b/72450130") @RunWith(AndroidJUnit4.class) public class DimmerTests extends WindowTestsBase { + + public DimmerTests() { + super(spy(new SurfaceAnimationRunner())); + } + private class TestWindowContainer extends WindowContainer<TestWindowContainer> { final SurfaceControl mControl = mock(SurfaceControl.class); final SurfaceControl.Transaction mTransaction = mock(SurfaceControl.Transaction.class); @@ -65,12 +71,14 @@ public class DimmerTests extends WindowTestsBase { private class MockSurfaceBuildingContainer extends WindowContainer<TestWindowContainer> { final SurfaceSession mSession = new SurfaceSession(); - SurfaceControl mBuiltSurface = null; - final SurfaceControl mHostControl = mock(SurfaceControl.class); final SurfaceControl.Transaction mHostTransaction = mock(SurfaceControl.Transaction.class); MockSurfaceBuildingContainer() { super(sWm); + mSurfaceControl = sWm.makeSurfaceBuilder(mSession) + .setName("test surface") + .setSize(1, 1) + .build(); } class MockSurfaceBuilder extends SurfaceControl.Builder { @@ -80,21 +88,23 @@ public class DimmerTests extends WindowTestsBase { @Override public SurfaceControl build() { - SurfaceControl sc = mock(SurfaceControl.class); - mBuiltSurface = sc; - return sc; + return spy(sWm.makeSurfaceBuilder(mSession) + .setName("test surface") + .setSize(1, 1) + .build()); } } - @Override - SurfaceControl.Builder makeChildSurface(WindowContainer child) { - return new MockSurfaceBuilder(mSession); + SurfaceControl.Builder makeSurface() { + return sWm.makeSurfaceBuilder(mSession) + .setName("test surface") + .setSize(1, 1); } @Override - public SurfaceControl getSurfaceControl() { - return mHostControl; + SurfaceControl.Builder makeChildSurface(WindowContainer child) { + return new MockSurfaceBuilder(mSession); } @Override @@ -114,29 +124,37 @@ public class DimmerTests extends WindowTestsBase { mTransaction = mock(SurfaceControl.Transaction.class); mDimmer = new Dimmer(mHost); + + doAnswer((Answer<Void>) invocation -> { + Runnable runnable = invocation.getArgument(3); + runnable.run(); + return null; + }).when(sWm.mSurfaceAnimationRunner).startAnimation(any(), any(), any(), any()); } @Test public void testDimAboveNoChildCreatesSurface() throws Exception { final float alpha = 0.8f; mDimmer.dimAbove(mTransaction, alpha); - assertNotNull("Dimmer should have created a surface", mHost.mBuiltSurface); - verify(mTransaction).setAlpha(mHost.mBuiltSurface, alpha); - verify(mTransaction).show(mHost.mBuiltSurface); - verify(mTransaction).setLayer(mHost.mBuiltSurface, Integer.MAX_VALUE); + SurfaceControl dimLayer = getDimLayer(null); + + assertNotNull("Dimmer should have created a surface", dimLayer); + + verify(mTransaction).setAlpha(dimLayer, alpha); + verify(mTransaction).setLayer(dimLayer, Integer.MAX_VALUE); } @Test public void testDimAboveNoChildRedundantlyUpdatesAlphaOnExistingSurface() throws Exception { float alpha = 0.8f; mDimmer.dimAbove(mTransaction, alpha); - final SurfaceControl firstSurface = mHost.mBuiltSurface; + final SurfaceControl firstSurface = getDimLayer(null); alpha = 0.9f; mDimmer.dimAbove(mTransaction, alpha); - assertEquals(firstSurface, mHost.mBuiltSurface); + assertEquals(firstSurface, getDimLayer(null)); verify(mTransaction).setAlpha(firstSurface, 0.9f); } @@ -148,16 +166,20 @@ public class DimmerTests extends WindowTestsBase { int height = 300; Rect bounds = new Rect(0, 0, width, height); mDimmer.updateDims(mTransaction, bounds); - verify(mTransaction).setSize(mHost.mBuiltSurface, width, height); + + verify(mTransaction).setSize(getDimLayer(null), width, height); + verify(mTransaction).show(getDimLayer(null)); } @Test public void testDimAboveNoChildNotReset() throws Exception { mDimmer.dimAbove(mTransaction, 0.8f); + SurfaceControl dimLayer = getDimLayer(null); mDimmer.resetDimStates(); mDimmer.updateDims(mTransaction, new Rect()); - verify(mHost.mBuiltSurface, never()).destroy(); + verify(mTransaction).show(getDimLayer(null)); + verify(dimLayer, never()).destroy(); } @Test @@ -167,11 +189,12 @@ public class DimmerTests extends WindowTestsBase { final float alpha = 0.8f; mDimmer.dimAbove(mTransaction, child, alpha); - assertNotNull("Dimmer should have created a surface", mHost.mBuiltSurface); + SurfaceControl mDimLayer = getDimLayer(child); + + assertNotNull("Dimmer should have created a surface", mDimLayer); - verify(mTransaction).setAlpha(mHost.mBuiltSurface, alpha); - verify(mTransaction).show(mHost.mBuiltSurface); - verify(mTransaction).setRelativeLayer(mHost.mBuiltSurface, child.mControl, 1); + verify(mTransaction).setAlpha(mDimLayer, alpha); + verify(mTransaction).setRelativeLayer(mDimLayer, child.mControl, 1); } @Test @@ -181,11 +204,12 @@ public class DimmerTests extends WindowTestsBase { final float alpha = 0.8f; mDimmer.dimBelow(mTransaction, child, alpha); - assertNotNull("Dimmer should have created a surface", mHost.mBuiltSurface); + SurfaceControl mDimLayer = getDimLayer(child); - verify(mTransaction).setAlpha(mHost.mBuiltSurface, alpha); - verify(mTransaction).show(mHost.mBuiltSurface); - verify(mTransaction).setRelativeLayer(mHost.mBuiltSurface, child.mControl, -1); + assertNotNull("Dimmer should have created a surface", mDimLayer); + + verify(mTransaction).setAlpha(mDimLayer, alpha); + verify(mTransaction).setRelativeLayer(mDimLayer, child.mControl, -1); } @Test @@ -195,9 +219,11 @@ public class DimmerTests extends WindowTestsBase { final float alpha = 0.8f; mDimmer.dimAbove(mTransaction, child, alpha); + SurfaceControl dimLayer = getDimLayer(child); mDimmer.resetDimStates(); + mDimmer.updateDims(mTransaction, new Rect()); - verify(mHost.mBuiltSurface).destroy(); + verify(dimLayer).destroy(); } @Test @@ -207,10 +233,16 @@ public class DimmerTests extends WindowTestsBase { final float alpha = 0.8f; mDimmer.dimAbove(mTransaction, child, alpha); + SurfaceControl dimLayer = getDimLayer(child); mDimmer.resetDimStates(); mDimmer.dimAbove(mTransaction, child, alpha); mDimmer.updateDims(mTransaction, new Rect()); - verify(mHost.mBuiltSurface, never()).destroy(); + verify(mTransaction).show(dimLayer); + verify(dimLayer, never()).destroy(); + } + + private SurfaceControl getDimLayer(WindowContainer windowContainer) { + return mDimmer.mDimLayerUsers.get(windowContainer).mDimLayer; } } diff --git a/services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java b/services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java index 897be34e36fe..f860195bd6ea 100644 --- a/services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java @@ -20,6 +20,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; import static org.junit.Assert.assertEquals; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; import android.graphics.Point; import android.graphics.Rect; @@ -134,4 +135,10 @@ public class RemoteAnimationControllerTest extends WindowTestsBase { verify(mMockRunner).onAnimationCancelled(); verify(mFinishedCallback).onAnimationFinished(eq(adapter)); } + + @Test + public void testZeroAnimations() throws Exception { + mController.goodToGo(); + verifyZeroInteractions(mMockRunner); + } } diff --git a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java index 35ca493e909f..81fd889c82c2 100644 --- a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java +++ b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java @@ -27,6 +27,7 @@ import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.any; import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.spy; import android.os.PowerSaveState; import android.util.proto.ProtoOutputStream; @@ -68,6 +69,11 @@ class TestWindowManagerPolicy implements WindowManagerPolicy { private Runnable mRunnableWhenAddingSplashScreen; static synchronized WindowManagerService getWindowManagerService(Context context) { + return getWindowManagerService(context, new SurfaceAnimationRunner()); + } + + static synchronized WindowManagerService getWindowManagerService(Context context, + SurfaceAnimationRunner surfaceAnimationRunner) { if (sWm == null) { // We only want to do this once for the test process as we don't want WM to try to // register a bunch of local services again. @@ -105,7 +111,7 @@ class TestWindowManagerPolicy implements WindowManagerPolicy { } sWm = WindowManagerService.main(context, ims, true, false, - false, new TestWindowManagerPolicy()); + false, new TestWindowManagerPolicy(), surfaceAnimationRunner); } return sWm; } diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java index 69b13787ef93..7918901f7e2d 100644 --- a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java +++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java @@ -84,6 +84,16 @@ class WindowTestsBase { WindowState mChildAppWindowBelow; HashSet<WindowState> mCommonWindows; + private final SurfaceAnimationRunner mSurfaceAnimationRunner; + + public WindowTestsBase() { + this(new SurfaceAnimationRunner()); + } + + public WindowTestsBase(SurfaceAnimationRunner surfaceAnimationRunner) { + mSurfaceAnimationRunner = surfaceAnimationRunner; + } + @Before public void setUp() throws Exception { if (!sOneTimeSetupDone) { @@ -98,7 +108,7 @@ class WindowTestsBase { final Context context = InstrumentationRegistry.getTargetContext(); AttributeCache.init(context); - sWm = TestWindowManagerPolicy.getWindowManagerService(context); + sWm = TestWindowManagerPolicy.getWindowManagerService(context, mSurfaceAnimationRunner); beforeCreateDisplay(); context.getDisplay().getDisplayInfo(mDisplayInfo); diff --git a/services/tests/uiservicestests/src/com/android/server/slice/PinnedSliceStateTest.java b/services/tests/uiservicestests/src/com/android/server/slice/PinnedSliceStateTest.java index d3bb80485dfb..1606bd9c6fdb 100644 --- a/services/tests/uiservicestests/src/com/android/server/slice/PinnedSliceStateTest.java +++ b/services/tests/uiservicestests/src/com/android/server/slice/PinnedSliceStateTest.java @@ -198,6 +198,7 @@ public class PinnedSliceStateTest extends UiServiceTestCase { when(binder.isBinderAlive()).thenReturn(false); arg.getValue().binderDied(); + verify(mSliceService).unlisten(eq(TEST_URI)); verify(mSliceService).removePinnedSlice(eq(TEST_URI)); assertFalse(mPinnedSliceManager.isPinned()); } diff --git a/telephony/java/android/telephony/ModemActivityInfo.java b/telephony/java/android/telephony/ModemActivityInfo.java index 03ce2d8e6f6d..521adef8497f 100644 --- a/telephony/java/android/telephony/ModemActivityInfo.java +++ b/telephony/java/android/telephony/ModemActivityInfo.java @@ -36,12 +36,12 @@ public class ModemActivityInfo implements Parcelable { */ public static final int TX_POWER_LEVELS = 5; - private final long mTimestamp; - private final int mSleepTimeMs; - private final int mIdleTimeMs; - private final int [] mTxTimeMs = new int[TX_POWER_LEVELS]; - private final int mRxTimeMs; - private final int mEnergyUsed; + private long mTimestamp; + private int mSleepTimeMs; + private int mIdleTimeMs; + private int [] mTxTimeMs = new int[TX_POWER_LEVELS]; + private int mRxTimeMs; + private int mEnergyUsed; public ModemActivityInfo(long timestamp, int sleepTimeMs, int idleTimeMs, int[] txTimeMs, int rxTimeMs, int energyUsed) { @@ -110,6 +110,10 @@ public class ModemActivityInfo implements Parcelable { return mTimestamp; } + public void setTimestamp(long timestamp) { + mTimestamp = timestamp; + } + /** * @return tx time in ms. It's an array of tx times * with each index... @@ -118,6 +122,10 @@ public class ModemActivityInfo implements Parcelable { return mTxTimeMs; } + public void setTxTimeMillis(int[] txTimeMs) { + mTxTimeMs = txTimeMs; + } + /** * @return sleep time in ms. */ @@ -125,6 +133,10 @@ public class ModemActivityInfo implements Parcelable { return mSleepTimeMs; } + public void setSleepTimeMillis(int sleepTimeMillis) { + mSleepTimeMs = sleepTimeMillis; + } + /** * @return idle time in ms. */ @@ -132,6 +144,10 @@ public class ModemActivityInfo implements Parcelable { return mIdleTimeMs; } + public void setIdleTimeMillis(int idleTimeMillis) { + mIdleTimeMs = idleTimeMillis; + } + /** * @return rx time in ms. */ @@ -139,6 +155,10 @@ public class ModemActivityInfo implements Parcelable { return mRxTimeMs; } + public void setRxTimeMillis(int rxTimeMillis) { + mRxTimeMs = rxTimeMillis; + } + /** * product of current(mA), voltage(V) and time(ms) * @return energy used @@ -147,6 +167,10 @@ public class ModemActivityInfo implements Parcelable { return mEnergyUsed; } + public void setEnergyUsed(int energyUsed) { + mEnergyUsed = energyUsed; + } + /** * @return if the record is valid */ diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl index 2a2ff0cf3a6f..309bc80b8864 100644 --- a/wifi/java/android/net/wifi/IWifiManager.aidl +++ b/wifi/java/android/net/wifi/IWifiManager.aidl @@ -158,9 +158,6 @@ interface IWifiManager int getVerboseLoggingLevel(); - void enableAggressiveHandover(int enabled); - int getAggressiveHandover(); - void enableWifiConnectivityManager(boolean enabled); void disableEphemeralNetwork(String SSID, String packageName); diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java index 13b7c1ad6dae..897b1eaa2a64 100644 --- a/wifi/java/android/net/wifi/WifiManager.java +++ b/wifi/java/android/net/wifi/WifiManager.java @@ -3532,31 +3532,6 @@ public class WifiManager { } /** - * Set wifi Aggressive Handover. Called from developer settings. - * @hide - */ - public void enableAggressiveHandover(int enabled) { - try { - mService.enableAggressiveHandover(enabled); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - /** - * Get the WiFi Handover aggressiveness.This is used by settings - * to decide what to show within the picker. - * @hide - */ - public int getAggressiveHandover() { - try { - return mService.getAggressiveHandover(); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - /** * Removes all saved wifi networks. * * @hide |