Merge "[QS] Fix irritating animation for QS"
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 0000000..fc6302e
--- /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 0000000..98f2bd5
--- /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 682885b..bab2a85 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 @@
@LargeTest
@RunWith(AndroidJUnit4.class)
public class StaticLayoutPerfTest {
+ 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 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;
-
- 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;
- }
+ 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
@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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 0000000..dccb34b
--- /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 7185c99..e774f07 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -38296,6 +38296,7 @@
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 @@
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 @@
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 @@
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 ed3897c..396ff1a 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -2603,12 +2603,17 @@
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 27fa672..b32af02 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -1214,8 +1214,14 @@
// # 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 @@
// # 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 @@
// # 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 fd7ad88..e7d0c8b 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 @@
* 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 @@
* @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 @@
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 4d5ac6f..e2ce8b1 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -449,31 +449,6 @@
*/
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 6dcecf1..02be002 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -456,11 +456,10 @@
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 f90b276..198bce6 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -1879,8 +1879,8 @@
*/
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 @@
.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 3b4511e..131abb5 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -8261,7 +8261,7 @@
}
/**
- * 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 @@
/**
* 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 @@
}
/**
+ * 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 7fc31b1..cba9311 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -413,4 +413,6 @@
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 7b27fc0..b3c8737 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -7057,7 +7057,7 @@
*
* @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 @@
* @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 @@
* @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 @@
* @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 @@
* @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 @@
* @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 @@
* @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 @@
* @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 @@
* @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 @@
*
* @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 @@
*
* @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 @@
*
* @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 @@
*
* @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 @@
*
* @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 @@
*
* @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 @@
*
* @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 @@
*
* @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 @@
*
* @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 @@
*
* @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 @@
*
* @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 @@
*
* @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 @@
*
* @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 @@
*
* @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 @@
*
* @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 @@
*
* @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 @@
*
* @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 @@
*
* @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 @@
*
* @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 @@
*
* @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 @@
*
* @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 @@
* @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 f84ec65..a748f4d 100644
--- a/core/java/android/database/CursorWindow.java
+++ b/core/java/android/database/CursorWindow.java
@@ -28,6 +28,7 @@
import android.util.LongSparseArray;
import android.util.SparseIntArray;
+import dalvik.annotation.optimization.FastNative;
import dalvik.system.CloseGuard;
/**
@@ -62,28 +63,43 @@
private static native void nativeDispose(long windowPtr);
private static native void nativeWriteToParcel(long windowPtr, Parcel parcel);
- private static native void nativeClear(long windowPtr);
-
- private static native int nativeGetNumRows(long windowPtr);
- private static native boolean nativeSetNumColumns(long windowPtr, int columnNum);
- private static native boolean nativeAllocRow(long windowPtr);
- private static native void nativeFreeLastRow(long windowPtr);
-
- private static native int nativeGetType(long windowPtr, int row, int column);
+ 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 long nativeGetLong(long windowPtr, int row, int column);
- 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);
+ 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);
+ @FastNative
+ private static native long nativeGetLong(long windowPtr, int row, int column);
+ @FastNative
+ private static native double nativeGetDouble(long windowPtr, 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 7fb0c89..7297426 100644
--- a/core/java/android/hardware/Sensor.java
+++ b/core/java/android/hardware/Sensor.java
@@ -22,7 +22,9 @@
/**
* 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 13b9206..2306e5f 100644
--- a/core/java/android/inputmethodservice/KeyboardView.java
+++ b/core/java/android/inputmethodservice/KeyboardView.java
@@ -61,6 +61,7 @@
* @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 f3a95b9..a86237d 100644
--- a/core/java/android/os/BatteryManagerInternal.java
+++ b/core/java/android/os/BatteryManagerInternal.java
@@ -24,26 +24,63 @@
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 738eb68..c4b7715 100644
--- a/core/java/android/security/IKeystoreService.aidl
+++ b/core/java/android/security/IKeystoreService.aidl
@@ -71,7 +71,7 @@
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 3464370..479231d 100644
--- a/core/java/android/security/keymaster/KeymasterDefs.java
+++ b/core/java/android/security/keymaster/KeymasterDefs.java
@@ -74,6 +74,7 @@
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 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 @@
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 45fbf6f..aafcf44 100644
--- a/core/java/android/text/MeasuredParagraph.java
+++ b/core/java/android/text/MeasuredParagraph.java
@@ -672,6 +672,13 @@
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 @@
@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 ff23395..bb7a9e0 100644
--- a/core/java/android/text/MeasuredText.java
+++ b/core/java/android/text/MeasuredText.java
@@ -333,6 +333,20 @@
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 25a177e..8af4b71 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -42,7 +42,6 @@
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 84d1850..a4da05f 100644
--- a/core/java/android/widget/MediaControlView2.java
+++ b/core/java/android/widget/MediaControlView2.java
@@ -26,7 +26,6 @@
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 @@
* 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 @@
@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 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 @@
}
/**
- * 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 @@
* 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 @@
}
/**
- * 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.
+ * Changes the visibility state of an individual button. Default value is View.Visible.
*
- * @param next Listener for clicking next button
- * @param prev Listener for clicking previous button
+ * @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 setPrevNextListeners(View.OnClickListener next, View.OnClickListener prev) {
- mProvider.setPrevNextListeners_impl(next, prev);
- }
-
- /**
- * Hides the specified button from view.
- *
- * @param button the constant integer assigned to individual buttons
- * @param visible whether the button should be visible or not
- */
- 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 997d47f..6e0ba341 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -841,7 +841,7 @@
}
@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 @@
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 86731bc..398d087 100644
--- a/core/java/com/android/internal/app/IntentForwarderActivity.java
+++ b/core/java/com/android/internal/app/IntentForwarderActivity.java
@@ -107,7 +107,7 @@
|| 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 d6d4490..ceb06f5 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.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 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 @@
}
@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 @@
* @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 e91b67a..7def876 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -10896,7 +10896,7 @@
return null;
}
- /**
+ /**
* Distribute WiFi energy info and network traffic to apps.
* @param info The energy information from the WiFi controller.
*/
@@ -11160,6 +11160,9 @@
}
}
+ 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 @@
}
}
+ 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 @@
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 @@
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 @@
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 @@
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 @@
}
}
+ // 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 @@
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 @@
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 @@
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 @@
}
}
- 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 @@
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 49cbb54..115d0d5 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 @@
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 @@
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 0d09b56..86cda44 100644
--- a/core/jni/android_database_CursorWindow.cpp
+++ b/core/jni/android_database_CursorWindow.cpp
@@ -519,8 +519,21 @@
(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 @@
(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 f0e449d..dddddbb 100644
--- a/core/jni/android_text_MeasuredParagraph.cpp
+++ b/core/jni/android_text_MeasuredParagraph.cpp
@@ -115,6 +115,10 @@
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 @@
// 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 36082ca..a0ba3ad 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 38f890a..a22ca87 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 e8ab0be..09d3121 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 c0bc3a8..b18fa74 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.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 @@
}
@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 @@
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 6c8aaf0..8addffbb 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -369,7 +369,6 @@
<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 ded427e..c429fd3 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -618,9 +618,9 @@
* @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 f721ed3..419eb24 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
@@ -243,12 +243,7 @@
// 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 @@
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 d1eb688..d68a33d 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
@@ -344,12 +344,7 @@
// 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 @@
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 440e086..fc86ca0 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java
@@ -497,12 +497,7 @@
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 @@
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 a896c72..0291b8a 100644
--- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
@@ -21,6 +21,7 @@
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 @@
* 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 @@
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 @@
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 @@
mUserAuthenticationValidWhileOnBody = userAuthenticationValidWhileOnBody;
mInvalidatedByBiometricEnrollment = invalidatedByBiometricEnrollment;
mIsStrongBoxBacked = isStrongBoxBacked;
+ mUnlockedDeviceRequired = unlockedDeviceRequired;
}
/**
@@ -646,6 +650,22 @@
}
/**
+ * 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 @@
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 @@
}
/**
+ * 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 @@
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 dbacb9c..a5b85ce 100644
--- a/keystore/java/android/security/keystore/KeyProtection.java
+++ b/keystore/java/android/security/keystore/KeyProtection.java
@@ -212,7 +212,7 @@
* ...
* }</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 @@
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 @@
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 @@
mInvalidatedByBiometricEnrollment = invalidatedByBiometricEnrollment;
mBoundToSecureUserId = boundToSecureUserId;
mCriticalToDeviceEncryption = criticalToDeviceEncryption;
+ mTrustedUserPresenceRequired = trustedUserPresenceRequired;
+ mUnlockedDeviceRequired = unlockedDeviceRequired;
}
/**
@@ -414,6 +420,14 @@
}
/**
+ * 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 @@
}
/**
+ * 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 @@
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 @@
}
/**
+ * 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 @@
}
/**
+ * 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 @@
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 34c8d1f..eb6a2a2 100644
--- a/keystore/java/android/security/keystore/KeymasterUtils.java
+++ b/keystore/java/android/security/keystore/KeymasterUtils.java
@@ -98,17 +98,23 @@
* 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 @@
}
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 @@
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 @@
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 0000000..6fb3486
--- /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 2ac4063..7533701 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -400,6 +400,18 @@
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 @@
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 6c65223..88d0a60 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -166,7 +166,8 @@
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 d36df84..d84eedf 100644
--- a/media/java/android/media/MediaPlayer2.java
+++ b/media/java/android/media/MediaPlayer2.java
@@ -96,22 +96,13 @@
* {@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 86a285c..222c66e 100644
--- a/media/java/android/media/MediaPlayer2Impl.java
+++ b/media/java/android/media/MediaPlayer2Impl.java
@@ -1960,6 +1960,13 @@
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 @@
stayAwake(false);
updateSurfaceScreenOn();
synchronized (mEventCbLock) {
- mEventCb = null;
- mEventExec = null;
+ mEventCallbackRecords.clear();
}
if (mTimeProvider != null) {
mTimeProvider.close();
@@ -3061,8 +3067,7 @@
// Modular DRM clean up
mOnDrmConfigHelper = null;
synchronized (mDrmEventCbLock) {
- mDrmEventCb = null;
- mDrmEventExec = null;
+ mDrmEventCallbackRecords.clear();
}
resetDrmState();
@@ -3118,18 +3123,8 @@
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 @@
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;
+ // The parcel was parsed already in postEventFromNative
+ final DrmInfoImpl drmInfo;
- synchronized (mDrmLock) {
- if (mDrmInfoImpl != null) {
- drmInfo = mDrmInfoImpl.makeCopy();
- } else {
- drmInfo = null;
- }
+ 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
}
- 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 @@
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 @@
@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 @@
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 @@
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 @@
@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 @@
// 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 @@
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 @@
} // 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 @@
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 7e88c27..d0bba6d 100644
--- a/media/java/android/media/audiopolicy/AudioPolicy.java
+++ b/media/java/android/media/audiopolicy/AudioPolicy.java
@@ -89,6 +89,8 @@
private AudioPolicyFocusListener mFocusListener;
+ private final AudioPolicyVolumeCallback mVolCb;
+
private Context mContext;
private AudioPolicyConfig mConfig;
@@ -99,12 +101,15 @@
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 @@
mFocusListener = fl;
mStatusListener = sl;
mIsFocusPolicy = isFocusPolicy;
+ mVolCb = vc;
}
/**
@@ -134,6 +140,7 @@
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 @@
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 @@
+ "an AudioPolicyFocusListener");
}
return new AudioPolicy(new AudioPolicyConfig(mMixes), mContext, mLooper,
- mFocusListener, mStatusListener, mIsFocusPolicy);
+ mFocusListener, mStatusListener, mIsFocusPolicy, mVolCb);
}
}
@@ -455,6 +478,23 @@
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 void notifyVolumeAdjust(int adjustment) {
+ sendMsg(MSG_VOL_ADJUST, null /* ignored */, adjustment);
+ if (DEBUG) {
+ Log.v(TAG, "notifyVolumeAdjust: " + adjustment);
+ }
+ }
};
//==================================================
@@ -528,6 +575,7 @@
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 @@
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 86abbb4..107e7cd 100644
--- a/media/java/android/media/audiopolicy/IAudioPolicyCallback.aidl
+++ b/media/java/android/media/audiopolicy/IAudioPolicyCallback.aidl
@@ -31,4 +31,7 @@
// 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 6b38c92..4464f8f 100644
--- a/media/java/android/media/update/MediaControlView2Provider.java
+++ b/media/java/android/media/update/MediaControlView2Provider.java
@@ -37,11 +37,8 @@
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 42ebf6a..ff854c5 100644
--- a/media/jni/android_media_MediaMetadataRetriever.cpp
+++ b/media/jni/android_media_MediaMetadataRetriever.cpp
@@ -68,17 +68,26 @@
}
}
-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 @@
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 @@
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_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 @@
{
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 @@
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 @@
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 @@
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 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 @@
{
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_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 fce5dd9..7728f66 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java
@@ -363,6 +363,26 @@
}
/**
+ * 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 5c73d54..3a0ae9f 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.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 @@
// 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 754b881..e11017c 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.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.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.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 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 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 @@
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 @@
*/
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 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 @@
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 @@
}
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 @@
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 @@
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 @@
*/
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 @@
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 @@
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 @@
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 @@
}
int rssi = UNREACHABLE_RSSI;
- for (ScanResult result : mScanResultCache.values()) {
+ for (ScanResult result : mScanResults) {
if (result.level > rssi) {
rssi = result.level;
}
@@ -853,7 +901,6 @@
}
if (WifiTracker.sVerboseLogging) {
- evictOldScanResults();
summary.append(WifiUtils.buildLoggingSummary(this, config));
}
@@ -950,28 +997,6 @@
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 @@
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 @@
}
/**
- * 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();
+ void setScanResults(Collection<ScanResult> scanResults) {
- /* 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);
- }
+ // 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 dd55188..109eb97 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 @@
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 3dec1d3..2993a0d 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.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 @@
private String mCarrierName = null;
Context mContext;
- private ArrayList<ScanResult> mScanResultCache;
+ private ArrayList<ScanResult> mScanResults;
private ArrayList<TimestampedScoredNetwork> mScoredNetworkCache;
@Keep
@@ -84,8 +85,9 @@
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 @@
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 c56e1da..1ac56a9 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.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 @@
*/
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 @@
= 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 @@
}
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 @@
/* 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 @@
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 @@
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 @@
scoresToRequest.add(key);
}
- 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;
- }
+ String apKey = AccessPoint.getKey(result);
+ List<ScanResult> resultList;
+ if (scanResultsByApKey.containsKey(apKey)) {
+ resultList = scanResultsByApKey.get(apKey);
+ } else {
+ resultList = new ArrayList<>();
+ scanResultsByApKey.put(apKey, resultList);
}
- if (!found && mIncludeScans) {
- AccessPoint accessPoint = getCachedOrCreate(result, cachedAccessPoints);
+
+ 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 : existingApMap.getAll(firstResult.SSID)) {
+ accessPoint.setScanResults(entry.getValue());
+ found = true;
+ break;
+ }
+
+ // 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 @@
}
accessPoints.add(accessPoint);
- apMap.put(accessPoint.getSsidStr(), accessPoint);
}
}
}
@@ -662,17 +699,18 @@
}
@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 932c6fd..fd48eea 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 @@
// 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 ec594a6..1440311 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.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 @@
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 @@
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 @@
}
@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 @@
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 @@
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 @@
.setActive(true)
.setNetworkId(networkId)
.setSsid(TEST_SSID)
- .setScanResultCache(scanResults)
+ .setScanResults(scanResults)
.setWifiInfo(info)
.build();
@@ -990,7 +995,7 @@
.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 @@
.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 6615b8c..b36dda9 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.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.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 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 @@
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 a4c821f..3fee16b 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 @@
}
@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 c5795d3..9310b73 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.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 @@
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 1fc36be..9613a6a 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 5f75f7d..21b0ed8 100644
--- a/packages/SystemUI/shared/Android.mk
+++ b/packages/SystemUI/shared/Android.mk
@@ -30,7 +30,7 @@
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 @@
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 8666b0c..1ae06d7 100644
--- a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
+++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
@@ -119,18 +119,10 @@
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 @@
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 085ece7..0000000
--- 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 ac22568..0000000
--- 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 77768b1..4d7333b 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 @@
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 @@
//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 907af69..11d20b2 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 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 @@
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 b519824..426268b 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 @@
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 e32914f..a79a41b 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.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 @@
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 8e0426a..0000000
--- 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 ba9883b..8265262 100644
--- a/services/core/java/com/android/server/BatteryService.java
+++ b/services/core/java/com/android/server/BatteryService.java
@@ -835,6 +835,9 @@
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 @@
}
@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 732ac66..219facd 100644
--- a/services/core/java/com/android/server/EventLogTags.logtags
+++ b/services/core/java/com/android/server/EventLogTags.logtags
@@ -34,6 +34,7 @@
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 3d7408e..2869114 100644
--- a/services/core/java/com/android/server/PinnerService.java
+++ b/services/core/java/com/android/server/PinnerService.java
@@ -372,15 +372,19 @@
@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 fffb4b5..f6f7d4b 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.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 @@
// 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 @@
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 @@
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 @@
}
}
} 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 @@
}
- /**
- * 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);
- }
+ 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) {
- 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;
- }
-
- @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 @@
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 @@
+ sourceRecord.launchedFromUid);
}
}
-
if (ignoreTargetSecurity) {
if (intent.getComponent() == null) {
throw new SecurityException(
@@ -14853,6 +14744,8 @@
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 ec8cf91..fd3f8ec 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 @@
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 f0b27a7..c188ccb 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 @@
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);
- 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);
- 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?
+ }
- // TODO: Maybe wait for app to draw in this particular case?
+ final ActivityRecord homeActivity = mStackSupervisor.getHomeActivity();
+ final ActivityDisplay display = homeActivity.getDisplay();
+
+ // Save the initial position of the home activity stack to be restored to after the
+ // animation completes
+ mRestoreHomeBehindStack = hasExistingHomeActivity
+ ? display.getStackAboveHome()
+ : null;
+
+ // Move the home activity into place for the animation
+ display.moveHomeStackBehindBottomMostVisibleStack();
+
+ // 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;
+
+ // Fetch all the surface controls and pass them to the client to get the animation
+ // started
+ mWindowManager.initializeRecentsAnimation(recentsAnimationRunner, this,
+ display.mDisplayId);
+
+ // 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();
}
-
- final ActivityRecord homeActivity = mStackSupervisor.getHomeActivity();
- final ActivityDisplay display = homeActivity.getDisplay();
-
- // Save the initial position of the home activity stack to be restored to after the
- // animation completes
- mRestoreHomeBehindStack = hasExistingHomeActivity
- ? display.getStackAboveHome()
- : null;
-
- // Move the home activity into place for the animation
- display.moveHomeStackBehindBottomMostVisibleStack();
-
- // 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;
-
- // Fetch all the surface controls and pass them to the client to get the animation
- // started
- mWindowManager.initializeRecentsAnimation(recentsAnimationRunner, this, display.mDisplayId);
-
- // 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);
}
@Override
@@ -128,35 +134,40 @@
if (mWindowManager.getRecentsAnimationController() == null) return;
mWindowManager.inSurfaceTransaction(() -> {
- mWindowManager.cleanupRecentsAnimation();
+ mWindowManager.deferSurfaceLayout();
+ try {
+ mWindowManager.cleanupRecentsAnimation();
- // Move the home stack to the front
- final ActivityRecord homeActivity = mStackSupervisor.getHomeActivity();
- if (homeActivity == null) {
- return;
+ // 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 f6bcb25..f247de7 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 @@
/** @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 @@
// 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 @@
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 @@
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 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 @@
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 @@
mMediaFocusControl.setFocusPolicy(mPolicyCallback);
}
}
+ if (mIsVolumeController) {
+ setExtVolumeController(mPolicyCallback);
+ }
connectMixes();
}
@@ -7220,6 +7254,11 @@
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 b5f94b1..25a2100 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 @@
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 bfc858f..0229a37 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 @@
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 0f394a4..0502848 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 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 @@
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 @@
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_NOSENSOR:
- // TODO Can sensor be used to indirectly determine the orientation?
- return false;
-
- 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
- case ActivityInfo.SCREEN_ORIENTATION_USER:
- // 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
+ 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:
+ // 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);
}
+
+ 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 941cd44..efcadad 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 @@
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 @@
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 @@
@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 a538967..847c90a 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 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 @@
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 d4627c2..32f38b7 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.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.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 @@
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 @@
@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 @@
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 @@
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 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 @@
enabled = mEnabled;
mIsInteractive = isInteractive;
-
if (enabled) {
fileValues = mBatterySaverPolicy.getFileValues(isInteractive);
} else {
@@ -293,6 +318,8 @@
pmi.powerHint(PowerHint.LOW_POWER, enabled ? 1 : 0);
}
+ updateBatterySavingStats();
+
if (ArrayUtils.isEmpty(fileValues)) {
mFileUpdater.restoreDefault();
} else {
@@ -332,7 +359,6 @@
mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
Manifest.permission.DEVICE_POWER);
-
for (LowPowerModeListener listener : listeners) {
final PowerSaveState result =
mBatterySaverPolicy.getBatterySaverPolicy(
@@ -388,4 +414,28 @@
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 0000000..df4f8ec
--- /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 5811714..192fd63 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 @@
ContentProviderClient getClient() {
ContentProviderClient client =
mService.getContext().getContentResolver().acquireContentProviderClient(mUri);
+ if (client == null) return null;
client.setDetectNotResponding(SLICE_TIMEOUT);
return client;
}
@@ -181,6 +182,7 @@
}
if (!isPinned()) {
// All the listeners died, remove from pinned state.
+ mService.unlisten(mUri);
mService.removePinnedSlice(mUri);
}
}
@@ -210,6 +212,7 @@
}
if (!isPinned()) {
// All the listeners died, remove from pinned state.
+ mService.unlisten(mUri);
mService.removePinnedSlice(mUri);
}
}
@@ -217,6 +220,7 @@
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 @@
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 @@
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 9fe16ae..b435605 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 DimState {
- SurfaceControl mSurfaceControl;
+ 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();
+ }
+ }
+
+ @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 @@
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 @@
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 @@
*/
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 @@
* 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 @@
/**
* 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 @@
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 @@
* 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 @@
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 3f49f0c..7674b5e 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 @@
}
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 @@
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 7d4eafb..8269a3b 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 @@
* 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 0628436..7d970d9 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 @@
} else {
mStack.getBounds(mTmpRect);
mTmpRect.intersect(getBounds());
+ out.set(mTmpRect);
}
- out.set(mTmpRect);
} else {
out.set(getBounds());
}
@@ -640,6 +640,7 @@
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 bc0f9ad..cf54b67 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 @@
|| 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 a1026bd..0c0ce0e 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 @@
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 4fb2390..066e4e6 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 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 @@
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 @@
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 477dd2b..55c982c 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 @@
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 @@
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 @@
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 5529426..d1cc5de 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 @@
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 0589790..dae7605 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 @@
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 @@
}
}
+ @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 6b87ea9..00a85a5 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 @@
() -> 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 0000000..f788cac
--- /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 70906df..396fef4 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.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 @@
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,12 +88,19 @@
@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 makeSurface() {
+ return sWm.makeSurfaceBuilder(mSession)
+ .setName("test surface")
+ .setSize(1, 1);
+ }
@Override
SurfaceControl.Builder makeChildSurface(WindowContainer child) {
@@ -93,11 +108,6 @@
}
@Override
- public SurfaceControl getSurfaceControl() {
- return mHostControl;
- }
-
- @Override
public SurfaceControl.Transaction getPendingTransaction() {
return mHostTransaction;
}
@@ -114,29 +124,37 @@
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 @@
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 @@
final float alpha = 0.8f;
mDimmer.dimAbove(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
@@ -181,11 +204,12 @@
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 @@
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 @@
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 897be34..f860195 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 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 @@
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 35ca493..81fd889 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.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 @@
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 @@
}
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 69b1378..7918901 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 @@
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 @@
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 d3bb804..1606bd9 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 @@
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 03ce2d8..521adef 100644
--- a/telephony/java/android/telephony/ModemActivityInfo.java
+++ b/telephony/java/android/telephony/ModemActivityInfo.java
@@ -36,12 +36,12 @@
*/
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 @@
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 @@
return mTxTimeMs;
}
+ public void setTxTimeMillis(int[] txTimeMs) {
+ mTxTimeMs = txTimeMs;
+ }
+
/**
* @return sleep time in ms.
*/
@@ -125,6 +133,10 @@
return mSleepTimeMs;
}
+ public void setSleepTimeMillis(int sleepTimeMillis) {
+ mSleepTimeMs = sleepTimeMillis;
+ }
+
/**
* @return idle time in ms.
*/
@@ -132,6 +144,10 @@
return mIdleTimeMs;
}
+ public void setIdleTimeMillis(int idleTimeMillis) {
+ mIdleTimeMs = idleTimeMillis;
+ }
+
/**
* @return rx time in ms.
*/
@@ -139,6 +155,10 @@
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 @@
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 2a2ff0c..309bc80 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -158,9 +158,6 @@
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 13b7c1a..897b1ea 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -3532,31 +3532,6 @@
}
/**
- * 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